mirror of
https://github.com/ceratic/project_vollidioten_website.git
synced 2026-05-14 00:16:47 +02:00
feat: add LogoManagementModal component for logo upload and management
This commit is contained in:
@@ -3,6 +3,9 @@ const session = require('express-session');
|
||||
const passport = require('passport');
|
||||
const DiscordStrategy = require('passport-discord').Strategy;
|
||||
const cors = require('cors');
|
||||
const multer = require('multer');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { db, init } = require('./database');
|
||||
|
||||
const app = express();
|
||||
@@ -13,6 +16,40 @@ const CLIENT_ID = process.env.DISCORD_CLIENT_ID || 'mock_id';
|
||||
const CLIENT_SECRET = process.env.DISCORD_CLIENT_SECRET || 'mock_secret';
|
||||
const CALLBACK_URL = process.env.CALLBACK_URL || 'http://localhost:3000/auth/discord/callback';
|
||||
const FRONTEND_URL = process.env.FRONTEND_URL || 'http://localhost:8000';
|
||||
const BACKEND_URL = process.env.BACKEND_URL || 'https://vollidioten.ceraticsoft.de';
|
||||
|
||||
// File upload configuration
|
||||
const UPLOAD_DIR = path.join(__dirname, 'uploads');
|
||||
if (!fs.existsSync(UPLOAD_DIR)) {
|
||||
fs.mkdirSync(UPLOAD_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
// Configure multer for file uploads
|
||||
const storage = multer.diskStorage({
|
||||
destination: (req, file, cb) => {
|
||||
cb(null, UPLOAD_DIR);
|
||||
},
|
||||
filename: (req, file, cb) => {
|
||||
// Generate unique filename with timestamp
|
||||
const uniqueName = Date.now() + '-' + Math.round(Math.random() * 1E9) + path.extname(file.originalname);
|
||||
cb(null, uniqueName);
|
||||
}
|
||||
});
|
||||
|
||||
const upload = multer({
|
||||
storage: storage,
|
||||
limits: {
|
||||
fileSize: 10 * 1024 * 1024, // 10MB limit
|
||||
},
|
||||
fileFilter: (req, file, cb) => {
|
||||
// Allow only image files
|
||||
if (file.mimetype.startsWith('image/')) {
|
||||
cb(null, true);
|
||||
} else {
|
||||
cb(new Error('Nur Bilddateien sind erlaubt'), false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
init(); // Initialize DB
|
||||
|
||||
@@ -239,13 +276,32 @@ app.get('/api/orgs', (req, res) => {
|
||||
if (err) return res.status(500).json({error: err.message});
|
||||
const parsed = rows.map(r => ({
|
||||
...r,
|
||||
gallery: JSON.parse(r.gallery),
|
||||
gallery: JSON.parse(r.gallery || '[]'),
|
||||
bannerUrl: getImageUrl(r.bannerImageId),
|
||||
logoUrl: getImageUrl(r.logoImageId),
|
||||
cityStats: r.cityStats ? JSON.parse(r.cityStats) : undefined
|
||||
}));
|
||||
res.json(parsed);
|
||||
});
|
||||
});
|
||||
|
||||
// Helper function to get full image URL
|
||||
function getImageUrl(imageId) {
|
||||
if (!imageId) return null;
|
||||
return `${BACKEND_URL}/api/images/${imageId}`;
|
||||
}
|
||||
|
||||
// Helper function to resolve gallery image IDs to URLs
|
||||
function resolveGalleryImages(gallery) {
|
||||
if (!gallery) return [];
|
||||
try {
|
||||
const imageIds = JSON.parse(gallery);
|
||||
return imageIds.map(id => ({ id, url: getImageUrl(id) }));
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// API: Projects
|
||||
app.get('/api/projects', (req, res) => {
|
||||
db.all("SELECT * FROM projects", (err, rows) => {
|
||||
@@ -254,7 +310,9 @@ app.get('/api/projects', (req, res) => {
|
||||
...r,
|
||||
employees: JSON.parse(r.employees),
|
||||
shopCatalog: JSON.parse(r.shopCatalog),
|
||||
gallery: JSON.parse(r.gallery),
|
||||
gallery: resolveGalleryImages(r.gallery),
|
||||
bannerUrl: getImageUrl(r.bannerImageId),
|
||||
logoUrl: getImageUrl(r.logoImageId),
|
||||
hiring: !!r.hiring
|
||||
}));
|
||||
res.json(parsed);
|
||||
@@ -283,8 +341,8 @@ app.post('/api/projects', (req, res) => {
|
||||
const projectId = 'proj_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
||||
|
||||
db.run(`INSERT INTO projects
|
||||
(id, title, description, category, status, progress, owner, employees, hiring, foundedDate, associatedOrgId, bannerUrl, shopCatalog, gallery)
|
||||
VALUES (?, ?, ?, ?, 'active', 0, ?, '[]', 0, ?, ?, '', '[]', '[]')`,
|
||||
(id, title, description, category, status, progress, owner, employees, hiring, foundedDate, associatedOrgId, bannerImageId, shopCatalog, gallery)
|
||||
VALUES (?, ?, ?, ?, 'active', 0, ?, '[]', 0, ?, ?, null, '[]', '[]')`,
|
||||
[projectId, title, description, category || 'Enterprise', projectOwner,
|
||||
new Date().toISOString().split('T')[0], associatedOrgId || null],
|
||||
function(err) {
|
||||
@@ -458,8 +516,8 @@ app.post('/api/admin/npc-company', (req, res) => {
|
||||
const projectId = 'npc_proj_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
||||
|
||||
db.run(`INSERT INTO projects
|
||||
(id, title, description, category, status, progress, owner, employees, hiring, foundedDate, associatedOrgId, bannerUrl, shopCatalog, gallery)
|
||||
VALUES (?, ?, ?, ?, 'active', ?, ?, '[]', 0, ?, ?, '', ?, '[]')`,
|
||||
(id, title, description, category, status, progress, owner, employees, hiring, foundedDate, associatedOrgId, bannerImageId, shopCatalog, gallery)
|
||||
VALUES (?, ?, ?, ?, 'active', ?, ?, '[]', 0, ?, ?, null, ?, '[]')`,
|
||||
[projectId, title, description || `${title} - Eine NPC-Firma im Obsidian-Tal.`, category || 'Enterprise',
|
||||
Math.floor(Math.random() * 100), owner, new Date().toISOString().split('T')[0],
|
||||
associatedOrgId || null, JSON.stringify(shopCatalog || [])],
|
||||
@@ -882,11 +940,10 @@ app.post('/api/projects/:projectId/gallery', (req, res) => {
|
||||
});
|
||||
|
||||
// Delete gallery image
|
||||
app.delete('/api/projects/:projectId/gallery/:imageIndex', (req, res) => {
|
||||
app.delete('/api/projects/:projectId/gallery/:imageId', (req, res) => {
|
||||
if (!req.isAuthenticated()) return res.status(401).send();
|
||||
|
||||
const { projectId, imageIndex } = req.params;
|
||||
const index = parseInt(imageIndex);
|
||||
const { projectId, imageId } = req.params;
|
||||
|
||||
// Check ownership
|
||||
db.get("SELECT owner, gallery FROM projects WHERE id = ?", [projectId], (err, row) => {
|
||||
@@ -898,26 +955,639 @@ app.delete('/api/projects/:projectId/gallery/:imageIndex', (req, res) => {
|
||||
|
||||
try {
|
||||
const gallery = JSON.parse(row.gallery || '[]');
|
||||
const imageIndex = gallery.indexOf(imageId);
|
||||
|
||||
if (index < 0 || index >= gallery.length) {
|
||||
return res.status(404).json({error: 'Bild nicht gefunden'});
|
||||
if (imageIndex === -1) {
|
||||
return res.status(404).json({error: 'Bild nicht in Galerie gefunden'});
|
||||
}
|
||||
|
||||
gallery.splice(index, 1);
|
||||
// Remove from gallery array
|
||||
gallery.splice(imageIndex, 1);
|
||||
|
||||
// Update project gallery
|
||||
db.run("UPDATE projects SET gallery = ? WHERE id = ?", [JSON.stringify(gallery), projectId], function(err) {
|
||||
if (err) {
|
||||
console.error('Error deleting gallery image:', err);
|
||||
return res.status(500).json({error: 'Fehler beim Löschen'});
|
||||
console.error('Error updating gallery:', err);
|
||||
return res.status(500).json({error: 'Fehler beim Aktualisieren der Galerie'});
|
||||
}
|
||||
res.json({success: true, gallery});
|
||||
|
||||
// Delete image record and file
|
||||
db.get("SELECT filename FROM images WHERE id = ?", [imageId], (err, imageRow) => {
|
||||
if (err) {
|
||||
console.error('Error finding image record:', err);
|
||||
return res.status(500).json({error: 'Fehler beim Löschen des Bildes'});
|
||||
}
|
||||
|
||||
if (imageRow) {
|
||||
// Delete file from filesystem
|
||||
const filePath = path.join(UPLOAD_DIR, imageRow.filename);
|
||||
try {
|
||||
if (fs.existsSync(filePath)) {
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
} catch (fileErr) {
|
||||
console.error('Error deleting file:', fileErr);
|
||||
}
|
||||
|
||||
// Delete image record from database
|
||||
db.run("DELETE FROM images WHERE id = ?", [imageId], function(err) {
|
||||
if (err) {
|
||||
console.error('Error deleting image record:', err);
|
||||
return res.status(500).json({error: 'Fehler beim Löschen des Bildes'});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
gallery: gallery,
|
||||
message: 'Bild erfolgreich gelöscht'
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Image record not found, but gallery was updated
|
||||
res.json({
|
||||
success: true,
|
||||
gallery: gallery,
|
||||
message: 'Bild aus Galerie entfernt'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
res.status(500).json({error: 'Fehler beim Verarbeiten der Daten'});
|
||||
res.status(500).json({error: 'Fehler beim Verarbeiten der Galerie-Daten'});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// === IMAGE MANAGEMENT API ===
|
||||
|
||||
// Get image by ID
|
||||
app.get('/api/images/:imageId', (req, res) => {
|
||||
const { imageId } = req.params;
|
||||
|
||||
db.get("SELECT * FROM images WHERE id = ?", [imageId], (err, row) => {
|
||||
if (err) return res.status(500).json({error: err.message});
|
||||
if (!row) return res.status(404).json({error: 'Bild nicht gefunden'});
|
||||
|
||||
// Serve the actual image file
|
||||
const filePath = path.join(UPLOAD_DIR, row.filename);
|
||||
if (fs.existsSync(filePath)) {
|
||||
res.sendFile(filePath);
|
||||
} else {
|
||||
res.status(404).json({error: 'Bild-Datei nicht gefunden'});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Get image metadata by ID
|
||||
app.get('/api/images/:imageId/meta', (req, res) => {
|
||||
const { imageId } = req.params;
|
||||
|
||||
db.get("SELECT * FROM images WHERE id = ?", [imageId], (err, row) => {
|
||||
if (err) return res.status(500).json({error: err.message});
|
||||
if (!row) return res.status(404).json({error: 'Bild nicht gefunden'});
|
||||
|
||||
res.json({
|
||||
id: row.id,
|
||||
filename: row.filename,
|
||||
originalName: row.originalName,
|
||||
mimeType: row.mimeType,
|
||||
size: row.size,
|
||||
uploadDate: row.uploadDate,
|
||||
altText: row.altText,
|
||||
url: `${BACKEND_URL}/api/images/${row.id}`
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Upload banner image
|
||||
app.post('/api/projects/:projectId/banner/upload', upload.single('banner'), (req, res) => {
|
||||
if (!req.isAuthenticated()) return res.status(401).json({error: 'Nicht authentifiziert'});
|
||||
|
||||
const { projectId } = req.params;
|
||||
|
||||
// Check ownership
|
||||
db.get("SELECT owner FROM projects WHERE id = ?", [projectId], (err, row) => {
|
||||
if (err) return res.status(500).json({error: err.message});
|
||||
if (!row) return res.status(404).json({error: 'Projekt nicht gefunden'});
|
||||
if (row.owner !== req.user.username && req.user.username !== 'admin') {
|
||||
return res.status(403).json({error: 'Keine Berechtigung'});
|
||||
}
|
||||
|
||||
if (!req.file) {
|
||||
return res.status(400).json({error: 'Keine Datei hochgeladen'});
|
||||
}
|
||||
|
||||
// Generate unique image ID
|
||||
const imageId = 'img_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
||||
|
||||
// Create image record
|
||||
const imageData = {
|
||||
id: imageId,
|
||||
filename: req.file.filename,
|
||||
originalName: req.file.originalname,
|
||||
mimeType: req.file.mimetype,
|
||||
size: req.file.size,
|
||||
uploadDate: new Date().toISOString(),
|
||||
altText: req.body.altText || null,
|
||||
entityType: 'project',
|
||||
entityId: projectId,
|
||||
imageType: 'banner'
|
||||
};
|
||||
|
||||
db.run(`INSERT INTO images (id, filename, originalName, mimeType, size, uploadDate, altText, entityType, entityId, imageType)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[imageData.id, imageData.filename, imageData.originalName, imageData.mimeType, imageData.size,
|
||||
imageData.uploadDate, imageData.altText, imageData.entityType, imageData.entityId, imageData.imageType],
|
||||
function(err) {
|
||||
if (err) {
|
||||
console.error('Error creating image record:', err);
|
||||
return res.status(500).json({error: 'Fehler beim Speichern des Bildes'});
|
||||
}
|
||||
|
||||
// Update project banner
|
||||
db.run("UPDATE projects SET bannerImageId = ? WHERE id = ?", [imageId, projectId], function(err) {
|
||||
if (err) {
|
||||
console.error('Error updating banner:', err);
|
||||
return res.status(500).json({error: 'Fehler beim Aktualisieren des Banners'});
|
||||
}
|
||||
res.json({
|
||||
success: true,
|
||||
imageId: imageId,
|
||||
imageUrl: `${BACKEND_URL}/api/images/${imageId}`,
|
||||
message: 'Banner erfolgreich hochgeladen'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Upload gallery image
|
||||
app.post('/api/projects/:projectId/gallery/upload', upload.single('gallery'), (req, res) => {
|
||||
if (!req.isAuthenticated()) return res.status(401).json({error: 'Nicht authentifiziert'});
|
||||
|
||||
const { projectId } = req.params;
|
||||
|
||||
// Check ownership
|
||||
db.get("SELECT owner, gallery FROM projects WHERE id = ?", [projectId], (err, row) => {
|
||||
if (err) return res.status(500).json({error: err.message});
|
||||
if (!row) return res.status(404).json({error: 'Projekt nicht gefunden'});
|
||||
if (row.owner !== req.user.username && req.user.username !== 'admin') {
|
||||
return res.status(403).json({error: 'Keine Berechtigung'});
|
||||
}
|
||||
|
||||
if (!req.file) {
|
||||
return res.status(400).json({error: 'Keine Datei hochgeladen'});
|
||||
}
|
||||
|
||||
// Generate unique image ID
|
||||
const imageId = 'img_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
||||
|
||||
// Create image record
|
||||
const imageData = {
|
||||
id: imageId,
|
||||
filename: req.file.filename,
|
||||
originalName: req.file.originalname,
|
||||
mimeType: req.file.mimetype,
|
||||
size: req.file.size,
|
||||
uploadDate: new Date().toISOString(),
|
||||
altText: req.body.altText || null,
|
||||
entityType: 'project',
|
||||
entityId: projectId,
|
||||
imageType: 'gallery'
|
||||
};
|
||||
|
||||
db.run(`INSERT INTO images (id, filename, originalName, mimeType, size, uploadDate, altText, entityType, entityId, imageType)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[imageData.id, imageData.filename, imageData.originalName, imageData.mimeType, imageData.size,
|
||||
imageData.uploadDate, imageData.altText, imageData.entityType, imageData.entityId, imageData.imageType],
|
||||
function(err) {
|
||||
if (err) {
|
||||
console.error('Error creating image record:', err);
|
||||
return res.status(500).json({error: 'Fehler beim Speichern des Bildes'});
|
||||
}
|
||||
|
||||
try {
|
||||
// Add image ID to gallery array
|
||||
const gallery = JSON.parse(row.gallery || '[]');
|
||||
gallery.push(imageId);
|
||||
|
||||
db.run("UPDATE projects SET gallery = ? WHERE id = ?", [JSON.stringify(gallery), projectId], function(err) {
|
||||
if (err) {
|
||||
console.error('Error updating gallery:', err);
|
||||
return res.status(500).json({error: 'Fehler beim Aktualisieren der Galerie'});
|
||||
}
|
||||
res.json({
|
||||
success: true,
|
||||
imageId: imageId,
|
||||
imageUrl: `${BACKEND_URL}/api/images/${imageId}`,
|
||||
gallery: gallery,
|
||||
message: 'Bild erfolgreich zur Galerie hinzugefügt'
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Error parsing gallery:', e);
|
||||
res.status(500).json({error: 'Fehler beim Verarbeiten der Galerie-Daten'});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Upload logo image
|
||||
app.post('/api/projects/:projectId/logo/upload', upload.single('logo'), (req, res) => {
|
||||
if (!req.isAuthenticated()) return res.status(401).json({error: 'Nicht authentifiziert'});
|
||||
|
||||
const { projectId } = req.params;
|
||||
|
||||
// Check ownership
|
||||
db.get("SELECT owner FROM projects WHERE id = ?", [projectId], (err, row) => {
|
||||
if (err) return res.status(500).json({error: err.message});
|
||||
if (!row) return res.status(404).json({error: 'Projekt nicht gefunden'});
|
||||
if (row.owner !== req.user.username && req.user.username !== 'admin') {
|
||||
return res.status(403).json({error: 'Keine Berechtigung'});
|
||||
}
|
||||
|
||||
if (!req.file) {
|
||||
return res.status(400).json({error: 'Keine Datei hochgeladen'});
|
||||
}
|
||||
|
||||
// Generate unique image ID
|
||||
const imageId = 'img_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
||||
|
||||
// Create image record
|
||||
const imageData = {
|
||||
id: imageId,
|
||||
filename: req.file.filename,
|
||||
originalName: req.file.originalname,
|
||||
mimeType: req.file.mimetype,
|
||||
size: req.file.size,
|
||||
uploadDate: new Date().toISOString(),
|
||||
altText: req.body.altText || `${row.title} Logo`,
|
||||
entityType: 'project',
|
||||
entityId: projectId,
|
||||
imageType: 'logo'
|
||||
};
|
||||
|
||||
db.run(`INSERT INTO images (id, filename, originalName, mimeType, size, uploadDate, altText, entityType, entityId, imageType)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[imageData.id, imageData.filename, imageData.originalName, imageData.mimeType, imageData.size,
|
||||
imageData.uploadDate, imageData.altText, imageData.entityType, imageData.entityId, imageData.imageType],
|
||||
function(err) {
|
||||
if (err) {
|
||||
console.error('Error creating logo image record:', err);
|
||||
return res.status(500).json({error: 'Fehler beim Speichern des Logos'});
|
||||
}
|
||||
|
||||
// Update project logo
|
||||
db.run("UPDATE projects SET logoImageId = ? WHERE id = ?", [imageId, projectId], function(err) {
|
||||
if (err) {
|
||||
console.error('Error updating logo:', err);
|
||||
return res.status(500).json({error: 'Fehler beim Aktualisieren des Logos'});
|
||||
}
|
||||
res.json({
|
||||
success: true,
|
||||
imageId: imageId,
|
||||
logoUrl: `${BACKEND_URL}/api/images/${imageId}`,
|
||||
message: 'Logo erfolgreich hochgeladen'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// === CITY MANAGEMENT API (Admin Only) ===
|
||||
|
||||
// Create new city
|
||||
app.post('/api/admin/cities', (req, res) => {
|
||||
if (!req.isAuthenticated()) return res.status(401).send();
|
||||
|
||||
// Check if user is admin
|
||||
if (!req.user.isAdmin) {
|
||||
return res.status(403).json({error: 'Admin-Berechtigung erforderlich'});
|
||||
}
|
||||
|
||||
const { name, description, mayor, establishedYear, cityStats } = req.body;
|
||||
|
||||
if (!name) {
|
||||
return res.status(400).json({error: 'Stadt-Name erforderlich'});
|
||||
}
|
||||
|
||||
// Generate unique ID
|
||||
const cityId = 'city_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
||||
|
||||
const cityData = {
|
||||
id: cityId,
|
||||
name: name.trim(),
|
||||
type: 'City',
|
||||
description: description || `${name} - Eine Stadt im Obsidian-Tal.`,
|
||||
memberCount: 0,
|
||||
status: 'active',
|
||||
mayor: mayor || '',
|
||||
establishedYear: establishedYear || new Date().getFullYear().toString(),
|
||||
bannerImageId: null,
|
||||
logoImageId: null,
|
||||
gallery: '[]',
|
||||
cityStats: cityStats ? JSON.stringify(cityStats) : JSON.stringify({
|
||||
taxRate: 5.0,
|
||||
biome: 'Ebene',
|
||||
defenseRating: 5,
|
||||
government: 'Demokratie',
|
||||
specialty: 'Handel'
|
||||
})
|
||||
};
|
||||
|
||||
db.run(`INSERT INTO orgs (id, name, type, description, memberCount, status, mayor, establishedYear, bannerImageId, logoImageId, gallery, cityStats)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[cityData.id, cityData.name, cityData.type, cityData.description, cityData.memberCount, cityData.status,
|
||||
cityData.mayor, cityData.establishedYear, cityData.bannerImageId, cityData.logoImageId, cityData.gallery, cityData.cityStats],
|
||||
function(err) {
|
||||
if (err) {
|
||||
console.error('Error creating city:', err);
|
||||
return res.status(500).json({error: 'Fehler beim Erstellen der Stadt'});
|
||||
}
|
||||
res.json({
|
||||
success: true,
|
||||
cityId: cityId,
|
||||
message: 'Stadt erfolgreich erstellt'
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Update city
|
||||
app.put('/api/admin/cities/:cityId', (req, res) => {
|
||||
if (!req.isAuthenticated()) return res.status(401).send();
|
||||
|
||||
// Check if user is admin
|
||||
if (!req.user.isAdmin) {
|
||||
return res.status(403).json({error: 'Admin-Berechtigung erforderlich'});
|
||||
}
|
||||
|
||||
const { cityId } = req.params;
|
||||
const updates = req.body;
|
||||
|
||||
// Build dynamic update query
|
||||
const allowedFields = ['name', 'description', 'mayor', 'establishedYear', 'status', 'cityStats'];
|
||||
const updateFields = [];
|
||||
const values = [];
|
||||
|
||||
for (const field of allowedFields) {
|
||||
if (updates[field] !== undefined) {
|
||||
if (field === 'cityStats') {
|
||||
updateFields.push(`${field} = ?`);
|
||||
values.push(JSON.stringify(updates[field]));
|
||||
} else {
|
||||
updateFields.push(`${field} = ?`);
|
||||
values.push(updates[field]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updateFields.length === 0) {
|
||||
return res.status(400).json({error: 'Keine gültigen Felder zum Aktualisieren'});
|
||||
}
|
||||
|
||||
const query = `UPDATE orgs SET ${updateFields.join(', ')} WHERE id = ? AND type = 'City'`;
|
||||
values.push(cityId);
|
||||
|
||||
db.run(query, values, function(err) {
|
||||
if (err) {
|
||||
console.error('Error updating city:', err);
|
||||
return res.status(500).json({error: 'Fehler beim Aktualisieren'});
|
||||
}
|
||||
if (this.changes === 0) {
|
||||
return res.status(404).json({error: 'Stadt nicht gefunden'});
|
||||
}
|
||||
res.json({success: true, message: 'Stadt erfolgreich aktualisiert'});
|
||||
});
|
||||
});
|
||||
|
||||
// Delete city
|
||||
app.delete('/api/admin/cities/:cityId', (req, res) => {
|
||||
if (!req.isAuthenticated()) return res.status(401).send();
|
||||
|
||||
// Check if user is admin
|
||||
if (!req.user.isAdmin) {
|
||||
return res.status(403).json({error: 'Admin-Berechtigung erforderlich'});
|
||||
}
|
||||
|
||||
const { cityId } = req.params;
|
||||
|
||||
db.run("DELETE FROM orgs WHERE id = ? AND type = 'City'", [cityId], function(err) {
|
||||
if (err) {
|
||||
console.error('Error deleting city:', err);
|
||||
return res.status(500).json({error: 'Fehler beim Löschen'});
|
||||
}
|
||||
if (this.changes === 0) {
|
||||
return res.status(404).json({error: 'Stadt nicht gefunden'});
|
||||
}
|
||||
res.json({success: true, message: 'Stadt erfolgreich gelöscht'});
|
||||
});
|
||||
});
|
||||
|
||||
// Upload city banner
|
||||
app.post('/api/admin/cities/:cityId/banner/upload', upload.single('banner'), (req, res) => {
|
||||
if (!req.isAuthenticated()) return res.status(401).json({error: 'Nicht authentifiziert'});
|
||||
|
||||
// Check if user is admin
|
||||
if (!req.user.isAdmin) {
|
||||
return res.status(403).json({error: 'Admin-Berechtigung erforderlich'});
|
||||
}
|
||||
|
||||
const { cityId } = req.params;
|
||||
|
||||
// Check if city exists
|
||||
db.get("SELECT name FROM orgs WHERE id = ? AND type = 'City'", [cityId], (err, row) => {
|
||||
if (err) return res.status(500).json({error: err.message});
|
||||
if (!row) return res.status(404).json({error: 'Stadt nicht gefunden'});
|
||||
|
||||
if (!req.file) {
|
||||
return res.status(400).json({error: 'Keine Datei hochgeladen'});
|
||||
}
|
||||
|
||||
// Generate unique image ID
|
||||
const imageId = 'img_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
||||
|
||||
// Create image record
|
||||
const imageData = {
|
||||
id: imageId,
|
||||
filename: req.file.filename,
|
||||
originalName: req.file.originalname,
|
||||
mimeType: req.file.mimetype,
|
||||
size: req.file.size,
|
||||
uploadDate: new Date().toISOString(),
|
||||
altText: req.body.altText || `${row.name} Banner`,
|
||||
entityType: 'org',
|
||||
entityId: cityId,
|
||||
imageType: 'banner'
|
||||
};
|
||||
|
||||
db.run(`INSERT INTO images (id, filename, originalName, mimeType, size, uploadDate, altText, entityType, entityId, imageType)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[imageData.id, imageData.filename, imageData.originalName, imageData.mimeType, imageData.size,
|
||||
imageData.uploadDate, imageData.altText, imageData.entityType, imageData.entityId, imageData.imageType],
|
||||
function(err) {
|
||||
if (err) {
|
||||
console.error('Error creating city banner image record:', err);
|
||||
return res.status(500).json({error: 'Fehler beim Speichern des Banners'});
|
||||
}
|
||||
|
||||
// Update city banner
|
||||
db.run("UPDATE orgs SET bannerImageId = ? WHERE id = ? AND type = 'City'", [imageId, cityId], function(err) {
|
||||
if (err) {
|
||||
console.error('Error updating city banner:', err);
|
||||
return res.status(500).json({error: 'Fehler beim Aktualisieren des Banners'});
|
||||
}
|
||||
res.json({
|
||||
success: true,
|
||||
imageId: imageId,
|
||||
bannerUrl: `${BACKEND_URL}/api/images/${imageId}`,
|
||||
message: 'Banner erfolgreich hochgeladen'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Upload city logo
|
||||
app.post('/api/admin/cities/:cityId/logo/upload', upload.single('logo'), (req, res) => {
|
||||
if (!req.isAuthenticated()) return res.status(401).json({error: 'Nicht authentifiziert'});
|
||||
|
||||
// Check if user is admin
|
||||
if (!req.user.isAdmin) {
|
||||
return res.status(403).json({error: 'Admin-Berechtigung erforderlich'});
|
||||
}
|
||||
|
||||
const { cityId } = req.params;
|
||||
|
||||
// Check if city exists
|
||||
db.get("SELECT name FROM orgs WHERE id = ? AND type = 'City'", [cityId], (err, row) => {
|
||||
if (err) return res.status(500).json({error: err.message});
|
||||
if (!row) return res.status(404).json({error: 'Stadt nicht gefunden'});
|
||||
|
||||
if (!req.file) {
|
||||
return res.status(400).json({error: 'Keine Datei hochgeladen'});
|
||||
}
|
||||
|
||||
// Generate unique image ID
|
||||
const imageId = 'img_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
||||
|
||||
// Create image record
|
||||
const imageData = {
|
||||
id: imageId,
|
||||
filename: req.file.filename,
|
||||
originalName: req.file.originalname,
|
||||
mimeType: req.file.mimetype,
|
||||
size: req.file.size,
|
||||
uploadDate: new Date().toISOString(),
|
||||
altText: req.body.altText || `${row.name} Logo`,
|
||||
entityType: 'org',
|
||||
entityId: cityId,
|
||||
imageType: 'logo'
|
||||
};
|
||||
|
||||
db.run(`INSERT INTO images (id, filename, originalName, mimeType, size, uploadDate, altText, entityType, entityId, imageType)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[imageData.id, imageData.filename, imageData.originalName, imageData.mimeType, imageData.size,
|
||||
imageData.uploadDate, imageData.altText, imageData.entityType, imageData.entityId, imageData.imageType],
|
||||
function(err) {
|
||||
if (err) {
|
||||
console.error('Error creating city logo image record:', err);
|
||||
return res.status(500).json({error: 'Fehler beim Speichern des Logos'});
|
||||
}
|
||||
|
||||
// Update city logo
|
||||
db.run("UPDATE orgs SET logoImageId = ? WHERE id = ? AND type = 'City'", [imageId, cityId], function(err) {
|
||||
if (err) {
|
||||
console.error('Error updating city logo:', err);
|
||||
return res.status(500).json({error: 'Fehler beim Aktualisieren des Logos'});
|
||||
}
|
||||
res.json({
|
||||
success: true,
|
||||
imageId: imageId,
|
||||
logoUrl: `${BACKEND_URL}/api/images/${imageId}`,
|
||||
message: 'Logo erfolgreich hochgeladen'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Upload city gallery image
|
||||
app.post('/api/admin/cities/:cityId/gallery/upload', upload.single('gallery'), (req, res) => {
|
||||
if (!req.isAuthenticated()) return res.status(401).json({error: 'Nicht authentifiziert'});
|
||||
|
||||
// Check if user is admin
|
||||
if (!req.user.isAdmin) {
|
||||
return res.status(403).json({error: 'Admin-Berechtigung erforderlich'});
|
||||
}
|
||||
|
||||
const { cityId } = req.params;
|
||||
|
||||
// Check if city exists
|
||||
db.get("SELECT name, gallery FROM orgs WHERE id = ? AND type = 'City'", [cityId], (err, row) => {
|
||||
if (err) return res.status(500).json({error: err.message});
|
||||
if (!row) return res.status(404).json({error: 'Stadt nicht gefunden'});
|
||||
|
||||
if (!req.file) {
|
||||
return res.status(400).json({error: 'Keine Datei hochgeladen'});
|
||||
}
|
||||
|
||||
// Generate unique image ID
|
||||
const imageId = 'img_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
||||
|
||||
// Create image record
|
||||
const imageData = {
|
||||
id: imageId,
|
||||
filename: req.file.filename,
|
||||
originalName: req.file.originalname,
|
||||
mimeType: req.file.mimetype,
|
||||
size: req.file.size,
|
||||
uploadDate: new Date().toISOString(),
|
||||
altText: req.body.altText || `${row.name} Galerie`,
|
||||
entityType: 'org',
|
||||
entityId: cityId,
|
||||
imageType: 'gallery'
|
||||
};
|
||||
|
||||
db.run(`INSERT INTO images (id, filename, originalName, mimeType, size, uploadDate, altText, entityType, entityId, imageType)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[imageData.id, imageData.filename, imageData.originalName, imageData.mimeType, imageData.size,
|
||||
imageData.uploadDate, imageData.altText, imageData.entityType, imageData.entityId, imageData.imageType],
|
||||
function(err) {
|
||||
if (err) {
|
||||
console.error('Error creating city gallery image record:', err);
|
||||
return res.status(500).json({error: 'Fehler beim Speichern des Bildes'});
|
||||
}
|
||||
|
||||
try {
|
||||
// Add image ID to gallery array
|
||||
const gallery = JSON.parse(row.gallery || '[]');
|
||||
gallery.push(imageId);
|
||||
|
||||
db.run("UPDATE orgs SET gallery = ? WHERE id = ? AND type = 'City'", [JSON.stringify(gallery), cityId], function(err) {
|
||||
if (err) {
|
||||
console.error('Error updating city gallery:', err);
|
||||
return res.status(500).json({error: 'Fehler beim Aktualisieren der Galerie'});
|
||||
}
|
||||
res.json({
|
||||
success: true,
|
||||
imageId: imageId,
|
||||
imageUrl: `${BACKEND_URL}/api/images/${imageId}`,
|
||||
gallery: gallery,
|
||||
message: 'Bild erfolgreich zur Galerie hinzugefügt'
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Error parsing city gallery:', e);
|
||||
res.status(500).json({error: 'Fehler beim Verarbeiten der Galerie-Daten'});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Serve uploaded files statically
|
||||
app.use('/uploads', express.static(UPLOAD_DIR));
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Backend running on http://localhost:${PORT}`);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user