feat: full site build — Project/Melody schema (Option A), admin CRUD, public sections, uploads, email+SMTP, internal analytics, legal pages, docs
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const uploadKindSchema = z.enum(["image", "audio"]);
|
||||
|
||||
export type UploadKind = z.infer<typeof uploadKindSchema>;
|
||||
|
||||
export const uploadRequestSchema = z.object({
|
||||
kind: uploadKindSchema,
|
||||
fileName: z.string().trim().min(1).max(255),
|
||||
mimeType: z.string().trim().min(1).max(120),
|
||||
size: z.number().int().positive(),
|
||||
});
|
||||
|
||||
export const uploadedFileSchema = z.object({
|
||||
kind: uploadKindSchema,
|
||||
url: z.string().startsWith("/api/uploads/"),
|
||||
fileName: z.string().min(1),
|
||||
mimeType: z.string().min(1),
|
||||
size: z.number().int().positive(),
|
||||
});
|
||||
|
||||
export type UploadRequest = z.infer<typeof uploadRequestSchema>;
|
||||
export type UploadedFileMetadata = z.infer<typeof uploadedFileSchema>;
|
||||
|
||||
export const UPLOAD_RULES: Record<
|
||||
UploadKind,
|
||||
{
|
||||
maxBytes: number;
|
||||
accept: string;
|
||||
mimeTypes: readonly string[];
|
||||
}
|
||||
> = {
|
||||
image: {
|
||||
maxBytes: 10 * 1024 * 1024,
|
||||
accept: "image/jpeg,image/png,image/webp",
|
||||
mimeTypes: ["image/jpeg", "image/png", "image/webp"],
|
||||
},
|
||||
audio: {
|
||||
maxBytes: 50 * 1024 * 1024,
|
||||
accept: "audio/mpeg,audio/wav,audio/x-wav,audio/ogg,audio/webm,audio/mp4,audio/aac,audio/flac",
|
||||
mimeTypes: [
|
||||
"audio/mpeg",
|
||||
"audio/wav",
|
||||
"audio/x-wav",
|
||||
"audio/ogg",
|
||||
"audio/webm",
|
||||
"audio/mp4",
|
||||
"audio/aac",
|
||||
"audio/flac",
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export function isAllowedUploadMimeType(kind: UploadKind, mimeType: string) {
|
||||
return UPLOAD_RULES[kind].mimeTypes.includes(mimeType.toLowerCase());
|
||||
}
|
||||
Reference in New Issue
Block a user