Fix brand theme application and add comprehensive translations

- Fixed theme not being reflected on gallery and admin login pages
- Created GlobalThemeProvider to apply themes globally
- Updated gallery and admin login pages to use dynamic CSS variables
- Added complete translations for all admin sections in English and German:
  - Notifications management
  - Event view and creation
  - Photo upload functionality
  - Category management
  - Archive page view
  - Analytics dashboard
  - Branding and theme settings
  - System settings
  - CMS page management
  - Email configuration
- Fixed admin photo management display issues
- Fixed photo upload category assignment
- Added password reset functionality for galleries
- Improved error handling and user feedback

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-07-08 17:07:40 +02:00
parent 2012b0bab9
commit d594d00227
79 changed files with 4570 additions and 329 deletions
+42
View File
@@ -0,0 +1,42 @@
#!/usr/bin/env node
const sqlite3 = require('sqlite3').verbose();
// Connect to the database
const dbPath = '/app/data/photo_sharing.db';
console.log(`Connecting to database at: ${dbPath}`);
const db = new sqlite3.Database(dbPath, sqlite3.OPEN_READONLY, (err) => {
if (err) {
console.error('Error opening database:', err.message);
process.exit(1);
}
console.log('Connected to the SQLite database.\n');
});
// Get schema for events table
console.log('=== EVENTS TABLE SCHEMA ===');
db.all("PRAGMA table_info(events)", [], (err, rows) => {
if (err) {
console.error('Error getting events schema:', err.message);
} else {
rows.forEach(row => {
console.log(`${row.name} (${row.type})`);
});
}
console.log('\n=== PHOTOS TABLE SCHEMA ===');
// Get schema for photos table
db.all("PRAGMA table_info(photos)", [], (err, rows) => {
if (err) {
console.error('Error getting photos schema:', err.message);
} else {
rows.forEach(row => {
console.log(`${row.name} (${row.type})`);
});
}
// Close the database
db.close();
});
});
+56
View File
@@ -0,0 +1,56 @@
const knex = require('knex')({
client: 'sqlite3',
connection: { filename: '/app/data/photo_sharing.db' },
useNullAsDefault: true
});
async function debugEventPhotos() {
try {
// Get all photos for event 12
const photos = await knex('photos')
.where('event_id', 12)
.select('id', 'filename', 'path', 'thumbnail_path')
.orderBy('id');
console.log('Total photos for event 12:', photos.length);
console.log('\nSample photos:');
// Show first few and specific IDs that were failing
const sampleIds = [1686, 1687, 1688, 1689, 1715, 1717, 1718, 1719];
const samples = photos.filter(p => sampleIds.includes(p.id));
samples.forEach(p => {
console.log(`\nID ${p.id}: ${p.filename}`);
console.log(` Path: ${p.path}`);
console.log(` Thumbnail: ${p.thumbnail_path}`);
});
// Check for any photos without thumbnails
const noThumbs = photos.filter(p => !p.thumbnail_path);
if (noThumbs.length > 0) {
console.log(`\nPhotos without thumbnails: ${noThumbs.length}`);
noThumbs.forEach(p => console.log(` ID ${p.id}: ${p.filename}`));
}
// Check file existence for failing photos
const fs = require('fs').promises;
console.log('\nChecking file existence for samples:');
for (const photo of samples) {
const thumbPath = `/app/storage/${photo.thumbnail_path}`;
try {
await fs.access(thumbPath);
console.log(`✓ ID ${photo.id}: Thumbnail exists at ${thumbPath}`);
} catch (err) {
console.log(`✗ ID ${photo.id}: Thumbnail NOT FOUND at ${thumbPath}`);
}
}
} catch (error) {
console.error('Error:', error);
} finally {
knex.destroy();
}
}
debugEventPhotos();
+81
View File
@@ -0,0 +1,81 @@
#!/usr/bin/env node
const sqlite3 = require('sqlite3').verbose();
const fs = require('fs');
const path = require('path');
// Connect to the database
const dbPath = '/app/data/photo_sharing.db';
console.log(`Connecting to database at: ${dbPath}`);
const db = new sqlite3.Database(dbPath, sqlite3.OPEN_READONLY, (err) => {
if (err) {
console.error('Error opening database:', err.message);
process.exit(1);
}
console.log('Connected to the SQLite database.\n');
});
// Query for photos with IDs 1688 and 1689 where event_id = 12
const query = `
SELECT p.id, p.filename, p.path, p.thumbnail_path, p.event_id,
e.slug as event_slug, e.is_active, e.is_archived
FROM photos p
JOIN events e ON p.event_id = e.id
WHERE p.id IN (1688, 1689) AND p.event_id = 12
`;
console.log('Executing query to get photo details with event information...\n');
db.all(query, [], (err, rows) => {
if (err) {
console.error('Error executing query:', err.message);
db.close();
process.exit(1);
}
console.log(`Found ${rows.length} photo(s):\n`);
if (rows.length === 0) {
console.log('No photos found matching the criteria.');
} else {
rows.forEach((row) => {
console.log('=== Photo ID:', row.id, '===');
console.log('Filename:', row.filename);
console.log('DB Path:', row.path);
console.log('DB Thumbnail Path:', row.thumbnail_path);
console.log('Event ID:', row.event_id);
console.log('Event Slug:', row.event_slug);
console.log('Event is_active:', row.is_active);
console.log('Event is_archived:', row.is_archived);
// Check file existence
const storageBase = '/app/storage';
const eventStatusDir = row.is_active ? 'active' : 'archived';
// Check full image path
const fullImagePath1 = path.join(storageBase, row.path);
const fullImagePath2 = path.join(storageBase, 'events', eventStatusDir, row.path);
console.log('\nChecking full image paths:');
console.log(` Path 1: ${fullImagePath1} - ${fs.existsSync(fullImagePath1) ? 'EXISTS' : 'NOT FOUND'}`);
console.log(` Path 2: ${fullImagePath2} - ${fs.existsSync(fullImagePath2) ? 'EXISTS' : 'NOT FOUND'}`);
// Check thumbnail path
const thumbnailPath = path.join(storageBase, row.thumbnail_path);
console.log('\nChecking thumbnail path:');
console.log(` ${thumbnailPath} - ${fs.existsSync(thumbnailPath) ? 'EXISTS' : 'NOT FOUND'}`);
console.log('\n---\n');
});
}
// Close the database connection
db.close((err) => {
if (err) {
console.error('Error closing database:', err.message);
} else {
console.log('Database connection closed.');
}
});
});
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/env node
const sqlite3 = require('sqlite3').verbose();
const path = require('path');
// Connect to the database
const dbPath = '/app/data/photo_sharing.db';
console.log(`Connecting to database at: ${dbPath}`);
const db = new sqlite3.Database(dbPath, sqlite3.OPEN_READONLY, (err) => {
if (err) {
console.error('Error opening database:', err.message);
process.exit(1);
}
console.log('Connected to the SQLite database.');
});
// Query for photos with IDs 1688 and 1689 where event_id = 12
const query = `
SELECT id, filename, path, thumbnail_path
FROM photos
WHERE id IN (1688, 1689) AND event_id = 12
`;
console.log('\nExecuting query:', query);
db.all(query, [], (err, rows) => {
if (err) {
console.error('Error executing query:', err.message);
db.close();
process.exit(1);
}
console.log(`\nFound ${rows.length} photo(s):\n`);
if (rows.length === 0) {
console.log('No photos found matching the criteria.');
} else {
rows.forEach((row) => {
console.log('Photo ID:', row.id);
console.log('Filename:', row.filename);
console.log('Path:', row.path);
console.log('Thumbnail Path:', row.thumbnail_path);
console.log('---');
});
}
// Close the database connection
db.close((err) => {
if (err) {
console.error('Error closing database:', err.message);
} else {
console.log('\nDatabase connection closed.');
}
});
});
+55
View File
@@ -0,0 +1,55 @@
const axios = require('axios');
async function testAdminPhotoEndpoint() {
try {
// First login
console.log('1. Logging in as admin...');
const loginResponse = await axios.post('http://localhost:3000/api/admin/auth/login', {
username: 'admin',
password: 'admin123'
});
const token = loginResponse.data.token;
console.log('✓ Login successful, got token');
// Test thumbnail endpoint
console.log('\n2. Testing thumbnail endpoint for photo 1688...');
try {
const thumbResponse = await axios.get('http://localhost:3000/api/admin/events/12/thumbnail/1688', {
headers: {
Authorization: `Bearer ${token}`
},
responseType: 'arraybuffer'
});
console.log('✓ Thumbnail request successful');
console.log(' Response headers:', thumbResponse.headers);
console.log(' Data size:', thumbResponse.data.length, 'bytes');
} catch (error) {
console.error('✗ Thumbnail request failed:', error.response?.status, error.response?.data?.toString());
}
// Test from frontend proxy port
console.log('\n3. Testing through nginx proxy (port 3001)...');
try {
const proxyResponse = await axios.get('http://localhost:3001/api/admin/events/12/thumbnail/1688', {
headers: {
Authorization: `Bearer ${token}`,
Origin: 'http://localhost:3005'
},
responseType: 'arraybuffer'
});
console.log('✓ Proxy request successful');
console.log(' Response headers:', proxyResponse.headers);
console.log(' Data size:', proxyResponse.data.length, 'bytes');
} catch (error) {
console.error('✗ Proxy request failed:', error.response?.status, error.response?.data?.toString());
}
} catch (error) {
console.error('Error:', error.message);
}
}
testAdminPhotoEndpoint();
+59
View File
@@ -0,0 +1,59 @@
const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');
const path = require('path');
async function testUpload() {
try {
// First login
console.log('1. Logging in as admin...');
const loginResponse = await axios.post('http://localhost:3000/api/admin/auth/login', {
username: 'admin',
password: 'admin123'
});
const token = loginResponse.data.token;
console.log('✓ Login successful');
// Create a test image file
const testImagePath = path.join(__dirname, 'test-image.png');
const imageBuffer = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==', 'base64');
fs.writeFileSync(testImagePath, imageBuffer);
// Test upload
console.log('\n2. Testing upload with category_id=7...');
const form = new FormData();
form.append('photos', fs.createReadStream(testImagePath), 'test-image.png');
form.append('category_id', '7');
console.log('Form data headers:', form.getHeaders());
try {
const uploadResponse = await axios.post(
'http://localhost:3000/api/admin/events/12/upload',
form,
{
headers: {
...form.getHeaders(),
'Authorization': `Bearer ${token}`
}
}
);
console.log('✓ Upload successful:', uploadResponse.data);
} catch (error) {
console.error('✗ Upload failed:', error.response?.status, error.response?.data);
if (error.response?.data) {
console.error('Error details:', JSON.stringify(error.response.data, null, 2));
}
}
// Clean up
fs.unlinkSync(testImagePath);
} catch (error) {
console.error('Error:', error.message);
}
}
testUpload();