feat: 新增webdav

This commit is contained in:
2026-10-16 09:03:23 -07:00
parent 86c96b1a32
commit 94203e5746
15 changed files with 883 additions and 5 deletions
+63
View File
@@ -0,0 +1,63 @@
// 统一启动入口:本地运行 Next CLI,Docker 保留 standalone,两者共管 WebDAV 生命周期。
import { execFile, spawn } from "node:child_process";
import { createRequire } from "node:module";
import { getWebdavConfig } from "../src/server/webdav/config.mjs";
const mode = process.argv[2] || "standalone";
if (!["dev", "start", "standalone"].includes(mode))
throw new Error("启动模式必须为 dev、start 或 standalone");
const local = mode !== "standalone";
const load = createRequire(import.meta.url);
if (local) {
process.env.NODE_ENV = mode === "dev" ? "development" : "production";
// 复用当前 Next 安装中的 env loader,与 Next 的 .env* 优先级和变量展开保持一致。
const loadNext = createRequire(load.resolve("next/package.json"));
loadNext("@next/env").loadEnvConfig(process.cwd(), mode === "dev");
process.env.PORT = process.env.PORT?.trim() || "3200";
process.env.WEBDAV_PORT = process.env.WEBDAV_PORT?.trim() || "3201";
}
const config = getWebdavConfig();
if (config.enabled && config.port === Number(process.env.PORT))
throw new Error("WebDAV 与管理页不能使用相同端口");
const children = [];
let stopping = false;
const stop = (code = 0) => {
if (stopping) return;
stopping = true;
process.exitCode = code;
for (const child of children) {
if (child.exitCode !== null || child.signalCode !== null) continue;
// next dev 还会创建服务进程;Windows 必须结束整个子进程树,避免残留监听端口。
if (process.platform === "win32")
execFile("taskkill", ["/PID", String(child.pid), "/T", "/F"], () => {});
else child.kill("SIGTERM");
}
setTimeout(() => {
for (const child of children) child.kill("SIGKILL");
}, 6000).unref();
};
const start = (file, args = [], env = process.env) => {
const child = spawn(process.execPath, [file, ...args], {
stdio: "inherit",
env
});
children.push(child);
child.on("error", () => stop(1));
child.on("exit", (code, signal) => {
if (!stopping) stop(code || (signal ? 1 : 0));
});
};
if (local)
start(load.resolve("next/dist/bin/next"), [mode, "-p", process.env.PORT]);
else start("server.js");
if (config.enabled) {
const env = { ...process.env };
// 本地无密钥的演示模式沿用网页的 mock 密钥,不改变 Next 进程的演示提示。
if (local && !env.TORBOX_API_KEY?.trim() && !env.ACCESS_TOKEN?.trim())
env.ACCESS_TOKEN = "mock";
start("scripts/webdav.mjs", [], env);
}
process.once("SIGINT", () => stop());
process.once("SIGTERM", () => stop());