diff --git a/public/pages/dashboard.js b/public/pages/dashboard.js
index e43655a..2d58090 100644
--- a/public/pages/dashboard.js
+++ b/public/pages/dashboard.js
@@ -265,7 +265,7 @@ function renderPinnedNotes(notes) {
// Wetter-Widget
// --------------------------------------------------------
-const WEATHER_ICON_BASE = 'https://openweathermap.org/img/wn/';
+const WEATHER_ICON_BASE = '/api/v1/weather/icon/';
function renderWeatherWidget(weather) {
if (!weather) return '';
@@ -279,7 +279,7 @@ function renderWeatherWidget(weather) {
return `
-
${forecast.length ? `${forecastHtml}
` : ''}
diff --git a/server/index.js b/server/index.js
index 7a1ebec..86ca38f 100644
--- a/server/index.js
+++ b/server/index.js
@@ -43,7 +43,7 @@ app.use(helmet({
'https://cdn.jsdelivr.net',
],
styleSrc: ["'self'", "'unsafe-inline'"],
- imgSrc: ["'self'", 'data:', 'https://openweathermap.org'],
+ imgSrc: ["'self'", 'data:'],
connectSrc: ["'self'"],
fontSrc: ["'self'"],
objectSrc: ["'none'"],
diff --git a/server/routes/weather.js b/server/routes/weather.js
index a555662..152d232 100644
--- a/server/routes/weather.js
+++ b/server/routes/weather.js
@@ -92,4 +92,33 @@ router.get('/', async (req, res) => {
}
});
+// --------------------------------------------------------
+// GET /api/v1/weather/icon/:code
+// Proxy für OpenWeatherMap-Icons — vermeidet externe Bild-Requests
+// im PWA-Standalone-Modus (CORS/CSP-Probleme auf Android Chrome).
+// Erlaubte Codes: 2–4 alphanumerische Zeichen (z.B. "01d", "10n").
+// Response: PNG-Bild mit 24h-Cache
+// --------------------------------------------------------
+router.get('/icon/:code', async (req, res) => {
+ const { code } = req.params;
+ if (!/^[a-zA-Z0-9]{2,4}$/.test(code)) {
+ return res.status(400).json({ error: 'Ungültiger Icon-Code.', code: 400 });
+ }
+
+ try {
+ const { default: fetch } = await import('node-fetch');
+ const url = `https://openweathermap.org/img/wn/${code}@2x.png`;
+ const upstream = await fetch(url, { signal: AbortSignal.timeout(5000) });
+ if (!upstream.ok) {
+ return res.status(502).json({ error: 'Icon nicht verfügbar.', code: 502 });
+ }
+ res.setHeader('Content-Type', 'image/png');
+ res.setHeader('Cache-Control', 'public, max-age=86400'); // 24 Stunden
+ upstream.body.pipe(res);
+ } catch (err) {
+ console.warn('[Weather] Icon-Proxy Fehler:', err.message);
+ res.status(502).json({ error: 'Icon-Proxy fehlgeschlagen.', code: 502 });
+ }
+});
+
module.exports = router;