feat: expand scheduling, notifications, and queue runtime
- add scheduled task plumbing across backend core, CLI, and frontend surfaces - add frontend notification UI and keep service pages using the repaired shared stylesheet - refactor code queue runtime and update baidu netdisk/service integration docs
This commit is contained in:
@@ -99,9 +99,9 @@ const serviceStartedAt = new Date().toISOString();
|
||||
const recentLogs: JsonRecord[] = [];
|
||||
|
||||
function normalizedAppRoot(value: string): string {
|
||||
const root = pathPosix.normalize(`/${String(value || "").replace(/^\/+/, "")}`);
|
||||
if (!root.startsWith("/apps/")) return "/apps/UniDeskBaiduNetdisk";
|
||||
return root.replace(/\/+$/u, "") || "/apps/UniDeskBaiduNetdisk";
|
||||
const raw = String(value || "/").trim() || "/";
|
||||
const normalized = pathPosix.normalize(raw.startsWith("/") ? raw : `/${raw}`);
|
||||
return normalized === "/" ? "/" : normalized.replace(/\/+$/u, "") || "/";
|
||||
}
|
||||
|
||||
function configFromEnv(): RuntimeConfig {
|
||||
@@ -116,7 +116,7 @@ function configFromEnv(): RuntimeConfig {
|
||||
clientId: process.env.BAIDU_NETDISK_CLIENT_ID || process.env.BAIDU_NETDISK_APP_KEY || "",
|
||||
clientSecret: process.env.BAIDU_NETDISK_CLIENT_SECRET || process.env.BAIDU_NETDISK_SECRET_KEY || "",
|
||||
tokenKey: process.env.BAIDU_NETDISK_TOKEN_KEY || "",
|
||||
appRoot: normalizedAppRoot(process.env.BAIDU_NETDISK_APP_ROOT || "/apps/UniDeskBaiduNetdisk"),
|
||||
appRoot: normalizedAppRoot(process.env.BAIDU_NETDISK_APP_ROOT || "/"),
|
||||
stagingDir: resolve(process.env.BAIDU_NETDISK_STAGING_DIR || "/data/staging"),
|
||||
partSizeBytes: Number.isFinite(partSizeBytes) && partSizeBytes > 0 ? partSizeBytes : 4 * 1024 * 1024,
|
||||
userAgent: process.env.BAIDU_NETDISK_USER_AGENT || "pan.baidu.com",
|
||||
@@ -448,6 +448,11 @@ async function createRemoteFolder(accessToken: string, path: string): Promise<Js
|
||||
}
|
||||
|
||||
async function ensureAppRoot(accessToken: string, force = false): Promise<void> {
|
||||
if (config.appRoot === "/") {
|
||||
appRootEnsuredAt = Date.now();
|
||||
if (force) log("remote_root_ready", { path: config.appRoot });
|
||||
return;
|
||||
}
|
||||
if (!force && appRootEnsuredAt > 0 && Date.now() - appRootEnsuredAt < 10 * 60 * 1000) return;
|
||||
if (appRootEnsurePromise) return appRootEnsurePromise;
|
||||
appRootEnsurePromise = (async () => {
|
||||
@@ -465,10 +470,11 @@ async function ensureAppRoot(accessToken: string, force = false): Promise<void>
|
||||
|
||||
function remotePathInsideRoot(input: string | undefined, fallback = config.appRoot): string {
|
||||
const raw = String(input || fallback).trim() || fallback;
|
||||
if (raw.includes("\0")) throw new HttpError(400, "remote path must not contain null bytes");
|
||||
const normalized = pathPosix.normalize(raw.startsWith("/") ? raw : pathPosix.join(config.appRoot, raw));
|
||||
const clean = normalized.replace(/\/+$/u, "") || config.appRoot;
|
||||
if (clean !== config.appRoot && !clean.startsWith(`${config.appRoot}/`)) {
|
||||
throw new HttpError(400, "remote path must stay inside app root", { appRoot: config.appRoot, path: clean });
|
||||
const clean = normalized === "/" ? "/" : normalized.replace(/\/+$/u, "") || config.appRoot;
|
||||
if (config.appRoot !== "/" && clean !== config.appRoot && !clean.startsWith(`${config.appRoot}/`)) {
|
||||
throw new HttpError(400, "remote path must stay inside configured root", { rootPath: config.appRoot, path: clean });
|
||||
}
|
||||
return clean;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user