first commit
Docker-Images bauen und veroeffentlichen / build-and-push (push) Failing after 1m32s

This commit is contained in:
Nick Szaunig committed 2026-09-27 06:06:56 +02:00
commit 59bb5a38a3
132 files changed
+19435

No files matched your search

+70
View File
@@ -0,0 +1,70 @@
/**
* Auslieferung freigegebener Medien aus dem persistenten Verzeichnis
* MEDIA_DIR (Kubernetes: eigenes PersistentVolume, kein emptyDir).
*
* Nur lesend, nur erlaubte Dateiendungen, kein Verlassen des Verzeichnisses.
* Unterstuetzt Range-Anfragen, damit Videos spulbar sind.
*/
import { createReadStream } from "node:fs";
import { stat } from "node:fs/promises";
import path from "node:path";
import { Readable } from "node:stream";
import { env } from "@/lib/env";
const TYPES: Record<string, string> = {
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".png": "image/png",
".webp": "image/webp",
".avif": "image/avif",
".mp4": "video/mp4",
".webm": "video/webm",
".vtt": "text/vtt",
};
const SEGMENT = /^[A-Za-z0-9][A-Za-z0-9._-]{0,120}$/;
export async function GET(request: Request, { params }: { params: Promise<{ path: string[] }> }) {
const { path: segments } = await params;
const notFound = new Response("Nicht gefunden", { status: 404 });
if (segments.length === 0 || segments.length > 4 || !segments.every((s) => SEGMENT.test(s) && !s.includes(".."))) return notFound;
const base = path.resolve(env().MEDIA_DIR);
const file = path.resolve(base, ...segments);
if (!file.startsWith(base + path.sep)) return notFound;
const type = TYPES[path.extname(file).toLowerCase()];
if (!type) return notFound;
let info;
try {
info = await stat(file);
} catch {
return notFound;
}
if (!info.isFile()) return notFound;
const headers: Record<string, string> = {
"Content-Type": type,
"Accept-Ranges": "bytes",
"Cache-Control": "public, max-age=86400",
"X-Content-Type-Options": "nosniff",
};
const range = /^bytes=(\d*)-(\d*)$/.exec(request.headers.get("range") ?? "");
if (range && (range[1] || range[2])) {
const size = info.size;
let start = range[1] ? Number(range[1]) : size - Number(range[2]);
let end = range[1] && range[2] ? Number(range[2]) : size - 1;
start = Math.max(0, start);
end = Math.min(end, size - 1);
if (start > end) return new Response(null, { status: 416, headers: { "Content-Range": `bytes */${size}` } });
const stream = Readable.toWeb(createReadStream(file, { start, end })) as ReadableStream;
return new Response(stream, {
status: 206,
headers: { ...headers, "Content-Range": `bytes ${start}-${end}/${size}`, "Content-Length": String(end - start + 1) },
});
}
const stream = Readable.toWeb(createReadStream(file)) as ReadableStream;
return new Response(stream, { headers: { ...headers, "Content-Length": String(info.size) } });
}