## 主要變更 ### 新增全域檔案傳輸模組 (src/transfer/) - constants.ts: 定義傳輸常數(10MB 門檻、5MB chunk 大小) - types.ts: 傳輸類型定義 - uploadManager.ts: 後端上傳管理器(支援直傳與 chunk) - downloadManager.ts: 後端下載管理器(支援直傳與 chunk) - archiveManager.ts: 封裝管理器(僅允許 .tar) - index.ts: 統一匯出 ### 新增 API 端點 - uploadChunk.tsx: Chunk 上傳 API - downloadChunk.tsx: Chunk 下載 API ### 前端更新 - public/script.js: 整合智慧傳輸策略(≤10MB 直傳,>10MB chunk) - public/transfer.js: 前端傳輸管理模組 ### 轉換器更新 - mineru.ts: 改用 .tar 格式(不壓縮),禁止 .tar.gz - download.tsx: 使用統一的封裝管理器 ### 測試 - tests/transfer/: 完整傳輸機制測試套件 - tests/converters/mineru.test.ts: 更新以符合 .tar 規範 ### 文件 - README.md: 新增檔案傳輸機制說明 ## 設計原則 - 檔案 ≤10MB:直接傳輸 - 檔案 >10MB:使用 5MB chunks 分段傳輸 - 多檔輸出:僅允許 .tar 封裝(禁止 .tar.gz/.zip) - 引擎層不感知 chunk(僅傳輸層處理)
103 lines
2.8 KiB
TypeScript
103 lines
2.8 KiB
TypeScript
import { rmSync } from "node:fs";
|
|
import { html } from "@elysiajs/html";
|
|
import { staticPlugin } from "@elysiajs/static";
|
|
import { Elysia } from "elysia";
|
|
import "./helpers/printVersions";
|
|
import db from "./db/db";
|
|
import { Jobs } from "./db/types";
|
|
import { AUTO_DELETE_EVERY_N_HOURS, WEBROOT } from "./helpers/env";
|
|
import { chooseConverter } from "./pages/chooseConverter";
|
|
import { convert } from "./pages/convert";
|
|
import { deleteFile } from "./pages/deleteFile";
|
|
import { deleteJob } from "./pages/deleteJob";
|
|
import { download } from "./pages/download";
|
|
import { downloadChunk } from "./pages/downloadChunk";
|
|
import { history } from "./pages/history";
|
|
import { listConverters } from "./pages/listConverters";
|
|
import { results } from "./pages/results";
|
|
import { root } from "./pages/root";
|
|
import { upload } from "./pages/upload";
|
|
import { uploadChunk, uploadInfo } from "./pages/uploadChunk";
|
|
import { user } from "./pages/user";
|
|
import { healthcheck } from "./pages/healthcheck";
|
|
|
|
export const uploadsDir = "./data/uploads/";
|
|
export const outputDir = "./data/output/";
|
|
|
|
// Fix for Elysia issue with Bun, (see https://github.com/oven-sh/bun/issues/12161)
|
|
process.getBuiltinModule = require;
|
|
|
|
const app = new Elysia({
|
|
serve: {
|
|
maxRequestBodySize: Number.MAX_SAFE_INTEGER,
|
|
},
|
|
prefix: WEBROOT,
|
|
})
|
|
.use(html())
|
|
.use(
|
|
staticPlugin({
|
|
assets: "public",
|
|
prefix: "",
|
|
}),
|
|
)
|
|
.use(user)
|
|
.use(root)
|
|
.use(upload)
|
|
.use(uploadChunk)
|
|
.use(uploadInfo)
|
|
.use(history)
|
|
.use(convert)
|
|
.use(download)
|
|
.use(downloadChunk)
|
|
.use(deleteJob)
|
|
.use(results)
|
|
.use(deleteFile)
|
|
.use(listConverters)
|
|
.use(chooseConverter)
|
|
.use(healthcheck)
|
|
.onError(({ error }) => {
|
|
console.error(error);
|
|
});
|
|
|
|
if (process.env.NODE_ENV !== "production") {
|
|
await import("./helpers/tailwind").then(async ({ generateTailwind }) => {
|
|
const result = await generateTailwind();
|
|
|
|
app.get("/generated.css", ({ set }) => {
|
|
set.headers["content-type"] = "text/css";
|
|
return result;
|
|
});
|
|
});
|
|
}
|
|
|
|
app.listen(3000);
|
|
|
|
console.log(`🦊 Elysia is running at http://${app.server?.hostname}:${app.server?.port}${WEBROOT}`);
|
|
|
|
const clearJobs = () => {
|
|
const jobs = db
|
|
.query("SELECT * FROM jobs WHERE date_created < ?")
|
|
.as(Jobs)
|
|
.all(new Date(Date.now() - AUTO_DELETE_EVERY_N_HOURS * 60 * 60 * 1000).toISOString());
|
|
|
|
for (const job of jobs) {
|
|
// delete the directories
|
|
rmSync(`${outputDir}${job.user_id}/${job.id}`, {
|
|
recursive: true,
|
|
force: true,
|
|
});
|
|
rmSync(`${uploadsDir}${job.user_id}/${job.id}`, {
|
|
recursive: true,
|
|
force: true,
|
|
});
|
|
|
|
// delete the job
|
|
db.query("DELETE FROM jobs WHERE id = ?").run(job.id);
|
|
}
|
|
|
|
setTimeout(clearJobs, AUTO_DELETE_EVERY_N_HOURS * 60 * 60 * 1000);
|
|
};
|
|
|
|
if (AUTO_DELETE_EVERY_N_HOURS > 0) {
|
|
clearJobs();
|
|
}
|