This commit is contained in:
2025-12-13 21:46:18 +01:00
parent 9dc66b02fa
commit aeaf2bfaa3
3 changed files with 62 additions and 18 deletions

View File

@@ -207,7 +207,9 @@ const faviconStorage = multer.diskStorage({
cb(null, UPLOAD_DIR); cb(null, UPLOAD_DIR);
}, },
filename: (req, file, cb) => { filename: (req, file, cb) => {
cb(null, 'favicon.png'); // Preserve original file extension (e.g., .ico, .png)
const ext = path.extname(file.originalname) || '.png';
cb(null, `favicon${ext}`);
} }
}); });
const uploadFavicon = multer({ storage: faviconStorage }); const uploadFavicon = multer({ storage: faviconStorage });
@@ -215,20 +217,65 @@ const uploadFavicon = multer({ storage: faviconStorage });
// Upload Favicon // Upload Favicon
app.post('/api/favicon', uploadFavicon.single('favicon'), (req, res) => { app.post('/api/favicon', uploadFavicon.single('favicon'), (req, res) => {
if (!req.file) return res.status(400).json({ error: 'No file uploaded' }); if (!req.file) return res.status(400).json({ error: 'No file uploaded' });
res.json({ success: true, url: '/uploads/favicon.png' }); // Respond with the stored filename (including extension)
const storedName = req.file.filename;
res.json({ success: true, url: `/uploads/${storedName}` });
}); });
// Serve Favicon (Dynamic) // Serve Favicon (Dynamic)
app.get('/api/favicon', (req, res) => { app.get('/api/favicon', (req, res) => {
const faviconPath = path.join(UPLOAD_DIR, 'favicon.png'); // Determine the stored favicon file (any supported extension)
if (fs.existsSync(faviconPath)) { const possibleExts = ['.ico', '.png', '.svg', '.jpg', '.jpeg'];
let faviconPath = null;
for (const ext of possibleExts) {
const candidate = path.join(UPLOAD_DIR, `favicon${ext}`);
if (fs.existsSync(candidate)) {
faviconPath = candidate;
break;
}
}
if (faviconPath) {
// Set appropriate Content-Type based on extension
const mimeMap = {
'.ico': 'image/x-icon',
'.png': 'image/png',
'.svg': 'image/svg+xml',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg'
};
const ext = path.extname(faviconPath).toLowerCase();
res.setHeader('Content-Type', mimeMap[ext] || 'application/octet-stream');
// Prevent caching so updates appear immediately
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate');
res.sendFile(faviconPath); res.sendFile(faviconPath);
} else { } else {
// Fallback to default // Fallback to default (404)
res.status(404).send('Not found'); res.status(404).send('Not found');
} }
}); });
// Fallback for browsers requesting /favicon.ico directly
app.get('/favicon.ico', (req, res) => {
const possibleExts = ['.ico', '.png', '.svg', '.jpg', '.jpeg'];
for (const ext of possibleExts) {
const candidate = path.join(UPLOAD_DIR, `favicon${ext}`);
if (fs.existsSync(candidate)) {
const mimeMap = {
'.ico': 'image/x-icon',
'.png': 'image/png',
'.svg': 'image/svg+xml',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg'
};
const mime = mimeMap[ext] || 'application/octet-stream';
res.setHeader('Content-Type', mime);
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate');
return res.sendFile(candidate);
}
}
res.status(404).send('Not found');
});
// Serve Frontend (Production) // Serve Frontend (Production)
const DIST_DIR = path.join(__dirname, 'dist'); const DIST_DIR = path.join(__dirname, 'dist');
if (fs.existsSync(DIST_DIR)) { if (fs.existsSync(DIST_DIR)) {

View File

@@ -1,9 +1,9 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Sun, Moon } from 'lucide-react';
import Timeline from './components/Timeline'; import Timeline from './components/Timeline';
import PhotoDetail from './components/PhotoDetail'; import PhotoDetail from './components/PhotoDetail';
import Admin from './components/Admin'; import Admin from './components/Admin';
import { fetchPhotos } from './data/photos'; import { fetchPhotos } from './data/photos';
import { Settings, Sun, Moon } from 'lucide-react';
function App() { function App() {
const [view, setView] = useState(() => { const [view, setView] = useState(() => {
@@ -37,7 +37,11 @@ function App() {
}; };
useEffect(() => { useEffect(() => {
loadPhotos(); const fetchAndSet = async () => {
const data = await fetchPhotos();
setPhotos(data);
};
fetchAndSet();
}, [view]); }, [view]);
// Handle browser back/forward // Handle browser back/forward

View File

@@ -1,8 +1,8 @@
import React, { useEffect, useState } from 'react'; import React, { useState } from 'react';
import { X, Camera, Aperture, Clock, Gauge, ArrowLeft, MapPin } from 'lucide-react'; import { X, Camera, Aperture, Clock, Gauge, ArrowLeft, MapPin } from 'lucide-react';
const PhotoDetail = ({ photo, onClose }) => { const PhotoDetail = ({ photo, onClose }) => {
const [loaded, setLoaded] = useState(false); // Removed unused loaded state
const [isFullScreen, setIsFullScreen] = useState(false); const [isFullScreen, setIsFullScreen] = useState(false);
const [showTitle, setShowTitle] = useState(false); const [showTitle, setShowTitle] = useState(false);
@@ -14,14 +14,7 @@ const PhotoDetail = ({ photo, onClose }) => {
} }
}; };
useEffect(() => { // Removed effect that set loaded state and handled body scroll
setLoaded(true);
// Disable scroll on body when modal is open
document.body.style.overflow = 'hidden';
return () => {
document.body.style.overflow = 'auto';
};
}, []);
if (!photo) return null; if (!photo) return null;
@@ -36,7 +29,7 @@ const PhotoDetail = ({ photo, onClose }) => {
zIndex: 100, zIndex: 100,
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
opacity: loaded ? 1 : 0, opacity: 1,
transition: 'opacity 0.3s ease', transition: 'opacity 0.3s ease',
overflowY: 'auto' overflowY: 'auto'
}} }}