diff --git a/backend/src/routes/calendar.js b/backend/src/routes/calendar.js index e12464a..0562f31 100644 --- a/backend/src/routes/calendar.js +++ b/backend/src/routes/calendar.js @@ -1,24 +1,20 @@ -const express = require('express'); -const https = require('https'); -const http = require('http'); +const express = require('express'); +const https = require('https'); +const http = require('http'); +const db = require('../db'); const { authenticate } = require('../middleware/auth'); -const router = express.Router(); +const router = express.Router(); -// Fetch mit Redirect-Unterstützung (bis 5 Hops) +// Fetch mit Redirect-Unterstützung function fetchWithRedirects(url, hops = 0) { return new Promise((resolve, reject) => { if (hops > 5) return reject(new Error('Zu viele Redirects')); let parsed; try { parsed = new URL(url); } catch { return reject(new Error('Ungültige URL')); } - const lib = parsed.protocol === 'https:' ? https : http; const req = lib.get(url, { - headers: { - 'User-Agent': 'Mozilla/5.0 DickenDock/1.0', - 'Accept': 'text/calendar, text/plain, */*', - } + headers: { 'User-Agent': 'Mozilla/5.0 DickenDock/1.0', 'Accept': 'text/calendar, text/plain, */*' } }, res => { - // Redirect folgen if ([301,302,303,307,308].includes(res.statusCode) && res.headers.location) { const next = res.headers.location.startsWith('http') ? res.headers.location @@ -26,59 +22,48 @@ function fetchWithRedirects(url, hops = 0) { res.destroy(); return fetchWithRedirects(next, hops + 1).then(resolve).catch(reject); } - if (res.statusCode !== 200) - return reject(new Error(`HTTP ${res.statusCode}`)); - + if (res.statusCode !== 200) return reject(new Error(`HTTP ${res.statusCode}`)); let data = ''; res.setEncoding('utf8'); - res.on('data', d => { - data += d; - if (data.length > 3 * 1024 * 1024) { res.destroy(); reject(new Error('Feed zu groß (max 3MB)')); } - }); + res.on('data', d => { data += d; if (data.length > 3*1024*1024) { res.destroy(); reject(new Error('Feed zu groß')); } }); res.on('end', () => resolve(data)); }); req.on('error', reject); - req.setTimeout(10000, () => { req.destroy(); reject(new Error('Timeout nach 10s')); }); + req.setTimeout(10000, () => { req.destroy(); reject(new Error('Timeout')); }); }); } +// GET /fetch?url=... router.get('/fetch', authenticate, async (req, res) => { const { url } = req.query; if (!url) return res.status(400).json({ error: 'URL fehlt' }); - try { - new URL(url); // validate - } catch { return res.status(400).json({ error: 'Ungültige URL' }); } - + try { new URL(url); } catch { return res.status(400).json({ error: 'Ungültige URL' }); } try { const data = await fetchWithRedirects(url); - // Prüfen ob es wirklich iCal ist if (!data.includes('BEGIN:VCALENDAR')) - return res.status(502).json({ error: 'Kein gültiger iCal-Feed (BEGIN:VCALENDAR fehlt)' }); + return res.status(502).json({ error: 'Kein gültiger iCal-Feed' }); res.type('text/calendar; charset=utf-8').send(data); - } catch(e) { - res.status(502).json({ error: e.message }); - } + } catch(e) { res.status(502).json({ error: e.message }); } }); -module.exports = router; - -// ── iCal Feed CRUD (gespeichert in DB statt localStorage) ──────────────────── -const db = require('../db'); -const { authenticate: auth } = require('../middleware/auth'); - -router.get('/feeds', auth, (req, res) => { +// GET /feeds +router.get('/feeds', authenticate, (req, res) => { res.json(db.prepare('SELECT * FROM calendar_feeds WHERE user_id=? ORDER BY id').all(req.user.id)); }); -router.post('/feeds', auth, (req, res) => { +// POST /feeds +router.post('/feeds', authenticate, (req, res) => { const { name, url, color='#4ecdc4' } = req.body; if (!name?.trim() || !url?.trim()) return res.status(400).json({ error: 'Name und URL erforderlich' }); const r = db.prepare('INSERT INTO calendar_feeds (user_id,name,url,color) VALUES (?,?,?,?)').run(req.user.id, name.trim(), url.trim(), color); res.json(db.prepare('SELECT * FROM calendar_feeds WHERE id=?').get(r.lastInsertRowid)); }); -router.delete('/feeds/:id', auth, (req, res) => { +// DELETE /feeds/:id +router.delete('/feeds/:id', authenticate, (req, res) => { const r = db.prepare('DELETE FROM calendar_feeds WHERE id=? AND user_id=?').run(req.params.id, req.user.id); if (!r.changes) return res.status(404).json({ error: 'Nicht gefunden' }); res.json({ success: true }); }); + +module.exports = router;