import { Response, Router } from 'express'; import type { Router as RouterType } from 'express'; import { randomUUID } from 'crypto'; import { StorageFactory } from '../services/StorageFactory'; import { asyncHandler } from '../middleware/errorHandler'; import { validateApiKey } from '../middleware/auth/validateApiKey'; import { requireProjectKey } from '../middleware/auth/requireProjectKey'; import { rateLimitByApiKey } from '../middleware/auth/rateLimiter'; import { uploadSingleImage, handleUploadErrors } from '../middleware/upload'; import { UploadFileResponse } from '../types/api'; export const uploadRouter: RouterType = Router(); /** * POST /api/upload - Upload a single image file */ uploadRouter.post( '/upload', // Authentication middleware validateApiKey, requireProjectKey, rateLimitByApiKey, // File upload middleware uploadSingleImage, handleUploadErrors, // Main handler asyncHandler(async (req: any, res: Response) => { const timestamp = new Date().toISOString(); const requestId = req.requestId; // Check if file was provided if (!req.file) { const errorResponse: UploadFileResponse = { success: false, message: 'File upload failed', error: 'No file provided', }; return res.status(400).json(errorResponse); } // Extract org/project slugs from validated API key const orgSlug = req.apiKey?.organizationSlug || process.env['DEFAULT_ORG_SLUG'] || 'default'; const projectSlug = req.apiKey?.projectSlug || process.env['DEFAULT_PROJECT_SLUG'] || 'main'; // Guaranteed by requireProjectKey middleware console.log( `[${timestamp}] [${requestId}] Starting file upload for org:${orgSlug}, project:${projectSlug}`, ); const file = req.file; try { // Initialize storage service const storageService = await StorageFactory.getInstance(); // Generate imageId (UUID) - this will be the filename in storage const imageId = randomUUID(); // Upload file to MinIO // Path format: {orgSlug}/{projectSlug}/img/{imageId} console.log( `[${timestamp}] [${requestId}] Uploading file: ${file.originalname} as ${imageId} (${file.size} bytes)`, ); const uploadResult = await storageService.uploadFile( orgSlug, projectSlug, imageId, file.buffer, file.mimetype, file.originalname, ); if (!uploadResult.success) { const errorResponse: UploadFileResponse = { success: false, message: 'File upload failed', error: uploadResult.error || 'Storage service error', }; return res.status(500).json(errorResponse); } // Prepare success response const successResponse: UploadFileResponse = { success: true, message: 'File uploaded successfully', data: { filename: uploadResult.filename, originalName: file.originalname, path: uploadResult.path, url: uploadResult.url, size: uploadResult.size, contentType: uploadResult.contentType, uploadedAt: timestamp, }, }; console.log(`[${timestamp}] [${requestId}] File uploaded successfully: ${uploadResult.url}`); return res.status(200).json(successResponse); } catch (error) { console.error(`[${timestamp}] [${requestId}] Unhandled error in upload endpoint:`, error); const errorResponse: UploadFileResponse = { success: false, message: 'File upload failed', error: error instanceof Error ? error.message : 'Unknown error occurred', }; return res.status(500).json(errorResponse); } }), );