Scaffold and Phase 1
This commit is contained in:
@@ -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) });
|
||||
}
|
||||
},
|
||||
);
|
||||
Reference in New Issue
Block a user