119 lines
5.4 KiB
JavaScript
119 lines
5.4 KiB
JavaScript
// zcbot dev 控制台入口 + 编排:enterApp(应用初始化)、loadStorage(存储用量)、
|
|
// Esc 关弹窗栈、boot。功能逻辑全在各模块(chat/files/preview/media/auth/newtask/embed/layout/…),
|
|
// 经各模块顶层 import 拉入依赖图(layout/markdown/media 由 chat 等引入,副作用照常)。
|
|
import { state, EMBED, setIdentity, userDisplayName, userDisplayTitle } from "./state.js";
|
|
import { humanSize, fmtTime } from "./format.js";
|
|
import { $ } from "./dom.js";
|
|
import { api } from "./api.js";
|
|
import { closeChpwModal } from "./auth.js";
|
|
import { closeSkillsModal } from "./skills.js";
|
|
import { closeMemoryModal } from "./memory.js";
|
|
import { closeCronsModal } from "./crons.js";
|
|
import { closeWechatModal } from "./wechat.js";
|
|
import { closeFilePreview, closeMiniPreview } from "./preview.js";
|
|
import { closeSrcPicker, loadFiles } from "./files.js";
|
|
import { loadFolderSuggestions } from "./newtask.js";
|
|
import { embedInit } from "./embed.js";
|
|
import { loadTaskList, loadModels } from "./chat.js";
|
|
|
|
// ───── enter app ─────
|
|
export function enterApp() {
|
|
$("login").style.display = "none";
|
|
$("app").classList.add("ready");
|
|
renderWho(); // 顶栏用户:默认显 name(兜底 user_name/email/uid8),hover 显完整身份
|
|
loadTaskList();
|
|
loadFiles(); // 文件面板与 task 解耦 — 启动即拉 user_root
|
|
loadModels(); // 模型清单缓存:chat-meta 下拉 + 新建对话框 + 历史小标
|
|
loadFolderSuggestions(); // 灌 filter-wd select(modal 打开时会重拉,这里让左 pane 先有选项)
|
|
loadStorage(); // 顶栏存储用量(后台扫描快照,非实时)
|
|
loadRole(); // 拉 /v1/me,admin 才显「管理」入口(/static/admin.html)
|
|
}
|
|
|
|
// 顶栏用户名:默认显 name(兜底 user_name → email → uid8),title 悬浮给完整身份。
|
|
function renderWho() {
|
|
const el = $("hd-who");
|
|
if (!el) return;
|
|
el.textContent = userDisplayName();
|
|
el.title = userDisplayTitle();
|
|
}
|
|
|
|
// 当前用户身份 + 角色:/v1/me 返 {user_id, role, name, user_name, email}。
|
|
// admin → 显顶栏「管理」链接;并用服务端权威值校准顶栏用户名(platform_key 登录 / 老 token
|
|
// 升级后,name/user_name 这一刻才到齐)。失败静默(增量功能,拉不到就维持登录时的兜底值)。
|
|
async function loadRole() {
|
|
const link = $("hd-admin");
|
|
try {
|
|
const me = await api("GET", "/v1/me");
|
|
if (me) {
|
|
setIdentity({ user_id: me.user_id, name: me.name, user_name: me.user_name, email: me.email });
|
|
renderWho();
|
|
}
|
|
if (link) link.style.display = (me && me.role === "admin") ? "" : "none";
|
|
} catch (e) { if (link) link.style.display = "none"; }
|
|
}
|
|
|
|
// 存储用量:拉 /v1/user/storage 渲染文件面板底部进度条。用量来自后台 15min 扫描,
|
|
// 故无需高频刷新 —— enterApp 拉一次即可。无配额上限时只显已用、不画进度条(nolimit)。
|
|
async function loadStorage() {
|
|
let s;
|
|
try { s = await api("GET", "/v1/user/storage"); } catch (e) { return; }
|
|
const el = $("storage-foot");
|
|
const used = s.bytes_used || 0;
|
|
const limit = s.limit_bytes;
|
|
if (limit && limit > 0) {
|
|
const pct = Math.min(100, Math.round(used / limit * 100));
|
|
$("storage-foot-bar").style.width = pct + "%";
|
|
$("storage-foot-txt").textContent = `${humanSize(used)} / ${humanSize(limit)}`;
|
|
el.classList.remove("nolimit");
|
|
el.classList.toggle("over", used >= limit);
|
|
} else {
|
|
// 不限额:只显已用,隐藏进度条
|
|
$("storage-foot-txt").textContent = humanSize(used);
|
|
el.classList.add("nolimit");
|
|
el.classList.remove("over");
|
|
}
|
|
const when = s.scanned_at ? fmtTime(s.scanned_at) : "尚未统计";
|
|
el.title = `已用 ${humanSize(used)} · ${s.file_count || 0} 个文件\n统计于 ${when}(后台每 15 分钟扫描,非实时)`;
|
|
el.classList.add("show");
|
|
}
|
|
|
|
|
|
// 版本号:/healthz 是 auth 豁免接口,embed / 未登录都拿得到 → boot 时无条件拉一次填进左栏底部。
|
|
// 失败静默(版本号是装饰性信息,不该因网络抖动报错)。
|
|
async function loadVersion() {
|
|
const el = $("app-version");
|
|
if (!el) return;
|
|
try {
|
|
const r = await fetch("/healthz");
|
|
const d = await r.json();
|
|
if (d && d.version) el.textContent = "v" + d.version;
|
|
} catch (e) {}
|
|
}
|
|
|
|
|
|
// ───── Esc 关弹窗栈(跨模块协调:chpw/选入/文件预览/小预览)─────
|
|
document.addEventListener("keydown", (e) => {
|
|
if (e.key !== "Escape") return;
|
|
// 多模态共存:优先关靠前栈顶 — 小预览(z 96)→ 选入(z 95)→ 文件预览(z 90)→ 新任务(z 80)
|
|
if ($("chpw-modal").classList.contains("show")) { closeChpwModal(); return; }
|
|
if ($("skills-modal").classList.contains("show")) { closeSkillsModal(); return; }
|
|
if ($("memory-modal").classList.contains("show")) { closeMemoryModal(); return; }
|
|
if ($("crons-modal").classList.contains("show")) { closeCronsModal(); return; }
|
|
if ($("wechat-modal").classList.contains("show")) { closeWechatModal(); return; }
|
|
if ($("mini-preview-modal").classList.contains("show")) { closeMiniPreview(); return; }
|
|
if ($("src-picker-modal").classList.contains("show")) { closeSrcPicker(); return; }
|
|
if ($("file-preview-modal").classList.contains("show")) { closeFilePreview(); return; }
|
|
});
|
|
|
|
|
|
// ───── boot ─────
|
|
loadVersion(); // 与登录态无关,立即拉
|
|
if (EMBED) {
|
|
embedInit();
|
|
} else if (state.token) {
|
|
// 已有 token:试探一下,失败回登录页
|
|
enterApp();
|
|
} else {
|
|
$("li-email").focus();
|
|
}
|