feat(betrieb): Health-Endpunkt und Seed für eine frische Datenbank
/api/health meldet Status, Version, Instanz und Erreichbarkeit der Datenbank – ohne Anmeldung, deshalb ohne Zählstände, Namen oder Adressen. update.sh wird sich später darauf stützen. Der Seed legt die vier Bereiche an und ein erstes Verwaltungskonto aus ADMIN_EMAIL und ADMIN_PASSWORT. Er läuft mehrfach ohne Schaden; ohne ADMIN_PASSWORT entsteht ein Konto, das sich nur über OIDC oder Passkey anmelden kann. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
e9a2b4b463
commit
1876ae4c6b
+156
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Grundausstattung für eine frische Datenbank.
|
||||
*
|
||||
* Läuft mehrfach ohne Schaden: alles wird per upsert angelegt, vorhandene
|
||||
* Einträge bleiben unangetastet. Es entstehen keine erfundenen Aufgaben oder
|
||||
* Termine – dafür gibt es prisma/demo-seed.ts.
|
||||
*/
|
||||
import 'dotenv/config'
|
||||
import { PrismaPg } from '@prisma/adapter-pg'
|
||||
import bcrypt from 'bcryptjs'
|
||||
import { PrismaClient } from '../src/generated/prisma/client.js'
|
||||
|
||||
const verbindung = process.env.DATABASE_URL
|
||||
if (!verbindung) {
|
||||
throw new Error('DATABASE_URL ist nicht gesetzt.')
|
||||
}
|
||||
|
||||
const db = new PrismaClient({ adapter: new PrismaPg({ connectionString: verbindung }) })
|
||||
|
||||
const BEREICHE = [
|
||||
{
|
||||
name: 'Firma',
|
||||
farbe: '#2563eb',
|
||||
reihenfolge: 1,
|
||||
arbeitsfensterVon: 7 * 60 + 30,
|
||||
arbeitsfensterBis: 16 * 60 + 30,
|
||||
kapazitaetMo: 360,
|
||||
kapazitaetDi: 360,
|
||||
kapazitaetMi: 360,
|
||||
kapazitaetDo: 360,
|
||||
kapazitaetFr: 240,
|
||||
kapazitaetSa: 0,
|
||||
kapazitaetSo: 0,
|
||||
},
|
||||
{
|
||||
name: 'Gewerbe',
|
||||
farbe: '#7c3aed',
|
||||
reihenfolge: 2,
|
||||
arbeitsfensterVon: 17 * 60,
|
||||
arbeitsfensterBis: 21 * 60,
|
||||
kapazitaetMo: 90,
|
||||
kapazitaetDi: 90,
|
||||
kapazitaetMi: 90,
|
||||
kapazitaetDo: 90,
|
||||
kapazitaetFr: 60,
|
||||
kapazitaetSa: 180,
|
||||
kapazitaetSo: 0,
|
||||
},
|
||||
{
|
||||
name: 'Privat',
|
||||
farbe: '#16a34a',
|
||||
reihenfolge: 3,
|
||||
arbeitsfensterVon: 17 * 60,
|
||||
arbeitsfensterBis: 22 * 60,
|
||||
kapazitaetMo: 60,
|
||||
kapazitaetDi: 60,
|
||||
kapazitaetMi: 60,
|
||||
kapazitaetDo: 60,
|
||||
kapazitaetFr: 120,
|
||||
kapazitaetSa: 240,
|
||||
kapazitaetSo: 240,
|
||||
},
|
||||
{
|
||||
name: 'Homelab',
|
||||
farbe: '#ea580c',
|
||||
reihenfolge: 4,
|
||||
arbeitsfensterVon: 19 * 60,
|
||||
arbeitsfensterBis: 23 * 60,
|
||||
kapazitaetMo: 0,
|
||||
kapazitaetDi: 60,
|
||||
kapazitaetMi: 0,
|
||||
kapazitaetDo: 60,
|
||||
kapazitaetFr: 0,
|
||||
kapazitaetSa: 120,
|
||||
kapazitaetSo: 60,
|
||||
},
|
||||
]
|
||||
|
||||
async function bereicheAnlegen(): Promise<void> {
|
||||
for (const bereich of BEREICHE) {
|
||||
await db.bereich.upsert({
|
||||
where: { name: bereich.name },
|
||||
update: {},
|
||||
create: bereich,
|
||||
})
|
||||
}
|
||||
console.log(`Bereiche geprüft: ${BEREICHE.map((b) => b.name).join(', ')}`)
|
||||
}
|
||||
|
||||
async function verwaltungAnlegen(): Promise<void> {
|
||||
const email = process.env.ADMIN_EMAIL?.trim().toLowerCase()
|
||||
const passwort = process.env.ADMIN_PASSWORT
|
||||
|
||||
if (!email) {
|
||||
console.log(
|
||||
'ADMIN_EMAIL ist nicht gesetzt – kein Verwaltungskonto angelegt. ' +
|
||||
'Bei reiner OIDC-Anmeldung ist das richtig: die erste Person, die sich ' +
|
||||
'anmeldet, muss danach von Hand hochgestuft werden.',
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
const vorhanden = await db.benutzer.findUnique({
|
||||
where: { email },
|
||||
select: { id: true, rolle: true },
|
||||
})
|
||||
|
||||
if (vorhanden) {
|
||||
if (vorhanden.rolle !== 'ADMIN') {
|
||||
await db.benutzer.update({ where: { id: vorhanden.id }, data: { rolle: 'ADMIN' } })
|
||||
console.log(`${email} zur Verwaltung hochgestuft.`)
|
||||
} else {
|
||||
console.log(`${email} ist bereits Verwaltung.`)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (!passwort) {
|
||||
await db.benutzer.create({
|
||||
data: { email, rolle: 'ADMIN', name: null },
|
||||
})
|
||||
console.log(
|
||||
`${email} als Verwaltung angelegt, ohne Passwort. ` +
|
||||
'Anmeldung ist damit nur über OIDC oder einen Passkey möglich.',
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (passwort.length < 12) {
|
||||
throw new Error('ADMIN_PASSWORT ist zu kurz, mindestens 12 Zeichen.')
|
||||
}
|
||||
|
||||
await db.benutzer.create({
|
||||
data: {
|
||||
email,
|
||||
rolle: 'ADMIN',
|
||||
passwortHash: await bcrypt.hash(passwort, 12),
|
||||
},
|
||||
})
|
||||
console.log(`${email} als Verwaltung mit Passwort angelegt.`)
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
await bereicheAnlegen()
|
||||
await verwaltungAnlegen()
|
||||
}
|
||||
|
||||
main()
|
||||
.then(async () => {
|
||||
await db.$disconnect()
|
||||
})
|
||||
.catch(async (fehler: unknown) => {
|
||||
console.error(fehler)
|
||||
await db.$disconnect()
|
||||
process.exit(1)
|
||||
})
|
||||
Reference in New Issue
Block a user