const express = require('express'); const multer = require('multer'); const path = require('path'); const fs = require('fs'); const crypto = require('crypto'); const cors = require('cors'); const app = express(); const PORT = process.env.PORT || 3000; // Enable CORS for React frontend app.use(cors()); app.use(express.json()); // Create uploads directory if it doesn't exist const uploadsDir = 'uploads'; if (!fs.existsSync(uploadsDir)) { fs.mkdirSync(uploadsDir, { recursive: true }); } // Configure multer for file upload const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, uploadsDir); }, filename: function (req, file, cb) { // Generate unique filename const uniqueSuffix = Date.now() + '-' + crypto.randomUUID(); const extension = path.extname(file.originalname); cb(null, file.fieldname + '-' + uniqueSuffix + extension); } }); // File filter for images only const fileFilter = (req, file, cb) => { const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp']; if (allowedTypes.includes(file.mimetype)) { cb(null, true); } else { cb(new Error('Invalid file type. Only JPEG, PNG, GIF, and WebP are allowed.'), false); } }; const upload = multer({ storage: storage, limits: { fileSize: 5 * 1024 * 1024, // 5MB limit }, fileFilter: fileFilter }); // Image upload endpoint app.post('/api/upload', upload.single('image'), async (req, res) => { try { if (!req.file) { return res.status(400).json({ success: false, message: 'No image file provided' }); } const filePath = req.file.path; const originalName = req.file.originalname; const filename = req.file.filename; const size = req.file.size; const mimetype = req.file.mimetype; // Save metadata to database (example with JSON file) const imageMetadata = { id: crypto.randomUUID(), originalName: originalName, filename: filename, filePath: filePath, size: size, mimetype: mimetype, uploadedAt: new Date().toISOString(), url: `/api/images/${filename}` }; // Save to simple JSON database (replace with real database) const dbPath = 'image_database.json'; let database = []; if (fs.existsSync(dbPath)) { const dbContent = fs.readFileSync(dbPath, 'utf8'); database = JSON.parse(dbContent); } database.push(imageMetadata); fs.writeFileSync(dbPath, JSON.stringify(database, null, 2)); res.status(200).json({ success: true, message: 'Image uploaded successfully', data: { id: imageMetadata.id, filename: filename, originalName: originalName, size: size, url: imageMetadata.url, uploadedAt: imageMetadata.uploadedAt } }); } catch (error) { console.error('Upload error:', error); // Clean up file if it was uploaded but processing failed if (req.file && fs.existsSync(req.file.path)) { fs.unlinkSync(req.file.path); } res.status(500).json({ success: false, message: 'Internal server error during upload' }); } }); // Multiple image upload endpoint app.post('/api/upload-multiple', upload.array('images', 10), async (req, res) => { try { if (!req.files || req.files.length === 0) { return res.status(400).json({ success: false, message: 'No image files provided' }); } const uploadedImages = []; const dbPath = 'image_database.json'; let database = []; if (fs.existsSync(dbPath)) { const dbContent = fs.readFileSync(dbPath, 'utf8'); database = JSON.parse(dbContent); } for (const file of req.files) { const imageMetadata = { id: crypto.randomUUID(), originalName: file.originalname, filename: file.filename, filePath: file.path, size: file.size, mimetype: file.mimetype, uploadedAt: new Date().toISOString(), url: `/api/images/${file.filename}` }; database.push(imageMetadata); uploadedImages.push({ id: imageMetadata.id, filename: file.filename, originalName: file.originalname, size: file.size, url: imageMetadata.url }); } fs.writeFileSync(dbPath, JSON.stringify(database, null, 2)); res.status(200).json({ success: true, message: `${uploadedImages.length} images uploaded successfully`, data: uploadedImages }); } catch (error) { console.error('Multiple upload error:', error); res.status(500).json({ success: false, message: 'Internal server error during upload' }); } }); // Serve uploaded images app.get('/api/images/:filename', (req, res) => { const filename = req.params.filename; const filePath = path.join(__dirname, uploadsDir, filename); if (fs.existsSync(filePath)) { res.sendFile(path.resolve(filePath)); } else { res.status(404).json({ success: false, message: 'Image not found' }); } }); // Get all uploaded images app.get('/api/images', (req, res) => { try { const dbPath = 'image_database.json'; if (!fs.existsSync(dbPath)) { return res.json({ success: true, data: [] }); } const dbContent = fs.readFileSync(dbPath, 'utf8'); const database = JSON.parse(dbContent); res.json({ success: true, data: database.map(img => ({ id: img.id, originalName: img.originalName, filename: img.filename, size: img.size, url: img.url, uploadedAt: img.uploadedAt })) }); } catch (error) { console.error('Get images error:', error); res.status(500).json({ success: false, message: 'Error retrieving images' }); } }); // Delete image app.delete('/api/images/:id', (req, res) => { try { const imageId = req.params.id; const dbPath = 'image_database.json'; if (!fs.existsSync(dbPath)) { return res.status(404).json({ success: false, message: 'Image not found' }); } const dbContent = fs.readFileSync(dbPath, 'utf8'); let database = JSON.parse(dbContent); const imageIndex = database.findIndex(img => img.id === imageId); if (imageIndex === -1) { return res.status(404).json({ success: false, message: 'Image not found' }); } const image = database[imageIndex]; // Delete file from filesystem if (fs.existsSync(image.filePath)) { fs.unlinkSync(image.filePath); } // Remove from database database.splice(imageIndex, 1); fs.writeFileSync(dbPath, JSON.stringify(database, null, 2)); res.json({ success: true, message: 'Image deleted successfully' }); } catch (error) { console.error('Delete image error:', error); res.status(500).json({ success: false, message: 'Error deleting image' }); } }); // Error handling middleware app.use((error, req, res, next) => { if (error instanceof multer.MulterError) { if (error.code === 'LIMIT_FILE_SIZE') { return res.status(400).json({ success: false, message: 'File too large. Maximum size is 5MB.' }); } } if (error.message.includes('Invalid file type')) { return res.status(400).json({ success: false, message: error.message }); } console.error('Unhandled error:', error); res.status(500).json({ success: false, message: 'Internal server error' }); }); // Health check endpoint app.get('/health', (req, res) => { res.json({ success: true, message: 'Image upload API is running', timestamp: new Date().toISOString() }); }); app.listen(PORT, () => { console.log(`Image upload API server running on port ${PORT}`); console.log(`Health check: http://localhost:${PORT}/health`); console.log(`Upload endpoint: http://localhost:${PORT}/api/upload`); }); module.exports = app;