Files
storybid/packages/server/src/routes/media.ts
T
2026-05-02 19:46:42 -05:00

65 lines
2.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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) });
}
},
);