const state = {
bootstrap: window.APP_BOOTSTRAP || {},
authenticated: false,
users: [],
page: 1,
pageSize: (window.APP_BOOTSTRAP && window.APP_BOOTSTRAP.pageSize) || 25,
total: 0,
totalBeforeSearch: 0,
summary: { active: 0, disabled: 0 },
search: "",
licenses: [],
selectedUser: null,
selectedUsers: new Set(),
activeTaskId: "",
activeTaskTimer: null,
};
const elements = {
platformStatus: document.getElementById("platform-status"),
platformSubstatus: document.getElementById("platform-substatus"),
metricTotal: document.getElementById("metric-total"),
metricActive: document.getElementById("metric-active"),
metricDisabled: document.getElementById("metric-disabled"),
metricLicense: document.getElementById("metric-license"),
usersTableBody: document.getElementById("users-table-body"),
paginationInfo: document.getElementById("pagination-info"),
licenseList: document.getElementById("license-list"),
resultConsole: document.getElementById("result-console"),
loginSection: document.getElementById("login-section"),
loginForm: document.getElementById("login-form"),
logoutBtn: document.getElementById("logout-btn"),
refreshAllBtn: document.getElementById("refresh-all-btn"),
searchForm: document.getElementById("search-form"),
searchInput: document.getElementById("search-input"),
prevPageBtn: document.getElementById("prev-page-btn"),
nextPageBtn: document.getElementById("next-page-btn"),
userForm: document.getElementById("user-form"),
selectedUserId: document.getElementById("selected-user-id"),
deleteUserBtn: document.getElementById("delete-user-btn"),
newUserBtn: document.getElementById("new-user-btn"),
clearFormBtn: document.getElementById("clear-form-btn"),
resetPasswordBtn: document.getElementById("reset-password-btn"),
selectedCount: document.getElementById("selected-count"),
selectAllResultsBtn: document.getElementById("select-all-results-btn"),
clearSelectionBtn: document.getElementById("clear-selection-btn"),
bulkEnableBtn: document.getElementById("bulk-enable-btn"),
bulkDisableBtn: document.getElementById("bulk-disable-btn"),
bulkResetBtn: document.getElementById("bulk-reset-btn"),
bulkDeleteBtn: document.getElementById("bulk-delete-btn"),
selectPageCheckbox: document.getElementById("select-page-checkbox"),
taskStatusCard: document.getElementById("task-status-card"),
taskStatusTitle: document.getElementById("task-status-title"),
taskStatusState: document.getElementById("task-status-state"),
taskProgressFill: document.getElementById("task-progress-fill"),
taskStatusText: document.getElementById("task-status-text"),
};
const TASK_LABELS = {
"create": "批量创建",
"update": "批量更新",
"delete": "批量删除",
"reset-password": "批量改密",
};
async function api(path, options = {}) {
const config = { ...options };
config.headers = config.headers || {};
if (config.body && !(config.body instanceof FormData)) {
config.headers["Content-Type"] = "application/json";
}
const response = await fetch(path, config);
const payload = await response.json().catch(() => ({
success: false,
message: "接口返回了无法解析的响应。",
}));
if (response.status === 401) {
state.authenticated = false;
updateAuthView();
}
if (!response.ok || !payload.success) {
throw new Error(payload.message || "请求失败");
}
return payload.data;
}
function updatePlatformStatus() {
if (state.bootstrap.graphReady) {
elements.platformStatus.textContent = "Graph 已就绪";
elements.platformSubstatus.textContent = state.bootstrap.graphFlavor || "Microsoft Graph Global";
} else {
elements.platformStatus.textContent = "等待配置";
const errors = state.bootstrap.validationErrors || [];
elements.platformSubstatus.textContent = errors.join(";") || "请补充 .env";
}
}
function updateAuthView() {
const needsLogin = state.bootstrap.authEnabled && !state.authenticated;
elements.loginSection.classList.toggle("hidden", !needsLogin);
}
function setConsole(content) {
elements.resultConsole.textContent = content;
}
function appendConsole(title, summary) {
const lines = [
`[${new Date().toLocaleString("zh-CN")}] ${title}`,
String(summary || ""),
"",
];
elements.resultConsole.textContent = lines.join("\n") + elements.resultConsole.textContent;
}
function formatTaskLabel(operation) {
return TASK_LABELS[operation] || operation;
}
function formatTaskState(task) {
if (task.status === "queued") {
return "已提交";
}
if (task.status === "running") {
return "执行中";
}
if (task.status === "succeeded") {
return "已完成";
}
if (task.status === "failed") {
return "失败";
}
return task.status || "未知";
}
function summarizeTask(task) {
const base = `${formatTaskLabel(task.operation)}:${task.completed}/${task.total},成功 ${task.successCount},失败 ${task.failureCount}`;
if (task.failureCount > 0) {
return `${base}。详细失败项请查看 logs/office365_admin.log`;
}
return base;
}
function showTaskCard(task) {
if (!elements.taskStatusCard) {
return;
}
elements.taskStatusCard.classList.remove("hidden");
elements.taskStatusTitle.textContent = formatTaskLabel(task.operation);
elements.taskStatusState.textContent = formatTaskState(task);
elements.taskProgressFill.style.width = `${task.progressPercent || 0}%`;
const statusText = [
`进度 ${task.completed || 0} / ${task.total || 0}`,
`成功 ${task.successCount || 0}`,
`失败 ${task.failureCount || 0}`,
].join(" · ");
const currentText = task.currentItem
? ` · 当前 ${task.currentItem}`
: "";
elements.taskStatusText.textContent = `${statusText}${currentText}`;
}
function showSingleActionStatus(title, text) {
if (!elements.taskStatusCard) {
return;
}
elements.taskStatusCard.classList.remove("hidden");
elements.taskStatusTitle.textContent = title;
elements.taskStatusState.textContent = "处理中";
elements.taskProgressFill.style.width = "30%";
elements.taskStatusText.textContent = text;
}
function completeSingleActionStatus(title, text) {
if (!elements.taskStatusCard) {
return;
}
elements.taskStatusCard.classList.remove("hidden");
elements.taskStatusTitle.textContent = title;
elements.taskStatusState.textContent = "已完成";
elements.taskProgressFill.style.width = "100%";
elements.taskStatusText.textContent = text;
}
function showFailedActionStatus(title, text) {
if (!elements.taskStatusCard) {
return;
}
elements.taskStatusCard.classList.remove("hidden");
elements.taskStatusTitle.textContent = title;
elements.taskStatusState.textContent = "失败";
elements.taskProgressFill.style.width = "100%";
elements.taskStatusText.textContent = text;
}
function clearTaskPolling() {
if (state.activeTaskTimer) {
window.clearTimeout(state.activeTaskTimer);
state.activeTaskTimer = null;
}
}
async function pollTask(taskId) {
try {
const task = await api(`/api/tasks/${encodeURIComponent(taskId)}`);
showTaskCard(task);
if (task.status === "queued" || task.status === "running") {
clearTaskPolling();
state.activeTaskTimer = window.setTimeout(() => pollTask(taskId), 1000);
return;
}
state.activeTaskId = "";
clearTaskPolling();
appendConsole(
task.status === "failed" ? `${formatTaskLabel(task.operation)}失败` : `${formatTaskLabel(task.operation)}完成`,
summarizeTask(task),
);
await refreshAll();
} catch (error) {
state.activeTaskId = "";
clearTaskPolling();
showFailedActionStatus("任务轮询失败", error.message);
appendConsole("任务轮询失败", error.message);
}
}
function startTask(task, submittedMessage) {
state.activeTaskId = task.id;
clearTaskPolling();
showTaskCard(task);
appendConsole("任务已提交", submittedMessage);
pollTask(task.id);
}
function statusPill(enabled) {
const klass = enabled ? "active" : "disabled";
const label = enabled ? "启用" : "停用";
return `${label}`;
}
function selectedIdentifiers() {
return Array.from(state.selectedUsers);
}
function updateSelectionUi() {
const count = state.selectedUsers.size;
if (elements.selectedCount) {
elements.selectedCount.textContent = count ? `已选择 ${count} 个账号` : "未选择账号";
}
const currentPageIds = state.users.map((user) => user.userPrincipalName).filter(Boolean);
const selectedOnPage = currentPageIds.filter((identifier) => state.selectedUsers.has(identifier)).length;
if (elements.selectPageCheckbox) {
elements.selectPageCheckbox.checked = currentPageIds.length > 0 && selectedOnPage === currentPageIds.length;
elements.selectPageCheckbox.indeterminate = selectedOnPage > 0 && selectedOnPage < currentPageIds.length;
elements.selectPageCheckbox.disabled = currentPageIds.length === 0;
}
[
elements.clearSelectionBtn,
elements.bulkEnableBtn,
elements.bulkDisableBtn,
elements.bulkResetBtn,
elements.bulkDeleteBtn,
].forEach((button) => {
if (button) {
button.disabled = count === 0;
}
});
if (elements.selectAllResultsBtn) {
elements.selectAllResultsBtn.disabled = state.total === 0;
}
}
function renderUsers() {
if (!state.users.length) {
elements.usersTableBody.innerHTML = `
| 没有匹配到用户 |
`;
updateSelectionUi();
return;
}
elements.usersTableBody.innerHTML = state.users.map((user) => {
const identifier = user.userPrincipalName || "";
const checked = state.selectedUsers.has(identifier) ? "checked" : "";
return `
|
|
${escapeHtml(user.displayName || "-")} |
${escapeHtml(identifier || "-")} |
${escapeHtml(user.department || "-")}
${escapeHtml(user.jobTitle || "-")}
|
${statusPill(user.accountEnabled)} |
${user.assignedLicensesCount || 0} |
|
`;
}).join("");
elements.paginationInfo.textContent = `第 ${state.page} 页,当前 ${state.users.length} / 共 ${state.total} 条`;
updateSelectionUi();
}
function renderMetrics() {
elements.metricTotal.textContent = String(state.total);
elements.metricActive.textContent = String(state.summary.active || 0);
elements.metricDisabled.textContent = String(state.summary.disabled || 0);
const available = state.licenses.reduce((sum, item) => sum + (item.availableUnits || 0), 0);
elements.metricLicense.textContent = String(available);
const totalFoot = state.search
? `搜索结果 ${state.total} / 全量 ${state.totalBeforeSearch}`
: `全量账号 ${state.totalBeforeSearch}`;
document.getElementById("metric-total-foot").textContent = totalFoot;
}
function renderLicenses() {
if (!state.licenses.length) {
elements.licenseList.innerHTML = `未获取到许可证信息
`;
return;
}
elements.licenseList.innerHTML = state.licenses.map((item) => `
${escapeHtml(item.skuPartNumber || "UNKNOWN")}
可用席位:${item.availableUnits}
已用席位:${item.consumedUnits}
总席位:${item.totalUnits}
`).join("");
}
function escapeHtml(value) {
return String(value)
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """)
.replaceAll("'", "'");
}
async function fetchSession() {
const data = await api("/api/session");
state.authenticated = data.authenticated;
updateAuthView();
}
async function fetchUsers() {
const query = new URLSearchParams({
page: String(state.page),
pageSize: String(state.pageSize),
search: state.search,
});
const data = await api(`/api/users?${query.toString()}`);
state.users = data.items;
state.total = data.total;
state.totalBeforeSearch = data.totalBeforeSearch;
state.summary = data.summary;
renderUsers();
renderMetrics();
}
async function fetchLicenses() {
const data = await api("/api/licenses");
state.licenses = data;
renderLicenses();
renderMetrics();
}
async function fetchUserDetail(identifier) {
const data = await api(`/api/users/${encodeURIComponent(identifier)}`);
state.selectedUser = data;
fillUserForm(data);
appendConsole("已加载用户", data.userPrincipalName || identifier);
}
function formPayload() {
return {
userPrincipalName: document.getElementById("userPrincipalName").value.trim(),
displayName: document.getElementById("displayName").value.trim(),
givenName: document.getElementById("givenName").value.trim(),
surname: document.getElementById("surname").value.trim(),
department: document.getElementById("department").value.trim(),
jobTitle: document.getElementById("jobTitle").value.trim(),
officeLocation: document.getElementById("officeLocation").value.trim(),
mobilePhone: document.getElementById("mobilePhone").value.trim(),
usageLocation: document.getElementById("usageLocation").value.trim(),
skuPartNumber: document.getElementById("skuPartNumber").value.trim(),
password: document.getElementById("password").value,
accountEnabled: document.getElementById("accountEnabled").checked,
forceChangePasswordNextSignIn: document.getElementById("forceChangePasswordNextSignIn").checked,
};
}
function fillUserForm(user) {
elements.selectedUserId.value = user.id || "";
document.getElementById("userPrincipalName").value = user.userPrincipalName || "";
document.getElementById("displayName").value = user.displayName || "";
document.getElementById("givenName").value = user.givenName || "";
document.getElementById("surname").value = user.surname || "";
document.getElementById("department").value = user.department || "";
document.getElementById("jobTitle").value = user.jobTitle || "";
document.getElementById("officeLocation").value = user.officeLocation || "";
document.getElementById("mobilePhone").value = user.mobilePhone || "";
document.getElementById("usageLocation").value = user.usageLocation || state.bootstrap.defaultUsageLocation || "";
document.getElementById("skuPartNumber").value = (user.licenseLabels && user.licenseLabels[0]) || "";
document.getElementById("password").value = "";
document.getElementById("accountEnabled").checked = Boolean(user.accountEnabled);
document.getElementById("forceChangePasswordNextSignIn").checked = state.bootstrap.forceChangePassword;
}
function clearUserForm() {
state.selectedUser = null;
elements.selectedUserId.value = "";
elements.userForm.reset();
document.getElementById("accountEnabled").checked = true;
document.getElementById("forceChangePasswordNextSignIn").checked = state.bootstrap.forceChangePassword;
document.getElementById("usageLocation").value = state.bootstrap.defaultUsageLocation || "";
}
function toggleUserSelection(identifier, checked) {
if (!identifier) {
return;
}
if (checked) {
state.selectedUsers.add(identifier);
} else {
state.selectedUsers.delete(identifier);
}
updateSelectionUi();
}
function clearSelection() {
state.selectedUsers.clear();
updateSelectionUi();
renderUsers();
}
function toggleCurrentPageSelection(checked) {
state.users.forEach((user) => {
if (!user.userPrincipalName) {
return;
}
if (checked) {
state.selectedUsers.add(user.userPrincipalName);
} else {
state.selectedUsers.delete(user.userPrincipalName);
}
});
renderUsers();
}
async function selectAllMatchingUsers() {
const query = new URLSearchParams({ search: state.search });
const data = await api(`/api/users/selection?${query.toString()}`);
state.selectedUsers = new Set(data.identifiers || []);
renderUsers();
appendConsole("已选中搜索结果", `共 ${state.selectedUsers.size} 个账号`);
}
async function saveUser(event) {
event.preventDefault();
const payload = formPayload();
const identifier = payload.userPrincipalName;
if (!identifier) {
throw new Error("请先填写账号或邮箱。");
}
showSingleActionStatus("保存用户", identifier);
let data;
if (state.selectedUser && state.selectedUser.userPrincipalName) {
data = await api(`/api/users/${encodeURIComponent(state.selectedUser.userPrincipalName)}`, {
method: "PATCH",
body: JSON.stringify(payload),
});
appendConsole("用户已更新", data.user.userPrincipalName || identifier);
} else {
data = await api("/api/users", {
method: "POST",
body: JSON.stringify(payload),
});
appendConsole("用户已创建", data.user.userPrincipalName || identifier);
}
completeSingleActionStatus("保存用户", data.user.userPrincipalName || identifier);
await refreshAll();
if (data.user && data.user.userPrincipalName) {
await fetchUserDetail(data.user.userPrincipalName);
}
}
async function deleteCurrentUser(identifier) {
if (!identifier) {
throw new Error("请先选择一个用户。");
}
const confirmDelete = window.confirm(`确认删除 ${identifier} 吗?此操作不可撤销。`);
if (!confirmDelete) {
return;
}
showSingleActionStatus("删除用户", identifier);
await api(`/api/users/${encodeURIComponent(identifier)}`, { method: "DELETE" });
state.selectedUsers.delete(identifier);
appendConsole("用户已删除", identifier);
completeSingleActionStatus("删除用户", identifier);
clearUserForm();
await refreshAll();
}
async function resetPassword(identifier) {
if (!identifier) {
throw new Error("请先选择一个用户。");
}
const payload = {
password: document.getElementById("password").value,
forceChangePasswordNextSignIn: document.getElementById("forceChangePasswordNextSignIn").checked,
};
showSingleActionStatus("重置密码", identifier);
await api(`/api/users/${encodeURIComponent(identifier)}/reset-password`, {
method: "POST",
body: JSON.stringify(payload),
});
appendConsole("密码已重置", identifier);
completeSingleActionStatus("重置密码", identifier);
}
async function submitBatchTask(action, body, summaryText) {
const task = await api(`/api/users/batch/${action}`, {
method: "POST",
body: JSON.stringify(body),
});
startTask(task, summaryText);
}
async function runSelectedBulk(action) {
const identifiers = selectedIdentifiers();
if (!identifiers.length) {
throw new Error("请先勾选至少一个用户。");
}
if (action === "delete") {
const confirmed = window.confirm(`确认批量删除这 ${identifiers.length} 个账号吗?此操作不可撤销。`);
if (!confirmed) {
return;
}
await submitBatchTask("delete", { identifiers }, `批量删除任务已提交,共 ${identifiers.length} 个账号`);
} else if (action === "enable" || action === "disable") {
const rows = identifiers.map((userPrincipalName) => ({
userPrincipalName,
accountEnabled: action === "enable",
}));
await submitBatchTask("update", { rows }, `${action === "enable" ? "批量启用" : "批量停用"}任务已提交,共 ${identifiers.length} 个账号`);
} else if (action === "reset-password") {
const promptedPassword = window.prompt("输入统一临时密码;留空则使用系统默认密码。", "");
if (promptedPassword === null) {
return;
}
const password = promptedPassword.trim();
const rows = identifiers.map((userPrincipalName) => ({
userPrincipalName,
...(password ? { password } : {}),
forceChangePasswordNextSignIn: true,
}));
await submitBatchTask("reset-password", { rows }, `批量改密任务已提交,共 ${identifiers.length} 个账号`);
} else {
throw new Error("不支持的批量操作。");
}
clearSelection();
}
async function runBatch(action) {
const textareaMap = {
"create": document.getElementById("batch-create-content"),
"update": document.getElementById("batch-update-content"),
"delete": document.getElementById("batch-delete-content"),
"reset-password": document.getElementById("batch-reset-content"),
};
const textarea = textareaMap[action];
const content = textarea.value.trim();
if (!content) {
throw new Error("请先粘贴批量内容或上传文件。");
}
await submitBatchTask(action, { content }, `${formatTaskLabel(action)}任务已提交,等待后台处理`);
}
async function refreshAll() {
updatePlatformStatus();
if (state.bootstrap.authEnabled && !state.authenticated) {
return;
}
if (!state.bootstrap.graphReady) {
return;
}
await Promise.all([fetchUsers(), fetchLicenses()]);
}
function bindEvents() {
elements.loginForm?.addEventListener("submit", async (event) => {
event.preventDefault();
try {
showSingleActionStatus("登录后台", "正在验证管理员账号");
await api("/api/login", {
method: "POST",
body: JSON.stringify({
username: document.getElementById("login-username").value.trim(),
password: document.getElementById("login-password").value,
}),
});
await fetchSession();
await refreshAll();
appendConsole("登录成功", "现在可以开始管理租户账号");
completeSingleActionStatus("登录后台", "登录成功");
} catch (error) {
showFailedActionStatus("登录后台", error.message);
appendConsole("登录失败", error.message);
}
});
elements.logoutBtn?.addEventListener("click", async () => {
await api("/api/logout", { method: "POST" });
state.authenticated = false;
updateAuthView();
appendConsole("已退出后台", "如需继续操作,请重新登录");
});
elements.refreshAllBtn?.addEventListener("click", async () => {
try {
showSingleActionStatus("刷新数据", "正在重新加载用户与许可证");
await refreshAll();
appendConsole("刷新完成", "用户列表和许可证信息已更新");
completeSingleActionStatus("刷新数据", "刷新完成");
} catch (error) {
showFailedActionStatus("刷新数据", error.message);
appendConsole("刷新失败", error.message);
}
});
elements.searchForm?.addEventListener("submit", async (event) => {
event.preventDefault();
state.search = elements.searchInput.value.trim();
state.page = 1;
try {
showSingleActionStatus("搜索用户", state.search || "全部用户");
await fetchUsers();
appendConsole("搜索完成", `${state.total} 条匹配结果`);
completeSingleActionStatus("搜索用户", `${state.total} 条匹配结果`);
} catch (error) {
showFailedActionStatus("搜索用户", error.message);
appendConsole("搜索失败", error.message);
}
});
elements.prevPageBtn?.addEventListener("click", async () => {
if (state.page <= 1) {
return;
}
state.page -= 1;
await fetchUsers();
});
elements.nextPageBtn?.addEventListener("click", async () => {
if (state.page * state.pageSize >= state.total) {
return;
}
state.page += 1;
await fetchUsers();
});
elements.selectPageCheckbox?.addEventListener("change", (event) => {
toggleCurrentPageSelection(event.target.checked);
});
elements.selectAllResultsBtn?.addEventListener("click", async () => {
try {
await selectAllMatchingUsers();
} catch (error) {
appendConsole("全选失败", error.message);
}
});
elements.clearSelectionBtn?.addEventListener("click", () => {
clearSelection();
appendConsole("已清空选择", "当前未勾选任何账号");
});
elements.bulkEnableBtn?.addEventListener("click", async () => {
try {
await runSelectedBulk("enable");
} catch (error) {
appendConsole("批量启用失败", error.message);
}
});
elements.bulkDisableBtn?.addEventListener("click", async () => {
try {
await runSelectedBulk("disable");
} catch (error) {
appendConsole("批量停用失败", error.message);
}
});
elements.bulkResetBtn?.addEventListener("click", async () => {
try {
await runSelectedBulk("reset-password");
} catch (error) {
appendConsole("批量改密失败", error.message);
}
});
elements.bulkDeleteBtn?.addEventListener("click", async () => {
try {
await runSelectedBulk("delete");
} catch (error) {
appendConsole("批量删除失败", error.message);
}
});
elements.usersTableBody?.addEventListener("change", (event) => {
const checkbox = event.target.closest("input[data-select-identifier]");
if (!checkbox) {
return;
}
const identifier = decodeURIComponent(checkbox.dataset.selectIdentifier || "");
toggleUserSelection(identifier, checkbox.checked);
});
elements.usersTableBody?.addEventListener("click", async (event) => {
const button = event.target.closest("button[data-action]");
if (!button) {
return;
}
const action = button.dataset.action;
const identifier = decodeURIComponent(button.dataset.identifier || "");
try {
if (action === "view") {
await fetchUserDetail(identifier);
} else if (action === "delete") {
await deleteCurrentUser(identifier);
} else if (action === "reset") {
await resetPassword(identifier);
}
} catch (error) {
appendConsole("用户操作失败", error.message);
showFailedActionStatus("用户操作失败", error.message);
}
});
elements.userForm?.addEventListener("submit", async (event) => {
try {
await saveUser(event);
} catch (error) {
appendConsole("保存失败", error.message);
showFailedActionStatus("保存用户", error.message);
}
});
elements.newUserBtn?.addEventListener("click", () => clearUserForm());
elements.clearFormBtn?.addEventListener("click", () => clearUserForm());
elements.deleteUserBtn?.addEventListener("click", async () => {
try {
await deleteCurrentUser(state.selectedUser && state.selectedUser.userPrincipalName);
} catch (error) {
appendConsole("删除失败", error.message);
showFailedActionStatus("删除用户", error.message);
}
});
elements.resetPasswordBtn?.addEventListener("click", async () => {
try {
await resetPassword(state.selectedUser && state.selectedUser.userPrincipalName);
} catch (error) {
appendConsole("重置密码失败", error.message);
showFailedActionStatus("重置密码", error.message);
}
});
document.querySelectorAll("[data-batch-action]").forEach((button) => {
button.addEventListener("click", async () => {
try {
await runBatch(button.dataset.batchAction);
} catch (error) {
appendConsole("批量任务提交失败", error.message);
showFailedActionStatus("批量任务提交失败", error.message);
}
});
});
document.querySelectorAll(".file-input").forEach((input) => {
input.addEventListener("change", async (event) => {
const file = event.target.files && event.target.files[0];
if (!file) {
return;
}
const targetId = event.target.dataset.target;
const target = document.getElementById(targetId);
target.value = await file.text();
appendConsole("文件已加载", `${file.name} 已写入输入框`);
});
});
}
async function boot() {
setConsole("等待操作...");
updatePlatformStatus();
bindEvents();
updateSelectionUi();
try {
await fetchSession();
if (!state.bootstrap.authEnabled || state.authenticated) {
await refreshAll();
}
} catch (error) {
appendConsole("初始化失败", error.message);
showFailedActionStatus("初始化失败", error.message);
}
}
boot();