first commit
This commit is contained in:
@@ -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;
|
||||
Reference in New Issue
Block a user