Persist event sign designs and harden invitation permissions
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { z } from "zod";
|
||||
export { SIGN_PAPERS, savedSignSchema, type SavedSign } from "./sign";
|
||||
|
||||
export const eventStatusSchema = z.enum(["draft", "published", "closed"]);
|
||||
export const groupRoleSchema = z.enum(["owner", "member"]);
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { savedSignSchema } from "./sign";
|
||||
|
||||
test("saved designs reject unsafe assets, invalid settings, and embedded runtime fonts", () => {
|
||||
const design = { title: "Our event", headline: "Share your moments", message: "Photos welcome", paper: "card6x4", ink: "indigo" };
|
||||
expect(savedSignSchema.safeParse(design).success).toBe(true);
|
||||
for (const patch of [{ paper: "constructor" }, { title: "" }, { headline: "x".repeat(45) }, { customWidth: Infinity }, { background: "red" }, { backgroundImage: "https://example.com/photo.png" }, { logoImage: "data:image/svg+xml;base64,AA==" }, { fontData: "data:font/woff2;base64,AA==" }]) {
|
||||
expect(savedSignSchema.safeParse({ ...design, ...patch }).success).toBe(false);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const SIGN_PAPERS = {
|
||||
letter: { label: "US Letter · 8.5 × 11 in", width: "8.5in", height: "11in", viewHeight: 1100 },
|
||||
a4: { label: "A4 · 210 × 297 mm", width: "210mm", height: "297mm", viewHeight: 1202 },
|
||||
letterLandscape: { label: "US Letter landscape · 11 × 8.5 in", width: "11in", height: "8.5in", viewHeight: 850 * 8.5 / 11 },
|
||||
a4Landscape: { label: "A4 landscape · 297 × 210 mm", width: "297mm", height: "210mm", viewHeight: 850 * 210 / 297 },
|
||||
a5: { label: "A5 · 148 × 210 mm", width: "148mm", height: "210mm", viewHeight: 850 * 210 / 148 },
|
||||
a5Landscape: { label: "A5 landscape · 210 × 148 mm", width: "210mm", height: "148mm", viewHeight: 850 * 148 / 210 },
|
||||
card3x5: { label: "Small card · 3 × 5 in", width: "3in", height: "5in", viewHeight: 850 * 5 / 3 },
|
||||
card5x3: { label: "Small landscape card · 5 × 3 in", width: "5in", height: "3in", viewHeight: 510 },
|
||||
card4x6: { label: "Postcard · 4 × 6 in", width: "4in", height: "6in", viewHeight: 1275 },
|
||||
card6x4: { label: "Landscape postcard · 6 × 4 in", width: "6in", height: "4in", viewHeight: 850 * 4 / 6 },
|
||||
card5x7: { label: "Table card · 5 × 7 in", width: "5in", height: "7in", viewHeight: 1190 },
|
||||
card7x5: { label: "Landscape table card · 7 × 5 in", width: "7in", height: "5in", viewHeight: 850 * 5 / 7 },
|
||||
square: { label: "Square card · 5 × 5 in", width: "5in", height: "5in", viewHeight: 850 },
|
||||
} as const;
|
||||
|
||||
const image = z.string().max(12 * 1024 * 1024).regex(/^data:image\/(png|jpeg|webp);base64,[A-Za-z0-9+/=]+$/).optional();
|
||||
const papers: ["custom", ...(keyof typeof SIGN_PAPERS)[]] = ["custom", ...Object.keys(SIGN_PAPERS) as (keyof typeof SIGN_PAPERS)[]];
|
||||
export const savedSignSchema = z.object({
|
||||
title: z.string().trim().min(1).max(120), headline: z.string().trim().min(1).max(44),
|
||||
message: z.string().trim().min(1).max(120),
|
||||
paper: z.enum(papers),
|
||||
ink: z.enum(["indigo", "black"]), layout: z.enum(["photo", "typography", "compact"]).optional(),
|
||||
customWidth: z.number().finite().positive().optional(), customHeight: z.number().finite().positive().optional(),
|
||||
unit: z.enum(["in", "mm"]).optional(), background: z.string().regex(/^#[a-f0-9]{6}$/i).optional(),
|
||||
accent: z.string().regex(/^#[a-f0-9]{6}$/i).optional(), font: z.enum(["funnel", "inter", "geologica"]).optional(),
|
||||
decoration: z.enum(["arch", "frame", "minimal"]).optional(), showBrand: z.boolean().optional(),
|
||||
backgroundImage: image, logoImage: image,
|
||||
}).strict();
|
||||
export type SavedSign = z.infer<typeof savedSignSchema>;
|
||||
Reference in New Issue
Block a user