internaluploader: server + docs
One-way upload server: authentik SSO pairing (QR + app-initiated deep link), hashed device tokens, streamed sha256-verified uploads into the metube directory as <sha256>.<ext>. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
72
server/test/upload.test.ts
Normal file
72
server/test/upload.test.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { createHash, randomBytes } from "node:crypto";
|
||||
import { mkdtempSync } from "node:fs";
|
||||
import { readFile, readdir, stat } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { Readable } from "node:stream";
|
||||
import { test } from "node:test";
|
||||
|
||||
import { UploadDir, hashOf, isValidObjectName } from "../src/upload.ts";
|
||||
|
||||
test("object name validation", () => {
|
||||
const hash = "a".repeat(64);
|
||||
assert.ok(isValidObjectName(hash));
|
||||
assert.ok(isValidObjectName(`${hash}.mp4`));
|
||||
assert.ok(isValidObjectName(`${hash}.webm`));
|
||||
assert.ok(!isValidObjectName(`${hash}.`));
|
||||
assert.ok(!isValidObjectName(`${hash}.MP4`), "uppercase extension");
|
||||
assert.ok(!isValidObjectName(`${hash}.m p4`), "space in extension");
|
||||
assert.ok(!isValidObjectName(`../../etc/passwd`), "traversal");
|
||||
assert.ok(!isValidObjectName(`${"a".repeat(63)}.mp4`), "short hash");
|
||||
assert.ok(!isValidObjectName(`${hash}.${"x".repeat(17)}`), "long extension");
|
||||
assert.equal(hashOf(`${hash}.mp4`), hash);
|
||||
});
|
||||
|
||||
function payload(bytes: Buffer): { name: string; body: Readable } {
|
||||
const digest = createHash("sha256").update(bytes).digest("hex");
|
||||
return { name: `${digest}.bin`, body: Readable.from([bytes]) };
|
||||
}
|
||||
|
||||
test("receive stores a verified upload and is idempotent", async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "uploads-"));
|
||||
const uploads = new UploadDir(dir, 1024 * 1024);
|
||||
await uploads.init();
|
||||
|
||||
const bytes = randomBytes(4096);
|
||||
const { name, body } = payload(bytes);
|
||||
|
||||
assert.deepEqual(await uploads.receive(name, body), { status: "created" });
|
||||
assert.deepEqual(await readFile(join(dir, name)), bytes);
|
||||
assert.ok(await uploads.exists(name));
|
||||
|
||||
const again = await uploads.receive(name, Readable.from([bytes]));
|
||||
assert.deepEqual(again, { status: "exists" });
|
||||
|
||||
// The temp dir holds no leftovers either way.
|
||||
assert.deepEqual(await readdir(join(dir, ".internaluploader-tmp")), []);
|
||||
});
|
||||
|
||||
test("receive rejects a body that does not match its name", async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "uploads-"));
|
||||
const uploads = new UploadDir(dir, 1024 * 1024);
|
||||
await uploads.init();
|
||||
|
||||
const name = `${"0".repeat(64)}.bin`;
|
||||
const outcome = await uploads.receive(name, Readable.from([randomBytes(16)]));
|
||||
assert.equal(outcome.status, "hash-mismatch");
|
||||
await assert.rejects(stat(join(dir, name)));
|
||||
assert.deepEqual(await readdir(join(dir, ".internaluploader-tmp")), []);
|
||||
});
|
||||
|
||||
test("receive enforces the size ceiling", async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "uploads-"));
|
||||
const uploads = new UploadDir(dir, 1000);
|
||||
await uploads.init();
|
||||
|
||||
const bytes = randomBytes(4096);
|
||||
const { name, body } = payload(bytes);
|
||||
const outcome = await uploads.receive(name, body);
|
||||
assert.equal(outcome.status, "too-large");
|
||||
assert.deepEqual(await readdir(join(dir, ".internaluploader-tmp")), []);
|
||||
});
|
||||
Reference in New Issue
Block a user