Files
Mieterportal/server/src/seed.js
T
ah-admin 311153b3c3 v1.3: Serverseitige Datenbank (PostgreSQL + Node-API) ersetzt localStorage
- server/: Express-API (Auth, Bootstrap, Tickets, Threads, Benutzerverwaltung, Codes)
  mit pg-Schema (11 Tabellen), idempotem Seed (50 Wohnungen, 552 Buchungen)
- mp-db (PostgreSQL 16, Volume mp_pgdata) + mp-api im Compose-Stack,
  nginx proxiet /api/ an mp-api; deploy.sh baut API-Image auf dem Server
- Frontend: Store laedt /api/bootstrap, Schreibzugriff async, kein localStorage
  mehr (ausser Theme), Ladebildschirm beim Session-Check, vite-Proxy fuer Dev
- Rollenrechte serverseitig: Mieter nur eigene Wohnung (getestet, 403-Verweis)
- Mail-Benachrichtigungen komplett serverseitig (server/src/mail.js)
- beispieldaten.py schreibt server/seed/generated.json; Verwaltungs-Mail = axel@hocks.eu
- E2E live verifiziert: Login, Scoping, Ticket + Zustellung (status=sent),
  Registrierung, Codes; Testdaten bereinigt; Doku nachgefuehrt
2026-10-02 12:54:44 +02:00

101 lines
3.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/** Idempotenter Seed: füllt die Datenbank beim ersten Start mit den
* Demo-Daten (50 Wohnungen, 552 Buchungen, Tickets, Dokumente, Threads,
* Ankündigungen, Demo-Benutzer, Registrierungscode). Läuft nur, wenn die
* users-Tabelle leer ist – bestehende Daten werden nie überschrieben.
*/
const { pool } = require('./db')
const { users, registrationCodes, tickets, documents, threads, announcements } = require('./seed-data')
const generated = require('../seed/generated.json')
async function seed() {
const { rows } = await pool.query('SELECT COUNT(*)::int AS n FROM users')
if (rows[0].n > 0) return false
const client = await pool.connect()
try {
await client.query('BEGIN')
for (const u of generated.units) {
await client.query(
`INSERT INTO units (id, building, number, floor, size, cold_rent, warm_rent, tenant, status)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)`,
[u.id, u.building, u.number, u.floor, u.size, u.coldRent, u.warmRent, u.tenant, u.status],
)
}
for (const t of generated.transactions) {
await client.query(
`INSERT INTO transactions (id, unit_id, tenant, month, year, amount, type, status, paid_at)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)`,
[t.id, t.unitId, t.tenant, t.month, t.year, t.amount, t.type, t.status, t.paidAt ?? null],
)
}
// Mieter-Namen in Tickets/Threads aus der units-Tabelle nachziehen
const { rows: unitRows } = await client.query('SELECT id, tenant FROM units')
const mieterVon = Object.fromEntries(unitRows.map((u) => [u.id, u.tenant]))
for (const t of tickets) {
await client.query(
`INSERT INTO tickets (id, unit_id, tenant, title, description, category, priority, status, created_at)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)`,
[t.id, t.unit_id, mieterVon[t.unit_id] ?? t.tenant, t.title, t.description, t.category, t.priority, t.status, t.created_at],
)
}
for (const d of documents) {
await client.query(
`INSERT INTO documents (id, unit_id, title, category, size, date) VALUES ($1,$2,$3,$4,$5,$6)`,
[d.id, d.unit_id, d.title, d.category, d.size, d.date],
)
}
for (const th of threads) {
await client.query(
`INSERT INTO threads (id, unit_id, tenant, subject, unread_by_admin, unread_by_tenant)
VALUES ($1,$2,$3,$4,$5,$6)`,
[th.id, th.unit_id, mieterVon[th.unit_id] ?? th.tenant, th.subject, th.unread_by_admin, th.unread_by_tenant],
)
for (const m of th.messages) {
await client.query(
`INSERT INTO messages (id, thread_id, from_role, author, text, at) VALUES ($1,$2,$3,$4,$5,$6)`,
[m.id, th.id, m.from_role, m.author, m.text, m.at],
)
}
}
for (const a of announcements) {
await client.query(
`INSERT INTO announcements (id, title, text, date, tag) VALUES ($1,$2,$3,$4,$5)`,
[a.id, a.title, a.text, a.date, a.tag],
)
}
for (const b of users) {
await client.query(
`INSERT INTO users (id, benutzername, name, passwort_hash, rolle, email, einheit_id, aktiv, erstellt_am, letzter_login)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)`,
[b.id, b.benutzername, b.name, b.passwort_hash, b.rolle, b.email, b.einheit_id, b.aktiv, b.erstellt_am, b.letzter_login ?? null],
)
}
for (const c of registrationCodes) {
await client.query(
`INSERT INTO registration_codes (code, einheit_id, erstellt_am, verwendet_am) VALUES ($1,$2,$3,$4)`,
[c.code, c.einheit_id, c.erstellt_am, c.verwendet_am],
)
}
await client.query('COMMIT')
return true
} catch (e) {
await client.query('ROLLBACK')
throw e
} finally {
client.release()
}
}
module.exports = { seed }