Files
Mieterportal/kimi.md
T

19 KiB
Raw Blame History

Kimi-Kontext: Hinterstein Immobilien · Mieterportal (mieterportal.hocks.eu)

Projekt-Kontextdatei für Kimi Work. Stand: 02.10.2026, 13:00 Uhr. Bitte bei größeren Änderungen am Projekt aktualisieren. Konventionen und Workflow sind am Projekt Schulportal (sfg.hocks.eu) orientiert.

1. Projektüberblick

Mieterportal der Hinterstein Immobilien (Demo, Inhaber Joseph Hinterstein) für 50 Wohnungen in 3 Häusern (Hauptstraße 12 · Ringweg 3 · Am Park 5). Analog zum Schulportal: alle Mietangelegenheiten digital an einem Ort.

Datenhaltung (seit v1.3): zentral serverseitig in PostgreSQL über eine Node-API (Express) – kein localStorage mehr für Mietdaten. Mehrere Nutzer sehen denselben Datenstand in Echtzeit; Rechte werden serverseitig erzwungen (Mieter nur eigene Wohnung, Verwaltung alles).

Authentifizierung: Login-Pflicht auf /login (Route-Guard in App.tsx). Gruppen verwaltung und mieter; Mieter registrieren sich per einmaligem Registrierungscode (/registrieren). Passwörter als SHA-256-Hash (im Browser gerechnet, Vergleich serverseitig), Sitzung als HttpOnly-Cookie mp_session (8 h, Tabelle sessions). Demo-Zugänge (Passwort 0815): j.hinterstein (Verwaltung), a.berger (Mieterin, Hauptstraße 12 Whg. 7), m.wolf (Mieter, Ringweg 3 Whg. 4). Demo-Registrierungscode: MP-2026-0815.

Die 6 Module:

  1. Dashboard (/) – Mieter: Begrüßung, nächste Zahlung, Mietdiagramm, Ankündigungen, offene Meldungen. Verwaltung: Kennzahlen (Vermietungsquote, Miet-Soll, Rückstände), Einnahmenverlauf + Belegung pro Haus (Recharts).
  2. Mietkonto & Zahlungen (/mietkonto) – Buchungsverlauf 2026 mit Statusfilter, Nebenkostenguthaben, Zahlungsdaten. Verwaltung: Zahlungsstatus aller Wohnungen, Rückstandsliste, Haus-Filter.
  3. Schadensmeldungen (/schadensmeldungen) – Tickets mit Kategorie, Dringlichkeit, Status; Neuanlage per Dialog; Verwaltung pflegt Status per Select.
  4. Dokumente (/dokumente) – Verträge/Abrechnungen/Hausordnung/Protokolle, Suche + Kategorie-Chips. Mieter sieht eigene + Hausdokumente (unitId=null).
  5. Nachrichten (/nachrichten) – Chat Mieter ↔ Verwaltung, Ungelesen-Markierung, neue Unterhaltungen.
  6. Benutzerverwaltung (/benutzer, nur Gruppe verwaltung) – Nutzerliste mit Gruppen und Wohnungsbezug, Registrierungscodes erzeugen/deaktivieren, Demo-Zugänge zurücksetzen.

2. Technischer Stack

Teil Technologie
Frontend React 18 + TypeScript, Vite 7, Tailwind v3.4 + shadcn/ui (Radix), React Router 7 (BrowserRouter), Recharts
State Store-Kontext im Browser, alle Daten serverseitig (kein localStorage mehr); Ladezustand bereit, Schreibzugriff async
API server/: Node 20 + Express + pg, Session-Cookie, Rollenrechte, serverseitige Mail-Benachrichtigungen
Datenbank PostgreSQL 16 (mp-db), Schema/Seed in server/src/db.js + seed.js, Volume mp_pgdata
Daten server/seed/generated.json (50 Wohnungen + 552 Buchungen via beispieldaten.py) + server/src/seed-data.js (handschriftlich)
Auth SHA-256-Hash (Browser) + HttpOnly-Cookie-Session (8 h), src/lib/api.ts, src/lib/auth.ts
Branding Kunde: src/assets/hinterstein/ (logo.png, joseph-hinterstein.jpg, kontakt.jpg); KI-generiert: src/assets/logo.png, hero.jpg, modules.jpg
Webserver Docker-Container mp-portal (nginx), statisches dist/, proxiet /api/ → mp-api:3000
Mail mail-server/: Postfix-Relay (mp-mail) + Python-API (mp-mail-api) – live, /opt/mp-mail; Portal-nginx proxyt POST /api/mail/send mit Token aus portal.env
Server Root-VPS hocks.eu (IONOS), Docker + Portainer, Subdomain via NPM (Nginx Proxy Manager, Let's Encrypt)

3. Wichtige Dateien

Projektverzeichnis: C:\Users\ah\Documents\Kimi\Workspaces\Mieterportal Git: https://git.hocks.eu/ah-admin/Mieterportal (Branch main). Secrets ausgesperrt via .gitignore (mail-server/mail.env etc.).

Datei Inhalt
src/store/store.tsx Store-Kontext: lädt /api/bootstrap, stellt Collections + me/role/meineEinheit bereit, async-Methoden für alle Schreibaktionen, fmtEUR
src/lib/api.ts Fetch-Client (same-origin, JSON, ApiFehler mit deutscher Meldung)
src/lib/auth.ts SHA-256 für Passwörter, DEMO_PASSWORT
server/src/index.js Express-API: Auth (Login/Logout/me/Register), Bootstrap, Tickets, Threads/Messages, Benutzerverwaltung, Codes, /api/healthz
server/src/db.js pg-Pool + idempotentes Schema (11 Tabellen, snake_case)
server/src/seed.js Idempotenter Seed (läuft nur bei leerer users-Tabelle)
server/src/seed-data.js Handschriftliche Demo-Daten (Tickets, Dokumente, Threads, Ankündigungen, Benutzer, Codes)
server/src/mail.js Serverseitiger Versand über mp-mail-api (fire-and-forget)
server/seed/generated.json 50 Wohnungen + 552 Buchungen, erzeugt von beispieldaten.py (Seed 20261002)
beispieldaten.py Deterministischer Generator: python beispieldaten.py [seed] → server/seed/generated.json
src/types/index.ts Alle Typen (Unit, Transaction, Ticket, DocumentItem, Thread, Announcement, Benutzer, RegistrierungsCode)
src/components/AppLayout.tsx Sidebar (Desktop) + Mobile Top-Bar/Nav, Logout, Ungelesen-Badge, ThemeToggle
src/components/ThemeToggle.tsx Dark-Mode-Umschalter (Sonne/Mond)
src/lib/theme.ts mp-theme in localStorage (nur noch Theme!), Init vor erstem Render (main.tsx)
src/pages/Login.tsx Login-Pflicht: Hero mit Joseph-Hinterstein-Portrait, Logo, Feature-Liste, Login + Registrierung + Demo-Einstiege
src/pages/BenutzerVerwaltung.tsx Benutzerverwaltung (nur Verwaltung): Nutzer anlegen/pflegen, Registrierungscodes erzeugen
docker/templates/default.conf.template nginx-Site als envsubst-Template: SPA, /api/ → mp-api, POST /api/mail/send → mp-mail-api mit ${MAIL_API_TOKEN}
docker-compose.yml Stack: mp-portal (nginx), mp-api (Node, Build ./server), mp-db (PostgreSQL 16, Volume mp_pgdata), Netz npm_default
deploy.sh Deploy-Workflow: Build, Paket (dist + compose + nginx + server), Upload, docker compose up -d --build, Health-Check
mail-server/ Postfix-Relay + schlanke Mail-API (Muster Schulportal, angepasst) – separater Stack unter /opt/mp-mail

4. Deploy-Workflow (bewährt, aus Schulportal übernommen)

# 1. Bauen (im Projektverzeichnis)
npm run build

# 2. Paketieren + Hochladen (pscp braucht cwd = Projektverzeichnis)
tar -czf mp-dist.tar.gz -C dist .
"/c/Program Files/PuTTY/pscp.exe" -batch \
  -hostkey "SHA256:4G3UxsugNkHH6BrPXScLXh+vX8P1sLgWKn8cZBKSRxw" \
  -i "Z:\Devel\ssh\priv_ionos_hocks_vps.ppk" -P 38592 \
  mp-dist.tar.gz root@hocks.eu:/tmp/

# 3. Entpacken + Container neu starten + Hash prüfen
"/c/Program Files/PuTTY/plink.exe" -batch -hostkey "SHA256:4G3UxsugNkHH6BrPXScLXh+vX8P1sLgWKn8cZBKSRxw" \
  -i "Z:\Devel\ssh\priv_ionos_hocks_vps.ppk" -P 38592 root@hocks.eu \
  "rm -rf /opt/mieterportal/dist && mkdir -p /opt/mieterportal/dist && \
   tar -xzf /tmp/mp-dist.tar.gz -C /opt/mieterportal/dist && \
   cd /opt/mieterportal && docker compose up -d && sleep 2 && \
   curl -s https://mieterportal.hocks.eu/ | grep -o 'index-[^\"]*\.js'"

# Alternativ alles in einem Schritt:
./deploy.sh
  • SSH: Port 38592, Key Z:\Devel\ssh\priv_ionos_hocks_vps.ppk (Pageant), Hostkey-Flag zwingend.
  • portal.env auf dem Server (gitignored, liegt nur dort): liefert MAIL_API_TOKEN (für nginx-envsubst und mp-api) und POSTGRES_PASSWORD (für mp-db und die DATABASE_URL des mp-api). Einmalig angelegt: grep '^MAIL_API_TOKEN=' /opt/mp-mail/mail.env > /opt/mieterportal/portal.env plus zufälliges POSTGRES_PASSWORD=…. Wichtig: compose interpoliert ${VARIABLE} nur aus einer .env-Datei im Projektverzeichnis (nicht aus env_file:!) – daher auf dem Server /opt/mieterportal/.env als Symlink auf portal.env: cd /opt/mieterportal && ln -sf portal.env .env
  • deploy.sh macht docker compose up -d --build (baut das mp-api-Image auf dem Server, npm ci im Container) und prüft danach Container-Status, Portal-Hash und GET /api/healthz.
  • Vorsicht Fail2Ban: zu viele gescheiterte SSH-Versuche von einer IP führen zu einem Ban (Timeout-Verbindungsbild). Ban aufheben auf dem Server: fail2ban-client unban --all.
  • Live (seit 02.10.2026): Container mp-portal läuft auf hocks.eu, statisches dist/ unter /opt/mieterportal/dist (gemountet). NPM-Proxy-Host id 7 (Datei /data/compose/1/data/nginx/proxy_host/7.conf, DB-Tabelle proxy_host), Zertifikat npm-9 (Let's Encrypt, läuft bis 31.12.2026; via certbot/webroot erstellt, liegt im npm-Container unter /etc/letsencrypt/live/npm-9/). HTTP→HTTPS-Redirect aktiv (HSTS, Block-Exploits, HTTP/2). Änderungen am Proxy-Host im NPM-UI werden die DB-Konfiguration konsistent halten; manuelle Conf-Edits werden beim nächsten UI-Speichern überschrieben.
  • HTML wird mit no-cache geliefert (nginx-Config), Assets hash-benannt → Reload genügt. Der eingebettete Kimi-Browser cached hart → beim Testen Cache-Buster anhängen: ?v=X.

5. Zugänge & Konfiguration

  • Demo-Zugänge (Passwort 0815): j.hinterstein (Verwaltung), a.berger (Mieterin, Hauptstraße 12 Whg. 7), m.wolf (Mieter, Ringweg 3 Whg. 4). Demo-Registrierungscode: MP-2026-0815. Passwörter liegen als SHA-256-Hash in der users-Tabelle; Sitzung als HttpOnly-Cookie (Tabelle sessions). Abmelden reicht zum „Reset" – nichts liegt mehr im Browser-localStorage (außer dem Dark-Mode-Theme).
  • Verwaltungs-E-Mail: axel@hocks.eu (echtes, zustellbares Postfach, damit Benachrichtigungen ankommen) – im Portal änderbar.
  • Mail-API-Token: bewusst nicht hier dokumentiert – liegt auf dem Server in /opt/mp-mail/mail.env (Vorlage mail.env.example im Repo).
  • Mail-Stack live (seit 02.10.2026): Container mp-mail + mp-mail-api laufen auf hocks.eu unter /opt/mp-mail (Relay über hocks-eu.mail.protection.outlook.com). Testmail und API-/send (mit X-Api-Token) verifiziert (status=sent); ohne Token 401.
  • Mail-Benachrichtigungen (seit v1.2 nginx-Proxy, seit v1.3 serverseitig, seit v1.4 im HTML-Format wie im Schulportal): Der Portal-nginx proxiet POST /api/mail/send an http://mp-mail-api:8080/send mit Token aus portal.env (403 für alles außer POST). Versand aus der Node-API (server/src/mail.js, fire-and-forget): neue Schadensmeldung → alle aktiven Verwaltungsbenutzer, neue Nachricht/Antwort → Gegenstelle. HTML im Schulportal-Stil (baueHtmlMail: 600-px-Karte, Kopfzeile in Hinterstein- Farben #22333b/Amber, Zeilen-Tabelle, Status-Box, Zitat-Box, Button „Zum Mieterportal“, Footer) immer mit Plain-Text-Fallback (multipart/alternative). E2E verifiziert: Zustellung an axel@hocks.eu, Postfix status=sent.

6. Konventionen & Besonderheiten

  • Dark Mode: class-Strategie, Präferenz mp-theme, Init vor erstem Render. Feste Farbklassen werden in index.css für .dark umrechnen (.dark .bg-white/.bg-slate-50/.bg-teal-50); Sidebar ist eigenes Dunkel-Teal.
  • Barrierefreiheit: aria-pressed/labels am ThemeToggle und Rollenwechsel, Fokus-Ringe.
  • Formatierung: Beträge via fmtEUR (Intl de-DE), Datumsformate TT.MM.JJJJ.
  • Datenpflege: Bestandsdaten ändern → beispieldaten.py anpassen und neu ausführen, nicht generated.json von Hand editieren.
  • Sicherheit: kein offenes Relay (Postfix mynetworks loopback + Docker-Netz, sender_canonical zentral). Upload/Download-Funktionen sind Demo-Darstellungen.
  • Keine destruktiven Aktionen auf dem Server ohne ausdrückliche Bestätigung.

7. Testing

  • npm run build (tsc -b) ist die echte Typprüfung; npx tsc --noEmit prüft wegen Solution-tsconfig ggf. nichts. Server-Syntax: node --check server/src/*.js.
  • API-Livetests mit curl (Cookie-Jar): Login → /api/auth/me → /api/bootstrap (Mieter-Sicht zählt kleiner!) → Ticket/Thread schreiben → Postfix-Log auf Zustellung prüfen (docker logs mp-mail | grep to=<empfaenger>).
  • Headless-Screenshots (Dark Mode = Systempräferenz, --virtual-time-budget=10000 wartet auf den Session-Check): "/c/Program Files/Google/Chrome/Application/chrome.exe" --headless --disable-gpu --screenshot=... --window-size=WxH "URL"
  • Screenshots ausgeliefert nach preview/.

8. Verlauf / offene Punkte

Erledigt 02.10. (v1.8): Schadensmeldungen starten jetzt automatisch eine Unterhaltung (Thread „Schadensmeldung: …" mit erster Nachricht inkl. Kategorie/ Priorität, ungelesen für die Gegenseite) – Antworten darauf lösen über /api/threads/:id/messages wie gehabt Mails an Verwaltung + Mieter aus. Zusätzlich Eingangsbestätigungs-Mail an den Mieter bei jeder Meldung. Verwaltungs-Mails gehen jetzt an zwei echte Postfächer: joseph@hinterstein-immo.com (Benutzer j.hinterstein, Seed + Live-DB geändert) und axel@hocks.eu (neuer Verwaltungsbenutzer a.hocks, Passwort 0815, Seed + Live-DB). E2E verifiziert: Meldung + Antwort → je Mail an beide Postfächer zugestellt (Postfix status=sent); Mieter-Adressen der Demoseeds sind example.de und bouncen bewusst.

Erledigt 02.10. (v1.7): Responsive Layout repariert. Sidebar skaliert jetzt in zwei Stufen (w-64 ab lg, xl:w-72) statt fixer 288 px. Mobile Ansicht (Top-Bar + horizontal scrollende Nav) gilt jetzt bis 1024 px (md:hidden → lg:hidden) – vorher stand zwischen 768–1024 px die volle Sidebar und quetschte Inhalt + Fußzeile. Sidebar-Fußzeile bricht um (flex-wrap, truncate-sichere Buttons), main-Padding folgt dem lg-Breakpoint. Tailwind war bereits das CSS-Framework (kein Wechsel nötig). Screenshots: preview/09-mobile-900px.png, preview/10-desktop-1280px.png.

Erledigt 02.10. (v1.6): Login-Race behoben: setMe läuft jetzt erst nach ladeAlles() (Start/Login/Registrierung) – vorher rendereten die Seiten gegen noch leere Daten → Absturz → schwarzer Bildschirm, erst Reload half. Zusätzlich FehlerGrenze (ErrorBoundary) in App.tsx: Render-Fehler zeigen Hinweis + Reload-Button statt schwarzem Bildschirm.

Erledigt 02.10. (v1.5): Lokales Datenbank-Backup: backup/vps-db-backup.cmd holt nächtlich (Aufgabenplanung „Mieterportal DB-Backup", täglich 03:17 Uhr, Benutzer ah, interaktiv) per plink ein pg_dump | gzip vom Container mp-db und legt es nach D:\VPS_Backup\mieterportal\mieterportal-db-YYYY-MM-DD.sql.gz (14-tägige Aufbewahrung via forfiles, Log: backup.log). Probelauf OK (11 KB, Dump-Format geprüft). Einschränkung: läuft nur, wenn der Rechner an und Benutzer ah angemeldet ist (Z:-Laufwerk mit dem SSH-Key, Pageant).

Erledigt 02.10. (v1.0): Portal komplett neu aufgesetzt (5 Module, Mieter-/Verwaltungsansicht, KI-Branding: Logo/Hero/Illustration), Docker-Setup (Dockerfile, nginx, compose), Repo in git.hocks.eu angelegt, Deploy-Workflow + Dark Mode + beispieldaten.py aus dem Schulportal übernommen, mail-server-Struktur angelegt (noch nicht live verkabelt).

Erledigt 02.10. (v1.0 live): Deployment auf hocks.eu durchgeführt: Container mp-portal läuft (/opt/mieterportal, compose + gemountetes dist), NPM-Proxy-Host id 7 + LE-Zertifikat npm-9 eingerichtet (per certbot/webroot, DB-Insert, 7.conf aus 5.conf abgeleitet, nginx reload). Live geprüft: https://mieterportal.hocks.eu/ → HTTP 200, Titel korrekt, SPA-Routing (/mietkonto) 200, HTTP→HTTPS-Redirect 301, Zertifikat CN=mieterportal.hocks.eu gültig bis 31.12.2026. Zertifikatsdateien liegen im npm-Container unter /etc/letsencrypt/live/npm-9/ (Backup der Zertifikate auf dem Host: /root/le-mp/). Fail2Ban-Ban nach frühen Fehlversuchen ist nach ~40 min automatisch ausgelaufen.

Erledigt 02.10. (v1.1 live): Rebrand auf „Hinterstein Immobilien" (Kunden-Logo, Joseph-Hinterstein-Portrait auf der Login-Seite, Kontakt-Banner), echtes Authentifizierungssystem (Login-Pflicht, Gruppen Verwaltung/Mieter, SHA-256, Session 8 h), Mieter-Registrierung per einmaligem Code, Benutzerverwaltung als 6. Modul (/benutzer), Demo-Zugänge mit Passwort 0815. Mail-Stack auf dem Server aktiviert (/opt/mp-mail, Testmail + API-/send verifiziert). Store-Key auf hinterstein-mieterportal-v2 gehoben. Live deployed und geprüft (HTTP 200, Titel „Hinterstein Immobilien · Mieterportal").

Erledigt 02.10. (v1.2 live): E-Mail-Benachrichtigungen an das Portal angeschlossen: nginx-Proxy /api/mail/send mit serverseitig injiziertem Token (envsubst-Template + portal.env auf dem Server), src/lib/mail.ts sendet bei neuen Schadensmeldungen (→ Verwaltung) und Nachrichten/Antworten (→ Gegenstelle). E2E-Versand live verifiziert (versandt: true), GET auf den Endpunkt 403.

Erledigt 02.10. (v1.3 live): Serverseitige Datenbank: PostgreSQL 16 (mp-db, Volume mp_pgdata) + Node-API (mp-api, Express + pg, eigenes Image via deploy.sh --build). Schema 11 Tabellen (idempotent), Seed einmalig aus server/seed/generated.json + seed-data.js (Verwaltungs-E-Mail auf echtes Postfach axel@hocks.eu gesetzt). Frontend komplett auf API umgestellt: src/lib/api.ts, Store lädt /api/bootstrap, kein localStorage mehr (außer Theme), Ladebildschirm während Session-Check. Rollenrechte serverseitig durchgesetzt und getestet (Mieterin sieht nur hs-7: 12 Buchungen/2 Tickets, 403 auf fremde Unterhaltung; Verwaltung sieht alles: 552 Buchungen/8 Tickets). Registrierung, Code-Erzeugung, Ticket inkl. Mail-Benachrichtigung (status=sent an axel@hocks.eu) E2E verifiziert; Testdaten danach bereinigt. deploy.sh baut das API-Image auf dem Server; .env-Symlink auf portal.env für Compose-Interpolation beachten (POSTGRES_PASSWORD!).

Erledigt 02.10. (v1.4 live): HTML-E-Mail-Benachrichtigungen im Schulportal- Stil: baueHtmlMail/buttonHtml in server/src/mail.js (600-px-Karte, Kopfzeile Hinterstein-Farben, Zeilen, Status-Box, Zitat, Aktions-Button, Footer), immer mit Plain-Text-Fallback (multipart/alternative). Umgesetzt für Schadensmeldung, neue Unterhaltung und Antwort. Vorschauen lokal renderbar (mail-vorschau*.html, Screenshots in preview/08-*.png). Live getestet: Zustellung an axel@hocks.eu status=sent, Test-Ticket bereinigt.

Offen:

  • Zertifikatserneuerung prüfen (per 01.12.2026): NPM-Renewer sollte cert 9 über HTTP-Challenge erneuern; sonst certbot/webroot + Kopie nach npm-9 wiederholen.
  • Demo-Empfängeradressen der Mieter (example.de) durch echte ersetzen, sobald bekannt.
  • Backup ggf. serverseitig ergänzen (zweite Sicherungslinie, falls lokaler Rechner nachts aus ist), Backup-Wiederherstellung einmal testen.
  • Sicherheit für Produktion: Passwort-Hash serverseitig salten (bcrypt/argon2), CSRF-Token, Rate-Limiting auf /api/auth/login.
  • Mögliche nächste Schritte: E-Mail-Verifizierung bei Registrierung, Dokumenten-Download mit echten Dateien.

9. Sonstiges

  • User kommuniziert auf Deutsch; alle UI-Texte und Antworten auf Deutsch.
  • Dateien außerhalb des Workspaces immer erst in den Workspace kopieren.
  • Vorsicht mit destruktiven Aktionen (Explizite Bestätigung einholen).