diff --git a/index.html b/index.html index 60a8af0..593b403 100644 --- a/index.html +++ b/index.html @@ -1256,165 +1256,147 @@ - - diff --git a/server/server.js b/server/server.js new file mode 100644 index 0000000..609b8fe --- /dev/null +++ b/server/server.js @@ -0,0 +1,188 @@ +const express = require('express'); +const Database = require('better-sqlite3'); +const path = require('path'); + +const app = express(); +app.use(express.json()); +app.use(express.static(path.join(__dirname, '../'))); + +const DB_PATH = path.join(__dirname, 'data.db'); + +function getDb() { + const db = new Database(DB_PATH); + db.pragma('journal_mode = WAL'); + db.exec(` + CREATE TABLE IF NOT EXISTS state ( + key TEXT PRIMARY KEY, value TEXT + ); + CREATE TABLE IF NOT EXISTS spieler ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT, rolle TEXT, quest_id INTEGER, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP + ); + CREATE TABLE IF NOT EXISTS notes ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + body TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP + ); + CREATE TABLE IF NOT EXISTS drinks_log ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + amount INTEGER, note TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP + ); + `); + return db; +} + +let db = getDb(); + +function initState() { + const row = db.prepare("SELECT value FROM state WHERE key = 'game'").get(); + if (!row) { + const s = { + gameName: '', gameStarted: false, drinks: 0, + items: [false,false,false,false,false,false,false,false], + quests: [false,false,false,false,false,false,false,false], + timestamp: Date.now() + }; + db.prepare('INSERT OR REPLACE INTO state (key,value) VALUES (?,?)').run('game', JSON.stringify(s)); + } +} +initState(); + +// GET state +app.get('/api/game', (req, res) => { + initState(); + const sv = db.prepare("SELECT value FROM state WHERE key='game'").get(); + res.json({ + state: JSON.parse(sv.value), + spieler: db.prepare('SELECT * FROM spieler ORDER BY id').all(), + notes: db.prepare('SELECT * FROM notes ORDER BY created_at DESC').all(), + stats: db.prepare('SELECT SUM(amount) as total, COUNT(*) as entries FROM drinks_log').get() || {total:0,entries:0} + }); +}); + +// Start +app.post('/api/game/start', (req, res) => { + initState(); + const {name} = req.body; + const sv = db.prepare("SELECT value FROM state WHERE key='game'").get(); + const s = JSON.parse(sv.value); + s.gameName = name || 'Der letzte Auftrag'; + s.gameStarted = true; s.timestamp = Date.now(); + db.prepare("UPDATE state SET value=? WHERE key='game'").run(JSON.stringify(s)); + res.json({success:true, state:s}); +}); + +// Drink +app.post('/api/game/drink', (req, res) => { + initState(); + const amt = req.body.amount || 1; + const sv = db.prepare("SELECT value FROM state WHERE key='game'").get(); + const s = JSON.parse(sv.value); + s.drinks = Math.max(0, s.drinks + amt); + s.timestamp = Date.now(); + db.prepare("UPDATE state SET value=? WHERE key='game'").run(JSON.stringify(s)); + db.prepare('INSERT INTO drinks_log (amount) VALUES (?)').run(amt); + res.json({success:true, drinks:s.drinks}); +}); + +// Reset drink +app.post('/api/game/drink/reset', (req, res) => { + initState(); + const sv = db.prepare("SELECT value FROM state WHERE key='game'").get(); + const s = JSON.parse(sv.value); + s.drinks = 0; s.timestamp = Date.now(); + db.prepare("UPDATE state SET value=? WHERE key='game'").run(JSON.stringify(s)); + res.json({success:true, drinks:0}); +}); + +// Toggle item +app.post('/api/game/toggle-item', (req, res) => { + initState(); + const idx = parseInt(req.body.index); + if (idx<0||idx>7) return res.status(400).json({error:'Index 0-7'}); + const sv = db.prepare("SELECT value FROM state WHERE key='game'").get(); + const s = JSON.parse(sv.value); + s.items[idx] = !s.items[idx]; + s.timestamp = Date.now(); + db.prepare("UPDATE state SET value=? WHERE key='game'").run(JSON.stringify(s)); + res.json({success:true, state:s, collected:s.items.filter(Boolean).length}); +}); + +// Toggle quest +app.post('/api/game/toggle-quest', (req, res) => { + initState(); + const idx = parseInt(req.body.index); + if (idx<0||idx>7) return res.status(400).json({error:'Index 0-7'}); + const sv = db.prepare("SELECT value FROM state WHERE key='game'").get(); + const s = JSON.parse(sv.value); + s.quests[idx] = !s.quests[idx]; + s.timestamp = Date.now(); + db.prepare("UPDATE state SET value=? WHERE key='game'").run(JSON.stringify(s)); + res.json({success:true, state:s}); +}); + +// Save full state +app.post('/api/game/save-state', (req, res) => { + initState(); + const s = req.body; s.timestamp = Date.now(); + db.prepare("UPDATE state SET value=? WHERE key='game'").run(JSON.stringify(s)); + res.json({success:true, state:s}); +}); + +// Reset all +app.post('/api/game/reset', (req, res) => { + initState(); + const sv = db.prepare("SELECT value FROM state WHERE key='game'").get(); + const s = JSON.parse(sv.value); + s.drinks=0; s.items=[false,false,false,false,false,false,false,false]; + s.quests=[false,false,false,false,false,false,false,false]; + s.gameStarted=false; s.gameName=''; s.timestamp=Date.now(); + db.prepare("UPDATE state SET value=? WHERE key='game'").run(JSON.stringify(s)); + db.prepare('DELETE FROM notes').run(); + db.prepare('DELETE FROM drinks_log').run(); + res.json({success:true, state:s}); +}); + +// Spieler CRUD +app.get('/api/spieler', (req, res) => res.json(db.prepare('SELECT * FROM spieler ORDER BY id').all())); +app.post('/api/spieler', (req, res) => { + const {name, rolle, quest_id} = req.body; + const r = db.prepare('INSERT INTO spieler (name,rolle,quest_id) VALUES (?,?,?)').run(name, rolle, quest_id||null); + res.json({success:true, spieler:db.prepare('SELECT * FROM spieler WHERE id=?').get(r.lastInsertRowid)}); +}); +app.put('/api/spieler/:id', (req, res) => { + const {name, rolle, quest_id} = req.body; + db.prepare('UPDATE spieler SET name=COALESCE(?,name),rolle=COALESCE(?,rolle),quest_id=COALESCE(?,quest_id) WHERE id=?') + .run(name||null, rolle||null, quest_id||null, req.params.id); + res.json({success:true, spieler:db.prepare('SELECT * FROM spieler WHERE id=?').get(req.params.id)}); +}); +app.delete('/api/spieler/:id', (req, res) => { + db.prepare('DELETE FROM spieler WHERE id=?').run(req.params.id); + res.json({success:true}); +}); + +// Notes +app.post('/api/notes', (req, res) => { + const {body} = req.body; + const r = db.prepare('INSERT INTO notes (body) VALUES (?)').run(body); + res.json({success:true, note:db.prepare('SELECT * FROM notes WHERE id=?').get(r.lastInsertRowid)}); +}); + +// Stats +app.get('/api/game/stats', (req, res) => { + initState(); + const sv = db.prepare("SELECT value FROM state WHERE key='game'").get(); + const s = JSON.parse(sv.value); + res.json({ + collected: s.items.filter(Boolean).length, total:8, + questsDone: s.quests.filter(Boolean).length, questsTotal:8, + drinks: s.drinks, gameName: s.gameName, + spielerCount: db.prepare('SELECT COUNT(*) as c FROM spieler').get().c, + spieler: db.prepare('SELECT * FROM spieler').all() + }); +}); + +const PORT = process.env.PORT || 8082; +app.listen(PORT, '0.0.0.0', () => { + console.log(`🏰 Der letzte Auftrag server on port ${PORT}`); +});