first commit

This commit is contained in:
Michaël
2026-01-29 14:13:11 -03:00
commit 2302748c87
105 changed files with 93301 additions and 0 deletions
+148
View File
@@ -0,0 +1,148 @@
import { Hono } from 'hono';
import { db, media } from '../db/index.js';
import { eq } 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, extname } from 'path';
const mediaRouter = new Hono();
const UPLOAD_DIR = './uploads';
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/avif'];
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
// 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 {
const body = await c.req.parseBody();
const file = body['file'] as File;
if (!file) {
return c.json({ error: 'No file provided' }, 400);
}
// Validate file type
if (!ALLOWED_TYPES.includes(file.type)) {
return c.json({ error: 'Invalid file type. Allowed: JPEG, PNG, GIF, WebP, AVIF' }, 400);
}
// Validate file size
if (file.size > MAX_FILE_SIZE) {
return c.json({ error: 'File too large. Maximum size: 5MB' }, 400);
}
await ensureUploadDir();
// Generate unique filename
const id = generateId();
const ext = extname(file.name) || '.jpg';
const filename = `${id}${ext}`;
const filepath = join(UPLOAD_DIR, filename);
// Write file
const arrayBuffer = await file.arrayBuffer();
await writeFile(filepath, Buffer.from(arrayBuffer));
// Get related info from form data
const relatedId = body['relatedId'] as string | undefined;
const relatedType = body['relatedType'] as string | undefined;
// Save to database
const now = getNow();
const mediaRecord = {
id,
fileUrl: `/uploads/${filename}`,
type: 'image' as const,
relatedId: relatedId || null,
relatedType: relatedType || null,
createdAt: now,
};
await (db as any).insert(media).values(mediaRecord);
return c.json({
media: mediaRecord,
url: mediaRecord.fileUrl,
}, 201);
} catch (error) {
console.error('Upload error:', error);
return c.json({ error: 'Failed to upload file' }, 500);
}
});
// Get media by ID
mediaRouter.get('/:id', async (c) => {
const id = c.req.param('id');
const mediaRecord = await (db as any)
.select()
.from(media)
.where(eq((media as any).id, id))
.get();
if (!mediaRecord) {
return c.json({ error: 'Media not found' }, 404);
}
return c.json({ media: mediaRecord });
});
// Delete media
mediaRouter.delete('/:id', requireAuth(['admin', 'organizer']), async (c) => {
const id = c.req.param('id');
const mediaRecord = await (db as any)
.select()
.from(media)
.where(eq((media as any).id, id))
.get();
if (!mediaRecord) {
return c.json({ error: 'Media not found' }, 404);
}
// Delete file from disk
try {
const filepath = join('.', mediaRecord.fileUrl);
if (existsSync(filepath)) {
await unlink(filepath);
}
} catch (error) {
console.error('Failed to delete file:', error);
}
// Delete from database
await (db as any).delete(media).where(eq((media as any).id, id));
return c.json({ message: 'Media deleted successfully' });
});
// List media (admin)
mediaRouter.get('/', requireAuth(['admin', 'organizer']), async (c) => {
const relatedType = c.req.query('relatedType');
const relatedId = c.req.query('relatedId');
let query = (db as any).select().from(media);
if (relatedType) {
query = query.where(eq((media as any).relatedType, relatedType));
}
if (relatedId) {
query = query.where(eq((media as any).relatedId, relatedId));
}
const result = await query.all();
return c.json({ media: result });
});
export default mediaRouter;