322 lines
10 KiB
JavaScript
322 lines
10 KiB
JavaScript
const $ = (id) => document.getElementById(id);
|
|
|
|
function setStatus(msg, danger = false) {
|
|
const el = $("status");
|
|
if (!el) return;
|
|
el.style.color = danger ? "#b42318" : "#0f5f3d";
|
|
el.textContent = msg;
|
|
}
|
|
|
|
function setLoading(button, loading, idleText, loadingText) {
|
|
if (!button) return;
|
|
button.disabled = loading;
|
|
button.textContent = loading ? loadingText : idleText;
|
|
}
|
|
|
|
async function postJSON(url, body) {
|
|
const res = await fetch(url, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(body),
|
|
});
|
|
const data = await res.json();
|
|
if (!res.ok) throw new Error(data.detail || "请求失败");
|
|
return data;
|
|
}
|
|
|
|
async function authMe() {
|
|
const res = await fetch("/api/auth/me");
|
|
const data = await res.json();
|
|
if (!data.logged_in) {
|
|
window.location.href = "/auth?next=/settings";
|
|
return null;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
function renderAccounts(me) {
|
|
const sel = $("accountSelect");
|
|
if (!sel) return;
|
|
const list = Array.isArray(me.wechat_accounts) ? me.wechat_accounts : [];
|
|
const active = me.active_wechat_account && me.active_wechat_account.id ? Number(me.active_wechat_account.id) : 0;
|
|
sel.innerHTML = "";
|
|
if (!list.length) {
|
|
const opt = document.createElement("option");
|
|
opt.value = "";
|
|
opt.textContent = "未绑定公众号";
|
|
sel.appendChild(opt);
|
|
return;
|
|
}
|
|
list.forEach((a) => {
|
|
const opt = document.createElement("option");
|
|
opt.value = String(a.id);
|
|
opt.textContent = `${a.account_name} (${a.appid})`;
|
|
if ((active && a.id === active) || a.active) opt.selected = true;
|
|
sel.appendChild(opt);
|
|
});
|
|
}
|
|
|
|
function renderModels(me) {
|
|
const sel = $("modelSelect");
|
|
if (!sel) return;
|
|
const list = Array.isArray(me.ai_models) ? me.ai_models : [];
|
|
const active = me.active_ai_model && me.active_ai_model.id ? Number(me.active_ai_model.id) : 0;
|
|
sel.innerHTML = "";
|
|
if (!list.length) {
|
|
const opt = document.createElement("option");
|
|
opt.value = "";
|
|
opt.textContent = "暂无模型配置,请先新增";
|
|
sel.appendChild(opt);
|
|
return;
|
|
}
|
|
list.forEach((m) => {
|
|
const opt = document.createElement("option");
|
|
opt.value = String(m.id);
|
|
opt.textContent = `${m.model_name} (${m.model})`;
|
|
if ((active && m.id === active) || m.active) opt.selected = true;
|
|
sel.appendChild(opt);
|
|
});
|
|
}
|
|
|
|
async function refresh() {
|
|
const me = await authMe();
|
|
if (!me) return;
|
|
renderAccounts(me);
|
|
renderModels(me);
|
|
}
|
|
|
|
const accountSelect = $("accountSelect");
|
|
const bindBtn = $("bindBtn");
|
|
const deleteWechatBtn = $("deleteWechatBtn");
|
|
const logoutBtn = $("logoutBtn");
|
|
const changePwdBtn = $("changePwdBtn");
|
|
const deleteAccountBtn = $("deleteAccountBtn");
|
|
const modelSelect = $("modelSelect");
|
|
const saveModelBtn = $("saveModelBtn");
|
|
const deleteModelBtn = $("deleteModelBtn");
|
|
|
|
if (accountSelect) {
|
|
accountSelect.addEventListener("change", async () => {
|
|
const id = Number(accountSelect.value || 0);
|
|
if (!id) return;
|
|
try {
|
|
const out = await postJSON("/api/auth/wechat/switch", { account_id: id });
|
|
if (!out.ok) {
|
|
setStatus(out.detail || "切换失败", true);
|
|
return;
|
|
}
|
|
setStatus("已切换当前公众号。");
|
|
await refresh();
|
|
} catch (e) {
|
|
setStatus(e.message || "切换失败", true);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (deleteWechatBtn) {
|
|
deleteWechatBtn.addEventListener("click", async () => {
|
|
const id = Number((accountSelect && accountSelect.value) || 0);
|
|
if (!id) {
|
|
setStatus("请先选择要删除的公众号", true);
|
|
return;
|
|
}
|
|
const sure = window.confirm("确定删除当前公众号绑定吗?删除后不可恢复。");
|
|
if (!sure) return;
|
|
setLoading(deleteWechatBtn, true, "删除当前公众号", "删除中...");
|
|
try {
|
|
const out = await postJSON("/api/auth/wechat/delete", { account_id: id });
|
|
if (!out.ok) {
|
|
setStatus(out.detail || "删除失败", true);
|
|
return;
|
|
}
|
|
setStatus("公众号账号已删除。");
|
|
await refresh();
|
|
} catch (e) {
|
|
setStatus(e.message || "删除失败", true);
|
|
} finally {
|
|
setLoading(deleteWechatBtn, false, "删除当前公众号", "删除中...");
|
|
}
|
|
});
|
|
}
|
|
|
|
if (bindBtn) {
|
|
bindBtn.addEventListener("click", async () => {
|
|
setLoading(bindBtn, true, "绑定并设为当前账号", "绑定中...");
|
|
try {
|
|
const out = await postJSON("/api/auth/wechat/bind", {
|
|
account_name: ($("accountName") && $("accountName").value.trim()) || "",
|
|
appid: ($("appid") && $("appid").value.trim()) || "",
|
|
secret: ($("secret") && $("secret").value.trim()) || "",
|
|
author: "",
|
|
thumb_media_id: "",
|
|
thumb_image_path: "",
|
|
});
|
|
if (!out.ok) {
|
|
setStatus(out.detail || "绑定失败", true);
|
|
return;
|
|
}
|
|
setStatus("公众号绑定成功,已切换为当前账号。");
|
|
if ($("appid")) $("appid").value = "";
|
|
if ($("secret")) $("secret").value = "";
|
|
await refresh();
|
|
} catch (e) {
|
|
setStatus(e.message || "绑定失败", true);
|
|
} finally {
|
|
setLoading(bindBtn, false, "绑定并设为当前账号", "绑定中...");
|
|
}
|
|
});
|
|
}
|
|
|
|
if (modelSelect) {
|
|
modelSelect.addEventListener("change", async () => {
|
|
const id = Number(modelSelect.value || 0);
|
|
if (!id) return;
|
|
try {
|
|
const out = await postJSON("/api/auth/ai-models/switch", { model_id: id });
|
|
if (!out.ok) {
|
|
setStatus(out.detail || "模型切换失败", true);
|
|
return;
|
|
}
|
|
setStatus("已切换当前模型。");
|
|
await refresh();
|
|
} catch (e) {
|
|
setStatus(e.message || "模型切换失败", true);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (saveModelBtn) {
|
|
saveModelBtn.addEventListener("click", async () => {
|
|
setLoading(saveModelBtn, true, "保存并设为当前模型", "保存中...");
|
|
try {
|
|
const out = await postJSON("/api/auth/ai-models/add", {
|
|
model_name: ($("modelName") && $("modelName").value.trim()) || "",
|
|
api_key: ($("apiKey") && $("apiKey").value.trim()) || "",
|
|
base_url: ($("baseUrl") && $("baseUrl").value.trim()) || "",
|
|
model: ($("modelValue") && $("modelValue").value.trim()) || "",
|
|
timeout_sec: Number((($("timeoutSec") && $("timeoutSec").value) || "120").trim()),
|
|
max_output_tokens: Number((($("maxOutputTokens") && $("maxOutputTokens").value) || "8192").trim()),
|
|
max_retries: Number((($("maxRetries") && $("maxRetries").value) || "0").trim()),
|
|
});
|
|
if (!out.ok) {
|
|
setStatus(out.detail || "模型保存失败", true);
|
|
return;
|
|
}
|
|
setStatus("模型配置已保存并设为当前。");
|
|
if ($("apiKey")) $("apiKey").value = "";
|
|
if ($("modelName")) $("modelName").value = "";
|
|
await refresh();
|
|
} catch (e) {
|
|
setStatus(e.message || "模型保存失败", true);
|
|
} finally {
|
|
setLoading(saveModelBtn, false, "保存并设为当前模型", "保存中...");
|
|
}
|
|
});
|
|
}
|
|
|
|
if (deleteModelBtn) {
|
|
deleteModelBtn.addEventListener("click", async () => {
|
|
const id = Number((modelSelect && modelSelect.value) || 0);
|
|
if (!id) {
|
|
setStatus("请先选择要删除的模型", true);
|
|
return;
|
|
}
|
|
const sure = window.confirm("确定删除当前模型配置吗?删除后不可恢复。");
|
|
if (!sure) return;
|
|
setLoading(deleteModelBtn, true, "删除当前模型", "删除中...");
|
|
try {
|
|
const out = await postJSON("/api/auth/ai-models/delete", { model_id: id });
|
|
if (!out.ok) {
|
|
setStatus(out.detail || "模型删除失败", true);
|
|
return;
|
|
}
|
|
setStatus("模型配置已删除。");
|
|
await refresh();
|
|
} catch (e) {
|
|
setStatus(e.message || "模型删除失败", true);
|
|
} finally {
|
|
setLoading(deleteModelBtn, false, "删除当前模型", "删除中...");
|
|
}
|
|
});
|
|
}
|
|
|
|
if (logoutBtn) {
|
|
logoutBtn.addEventListener("click", async () => {
|
|
setLoading(logoutBtn, true, "退出登录", "退出中...");
|
|
try {
|
|
await postJSON("/api/auth/logout", {});
|
|
window.location.href = "/auth?next=/";
|
|
} catch (e) {
|
|
setStatus(e.message || "退出失败", true);
|
|
} finally {
|
|
setLoading(logoutBtn, false, "退出登录", "退出中...");
|
|
}
|
|
});
|
|
}
|
|
|
|
if (changePwdBtn) {
|
|
changePwdBtn.addEventListener("click", async () => {
|
|
setLoading(changePwdBtn, true, "修改密码", "提交中...");
|
|
try {
|
|
const out = await postJSON("/api/auth/password/change", {
|
|
old_password: ($("oldPassword") && $("oldPassword").value) || "",
|
|
new_password: ($("newPassword") && $("newPassword").value) || "",
|
|
});
|
|
if (!out.ok) {
|
|
setStatus(out.detail || "修改密码失败", true);
|
|
return;
|
|
}
|
|
setStatus("密码修改成功。");
|
|
if ($("oldPassword")) $("oldPassword").value = "";
|
|
if ($("newPassword")) $("newPassword").value = "";
|
|
} catch (e) {
|
|
setStatus(e.message || "修改密码失败", true);
|
|
} finally {
|
|
setLoading(changePwdBtn, false, "修改密码", "提交中...");
|
|
}
|
|
});
|
|
}
|
|
|
|
if (deleteAccountBtn) {
|
|
deleteAccountBtn.addEventListener("click", async () => {
|
|
const pwd = ($("deletePassword") && $("deletePassword").value) || "";
|
|
const rkey = ($("deleteResetKey") && $("deleteResetKey").value.trim()) || "";
|
|
if (!pwd) {
|
|
setStatus("请输入注销校验密码", true);
|
|
return;
|
|
}
|
|
if (!rkey) {
|
|
setStatus("请输入注销校验重置码", true);
|
|
return;
|
|
}
|
|
const sure = window.confirm("确定注销账户吗?将清空此账号所有业务数据,操作不可恢复。");
|
|
if (!sure) return;
|
|
const confirmText = window.prompt("为防止误删,请输入「注销账户」后确认:", "");
|
|
if ((confirmText || "").trim() !== "注销账户") {
|
|
setStatus("二次确认未通过,已取消注销。", true);
|
|
return;
|
|
}
|
|
setLoading(deleteAccountBtn, true, "注销账户", "注销中...");
|
|
try {
|
|
const out = await postJSON("/api/auth/account/delete", {
|
|
password: pwd,
|
|
reset_key: rkey,
|
|
});
|
|
if (!out.ok) {
|
|
setStatus(out.detail || "注销失败", true);
|
|
return;
|
|
}
|
|
setStatus("账号已注销,正在返回登录页。");
|
|
window.setTimeout(() => {
|
|
window.location.href = "/auth?next=/";
|
|
}, 900);
|
|
} catch (e) {
|
|
setStatus(e.message || "注销失败", true);
|
|
} finally {
|
|
setLoading(deleteAccountBtn, false, "注销账户", "注销中...");
|
|
}
|
|
});
|
|
}
|
|
|
|
refresh();
|