banatie-service/apps/api-service/src/routes/upload.ts

115 lines
3.6 KiB
TypeScript

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);
}
}),
);