Bahn: aisupport, Analyse-O2C-C2S, awesome-bahn-mcp-servers, beam-mcp,
Confluence_Bot, db-planet-mcp-server, O2C-Harness, project-audit,
Projekt-KIQ-HP, teamlandkarte-mcp
Dhive: Jury-Voting
Privat: CV, NoteGraph (NOTE: NoteGraph needs complete redo after consolidation)
Shared: AI-Orchestrator, OrgMyLife, power_skills_and_more
Shared/references: symphony (read-only)
Bahn repos remain available as independent remotes - this monorepo
pulls them in via subtree, the originals are untouched.
137 lines
3.2 KiB
TypeScript
137 lines
3.2 KiB
TypeScript
import fs from 'fs';
|
|
import path from 'path';
|
|
import { StoreData, Session, Team, Juror, Score } from '../shared/types.js';
|
|
|
|
const DATA_DIR = path.join(process.cwd(), 'server', 'data');
|
|
const SESSION_FILE = path.join(DATA_DIR, 'session.json');
|
|
const SEED_FILE = path.join(DATA_DIR, 'seed.json');
|
|
const BACKUP_FILE = path.join(DATA_DIR, 'session.json.bak');
|
|
|
|
/**
|
|
* Ensure the data directory exists.
|
|
*/
|
|
function ensureDataDir(): void {
|
|
if (!fs.existsSync(DATA_DIR)) {
|
|
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize session.json from seed.json if it doesn't exist.
|
|
*/
|
|
function initializeStore(): void {
|
|
ensureDataDir();
|
|
if (!fs.existsSync(SESSION_FILE)) {
|
|
const seedData = fs.readFileSync(SEED_FILE, 'utf-8');
|
|
fs.writeFileSync(SESSION_FILE, seedData, 'utf-8');
|
|
}
|
|
}
|
|
|
|
// Initialize on module load
|
|
initializeStore();
|
|
|
|
/**
|
|
* Read the current store data from session.json.
|
|
*/
|
|
export function getStore(): StoreData {
|
|
const raw = fs.readFileSync(SESSION_FILE, 'utf-8');
|
|
return JSON.parse(raw) as StoreData;
|
|
}
|
|
|
|
/**
|
|
* Write store data atomically: write to temp file, then rename.
|
|
* Creates a .bak backup before each write.
|
|
*/
|
|
export function saveStore(data: StoreData): void {
|
|
ensureDataDir();
|
|
|
|
// Create backup of current file if it exists
|
|
if (fs.existsSync(SESSION_FILE)) {
|
|
fs.copyFileSync(SESSION_FILE, BACKUP_FILE);
|
|
}
|
|
|
|
// Write to temp file, then rename (atomic on most filesystems)
|
|
const tempFile = path.join(DATA_DIR, 'session.json.tmp');
|
|
fs.writeFileSync(tempFile, JSON.stringify(data, null, 2), 'utf-8');
|
|
fs.renameSync(tempFile, SESSION_FILE);
|
|
}
|
|
|
|
/**
|
|
* Get the current session.
|
|
*/
|
|
export function getSession(): Session {
|
|
const store = getStore();
|
|
return store.session;
|
|
}
|
|
|
|
/**
|
|
* Get all teams.
|
|
*/
|
|
export function getTeams(): Team[] {
|
|
const store = getStore();
|
|
return store.teams;
|
|
}
|
|
|
|
/**
|
|
* Get all jurors.
|
|
*/
|
|
export function getJurors(): Juror[] {
|
|
const store = getStore();
|
|
return store.jurors;
|
|
}
|
|
|
|
/**
|
|
* Get all scores.
|
|
*/
|
|
export function getScores(): Score[] {
|
|
const store = getStore();
|
|
return store.scores;
|
|
}
|
|
|
|
/**
|
|
* Add or update a score. Matches by jurorId + teamId + criterionId.
|
|
*/
|
|
export function addScore(score: Score): void {
|
|
const store = getStore();
|
|
const existingIndex = store.scores.findIndex(
|
|
(s) =>
|
|
s.jurorId === score.jurorId &&
|
|
s.teamId === score.teamId &&
|
|
s.criterionId === score.criterionId
|
|
);
|
|
|
|
if (existingIndex >= 0) {
|
|
store.scores[existingIndex] = score;
|
|
} else {
|
|
store.scores.push(score);
|
|
}
|
|
|
|
saveStore(store);
|
|
}
|
|
|
|
/**
|
|
* Update session with partial updates. Returns the updated session.
|
|
*/
|
|
export function updateSession(updates: Partial<Session>): Session {
|
|
const store = getStore();
|
|
store.session = { ...store.session, ...updates };
|
|
saveStore(store);
|
|
return store.session;
|
|
}
|
|
|
|
/**
|
|
* Update a juror by ID with partial updates. Returns the updated juror or null if not found.
|
|
*/
|
|
export function updateJuror(jurorId: string, updates: Partial<Juror>): Juror | null {
|
|
const store = getStore();
|
|
const jurorIndex = store.jurors.findIndex((j) => j.id === jurorId);
|
|
|
|
if (jurorIndex < 0) {
|
|
return null;
|
|
}
|
|
|
|
store.jurors[jurorIndex] = { ...store.jurors[jurorIndex], ...updates };
|
|
saveStore(store);
|
|
return store.jurors[jurorIndex];
|
|
}
|