feat: 新增webdav
This commit is contained in:
@@ -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());
|
||||
@@ -0,0 +1,30 @@
|
||||
import { createWebDavServer } from "../src/server/webdav/server.mjs";
|
||||
import { getWebdavConfig } from "../src/server/webdav/config.mjs";
|
||||
|
||||
process.env.WEBDAV_PORT ||= "3201";
|
||||
const config = getWebdavConfig();
|
||||
if (!config.enabled) process.exit(0);
|
||||
|
||||
const server = createWebDavServer({
|
||||
appUrl: config.appUrl,
|
||||
username: config.username,
|
||||
password: process.env.ACCESS_TOKEN,
|
||||
basePath: config.basePath
|
||||
});
|
||||
server.headersTimeout = 10_000;
|
||||
server.requestTimeout = 30_000;
|
||||
server.on("error", () => {
|
||||
console.error("WebDAV 启动失败,请检查监听地址和端口是否被占用");
|
||||
process.exitCode = 1;
|
||||
});
|
||||
server.listen(config.port, config.host, () => {
|
||||
console.info(`WebDAV 已启动:端口 ${config.port},仅提供只读文件访问`);
|
||||
});
|
||||
|
||||
const close = () => {
|
||||
server.close();
|
||||
// 先允许正在进行的传输结束,容器停止时最多等待 5 秒。
|
||||
setTimeout(() => server.closeAllConnections(), 5000).unref();
|
||||
};
|
||||
process.once("SIGINT", close);
|
||||
process.once("SIGTERM", close);
|
||||
Reference in New Issue
Block a user