update
This commit is contained in:
57
server.js
57
server.js
@@ -207,7 +207,9 @@ const faviconStorage = multer.diskStorage({
|
||||
cb(null, UPLOAD_DIR);
|
||||
},
|
||||
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 });
|
||||
@@ -215,20 +217,65 @@ const uploadFavicon = multer({ storage: faviconStorage });
|
||||
// Upload Favicon
|
||||
app.post('/api/favicon', uploadFavicon.single('favicon'), (req, res) => {
|
||||
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)
|
||||
app.get('/api/favicon', (req, res) => {
|
||||
const faviconPath = path.join(UPLOAD_DIR, 'favicon.png');
|
||||
if (fs.existsSync(faviconPath)) {
|
||||
// Determine the stored favicon file (any supported extension)
|
||||
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);
|
||||
} else {
|
||||
// Fallback to default
|
||||
// Fallback to default (404)
|
||||
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)
|
||||
const DIST_DIR = path.join(__dirname, 'dist');
|
||||
if (fs.existsSync(DIST_DIR)) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Sun, Moon } from 'lucide-react';
|
||||
import Timeline from './components/Timeline';
|
||||
import PhotoDetail from './components/PhotoDetail';
|
||||
import Admin from './components/Admin';
|
||||
import { fetchPhotos } from './data/photos';
|
||||
import { Settings, Sun, Moon } from 'lucide-react';
|
||||
|
||||
function App() {
|
||||
const [view, setView] = useState(() => {
|
||||
@@ -37,7 +37,11 @@ function App() {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadPhotos();
|
||||
const fetchAndSet = async () => {
|
||||
const data = await fetchPhotos();
|
||||
setPhotos(data);
|
||||
};
|
||||
fetchAndSet();
|
||||
}, [view]);
|
||||
|
||||
// Handle browser back/forward
|
||||
|
||||
@@ -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';
|
||||
|
||||
const PhotoDetail = ({ photo, onClose }) => {
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
// Removed unused loaded state
|
||||
const [isFullScreen, setIsFullScreen] = useState(false);
|
||||
const [showTitle, setShowTitle] = useState(false);
|
||||
|
||||
@@ -14,14 +14,7 @@ const PhotoDetail = ({ photo, onClose }) => {
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setLoaded(true);
|
||||
// Disable scroll on body when modal is open
|
||||
document.body.style.overflow = 'hidden';
|
||||
return () => {
|
||||
document.body.style.overflow = 'auto';
|
||||
};
|
||||
}, []);
|
||||
// Removed effect that set loaded state and handled body scroll
|
||||
|
||||
if (!photo) return null;
|
||||
|
||||
@@ -36,7 +29,7 @@ const PhotoDetail = ({ photo, onClose }) => {
|
||||
zIndex: 100,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
opacity: loaded ? 1 : 0,
|
||||
opacity: 1,
|
||||
transition: 'opacity 0.3s ease',
|
||||
overflowY: 'auto'
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user