# CLAUDE.md Website des Elternbeirats der IGMH. Blazor Web App mit statischem Server-Side-Rendering, .NET 10. Läuft als Container auf Unraid hinter dem Nginx Proxy Manager. Architekturentscheidungen und deren Begründung: `docs/architektur.md`. Dort nachlesen, bevor eine davon in Frage gestellt wird. ## Wann welches Dokument | Thema | Datei | |---|---| | Warum SSR statt WASM, warum keine DB (AE-1 bis AE-4) | `docs/architektur.md` | | Stack lokal starten, Tests, compose-Overlays | `docs/entwicklung.md` | | Unraid, compose, NPM-Proxy-Host, Registry, Rollback | `docs/deployment.md` | | Inhalte anlegen und ändern (PocketBase-Admin) | `docs/redaktion.md` | | Impressum, Datenschutz, Fotos | `docs/recht.md` | | Offene Arbeit, Meilensteine, offene Punkte | Gitea-Issues (Milestone „Elternbeirat-Website") | Offene Aufgaben liegen als **Gitea-Issues**, nicht als Markdown im Repo. So löst das Anlegen oder Ändern eines Tickets keinen Build aus. Die vier Arbeitsstränge (Redaktionssystem, Deployment, Inhalte, Infrastruktur) sind Parent-Issues mit Sub-Issues als Checkliste im Body, alle am Milestone „Elternbeirat-Website". ## Kommandos ```bash dotnet run --project Elternbeirat.Web # lokal; Inhalte werden beim Start gelesen (kein Auto-Reload) dotnet test # Smoke-Tests: jede Route liefert 200 docker compose build && docker compose up -d ``` Es gibt keine Datenbank, keine Migrationen, kein Seeding. Ein `git clone` ist der vollständige Stand. ## Nicht offensichtlich - **Inhalte liegen im Image**, nicht in einem Volume. Jede Textänderung braucht Commit und Rebuild. Das ist Absicht (`docs/architektur.md`, AE-3) — nicht „vereinfachen". - **Kein `UseHttpsRedirection()`, kein `UseHsts()`.** NPM terminiert TLS; beides erzeugt hinter dem Proxy eine Redirect-Schleife. `UseForwardedHeaders` mit geleerten `KnownNetworks`/`KnownProxies` ist korrekt so, weil der Container kein Port-Mapping hat und nur über NPM erreichbar ist. - **Das chiseled-Runtime-Image hat keine Shell.** `HEALTHCHECK` mit `curl` oder `sh` schlägt dort fehl. - Neue Seite = Markdown in `Content/pages/`. Neuer Beitrag = Markdown in `Content/posts/`. Neuer Termin = Eintrag in `Content/events.yml`. In allen Fällen wird **kein** `.razor` angefasst. - Slugs ohne Umlaute (`ueber-uns`, nicht `über-uns`). ## Regeln - **Statisches SSR ist der Default.** Kein `@rendermode` ohne konkreten Anlass, und wenn, dann an genau der einen Komponente — nie global. - **Keine externen Ressourcen.** Keine CDN-Skripte, keine Google Fonts, keine Maps- oder Video-Embeds. Schriften werden selbst ausgeliefert. Grund ist Datenschutz, nicht Geschmack (`docs/recht.md`). - **Keine Datenbank, kein EF Core, kein ORM.** Inhalte sind Dateien. - **Inhalte gehören nach `Content/`**, nicht als Markup in Komponenten. - Kein CMS, kein Login, keine Benutzerkonten. - UI-Texte auf Deutsch, ohne Anglizismen. `Termine`, nicht `Events`. - Neue NuGet-Abhängigkeiten vorher begründen. Bestand: `Markdig`, `YamlDotNet`. ## Stil - Nullable aktiviert, `ImplicitUsings` an, File-scoped Namespaces. - Services über DI, als Singleton registriert (Inhalte werden beim Start eingelesen und gecacht). - Öffentliche Typen und Methoden der `Services` bekommen XML-Doc (auf Englisch, leicht verständlich), Razor-Markup nicht. - **Code durchgängig auf Englisch** — Typen, Member, Variablen, Kommentare, Skripte und Doku. Das gilt **auch für Domänenbegriffe**: im Code `Event`, nicht `Termin`; `Post`, nicht `Beitrag`. Deutsch bleibt ausschließlich, was ein Besucher liest oder ein Redakteur pflegt: UI-Texte, Markdown-Inhalte, Frontmatter-Schlüssel wie `titel:` sowie Slugs/Dateinamen unter `Content/` (z. B. `vorstandsteam`, `/termine`).