继续更新 update 门户站点界面和功能
build-winui / winui (push) Has been cancelled

This commit is contained in:
QWQLwToo
2026-06-26 20:17:34 +08:00
parent f525e5f3ba
commit 2513eb2903
68 changed files with 5586 additions and 3195 deletions
@@ -0,0 +1,105 @@
export type UploadProgress = {
loaded: number;
total: number;
};
export type AdminApiOptions = {
csrf?: string;
};
const exactMessages: Record<string, string> = {
"current password is invalid": "当前密码不正确",
"new password is required": "新密码不能为空",
"new password must be at least 8 characters": "新密码至少需要 8 位",
"new password cannot be admin": "新密码不能为 admin",
"new password must be different from current password": "新密码不能与当前密码相同",
"invalid password or captcha": "密码或验证码不正确",
"login required": "需要登录后继续操作",
"csrf token required": "页面安全令牌已失效,请刷新后重试",
"csrf token invalid": "页面安全令牌无效,请刷新后重试",
"code is required": "缺少反馈编号",
"revisionid is required": "请选择要恢复的历史版本",
"post required": "该操作需要使用 POST 请求",
"get required": "该操作需要使用 GET 请求",
"file is required": "请选择要上传的文件",
"invalid filename": "文件名不合法",
"path escape rejected": "文件路径不合法",
"check job not found": "未找到心跳检测任务",
"streaming is not supported": "当前运行环境不支持实时事件流",
};
const codeMessages: Record<string, string> = {
UNAUTHORIZED: "需要登录后继续操作",
LOGIN_FAILED: "登录失败,请检查密码和验证码",
PASSWORD_CHANGE_FAILED: "密码修改失败",
INVALID_PAYLOAD: "提交内容格式不正确",
DATABASE_TEST_FAILED: "数据库连接测试失败",
DATABASE_IMPORT_FAILED: "SQLite 导入远端库失败",
DATABASE_SYNC_FAILED: "远端库同步回本地失败",
LEGACY_SAVE_FAILED: "兼容 JSON 保存失败",
LEGACY_VALIDATE_FAILED: "兼容 JSON 校验失败",
LEGACY_RESTORE_FAILED: "兼容 JSON 恢复失败",
NOTICE_SAVE_FAILED: "版本日志保存失败",
NOTICE_VALIDATE_FAILED: "版本日志校验失败",
NOTICE_RESTORE_FAILED: "版本日志恢复失败",
PACKAGE_UPLOAD_FAILED: "发布包上传失败",
SOURCE_SAVE_FAILED: "接口源保存失败",
CHECK_FAILED: "接口健康检测失败",
SYNC_FAILED: "同步操作失败",
FORBIDDEN: "没有权限执行该操作",
METHOD_NOT_ALLOWED: "请求方法不正确",
};
export async function adminFetch<T>(target: string, init: RequestInit = {}, options: AdminApiOptions = {}): Promise<T> {
const headers = new Headers(init.headers);
if (!headers.has("Content-Type") && init.body && !(init.body instanceof FormData)) {
headers.set("Content-Type", "application/json");
}
if (options.csrf) headers.set("X-CSRF-Token", options.csrf);
const res = await fetch(target, { ...init, headers, credentials: "include" });
const data = await res.json().catch(() => ({}));
if (!res.ok || data.ok === false) {
throw new Error(toChineseError(data.message || data.error || `HTTP ${res.status}`));
}
return data as T;
}
export function uploadAdminFile<T>(target: string, form: FormData, options: AdminApiOptions, onProgress: (progress: UploadProgress) => void): Promise<T> {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("POST", target);
xhr.withCredentials = true;
if (options.csrf) xhr.setRequestHeader("X-CSRF-Token", options.csrf);
xhr.upload.onprogress = (event) => {
if (event.lengthComputable) onProgress({ loaded: event.loaded, total: event.total });
};
xhr.onload = () => {
const data = parseJSONSafe(xhr.responseText, {});
if (xhr.status < 200 || xhr.status >= 300 || data.ok === false) {
reject(new Error(toChineseError(data.message || data.error || `HTTP ${xhr.status}`)));
return;
}
resolve(data as T);
};
xhr.onerror = () => reject(new Error("网络异常,发布包上传失败"));
xhr.onabort = () => reject(new Error("发布包上传已取消"));
xhr.send(form);
});
}
export function toChineseError(value: string) {
const raw = String(value || "").trim();
const lower = raw.toLowerCase();
if (exactMessages[lower]) return exactMessages[lower];
if (codeMessages[raw]) return codeMessages[raw];
if (/^HTTP\s+\d+/.test(raw)) return `请求失败:${raw}`;
return raw || "操作失败";
}
function parseJSONSafe(value: string, fallback: any) {
try {
return JSON.parse(value || "{}");
} catch {
return fallback;
}
}