Files
Rotatory/server/store.js
T
ferdi2goandClaude Fable 5.1 6a3c68aaac Tauschanfragen für Teammitglieder ergänzen
Betrachter klicken ihren Tag an, wählen ihren Namen, optional Wunschperson
und Wunschtag, und schreiben eine Notiz; keine Anmeldung nötig, zehn
Anfragen je Stunde und Adresse. Offene Anfragen markieren den Tag im
Kalender. Bearbeiter sehen sie unter „Anfragen" mit Zähler und führen den
Tausch aus (über Kreuz oder Übernahme), öffnen den Tag oder lehnen ab.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 08:29:02 +00:00

65 lines
2.1 KiB
JavaScript

import fs from 'node:fs';
import path from 'node:path';
const DEFAULT_STATE = () => ({
persons: [],
absences: [],
holidays: [],
assignments: {},
requests: [],
exported: {},
settings: {
meetingTime: '09:30',
durationMinutes: 15,
bundesland: 'BY',
calendarDisplayName: 'Morning Meeting',
inviteAttendees: false,
historyWindowDays: 60,
holidaysSeeded: false,
targetCalendarHref: '',
targetCalendarName: '',
absenceCalendarHref: '',
absenceCalendarName: '',
},
});
/** Bildtyp anhand der ersten Bytes erkennen. */
export function imageMime(buffer) {
if (!buffer || buffer.length < 12) return null;
if (buffer[0] === 0xff && buffer[1] === 0xd8) return 'image/jpeg';
if (buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4e && buffer[3] === 0x47) return 'image/png';
if (buffer.subarray(0, 4).toString('ascii') === 'RIFF' && buffer.subarray(8, 12).toString('ascii') === 'WEBP') return 'image/webp';
if (buffer.subarray(0, 4).toString('ascii') === 'GIF8') return 'image/gif';
return null;
}
/** Einfacher JSON-Speicher mit atomarem Schreiben. Fotos liegen daneben im Ordner photos/. */
export function createStore(dataDir) {
const file = path.join(dataDir, 'rotatory.json');
const photoDir = path.join(dataDir, 'photos');
fs.mkdirSync(photoDir, { recursive: true });
let state = DEFAULT_STATE();
if (fs.existsSync(file)) {
const loaded = JSON.parse(fs.readFileSync(file, 'utf8'));
state = { ...state, ...loaded, settings: { ...state.settings, ...(loaded.settings || {}) } };
}
function save() {
const tmp = file + '.tmp';
fs.writeFileSync(tmp, JSON.stringify(state, null, 2));
fs.renameSync(tmp, file);
}
const photoPath = (id) => path.join(photoDir, `${id}.jpg`);
return {
get state() { return state; },
save,
photoPath,
hasPhoto: (id) => fs.existsSync(photoPath(id)),
readPhoto: (id) => (fs.existsSync(photoPath(id)) ? fs.readFileSync(photoPath(id)) : null),
savePhoto: (id, buffer) => fs.writeFileSync(photoPath(id), buffer),
deletePhoto: (id) => { try { fs.unlinkSync(photoPath(id)); } catch {} },
};
}