'use strict'; const fs = require('node:fs'); const path = require('node:path'); const { DatabaseSync } = require('node:sqlite'); const { createModuleServer, loadManifest, createLogger } = require('./platform-module-sdk'); process.umask(0o077); const manifest = loadManifest(path.join(__dirname, '..', 'module.json')); const logger = createLogger({ moduleId: manifest.id }); const dataDirectory = process.env.MPM_MODULE_DATA_DIR || path.join(__dirname, '..', 'data'); fs.mkdirSync(dataDirectory, { recursive: true, mode: 0o700 }); const db = new DatabaseSync(path.join(dataDirectory, manifest.id + '.sqlite')); db.exec('CREATE TABLE IF NOT EXISTS events (id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT NOT NULL, title TEXT NOT NULL CHECK(length(title)<=120), event_date TEXT NOT NULL, notes TEXT NOT NULL DEFAULT "" CHECK(length(notes)<=1000), created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP)'); const listEvents = db.prepare('SELECT id,title,event_date,notes FROM events WHERE user_id=? AND event_date>=? AND event_date16384) throw new Error('too large'); } return JSON.parse(data||'{}'); } const server = createModuleServer({manifest,logger,routes:async(req,res,{path:pathname,url,identity})=>{ if(req.method==='GET'&&(pathname==='/'||pathname==='/index.html')){res.writeHead(200,{'content-type':'text/html; charset=utf-8','cache-control':'no-store'});res.end(html);return;} if(pathname==='/api/events'&&req.method==='GET'){ const month=url.searchParams.get('month')||new Date().toISOString().slice(0,7); if(!/^\d{4}-(0[1-9]|1[0-2])$/.test(month))return json(res,400,{error:'Ungültiger Monat.'}); const start=month+'-01',next=new Date(Date.UTC(Number(month.slice(0,4)),Number(month.slice(5,7)),1)).toISOString().slice(0,10); return json(res,200,{events:listEvents.all(identity.userId,start,next)}); } if(pathname==='/api/events'&&req.method==='POST'){ try {const data=await bodyJson(req); if(typeof data.title!=='string'||!data.title.trim()||data.title.trim().length>120||typeof data.event_date!=='string'||!/^\d{4}-\d{2}-\d{2}$/.test(data.event_date)||Number.isNaN(Date.parse(data.event_date+'T00:00:00Z'))||(data.notes!==undefined&&(typeof data.notes!=='string'||data.notes.length>1000)))return json(res,400,{error:'Titel, Datum oder Notiz ist ungültig.'}); const result=insertEvent.run(identity.userId,data.title.trim(),data.event_date,data.notes||'');return json(res,201,{id:Number(result.lastInsertRowid)}); } catch(error){return json(res,400,{error:error.message==='too large'?'Anfrage zu groß.':'Ungültige Anfrage.'});} } if(pathname.startsWith('/api/'))return json(res,404,{error:'Route nicht gefunden.'}); res.writeHead(404,{'content-type':'text/plain; charset=utf-8'});res.end('Nicht gefunden'); }}); server.listen(Number(process.env.PORT||manifest.port),'0.0.0.0',()=>logger.info('Kalender MVP gestartet',{port:Number(process.env.PORT||manifest.port)})); function shutdown() { try { db.close(); } finally { server.close(() => process.exit(0)); } } process.once('SIGTERM', shutdown); process.once('SIGINT', shutdown);