since3-portal/modules/shared/schemas/documents.ts
2026-09-29 16:15:28 +02:00

79 lines
2.9 KiB
TypeScript

import * as z from "zod";
export const MAX_FILE_BYTES = 50 * 1024 * 1024;
export const MAX_FILES_PER_UPLOAD = 20;
export const DOCUMENTS_BUCKET = "client-documents";
export type Folder = {
id: string;
name: string;
documentCount: number;
};
export type DocumentItem = {
id: string;
name: string;
mimeType: string | null;
sizeBytes: number | null;
createdAt: string;
thumbnailUrl: string | null;
};
export type FileKind = "image" | "pdf" | "text" | "spreadsheet" | "presentation" | "document" | "archive" | "video" | "audio" | "other";
const EXTENSION_KINDS: Record<string, FileKind> = {
png: "image", jpg: "image", jpeg: "image", gif: "image", webp: "image", svg: "image",
pdf: "pdf",
txt: "text", md: "text", csv: "spreadsheet", json: "text",
xls: "spreadsheet", xlsx: "spreadsheet", ods: "spreadsheet",
ppt: "presentation", pptx: "presentation", odp: "presentation",
doc: "document", docx: "document", odt: "document", rtf: "document",
zip: "archive", rar: "archive", "7z": "archive", gz: "archive",
mp4: "video", mov: "video", webm: "video",
mp3: "audio", wav: "audio", m4a: "audio",
};
export function fileKind(name: string, mimeType: string | null): FileKind {
const extension = name.split(".").pop()?.toLowerCase() ?? "";
if (EXTENSION_KINDS[extension]) return EXTENSION_KINDS[extension];
if (mimeType?.startsWith("image/")) return "image";
if (mimeType?.startsWith("video/")) return "video";
if (mimeType?.startsWith("audio/")) return "audio";
if (mimeType === "application/pdf") return "pdf";
if (mimeType?.startsWith("text/")) return "text";
return "other";
}
// SVG wird bewusst nicht inline angezeigt (kann Skripte enthalten), nur heruntergeladen.
export function canPreview(kind: FileKind, name: string) {
if (kind === "image") return !name.toLowerCase().endsWith(".svg");
return kind === "pdf" || kind === "text" || kind === "video" || kind === "audio";
}
export function formatFileSize(bytes: number | null) {
if (bytes === null) return "";
if (bytes < 1024) return `${bytes} B`;
const units = ["KB", "MB", "GB"];
let value = bytes / 1024;
let unit = 0;
while (value >= 1024 && unit < units.length - 1) {
value /= 1024;
unit++;
}
return `${value.toLocaleString("de-DE", { maximumFractionDigits: value < 10 ? 1 : 0 })} ${units[unit]}`;
}
export const UploadFileSchema = z.object({
name: z.string().trim().min(1).max(255),
size: z.number().int().positive({ error: "Leere Dateien können nicht hochgeladen werden." }).max(MAX_FILE_BYTES, {
error: "Dateien dürfen maximal 50 MB groß sein.",
}),
type: z.string().max(255),
});
export const FolderNameSchema = z
.string()
.trim()
.min(1, { error: "Name ist erforderlich." })
.max(100, { error: "Name ist zu lang." })
.refine((value) => !/[\\/]/.test(value), { error: "Name darf keine Schrägstriche enthalten." });