Scaffold and Phase 1

This commit is contained in:
2026-05-02 19:46:42 -05:00
parent ab74e7cad4
commit d909cb7c30
92 changed files with 4967 additions and 0 deletions
+64
View File
@@ -0,0 +1,64 @@
/**
* POST /api/media/upload multipart upload; saves to local disk
* DELETE /api/media/:key delete a file by key (admin/event_manager)
*
* Upload flow (replaces the old presigned-URL pattern):
* 1. Client POSTs multipart/form-data with fields: itemId, mediaType, plus the file
* 2. Server saves to UPLOAD_DIR/items/<itemId>/<uuid>.<ext>
* 3. Server returns { url, key, mimetype, sizeBytes }
* 4. Client calls POST /api/items/:id/media with { mediaType, url } to attach the
* record to the item (existing endpoint in routes/items.ts)
*
* Files are served as static assets at /media/* (see app.ts).
* Everything stays on the local machine — no internet required.
*/
import { Router } from "express";
import { requireAuth, requireRole } from "../middleware/auth.js";
import { upload, resolveFile, deleteFile, type MediaType } from "../services/storage.js";
export const mediaRouter = Router();
const STAFF_WRITE = requireRole("admin", "event_manager");
// ── Upload ─────────────────────────────────────────────────────────────────────
mediaRouter.post(
"/upload",
requireAuth,
STAFF_WRITE,
// Parse a single file field named "file" plus any text fields (itemId, mediaType)
upload.single("file"),
(req, res) => {
if (!req.file) {
res.status(400).json({ error: "No file received" });
return;
}
const mediaType = (req.body as { mediaType?: string }).mediaType as MediaType | undefined;
if (!mediaType || !["image", "video", "document"].includes(mediaType)) {
res.status(400).json({ error: "mediaType must be image, video, or document" });
return;
}
try {
const saved = resolveFile(req.file, mediaType);
res.status(201).json(saved);
} catch (err) {
res.status(400).json({ error: String(err) });
}
},
);
// ── Delete ─────────────────────────────────────────────────────────────────────
mediaRouter.delete(
"/:key(*)", // key contains slashes, e.g. items/abc/uuid.jpg
requireAuth,
STAFF_WRITE,
async (req, res) => {
try {
await deleteFile(req.params["key"] ?? "");
res.json({ ok: true });
} catch (err) {
res.status(400).json({ error: String(err) });
}
},
);