feat: Add comprehensive system enhancements

- Add version display above storage consumption in admin sidebar
- Fix storage consumption to stick to bottom of window using flexbox
- Add user upload settings to events (allow uploads, category selection)
- Enhance disk space tab to comprehensive system status view
- Add localized date formatting for German/English language support
- Remove quick actions from dashboard for cleaner interface
- Create user photo upload functionality for galleries
- Add database migration for user upload settings
- Update all TypeScript types and interfaces

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
This commit is contained in:
2025-07-08 22:47:04 +02:00
co-authored by Claude
parent 69b56ed582
commit 12ba91952e
24 changed files with 1210 additions and 128 deletions
+73
View File
@@ -304,4 +304,77 @@ router.get('/:slug/stats', verifyGalleryAccess, async (req, res) => {
}
});
// User photo upload endpoint
router.post('/:eventId/upload', verifyGalleryAccess, async (req, res) => {
try {
const eventId = parseInt(req.params.eventId);
// Verify the event matches the token
if (req.event.id !== eventId) {
return res.status(403).json({ error: 'Access denied' });
}
// Check if user uploads are allowed
if (!req.event.allow_user_uploads) {
return res.status(403).json({ error: 'User uploads are not allowed for this event' });
}
// Import multer and photo processing
const multer = require('multer');
const upload = multer({
dest: '/tmp/uploads/',
limits: {
fileSize: 50 * 1024 * 1024, // 50MB
files: 10 // Max 10 files at once
},
fileFilter: (req, file, cb) => {
const allowedTypes = ['image/jpeg', 'image/png', 'image/webp'];
if (allowedTypes.includes(file.mimetype)) {
cb(null, true);
} else {
cb(new Error('Invalid file type'));
}
}
}).array('photos', 10);
// Handle upload
upload(req, res, async (err) => {
if (err) {
console.error('Upload error:', err);
return res.status(400).json({ error: err.message });
}
if (!req.files || req.files.length === 0) {
return res.status(400).json({ error: 'No files uploaded' });
}
const { processUploadedPhotos } = require('../services/photoProcessor');
const categoryId = req.body.category_id || req.event.upload_category_id || null;
try {
// Process uploaded photos
const results = await processUploadedPhotos(req.files, eventId, 'user', categoryId);
// Clean up temp files
const fs = require('fs').promises;
for (const file of req.files) {
await fs.unlink(file.path).catch(console.error);
}
res.json({
message: 'Photos uploaded successfully',
count: results.length,
photos: results
});
} catch (processError) {
console.error('Photo processing error:', processError);
res.status(500).json({ error: 'Failed to process photos' });
}
});
} catch (error) {
console.error('Upload route error:', error);
res.status(500).json({ error: 'Failed to upload photos' });
}
});
module.exports = router;