feat: wire wechat archive through langbot and n8n
This commit is contained in:
@@ -1032,6 +1032,38 @@ async function createTransferJob(direction: TransferDirection, body: JsonRecord)
|
||||
return { ok: true, job: transferFromRow(row) };
|
||||
}
|
||||
|
||||
function safeStagingFilename(value: string, fallback: string): string {
|
||||
const base = basename(value || fallback || "upload.txt").replace(/[^A-Za-z0-9._-]+/gu, "_").replace(/^_+|_+$/gu, "");
|
||||
return base || fallback || "upload.txt";
|
||||
}
|
||||
|
||||
function contentUploadBuffer(body: JsonRecord): { buffer: Buffer; source: string } {
|
||||
if (typeof body.dataBase64 === "string" && body.dataBase64.length > 0) return { buffer: Buffer.from(body.dataBase64, "base64"), source: "dataBase64" };
|
||||
if (typeof body.content === "string") return { buffer: Buffer.from(body.content, "utf8"), source: "content" };
|
||||
throw new HttpError(400, "upload-content requires content or dataBase64");
|
||||
}
|
||||
|
||||
async function createContentUpload(body: JsonRecord): Promise<JsonRecord> {
|
||||
const { buffer, source } = contentUploadBuffer(body);
|
||||
const maxBytes = Math.max(1, Math.min(10 * 1024 * 1024, asNumber(body.maxBytes, 10 * 1024 * 1024)));
|
||||
if (buffer.byteLength > maxBytes) throw new HttpError(413, "upload-content body is too large", { maxBytes, bytes: buffer.byteLength });
|
||||
const remotePath = remoteFilePath(String(body.remotePath || ""), safeStagingFilename(String(body.filename || ""), "upload.txt"));
|
||||
const filename = safeStagingFilename(String(body.filename || pathPosix.basename(remotePath)), pathPosix.basename(remotePath) || "upload.txt");
|
||||
const relativePath = pathPosix.join("content-uploads", `${Date.now()}-${randomUUID().slice(0, 8)}-${filename}`);
|
||||
const localPath = resolveStagingPath(relativePath);
|
||||
mkdirSync(dirname(localPath), { recursive: true });
|
||||
await writeFile(localPath, buffer);
|
||||
const sha256 = createHash("sha256").update(buffer).digest("hex");
|
||||
const created = await createTransferJob("upload", { localPath: relativePath, remotePath });
|
||||
return {
|
||||
ok: created.ok === true,
|
||||
source,
|
||||
remotePath,
|
||||
local: { relativePath, bytes: buffer.byteLength, sha256 },
|
||||
job: asRecord(created.job),
|
||||
};
|
||||
}
|
||||
|
||||
async function computeMd5Blocks(filePath: string, jobId: string): Promise<{ size: number; fullMd5: string; blocks: string[] }> {
|
||||
const info = await stat(filePath);
|
||||
if (!info.isFile()) throw new HttpError(400, "localPath must be a file inside staging directory", { localPath: filePath });
|
||||
@@ -1363,6 +1395,7 @@ async function route(req: Request): Promise<Response> {
|
||||
if (path === "/api/folders" && req.method === "POST") return jsonResponse(await createFolder(await readJsonBody(req)));
|
||||
if (path === "/api/files/manage" && req.method === "POST") return jsonResponse(await manageFiles(await readJsonBody(req)));
|
||||
if (path === "/api/transfers/upload-from-path" && req.method === "POST") return jsonResponse(await createTransferJob("upload", await readJsonBody(req)));
|
||||
if (path === "/api/transfers/upload-content" && req.method === "POST") return jsonResponse(await createContentUpload(await readJsonBody(req)));
|
||||
if (path === "/api/transfers/download-to-path" && req.method === "POST") return jsonResponse(await createTransferJob("download", await readJsonBody(req)));
|
||||
if (path === "/api/self-test" && req.method === "POST") return jsonResponse(await runSelfTest(await readJsonBody(req)));
|
||||
if (path === "/api/transfers" && req.method === "GET") return jsonResponse(await listTransfers(url));
|
||||
|
||||
Reference in New Issue
Block a user