const express = require('express'); const multer = require('multer'); const path = require('path'); const fs = require('fs'); const { v4: uuidv4 } = require('uuid'); const { db, canAccessTrip } = require('../db/database'); const { authenticate, demoUploadBlock } = require('../middleware/auth'); const router = express.Router({ mergeParams: true }); const photosDir = path.join(__dirname, '../../uploads/photos'); const storage = multer.diskStorage({ destination: (req, file, cb) => { if (!fs.existsSync(photosDir)) fs.mkdirSync(photosDir, { recursive: true }); cb(null, photosDir); }, filename: (req, file, cb) => { const ext = path.extname(file.originalname); cb(null, `${uuidv4()}${ext}`); }, }); const upload = multer({ storage, limits: { fileSize: 10 * 1024 * 1024 }, // 10MB fileFilter: (req, file, cb) => { if (file.mimetype.startsWith('image/')) { cb(null, true); } else { cb(new Error('Nur Bilddateien sind erlaubt')); } }, }); function formatPhoto(photo) { return { ...photo, url: `/uploads/photos/${photo.filename}`, }; } // GET /api/trips/:tripId/photos router.get('/', authenticate, (req, res) => { const { tripId } = req.params; const { day_id, place_id } = req.query; const trip = canAccessTrip(tripId, req.user.id); if (!trip) return res.status(404).json({ error: 'Reise nicht gefunden' }); let query = 'SELECT * FROM photos WHERE trip_id = ?'; const params = [tripId]; if (day_id) { query += ' AND day_id = ?'; params.push(day_id); } if (place_id) { query += ' AND place_id = ?'; params.push(place_id); } query += ' ORDER BY created_at DESC'; const photos = db.prepare(query).all(...params); res.json({ photos: photos.map(formatPhoto) }); }); // POST /api/trips/:tripId/photos router.post('/', authenticate, demoUploadBlock, upload.array('photos', 20), (req, res) => { const { tripId } = req.params; const { day_id, place_id, caption } = req.body; const trip = canAccessTrip(tripId, req.user.id); if (!trip) { // Delete uploaded files on auth failure if (req.files) req.files.forEach(f => fs.unlinkSync(f.path)); return res.status(404).json({ error: 'Reise nicht gefunden' }); } if (!req.files || req.files.length === 0) { return res.status(400).json({ error: 'Keine Dateien hochgeladen' }); } const insertPhoto = db.prepare(` INSERT INTO photos (trip_id, day_id, place_id, filename, original_name, file_size, mime_type, caption) VALUES (?, ?, ?, ?, ?, ?, ?, ?) `); const photos = []; db.exec('BEGIN'); try { for (const file of req.files) { const result = insertPhoto.run( tripId, day_id || null, place_id || null, file.filename, file.originalname, file.size, file.mimetype, caption || null ); const photo = db.prepare('SELECT * FROM photos WHERE id = ?').get(result.lastInsertRowid); photos.push(formatPhoto(photo)); } db.exec('COMMIT'); } catch (e) { db.exec('ROLLBACK'); throw e; } res.status(201).json({ photos }); }); // PUT /api/trips/:tripId/photos/:id router.put('/:id', authenticate, (req, res) => { const { tripId, id } = req.params; const { caption, day_id, place_id } = req.body; const trip = canAccessTrip(tripId, req.user.id); if (!trip) return res.status(404).json({ error: 'Reise nicht gefunden' }); const photo = db.prepare('SELECT * FROM photos WHERE id = ? AND trip_id = ?').get(id, tripId); if (!photo) return res.status(404).json({ error: 'Foto nicht gefunden' }); db.prepare(` UPDATE photos SET caption = COALESCE(?, caption), day_id = ?, place_id = ? WHERE id = ? `).run( caption !== undefined ? caption : photo.caption, day_id !== undefined ? (day_id || null) : photo.day_id, place_id !== undefined ? (place_id || null) : photo.place_id, id ); const updated = db.prepare('SELECT * FROM photos WHERE id = ?').get(id); res.json({ photo: formatPhoto(updated) }); }); // DELETE /api/trips/:tripId/photos/:id router.delete('/:id', authenticate, (req, res) => { const { tripId, id } = req.params; const trip = canAccessTrip(tripId, req.user.id); if (!trip) return res.status(404).json({ error: 'Reise nicht gefunden' }); const photo = db.prepare('SELECT * FROM photos WHERE id = ? AND trip_id = ?').get(id, tripId); if (!photo) return res.status(404).json({ error: 'Foto nicht gefunden' }); // Delete file const filePath = path.join(photosDir, photo.filename); if (fs.existsSync(filePath)) { try { fs.unlinkSync(filePath); } catch (e) { console.error('Error deleting photo file:', e); } } db.prepare('DELETE FROM photos WHERE id = ?').run(id); res.json({ success: true }); }); module.exports = router;