feat(backend): GET/PUT /api/state mit Dokument-Persistenz
This commit is contained in:
parent
9714d845bb
commit
45503f03ee
2 changed files with 67 additions and 0 deletions
|
|
@ -1,11 +1,36 @@
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
|
const { getDoc, putDoc } = require('./db');
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use(express.json({ limit: '5mb' }));
|
app.use(express.json({ limit: '5mb' }));
|
||||||
|
|
||||||
|
const KEYS = ['employees', 'duties', 'vacation'];
|
||||||
|
const EMPTY = { employees: [], duties: {}, vacation: {} };
|
||||||
|
|
||||||
app.get('/api/health', (req, res) => res.json({ status: 'ok' }));
|
app.get('/api/health', (req, res) => res.json({ status: 'ok' }));
|
||||||
|
|
||||||
|
app.get('/api/state', (req, res) => {
|
||||||
|
const state = { ...EMPTY, updatedAt: null };
|
||||||
|
for (const key of KEYS) {
|
||||||
|
const doc = getDoc(key);
|
||||||
|
if (doc) {
|
||||||
|
state[key] = doc.value;
|
||||||
|
if (!state.updatedAt || doc.updatedAt > state.updatedAt) state.updatedAt = doc.updatedAt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.json(state);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.put('/api/state', (req, res) => {
|
||||||
|
const body = req.body || {};
|
||||||
|
const now = new Date().toISOString();
|
||||||
|
for (const key of KEYS) {
|
||||||
|
if (body[key] !== undefined) putDoc(key, body[key], now);
|
||||||
|
}
|
||||||
|
res.json({ status: 'ok', updatedAt: now });
|
||||||
|
});
|
||||||
|
|
||||||
// Statisches Frontend (Repo-Root). Dotfiles werden per Default nicht ausgeliefert.
|
// Statisches Frontend (Repo-Root). Dotfiles werden per Default nicht ausgeliefert.
|
||||||
app.use(express.static(path.join(__dirname, '..')));
|
app.use(express.static(path.join(__dirname, '..')));
|
||||||
|
|
||||||
|
|
|
||||||
42
server/state.test.js
Normal file
42
server/state.test.js
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
const { test } = require('node:test');
|
||||||
|
const assert = require('node:assert');
|
||||||
|
const fs = require('fs');
|
||||||
|
const os = require('os');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
process.env.DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), 'dp-state-'));
|
||||||
|
const app = require('./index');
|
||||||
|
|
||||||
|
async function req(port, method, body) {
|
||||||
|
const opts = { method, headers: { 'Content-Type': 'application/json' } };
|
||||||
|
if (body) opts.body = JSON.stringify(body);
|
||||||
|
const res = await fetch(`http://127.0.0.1:${port}/api/state`, opts);
|
||||||
|
return { status: res.status, body: await res.json() };
|
||||||
|
}
|
||||||
|
|
||||||
|
test('leerer Server liefert leeren State', async () => {
|
||||||
|
const server = app.listen(0);
|
||||||
|
const { port } = server.address();
|
||||||
|
try {
|
||||||
|
const { status, body } = await req(port, 'GET');
|
||||||
|
assert.strictEqual(status, 200);
|
||||||
|
assert.deepStrictEqual(body.employees, []);
|
||||||
|
assert.deepStrictEqual(body.duties, {});
|
||||||
|
assert.deepStrictEqual(body.vacation, {});
|
||||||
|
assert.strictEqual(body.updatedAt, null);
|
||||||
|
} finally { server.close(); }
|
||||||
|
});
|
||||||
|
|
||||||
|
test('PUT dann GET Roundtrip', async () => {
|
||||||
|
const server = app.listen(0);
|
||||||
|
const { port } = server.address();
|
||||||
|
try {
|
||||||
|
const put = await req(port, 'PUT', { employees: ['Max'], duties: { Max: { '2026-07': [] } } });
|
||||||
|
assert.strictEqual(put.status, 200);
|
||||||
|
assert.strictEqual(put.body.status, 'ok');
|
||||||
|
const get = await req(port, 'GET');
|
||||||
|
assert.deepStrictEqual(get.body.employees, ['Max']);
|
||||||
|
assert.deepStrictEqual(get.body.duties, { Max: { '2026-07': [] } });
|
||||||
|
assert.strictEqual(typeof get.body.updatedAt, 'string');
|
||||||
|
} finally { server.close(); }
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue