Refactor monolithic modules and harden booking, email, and auth infrastructure.
Split oversized frontend API client, email service, and admin/booking pages into focused modules while preserving import surfaces, and add Redis-backed queues, stale booking cleanup, stronger auth, and scale deployment configs. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -3,13 +3,10 @@ import { db, dbGet, dbAll, media } from '../db/index.js';
|
||||
import { eq, and } from 'drizzle-orm';
|
||||
import { requireAuth } from '../lib/auth.js';
|
||||
import { generateId, getNow } from '../lib/utils.js';
|
||||
import { writeFile, mkdir, unlink } from 'fs/promises';
|
||||
import { existsSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { getStorage, keyFromUrl } from '../lib/storage.js';
|
||||
|
||||
const mediaRouter = new Hono();
|
||||
|
||||
const UPLOAD_DIR = './uploads';
|
||||
const MAX_FILE_SIZE =
|
||||
(Number(process.env.MEDIA_MAX_UPLOAD_MB || '10') || 10) * 1024 * 1024; // default 10MB
|
||||
|
||||
@@ -51,13 +48,6 @@ function detectImageType(buf: Buffer): { mime: string; ext: string } | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Ensure upload directory exists
|
||||
async function ensureUploadDir() {
|
||||
if (!existsSync(UPLOAD_DIR)) {
|
||||
await mkdir(UPLOAD_DIR, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
// Upload image
|
||||
mediaRouter.post('/upload', requireAuth(['admin', 'organizer']), async (c) => {
|
||||
try {
|
||||
@@ -83,15 +73,13 @@ mediaRouter.post('/upload', requireAuth(['admin', 'organizer']), async (c) => {
|
||||
return c.json({ error: 'Invalid file. Allowed: JPEG, PNG, GIF, WebP, AVIF' }, 400);
|
||||
}
|
||||
|
||||
await ensureUploadDir();
|
||||
|
||||
// Generate unique filename using the *detected* extension (ignore client filename)
|
||||
const id = generateId();
|
||||
const filename = `${id}${detected.ext}`;
|
||||
const filepath = join(UPLOAD_DIR, filename);
|
||||
|
||||
// Write file
|
||||
await writeFile(filepath, buffer);
|
||||
|
||||
// Persist via the storage backend (local disk or S3-compatible object store).
|
||||
const storage = getStorage();
|
||||
await storage.put(filename, buffer, detected.mime);
|
||||
|
||||
// Get related info from form data
|
||||
const relatedId = body['relatedId'] as string | undefined;
|
||||
@@ -101,7 +89,7 @@ mediaRouter.post('/upload', requireAuth(['admin', 'organizer']), async (c) => {
|
||||
const now = getNow();
|
||||
const mediaRecord = {
|
||||
id,
|
||||
fileUrl: `/uploads/${filename}`,
|
||||
fileUrl: storage.publicUrl(filename),
|
||||
type: 'image' as const,
|
||||
relatedId: relatedId || null,
|
||||
relatedType: relatedType || null,
|
||||
@@ -147,12 +135,9 @@ mediaRouter.delete('/:id', requireAuth(['admin', 'organizer']), async (c) => {
|
||||
return c.json({ error: 'Media not found' }, 404);
|
||||
}
|
||||
|
||||
// Delete file from disk
|
||||
// Delete the underlying object from the storage backend.
|
||||
try {
|
||||
const filepath = join('.', mediaRecord.fileUrl);
|
||||
if (existsSync(filepath)) {
|
||||
await unlink(filepath);
|
||||
}
|
||||
await getStorage().delete(keyFromUrl(mediaRecord.fileUrl));
|
||||
} catch (error) {
|
||||
console.error('Failed to delete file:', error);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user