35 lines
3.2 KiB
JavaScript
35 lines
3.2 KiB
JavaScript
'use strict';
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const os = require('node:os');
|
|
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 db = new DatabaseSync(path.join(os.tmpdir(), 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_date<? ORDER BY event_date,title');
|
|
const insertEvent = db.prepare('INSERT INTO events(user_id,title,event_date,notes) VALUES(?,?,?,?)');
|
|
const html = fs.readFileSync(path.join(__dirname, '..', 'frontend', 'index.html'));
|
|
function json(res, status, value) { res.writeHead(status, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' }); res.end(JSON.stringify(value)); }
|
|
async function bodyJson(req) { let data=''; for await (const chunk of req) { data+=chunk; if(data.length>16384) 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),'127.0.0.1',()=>logger.info('Kalender MVP gestartet',{port:Number(process.env.PORT||manifest.port)}));
|