- server/src/mail.js: baueHtmlMail/buttonHtml (600-px-Karte, Kopfzeile in Hinterstein-Farben #22333b + Amber, Zeilen-Tabelle, Status-Box, Zitat-Box, Aktions-Button 'Zum Mieterportal', Footer), immer mit Plain-Text-Fallback - index.js: Ticket, neue Unterhaltung und Antwort versenden HTML + Text (multipart/alternative ueber die Mail-API) - Vorschauen: preview/08-mail-ticket.png, preview/08-mail-nachricht.png - E2E live verifiziert (Zustellung an axel@hocks.eu, status=sent), Doku
16 KiB
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:
- 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). - Mietkonto & Zahlungen (
/mietkonto) – Buchungsverlauf 2026 mit Statusfilter, Nebenkostenguthaben, Zahlungsdaten. Verwaltung: Zahlungsstatus aller Wohnungen, Rückstandsliste, Haus-Filter. - Schadensmeldungen (
/schadensmeldungen) – Tickets mit Kategorie, Dringlichkeit, Status; Neuanlage per Dialog; Verwaltung pflegt Status per Select. - Dokumente (
/dokumente) – Verträge/Abrechnungen/Hausordnung/Protokolle, Suche + Kategorie-Chips. Mieter sieht eigene + Hausdokumente (unitId=null). - Nachrichten (
/nachrichten) – Chat Mieter ↔ Verwaltung, Ungelesen-Markierung, neue Unterhaltungen. - Benutzerverwaltung (
/benutzer, nur Gruppeverwaltung) – 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-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.envauf dem Server (gitignored, liegt nur dort): liefertMAIL_API_TOKEN(für nginx-envsubst und mp-api) undPOSTGRES_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.envplus zufälligesPOSTGRES_PASSWORD=…. Wichtig: compose interpoliert${VARIABLE}nur aus einer.env-Datei im Projektverzeichnis (nicht ausenv_file:!) – daher auf dem Server/opt/mieterportal/.envals 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 undGET /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-portalläuft auf hocks.eu, statischesdist/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-cachegeliefert (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(Vorlagemail.env.exampleim Repo). - Mail-Stack live (seit 02.10.2026): Container
mp-mail+mp-mail-apilaufen auf hocks.eu unter/opt/mp-mail(Relay überhocks-eu.mail.protection.outlook.com). Testmail und API-/send(mitX-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/sendanhttp://mp-mail-api:8080/sendmit Token ausportal.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 inindex.cssfür.darkumrechnen (.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.pyanpassen 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 --noEmitprü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=10000wartet 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.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.
- Sicherheit für Produktion: Passwort-Hash serverseitig salten (bcrypt/argon2), CSRF-Token, Rate-Limiting auf /api/auth/login.
- Mögliche nächste Schritte: Datenbank-Backup-Job (nächtlicher pg_dump), 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).