This commit is contained in:
qsc
2026-09-07 14:31:57 +08:00
parent 2cf54a5e6c
commit 670164b804
11 changed files with 658 additions and 202 deletions
+144 -9
View File
@@ -6,18 +6,20 @@ const state = {
kungfu: [],
servers: [],
events: {},
free_event_actions: [],
session_control: { mode: "all", entries: [] },
legacy_bilei: [],
token_stats: null,
cache: {
defaults: { api: 300, image: 300 },
limits: { api_memory_entries: 256, image_max_mb: 512 },
limits: { api_memory_max_mb: 16, api_max_entries: 1024, image_max_mb: 512 },
api: [],
images: [],
stats: {},
},
};
const editing = { bindingSession: null, controlSession: null, aliasServer: null, kungfuPzid: null };
const editing = { bindingSession: null, controlSession: null, aliasServer: null, kungfuPzid: null, subscriptionSession: null };
let subscriptionSaving = false;
const restoreConfirmationTimers = new WeakMap();
let toastTimer;
@@ -478,11 +480,63 @@ function renderLegacyBilei() {
}));
}
function openSubscriptionEditor(item = null) {
if (subscriptionSaving) return;
editing.subscriptionSession = item?.session_id ?? null;
const form = byId("subscription-form");
form.reset();
byId("subscription-editor-title").textContent = item ? "编辑推送配置" : "添加推送会话";
const sessionInput = byId("subscription-session");
sessionInput.value = item?.session_id || "";
sessionInput.readOnly = Boolean(item);
byId("subscription-enabled").checked = item?.enabled ?? false;
const selected = new Set(item?.actions || []);
const freeActions = new Set(state.free_event_actions);
const groups = [
{ title: "免费事件", free: true },
{ title: "令牌事件", free: false },
];
byId("subscription-events").replaceChildren(...groups.map((group) => {
const fieldset = document.createElement("fieldset");
fieldset.className = "subscription-event-group";
const legend = document.createElement("legend");
legend.textContent = group.title;
const grid = document.createElement("div");
grid.className = "subscription-event-grid";
Object.entries(state.events).forEach(([action, name]) => {
if (freeActions.has(Number(action)) !== group.free) return;
const label = document.createElement("label");
label.className = "subscription-choice";
const input = document.createElement("input");
input.type = "checkbox";
input.name = "subscription_action";
input.value = action;
input.checked = selected.has(Number(action));
const text = document.createElement("span");
text.textContent = `${action} ${name}`;
label.append(input, text);
grid.append(label);
});
fieldset.append(legend, grid);
return fieldset;
}));
updateSubscriptionSelectionCount();
form.hidden = false;
form.scrollIntoView({ block: "nearest" });
(item ? byId("subscription-enabled") : sessionInput).focus({ preventScroll: true });
}
function updateSubscriptionSelectionCount() {
const count = byId("subscription-events").querySelectorAll("input:checked").length;
byId("subscription-selection-count").textContent = `已选择 ${count}`;
}
function renderSubscriptions() {
const body = byId("subscriptions-body");
const bindings = bindingMap();
if (!state.subscriptions.length) {
body.replaceChildren(emptyRow(4, "暂无事件订阅会话"));
body.replaceChildren(emptyRow(5, "暂无事件订阅会话,点击“添加推送会话”开始配置"));
return;
}
body.replaceChildren(...state.subscriptions.map((item) => {
@@ -491,10 +545,14 @@ function renderSubscriptions() {
const server = document.createElement("td");
const enabled = document.createElement("td");
const actions = document.createElement("td");
const controls = document.createElement("td");
session.dataset.label = "会话 ID";
server.dataset.label = "绑定区服";
enabled.dataset.label = "总开关";
actions.dataset.label = "已订阅事件";
controls.dataset.label = "操作";
controls.className = "actions";
session.className = "subscription-session-cell";
session.textContent = item.session_id;
server.textContent = bindings.get(item.session_id) || "未绑定(全部区服)";
const stateLabel = document.createElement("span");
@@ -514,7 +572,34 @@ function renderSubscriptions() {
tags.textContent = "无";
}
actions.append(tags);
row.append(session, server, enabled, actions);
controls.append(
button("编辑", "", () => openSubscriptionEditor(item)),
button("删除", "link-button--danger", async (event) => {
if (subscriptionSaving) return;
const control = event.currentTarget;
control.disabled = true;
subscriptionSaving = true;
byId("subscription-fields").disabled = true;
byId("add-subscription").disabled = true;
try {
const deleted = await mutate(
"subscriptions/delete",
{ session_id: item.session_id },
"会话推送配置已删除",
);
if (deleted && editing.subscriptionSession === item.session_id) {
byId("subscription-form").hidden = true;
editing.subscriptionSession = null;
}
} finally {
subscriptionSaving = false;
control.disabled = false;
byId("subscription-fields").disabled = false;
byId("add-subscription").disabled = false;
}
}),
);
row.append(session, server, enabled, actions, controls);
return row;
}));
}
@@ -739,17 +824,20 @@ function renderCache() {
const stats = cache.stats || {};
const apiDefault = cache.defaults?.api ?? 300;
const imageDefault = cache.defaults?.image ?? 600;
const memoryLimit = cache.limits?.api_memory_entries ?? stats.api_memory_limit ?? 256;
const memoryLimitMb = cache.limits?.api_memory_max_mb ?? 16;
const apiEntryLimit = cache.limits?.api_max_entries ?? stats.api_entry_limit ?? 256;
const imageLimitMb = cache.limits?.image_max_mb ?? 512;
byId("api-cache-count").textContent = `${stats.api_count || 0}`;
byId("api-cache-count").textContent = `${stats.api_count || 0} / ${apiEntryLimit}`;
byId("api-cache-size").textContent = formatBytes(stats.api_size_bytes);
byId("image-cache-count").textContent = `${stats.image_count || 0}`;
byId("image-cache-size").textContent = `${formatBytes(stats.image_size_bytes)} / ${formatBytes(stats.image_limit_bytes)}`;
byId("api-default-ttl").value = String(apiDefault);
byId("image-default-ttl").value = String(imageDefault);
byId("api-memory-limit").value = String(memoryLimit);
byId("api-memory-size-limit").value = String(memoryLimitMb);
byId("api-entry-limit").value = String(apiEntryLimit);
byId("image-size-limit").value = String(imageLimitMb);
byId("api-memory-summary").textContent = `${stats.api_memory_count || 0} / ${memoryLimit}`;
byId("api-memory-summary").textContent = `${formatBytes(stats.api_memory_size_bytes)} / ${formatBytes(stats.api_memory_limit_bytes)}`;
byId("api-memory-count").textContent = `${stats.api_memory_count || 0} 条,最久未使用优先淘汰`;
byId("cache-default-summary").textContent = `${apiDefault} / ${imageDefault}`;
renderCacheTable("api");
renderCacheTable("image");
@@ -845,6 +933,52 @@ document.querySelectorAll(".tab").forEach((tab) => {
});
});
byId("add-subscription").addEventListener("click", () => openSubscriptionEditor());
byId("cancel-subscription").addEventListener("click", () => {
byId("subscription-form").hidden = true;
editing.subscriptionSession = null;
});
byId("subscription-events").addEventListener("change", updateSubscriptionSelectionCount);
byId("subscription-select-all").addEventListener("click", () => {
byId("subscription-events").querySelectorAll("input").forEach((input) => { input.checked = true; });
updateSubscriptionSelectionCount();
});
byId("subscription-clear-all").addEventListener("click", () => {
byId("subscription-events").querySelectorAll("input").forEach((input) => { input.checked = false; });
updateSubscriptionSelectionCount();
});
byId("subscription-form").addEventListener("submit", async (event) => {
event.preventDefault();
if (subscriptionSaving) return;
const form = event.currentTarget;
const sessionId = (editing.subscriptionSession ?? byId("subscription-session").value).trim();
if (!sessionId) {
showToast("会话 ID 不能为空", true);
byId("subscription-session").focus();
return;
}
const payload = {
session_id: sessionId,
enabled: byId("subscription-enabled").checked,
actions: [...byId("subscription-events").querySelectorAll("input:checked")].map((input) => Number(input.value)),
mode: editing.subscriptionSession === null ? "create" : "update",
};
subscriptionSaving = true;
byId("subscription-fields").disabled = true;
byId("add-subscription").disabled = true;
try {
const saved = await mutate("subscriptions/save", payload, "会话推送配置已保存");
if (saved) {
form.hidden = true;
editing.subscriptionSession = null;
}
} finally {
subscriptionSaving = false;
byId("subscription-fields").disabled = false;
byId("add-subscription").disabled = false;
}
});
byId("binding-form").addEventListener("submit", async (event) => {
event.preventDefault();
const saved = await mutate("bindings/save", {
@@ -929,7 +1063,8 @@ byId("cache-limit-form").addEventListener("submit", async (event) => {
submit.disabled = true;
try {
await bridge.apiPost("cache/limits/save", {
api_memory_entries: Number(byId("api-memory-limit").value),
api_memory_max_mb: Number(byId("api-memory-size-limit").value),
api_max_entries: Number(byId("api-entry-limit").value),
image_max_mb: Number(byId("image-size-limit").value),
});
await loadData();