2026-07-06 19:08:24 +02:00
|
|
|
import express from 'express'
|
2026-09-16 14:17:25 +02:00
|
|
|
import { existsSync } from 'node:fs'
|
2026-07-06 19:08:24 +02:00
|
|
|
import './database'
|
2026-09-16 14:17:25 +02:00
|
|
|
import { getAllRows } from './db/crud'
|
2026-07-06 19:08:24 +02:00
|
|
|
import { rpcModules } from './rpcModules'
|
2026-09-16 14:17:25 +02:00
|
|
|
import {
|
|
|
|
|
STARTER_FILE,
|
|
|
|
|
readStarterPackage,
|
|
|
|
|
importStarterPackage,
|
|
|
|
|
summarizeStarter,
|
|
|
|
|
} from './db/starterPackage'
|
2026-07-06 19:08:24 +02:00
|
|
|
|
|
|
|
|
const app = express()
|
|
|
|
|
app.use(express.json({ limit: '50mb' })) // Backup-Import kann alle Tabellen als ein JSON-Body enthalten
|
|
|
|
|
|
|
|
|
|
app.post('/api/rpc', (req, res) => {
|
|
|
|
|
const { module, fn, args } = req.body as { module?: string; fn?: string; args?: unknown[] }
|
|
|
|
|
|
|
|
|
|
const mod = module ? rpcModules[module] : undefined
|
|
|
|
|
const handler = fn ? mod?.[fn] : undefined
|
|
|
|
|
if (!mod || typeof handler !== 'function') {
|
|
|
|
|
res.json({ ok: false, error: `Unbekannte RPC-Zielfunktion: ${module}.${fn}` })
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const result = handler(...(args ?? []))
|
|
|
|
|
res.json({ ok: true, result: result ?? null })
|
|
|
|
|
} catch (err) {
|
|
|
|
|
res.json({ ok: false, error: err instanceof Error ? err.message : 'Unbekannter Server-Fehler' })
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2026-08-08 17:35:05 +02:00
|
|
|
/** V2-Default 4001 — Produktiv bleibt auf 4000. */
|
|
|
|
|
const port = process.env.API_PORT ? Number(process.env.API_PORT) : 4001
|
|
|
|
|
if (!Number.isFinite(port) || port === 4000) {
|
|
|
|
|
throw new Error(
|
|
|
|
|
`[server] SAFETY: API_PORT=${process.env.API_PORT ?? '(unset)'} ist ungültig oder kollidiert mit Produktiv-Port 4000. ` +
|
|
|
|
|
`V2 erwartet z.B. 4001 (siehe .env).`,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-16 14:17:25 +02:00
|
|
|
/** Team-ZIP: leere DB automatisch mit Struktur befüllen (keine Meetings/Personen). */
|
|
|
|
|
function seedStarterIfEmpty(): void {
|
|
|
|
|
const dims = getAllRows('feedbackDimensions')
|
|
|
|
|
if (dims.length > 0) return
|
|
|
|
|
if (!existsSync(STARTER_FILE)) {
|
|
|
|
|
console.warn('[server] DB ohne Feedback-Struktur und ohne Starter-Paket — Config bleibt leer.')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
const pkg = readStarterPackage()
|
|
|
|
|
const { structureRows } = importStarterPackage(pkg)
|
|
|
|
|
const counts = summarizeStarter(pkg)
|
|
|
|
|
console.log(`[server] Starter-Struktur automatisch geladen (${structureRows} Zeilen).`)
|
|
|
|
|
console.log(
|
|
|
|
|
`[server] Dimensionen=${counts.feedbackDimensions} Kategorien=${counts.feedbackCategories} ` +
|
|
|
|
|
`Kriterien=${counts.feedbackCriterionItems} Typen=${counts.assignmentTypes}`,
|
|
|
|
|
)
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.warn('[server] Auto-Seed fehlgeschlagen:', err instanceof Error ? err.message : err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
seedStarterIfEmpty()
|
|
|
|
|
|
2026-07-06 19:08:24 +02:00
|
|
|
app.listen(port, () => {
|
2026-08-08 17:35:05 +02:00
|
|
|
console.log(`[server] DEV-RPC-API (AssignmentMonitorV2) auf http://localhost:${port}/api/rpc`)
|
|
|
|
|
console.log(`[server] Produktiv (AssigmentMonitor) nutzt Port 4000 — diese Instanz ist davon getrennt.`)
|
2026-07-06 19:08:24 +02:00
|
|
|
})
|