chore(docker): .dockerignore angepasst; lokale Build-Schritte in Rebuild-Skripten; Doku/README zu production vs production-prebuilt aktualisiert

This commit is contained in:
2025-10-06 18:59:17 +02:00
parent 7a84130aec
commit 1124b1f40b
24 changed files with 1149 additions and 270 deletions

View File

@@ -3,6 +3,7 @@ import { z } from "zod";
import { randomUUID } from "crypto";
import { createKV } from "../lib/create-kv.js";
import { assertOwner } from "../lib/auth.js";
import { enforceAdminRateLimit } from "../lib/rate-limiter.js";
// Schema Definition
const GalleryPhotoSchema = z.object({
id: z.string(),
@@ -18,15 +19,16 @@ const galleryPhotosKV = createKV("galleryPhotos");
// CRUD Endpoints
const uploadPhoto = os
.input(z.object({
sessionId: z.string(),
base64Data: z
.string()
.regex(/^data:image\/(png|jpe?g|webp|gif);base64,/i, 'Unsupported image format'),
title: z.string().optional().default(""),
}))
.handler(async ({ input }) => {
.handler(async ({ input, context }) => {
try {
await assertOwner(input.sessionId);
await assertOwner(context);
// Admin Rate Limiting
await enforceAdminRateLimit(context);
const id = randomUUID();
const existing = await galleryPhotosKV.getAllItems();
const maxOrder = existing.length > 0 ? Math.max(...existing.map((p) => p.order)) : -1;
@@ -48,9 +50,11 @@ const uploadPhoto = os
}
});
const setCoverPhoto = os
.input(z.object({ sessionId: z.string(), id: z.string() }))
.handler(async ({ input }) => {
await assertOwner(input.sessionId);
.input(z.object({ id: z.string() }))
.handler(async ({ input, context }) => {
await assertOwner(context);
// Admin Rate Limiting
await enforceAdminRateLimit(context);
const all = await galleryPhotosKV.getAllItems();
let updatedCover = null;
for (const p of all) {
@@ -63,18 +67,21 @@ const setCoverPhoto = os
return updatedCover;
});
const deletePhoto = os
.input(z.object({ sessionId: z.string(), id: z.string() }))
.handler(async ({ input }) => {
await assertOwner(input.sessionId);
.input(z.object({ id: z.string() }))
.handler(async ({ input, context }) => {
await assertOwner(context);
// Admin Rate Limiting
await enforceAdminRateLimit(context);
await galleryPhotosKV.removeItem(input.id);
});
const updatePhotoOrder = os
.input(z.object({
sessionId: z.string(),
photoOrders: z.array(z.object({ id: z.string(), order: z.number().int() })),
}))
.handler(async ({ input }) => {
await assertOwner(input.sessionId);
.handler(async ({ input, context }) => {
await assertOwner(context);
// Admin Rate Limiting
await enforceAdminRateLimit(context);
const updated = [];
for (const { id, order } of input.photoOrders) {
const existing = await galleryPhotosKV.getItem(id);
@@ -92,9 +99,9 @@ const listPhotos = os.handler(async () => {
return all.sort((a, b) => (a.order - b.order) || a.createdAt.localeCompare(b.createdAt) || a.id.localeCompare(b.id));
});
const adminListPhotos = os
.input(z.object({ sessionId: z.string() }))
.handler(async ({ input }) => {
await assertOwner(input.sessionId);
.input(z.object({}))
.handler(async ({ context }) => {
await assertOwner(context);
const all = await galleryPhotosKV.getAllItems();
return all.sort((a, b) => (a.order - b.order) || a.createdAt.localeCompare(b.createdAt) || a.id.localeCompare(b.id));
});
@@ -107,9 +114,9 @@ const live = {
}
}),
adminListPhotos: os
.input(z.object({ sessionId: z.string() }))
.handler(async function* ({ input, signal }) {
await assertOwner(input.sessionId);
.input(z.object({}))
.handler(async function* ({ context, signal }) {
await assertOwner(context);
const all = await galleryPhotosKV.getAllItems();
const sorted = all.sort((a, b) => (a.order - b.order) || a.createdAt.localeCompare(b.createdAt) || a.id.localeCompare(b.id));
yield sorted;