Remove the leftover file content layer and align the docs
This commit is contained in:
1 parent
3fd3796d73
commit
47d9841532
21 files changed
+154
-327
No files matched your search
@@ -2,7 +2,8 @@
|
||||
|
||||
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.
|
||||
Proxy Manager. Die Inhalte liegen in **PocketBase** (zweiter Container); die App
|
||||
liest sie pro Request über einen typisierten `PocketBaseClient`.
|
||||
|
||||
Architekturentscheidungen und deren Begründung: `docs/architektur.md`. Dort
|
||||
nachlesen, bevor eine davon in Frage gestellt wird.
|
||||
@@ -11,7 +12,7 @@ nachlesen, bevor eine davon in Frage gestellt wird.
|
||||
|
||||
| Thema | Datei |
|
||||
|---|---|
|
||||
| Warum SSR statt WASM, warum keine DB (AE-1 bis AE-4) | `docs/architektur.md` |
|
||||
| Warum SSR statt WASM, warum PocketBase als Datenschicht (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` |
|
||||
@@ -26,28 +27,36 @@ 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
|
||||
# Lokaler Stack (App + PocketBase) über das Dev-Overlay, siehe docs/entwicklung.md:
|
||||
docker compose -f compose.yaml -f compose.dev.yaml up --build # App :5000, PocketBase-Admin :8090/_/
|
||||
dotnet test # Smoke-Tests (starten eigene PocketBase) + Unit-Tests
|
||||
```
|
||||
|
||||
Es gibt keine Datenbank, keine Migrationen, kein Seeding. Ein `git clone` ist der
|
||||
vollständige Stand.
|
||||
Die App liest ihren Inhalt pro Request aus PocketBase; sie hält selbst keinen
|
||||
Inhalt und liest nichts beim Start ein. Kein EF Core, keine Migrationen, kein
|
||||
Seeding im Code. Ein `git clone` ist **nicht** der vollständige Stand — der
|
||||
Inhalt liegt in PocketBase (`pb_data`), das separat gesichert wird (siehe
|
||||
`docs/deployment.md`).
|
||||
|
||||
## 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".
|
||||
- **Inhalte liegen in PocketBase** (`pb_data`-Volume), nicht im Image. Eine
|
||||
Textänderung ist ein Eintrag im PocketBase-Admin und sofort live — kein Commit,
|
||||
kein Rebuild (`docs/architektur.md`, AE-2/AE-3). Nur die statischen Assets unter
|
||||
`wwwroot/` (CSS, Fonts, PDFs) sind ins Image gebacken.
|
||||
- **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.
|
||||
- Neue Seite / neuer Beitrag / neuer Termin / neue Frage = **Record in der
|
||||
passenden PocketBase-Collection** (`pages` / `posts` / `events` / `faqs`), nicht
|
||||
eine Datei und **kein** `.razor`. `pages` mit `slug` ist sofort unter `/<slug>`
|
||||
erreichbar; `location`+`order` steuern das Menü, `embed` bettet Listen ein
|
||||
(`docs/redaktion.md`). Eine **neue Art von Liste** (eigene Route/Logik) ist
|
||||
dagegen Code: neue Collection + `PocketBaseClient`-Methode + Komponente
|
||||
(`docs/entwicklung.md`).
|
||||
- Slugs ohne Umlaute (`ueber-uns`, nicht `über-uns`).
|
||||
|
||||
## Regeln
|
||||
@@ -57,22 +66,28 @@ vollständige Stand.
|
||||
- **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.
|
||||
- **Kein EF Core, kein ORM, keine Migrationen.** Inhalte kommen aus PocketBase
|
||||
über den `PocketBaseClient` (REST), nicht aus einer relationalen DB im Code.
|
||||
- **Inhalte gehören nach PocketBase**, nicht als Markup in Komponenten und nicht
|
||||
in Dateien im Repo.
|
||||
- Kein eigenes CMS, kein Login und keine Benutzerkonten **in der Website** —
|
||||
gepflegt wird ausschließlich im PocketBase-Admin (Redakteure sind Superuser).
|
||||
- UI-Texte auf Deutsch, ohne Anglizismen. `Termine`, nicht `Events`.
|
||||
- Neue NuGet-Abhängigkeiten vorher begründen. Bestand: `Markdig`, `YamlDotNet`.
|
||||
- Neue NuGet-Abhängigkeiten vorher begründen. Genutzt wird nur `Markdig`
|
||||
(Markdown → HTML). `YamlDotNet` stammt aus der abgelösten Datei-Schicht, wird
|
||||
nicht mehr verwendet und soll aus dem `csproj` entfernt werden.
|
||||
|
||||
## 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.
|
||||
- Datenzugriff über den typisierten `PocketBaseClient` (registriert via
|
||||
`AddHttpClient`), der pro Request liest — kein Start-Cache, kein Singleton mit
|
||||
Inhalten. Komponenten liegen feature-basiert unter `Features/<Bereich>/`.
|
||||
- Öffentliche Typen und Methoden im `PocketBaseClient` und in `Contracts`
|
||||
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`).
|
||||
Besucher liest oder ein Redakteur pflegt: UI-Texte sowie die **Werte** der
|
||||
PocketBase-Records (z. B. `title: Vorstandsteam`, der Markdown-`body`). Die
|
||||
Feldnamen und Slugs bleiben dagegen englisch (`title`, `slug`, `/board`).
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Markdig" Version="0.38.0" />
|
||||
<PackageVersion Include="YamlDotNet" Version="16.2.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Test-only packages. -->
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
# Calendar entries of the Elternbeirat.
|
||||
#
|
||||
# One entry per event. Required keys: title, start.
|
||||
# start/end:
|
||||
# date only -> "2026-03-15" (all-day)
|
||||
# date with time -> "2026-03-15 19:30"
|
||||
# location and note are optional.
|
||||
|
||||
- title: Elternbeiratssitzung
|
||||
start: "2026-10-08 19:30"
|
||||
location: Lehrerzimmer
|
||||
note: Themen bitte vorab per E-Mail einreichen.
|
||||
|
||||
- title: Herbstbasar
|
||||
start: "2026-11-14 10:00"
|
||||
end: "2026-11-14 16:00"
|
||||
location: Aula
|
||||
|
||||
- title: Weihnachtsferien
|
||||
start: "2026-12-21"
|
||||
end: "2027-01-06"
|
||||
|
||||
- title: Elternsprechtag
|
||||
start: "2026-09-05 15:00"
|
||||
end: "2026-09-05 18:00"
|
||||
location: Klassenräume
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
title: Datenschutz
|
||||
---
|
||||
|
||||
# Datenschutzerklärung
|
||||
|
||||
*TODO: Rechtlich verbindliche Datenschutzerklärung ergänzen. Erst nach Freigabe
|
||||
finalisieren – siehe `docs/recht.md`.*
|
||||
|
||||
Diese Website wird bewusst ohne externe Ressourcen betrieben: keine CDN-Skripte,
|
||||
keine Google Fonts, keine Karten- oder Video-Einbettungen. Schriften werden von
|
||||
unserem eigenen Server ausgeliefert. Dadurch werden beim Besuch keine Daten an
|
||||
Dritte übertragen.
|
||||
|
||||
## Verantwortliche Stelle
|
||||
|
||||
*TODO: siehe [Impressum](/impressum).*
|
||||
|
||||
## Server-Logdaten
|
||||
|
||||
*TODO: beschreiben, welche Zugriffsdaten der Server (bzw. der vorgelagerte
|
||||
Proxy) protokolliert und wie lange.*
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: Downloads
|
||||
---
|
||||
|
||||
# Downloads
|
||||
|
||||
Hier stellen wir Unterlagen zur Elternarbeit als Datei bereit: Protokolle der
|
||||
Sitzungen, Elterninformationen und die Geschäftsordnung.
|
||||
|
||||
## Unterlagen
|
||||
|
||||
*TODO: Download-Liste ergänzen. Dateien liegen unter `wwwroot/downloads/` und
|
||||
werden hier verlinkt, z. B.:*
|
||||
|
||||
- *Protokoll der Vollversammlung (PDF)*
|
||||
- *Geschäftsordnung des Elternbeirats (PDF)*
|
||||
- *Elterninformation Elternvertreter (PDF)*
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
title: FAQ Elternarbeit
|
||||
---
|
||||
|
||||
# Elternarbeit
|
||||
|
||||
Unterstützung für neu gewählte und erfahrene Elternvertreterinnen und
|
||||
Elternvertreter.
|
||||
|
||||
## Welche Aufgaben habe ich als Elternvertreter?
|
||||
|
||||
*TODO: Aufgaben und Rechte der Klassenelternvertretung beschreiben.*
|
||||
|
||||
## Wie läuft die Zusammenarbeit mit dem Elternbeirat?
|
||||
|
||||
*TODO: Sitzungsrhythmus und Ansprechpartner ergänzen.*
|
||||
|
||||
## Wo finde ich Vorlagen und Unterlagen?
|
||||
|
||||
Unterlagen und Protokolle finden Sie im Bereich [Downloads](/downloads).
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
title: FAQ Elterneuro
|
||||
---
|
||||
|
||||
# Elterneuro
|
||||
|
||||
Der Elterneuro ist ein freiwilliger Beitrag der Eltern, mit dem der Elternbeirat
|
||||
Projekte an der Schule unterstützt.
|
||||
|
||||
## Wofür wird der Elterneuro verwendet?
|
||||
|
||||
*TODO: konkrete Beispiele für die Verwendung ergänzen.*
|
||||
|
||||
## Wie hoch ist der Beitrag?
|
||||
|
||||
Der Elterneuro ist freiwillig.
|
||||
|
||||
*TODO: übliche Beitragshöhe und Zahlungsweg ergänzen.*
|
||||
|
||||
## An wen kann ich mich bei Fragen wenden?
|
||||
|
||||
Bei Fragen erreichen Sie uns über die [Kontaktseite](/kontakt).
|
||||
@@ -1,21 +0,0 @@
|
||||
---
|
||||
title: FAQ Mensa
|
||||
---
|
||||
|
||||
# Mensa
|
||||
|
||||
Alles rund um das Mittagessen an der IGMH: Anmeldung, Guthaben und Fristen.
|
||||
|
||||
## Wie melde ich mein Kind an?
|
||||
|
||||
Die Essensbestellung läuft über das System i-NET Menue.
|
||||
|
||||
*TODO: Ablauf der Erstanmeldung und Zugangsdaten beschreiben.*
|
||||
|
||||
## Wie lade ich Guthaben auf?
|
||||
|
||||
*TODO: Aufladeweg und Zahlungsarten ergänzen.*
|
||||
|
||||
## Bis wann kann ich bestellen oder stornieren?
|
||||
|
||||
*TODO: Fristen für Bestellung und Stornierung ergänzen.*
|
||||
@@ -1,21 +0,0 @@
|
||||
---
|
||||
title: FAQ Schließfach
|
||||
---
|
||||
|
||||
# Schließfächer
|
||||
|
||||
Informationen zur Anmietung eines Schließfachs an der IGMH.
|
||||
|
||||
## Welche Größen gibt es und was kosten sie?
|
||||
|
||||
*TODO: Verfügbare Größen und Preise ergänzen.*
|
||||
|
||||
## Wie miete ich ein Schließfach an?
|
||||
|
||||
Die Verwaltung läuft über das Serviceportal von AstraDirect.
|
||||
|
||||
*TODO: Ablauf der Anmietung und Link zum Portal ergänzen.*
|
||||
|
||||
## Wie tausche oder kündige ich mein Fach?
|
||||
|
||||
*TODO: Vorgehen für Fachtausch und Kündigung ergänzen.*
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
title: FAQ
|
||||
---
|
||||
|
||||
# Häufige Fragen
|
||||
|
||||
Hier finden Eltern Antworten auf wiederkehrende Fragen rund um den Schulalltag.
|
||||
Die Themen sind nach Bereichen aufgeteilt:
|
||||
|
||||
- [Mensa](/faq-mensa) – Anmeldung, Guthaben, Fristen
|
||||
- [Schließfächer](/faq-schliessfach) – Größen, Preise, Verwaltung
|
||||
- [Elterneuro](/faq-elterneuro) – freiwilliger Beitrag und Verwendung
|
||||
- [Elternarbeit](/faq-elternarbeit) – Tipps für Elternvertreterinnen und Elternvertreter
|
||||
|
||||
*TODO: Reihenfolge und weitere Themen ergänzen, sobald die Unterseiten stehen.*
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
title: Förderverein
|
||||
---
|
||||
|
||||
# Förderverein „Freunde der IGMH"
|
||||
|
||||
Der Förderverein „Freunde der IGMH" unterstützt die Schule bei Anschaffungen und
|
||||
Projekten, die aus dem regulären Budget nicht finanziert werden können. Mitglieder
|
||||
sind Eltern, Lehrkräfte, Ehemalige und Förderer der Schule.
|
||||
|
||||
## Was der Verein fördert
|
||||
|
||||
- Ausstattung für Unterricht und Arbeitsgemeinschaften
|
||||
- Musische, sportliche und kulturelle Projekte
|
||||
- Anschaffungen, die allen Schülerinnen und Schülern zugutekommen
|
||||
|
||||
## Mitglied werden
|
||||
|
||||
*TODO: Beitrittsformular bzw. Ansprechpartner und Beitragshöhe ergänzen.*
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
title: Impressum
|
||||
---
|
||||
|
||||
# Impressum
|
||||
|
||||
*TODO: Rechtlich verbindliches Impressum ergänzen. Erst nach Freigabe mit echten
|
||||
Daten füllen – siehe `docs/recht.md`.*
|
||||
|
||||
## Angaben gemäß § 5 DDG
|
||||
|
||||
*TODO: Name und Anschrift des Diensteanbieters (Elternbeirat / Schule).*
|
||||
|
||||
## Vertreten durch
|
||||
|
||||
*TODO: gesetzlicher Vertreter.*
|
||||
|
||||
## Kontakt
|
||||
|
||||
*TODO: E-Mail-Adresse (siehe [Kontakt](/kontakt)).*
|
||||
|
||||
## Verantwortlich i. S. d. § 18 Abs. 2 MStV
|
||||
|
||||
*TODO: Name und Anschrift der verantwortlichen Person.*
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
title: Kontakt
|
||||
---
|
||||
|
||||
# Kontakt
|
||||
|
||||
Sie erreichen den Elternbeirat der IGMH per E-Mail:
|
||||
|
||||
[TODO-adresse@example.org](mailto:TODO-adresse@example.org)
|
||||
|
||||
*TODO: Echte Kontakt-E-Mail-Adresse eintragen.*
|
||||
|
||||
## Anschrift
|
||||
|
||||
Elternbeirat der IGMH
|
||||
*TODO: Anschrift der Schule ergänzen.*
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
title: Vorstandsteam
|
||||
---
|
||||
|
||||
# Das Vorstandsteam
|
||||
|
||||
Der Elternbeirat der IGMH wird von einem ehrenamtlichen Vorstand geleitet.
|
||||
Er vertritt die Elternschaft gegenüber Schule und Schulträger und koordiniert
|
||||
die Arbeit der Klassenelternvertreter.
|
||||
|
||||
## Aufgaben des Vorstands
|
||||
|
||||
- Vertretung der Eltern in der Schulkonferenz
|
||||
- Zusammenarbeit mit Schulleitung und Kollegium
|
||||
- Organisation der Vollversammlungen und Sitzungen
|
||||
- Ansprechpartner für Fragen rund um Mensa, Schließfächer und Förderverein
|
||||
|
||||
*Die namentliche Vorstellung der Vorstandsmitglieder folgt.*
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
title: Neue Sporthalle feierlich eröffnet
|
||||
date: 2025-10-05
|
||||
---
|
||||
|
||||
# Neue Sporthalle feierlich eröffnet
|
||||
|
||||
Die neue Sporthalle der IGMH ist eröffnet. Schülerinnen, Schüler und Lehrkräfte
|
||||
haben damit deutlich mehr Platz für Sportunterricht und Arbeitsgemeinschaften.
|
||||
|
||||
*TODO: Bericht und Fotos ergänzen.*
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
title: Neuer Vorstand des Elternbeirats gewählt
|
||||
date: 2025-02-26
|
||||
---
|
||||
|
||||
# Neuer Vorstand des Elternbeirats gewählt
|
||||
|
||||
Bei der Vollversammlung hat der Elternbeirat der IGMH einen neuen Vorstand
|
||||
gewählt. Das Team bedankt sich für das entgegengebrachte Vertrauen und freut
|
||||
sich auf die gemeinsame Arbeit im neuen Schuljahr.
|
||||
|
||||
*TODO: Namen und Ämter des neuen Vorstands ergänzen (nach Freigabe).*
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Markdig" />
|
||||
<PackageReference Include="YamlDotNet" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- The typed PocketBaseClient the app reads its content from. Referencing
|
||||
@@ -28,10 +27,4 @@
|
||||
<ProjectReference Include="..\Elternbeirat.PocketBase\Elternbeirat.PocketBase.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Content ships as files inside the image, not in a volume. Copy it to
|
||||
the output directory so the container can find it. -->
|
||||
<ItemGroup>
|
||||
<Content Include="Content\**\*" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+42
-22
@@ -2,12 +2,13 @@
|
||||
|
||||
Why the site is built the way it is. Read this before questioning one of these
|
||||
decisions — each records the reasoning, not just the choice. Operational how-to
|
||||
lives in `docs/deployment.md`; open work lives in `tasks/`.
|
||||
lives in `docs/deployment.md`; open work lives in the Gitea issues (milestone
|
||||
„Elternbeirat-Website").
|
||||
|
||||
> **Note on scope.** These decisions describe stage 1: a static information site
|
||||
> with content as files. The move to editor-based content maintenance
|
||||
> (`tasks/001-redaktion-ohne-entwickler.md`) deliberately revisits AE-2 and AE-3 —
|
||||
> when that lands, update this file.
|
||||
> **Note on scope.** The site started as a static information site with content
|
||||
> as files (stage 1). Editorial maintenance without a developer then replaced
|
||||
> that file layer with PocketBase, which revised **AE-2** and **AE-3** below —
|
||||
> each now records both the original decision and why it changed.
|
||||
|
||||
## AE-1: Blazor with static server-side rendering, not WebAssembly
|
||||
|
||||
@@ -24,30 +25,49 @@ costs around 60 MB of RAM in the container.
|
||||
(`@rendermode InteractiveServer` on exactly that one component), without changing the
|
||||
architecture. Never global.
|
||||
|
||||
## AE-2: Content as files in the repo, not in a database
|
||||
## AE-2: Content in PocketBase, read per request (revised)
|
||||
|
||||
**Decision:** Page content as Markdown with YAML frontmatter, events as structured
|
||||
YAML (`Content/events.yml`), documents as PDF under `wwwroot`.
|
||||
**Decision:** All visible content lives in **PocketBase** (collections `pages`,
|
||||
`posts`, `events`, `faqs`); the app reads it over the REST API per request via a
|
||||
typed `PocketBaseClient`. Documents remain PDFs under `wwwroot`.
|
||||
|
||||
**Rationale:** No DB backup, no migration schema, no consistency problems between
|
||||
files and database. Changes are commits and are therefore traceable and reversible.
|
||||
For a site with ~10 subpages and a few events per year, anything else is overhead.
|
||||
**Original decision (stage 1):** Content as files in the repo — Markdown with YAML
|
||||
frontmatter, events as `Content/events.yml`. The rationale then was: no DB backup,
|
||||
no migration schema, no file/DB drift, and "backup = `git clone`". For a handful of
|
||||
pages that was the leanest option.
|
||||
|
||||
**Consequence:** Text changes require a commit and a redeploy. With an expected five
|
||||
changes a year that is acceptable — and the reason the editor expansion
|
||||
(`tasks/001`) is treated as a separate stage.
|
||||
**Why it changed:** The site needs to be maintainable by the board **without a
|
||||
developer and without a redeploy**. Files meant every text fix was a commit and a
|
||||
rebuild. PocketBase keeps the "small, self-hostable, one binary" spirit while
|
||||
letting an editor change content live in its admin UI. It has **no** migration
|
||||
schema of the EF/ORM kind — the collections are its data — so the objection that
|
||||
weighed against "a database" in stage 1 does not apply here.
|
||||
|
||||
## AE-3: Content is baked into the image, not mounted as a volume
|
||||
**Consequence:** Editing is now a task in the PocketBase admin, not a commit (see
|
||||
`redaktion.md`). The app holds no content of its own; if PocketBase is unreachable a
|
||||
page degrades to empty rather than failing. "Backup = `git clone`" no longer covers
|
||||
the content — the backup is now `pb_data` (see AE-3 and `deployment.md`).
|
||||
|
||||
**Decision:** `Content/` and `wwwroot/` are part of the image.
|
||||
## AE-3: Content lives in PocketBase's `pb_data`, not baked into the image (revised)
|
||||
|
||||
**Rationale:** Mounted content from the appdata share could be edited directly on the
|
||||
NAS, but that is exactly when the repo and the live state drift apart — and the
|
||||
"backup = `git clone`" property from AE-2 would be worthless.
|
||||
**Decision:** The editable content lives in PocketBase's `pb_data`, a persistent
|
||||
volume separate from the app image. Only the static assets under `wwwroot/` (CSS,
|
||||
fonts, PDFs) are baked into the image.
|
||||
|
||||
**Consequence:** No quick fix on the live system. Typos are corrected properly via a
|
||||
commit. Note: the content is read once at startup and cached — there is **no**
|
||||
`FileSystemWatcher` (a change needs a restart / `dotnet watch`).
|
||||
**Original decision (stage 1):** `Content/` and `wwwroot/` were both baked into the
|
||||
image, deliberately **not** a mounted volume, so the live state could never drift
|
||||
from the repo and "backup = `git clone`" held.
|
||||
|
||||
**Why it changed:** Editing content live (AE-2) is only possible if that content
|
||||
survives a redeploy — so it must be a volume, not part of the image. The drift
|
||||
concern is answered differently now: the content simply has one home (PocketBase),
|
||||
and the repo no longer claims to hold it. The app image stays stateless and can be
|
||||
rebuilt and redeployed at any time without touching the data.
|
||||
|
||||
**Consequence:** The backup is a copy of `pb_data` (plus a restore test), not a
|
||||
`git clone` (set up in issue #9, see `deployment.md`). The app reads content per
|
||||
request over HTTP, so there is nothing cached at startup and no `FileSystemWatcher`
|
||||
— an editorial change is live immediately, no restart needed.
|
||||
|
||||
## AE-4: TLS and certificates exclusively in the Nginx Proxy Manager
|
||||
|
||||
|
||||
@@ -100,3 +100,32 @@ The app reads its content from PocketBase over the compose network
|
||||
inside the compose network, independent of the container name). Editing content
|
||||
means editing records in the PocketBase admin UI at
|
||||
<http://localhost:8090/_/>, not editing files. See `redaktion.md`.
|
||||
|
||||
---
|
||||
|
||||
## Adding a new content type
|
||||
|
||||
Editors can add **records** to the existing collections and add plain text
|
||||
`pages`, but a **new kind of list** — its own collection rendered on its own
|
||||
route with its own sorting or grouping — is a development task, not editorial.
|
||||
It takes three steps, all small because the plumbing is shared:
|
||||
|
||||
1. **Collection + DTO.** Create the collection in the PocketBase admin (give it a
|
||||
`public` bool and open List/View rules, like the others), then add a matching
|
||||
record type in `Elternbeirat.Contracts` with `[JsonPropertyName(...)]` on each
|
||||
field — mirror `Page`/`Post`.
|
||||
2. **Client method.** Add one method to `PocketBaseClient`. The shared
|
||||
`GetRecordsAsync<T>(collection, sort, ct)` already does the `public=true`
|
||||
filter, the sort and the deserialization, so a new type is a one-liner:
|
||||
`public Task<IReadOnlyList<Minutes>> GetMinutesAsync(CancellationToken ct = default)`
|
||||
`=> GetRecordsAsync<Minutes>("minutes", "-date", ct);`
|
||||
3. **Feature component.** Add a component under `Features/<Name>/` with its own
|
||||
`@page "/…"` route that injects `PocketBaseClient`, calls the new method and
|
||||
renders the result. Literal routes win over the `/{Slug}` catch-all, so pick a
|
||||
slug that no `pages` record needs. Follow an existing list (`PostList`,
|
||||
`EventList`, `FaqList`) for the load-in-`OnInitializedAsync`, log-and-degrade
|
||||
pattern, and add a route smoke test plus a fixture seed.
|
||||
|
||||
Text pages need none of this — they are all served generically by
|
||||
`ContentPage` (`@page "/{Slug}"`), which is why a new `pages` record is live
|
||||
without a rebuild.
|
||||
+4
-3
@@ -1,9 +1,10 @@
|
||||
# Legal — imprint, privacy, photos
|
||||
|
||||
Not legal advice — but the points where school sites regularly get flagged. The
|
||||
actual imprint and privacy texts live in `Content/pages/impressum.md` and
|
||||
`Content/pages/datenschutz.md`; filling them in with released, real data is tracked
|
||||
in `tasks/003-echte-inhalte-vor-go-live.md`.
|
||||
actual imprint and privacy texts are `pages` records in PocketBase (slugs
|
||||
`imprint` and `privacy`), edited in the admin UI — see `redaktion.md`. Filling
|
||||
them in with released, real data before go-live is tracked as a Gitea issue
|
||||
(milestone „Elternbeirat-Website").
|
||||
|
||||
## Imprint (§ 5 DDG)
|
||||
|
||||
|
||||
+40
-6
@@ -5,11 +5,9 @@ Wie die sichtbaren Inhalte der Website gepflegt werden. Der Inhalt liegt in
|
||||
über die Weboberfläche bearbeitet. Keine Dateien, kein Commit, kein Rebuild: eine
|
||||
Änderung im Admin ist sofort live.
|
||||
|
||||
> **Übergang (Stand #8).** Der Inhalt ist bereits vollständig in PocketBase; die
|
||||
> Blazor-App wird gerade darauf umgestellt, ihn von dort zu lesen (Issue #8).
|
||||
> Solange das läuft, kann die ausgelieferte Seite noch aus den alten Dateien
|
||||
> unter `Content/` stammen. Sobald #8 durch ist, ist PocketBase die einzige
|
||||
> Quelle und dieser Abschnitt der einzige Pflegeweg.
|
||||
> **Stand.** Die Blazor-App liest ihren gesamten Inhalt aus PocketBase (Issue #8);
|
||||
> die alte Datei-Schicht unter `Content/` gibt es nicht mehr. PocketBase ist damit
|
||||
> die einzige Quelle und dieser Abschnitt der einzige Pflegeweg.
|
||||
|
||||
> **Sprachkonvention.** Feldnamen und Slugs sind **englisch** (`title`, `slug`,
|
||||
> `board`, `posts`). Der Text, den ein Besucher liest, bleibt **deutsch** — also
|
||||
@@ -49,16 +47,52 @@ Jede Collection hat als **letztes Feld `public`** (ja/nein). Nur Records mit
|
||||
`public = true` erscheinen auf der Website — so lässt sich ein Entwurf anlegen,
|
||||
ohne dass er schon sichtbar ist.
|
||||
|
||||
### Was redaktionell geht — und was Code braucht
|
||||
|
||||
Redaktion arbeitet mit **Daten**, nicht mit Struktur. Konkret:
|
||||
|
||||
| Was du willst | Geht redaktionell? | Wie |
|
||||
|---|---|---|
|
||||
| Beitrag / Termin / Frage hinzufügen | **Ja** | Neuer Record in `posts` / `events` / `faqs` — erscheint sofort in der jeweiligen Liste |
|
||||
| Neue Textseite | **Ja** | Neuer Record in `pages` (siehe unten) |
|
||||
| Eine Seite mit einer Liste anteasern | **Ja** | Feld `embed` der Seite (`posts`/`events`/`faqs`) |
|
||||
| **Neue Art von Liste** (eigene Seite mit eigener Sortierung/Gruppierung und eigener Adresse, z. B. „Protokolle") | **Nein** | Braucht Entwicklung: neue Collection **plus** Code |
|
||||
|
||||
Das heißt: **Bestehende Listen lassen sich beliebig erweitern**, aber eine *neue*
|
||||
Listenart entsteht nicht durch Anlegen einer Collection allein. Die drei Listen
|
||||
(`posts`, `events`, `faqs`) sind fest verdrahtet, weil jede eine eigene
|
||||
Darstellung hat — Datum und Sortierung, Kalender/`.ics`, Themen-Gruppierung. Eine
|
||||
weitere solche Ansicht anzulegen ist ein **Entwickler-Task**: eine neue Collection
|
||||
in PocketBase, eine Lesemethode im `PocketBaseClient` und eine eigene Komponente
|
||||
mit ihrer Route (siehe `docs/entwicklung.md`). Das ist Absicht: Inhalte sind
|
||||
Daten, Struktur und Darstellung sind Code.
|
||||
|
||||
---
|
||||
|
||||
## Seiten (`pages`)
|
||||
|
||||
Eine Seite hat `title` (Überschrift für Menschen, Umlaute erlaubt), `slug` (die
|
||||
URL, klein und **ohne Umlaute**: `board`, nicht `über-uns`) und `body` (der
|
||||
Text, als Editor-Feld).
|
||||
Text, als **Markdown**).
|
||||
|
||||
**Eine neue Seite anlegen** — Record in `pages`, dann ist die Adresse `/<slug>`
|
||||
sofort live, ohne Code und ohne Rebuild:
|
||||
|
||||
1. `title` setzen (Pflicht).
|
||||
2. `slug` setzen (Pflicht), englisch und ohne Umlaute — z. B. `slug = schulweg`
|
||||
ergibt `/schulweg`.
|
||||
3. `body` als Markdown schreiben (optional; darf leer bleiben).
|
||||
4. **`public` anhaken.** Ohne Haken ist die Seite ein Entwurf und erscheint auf
|
||||
der Website als „nicht gefunden" (404) — das ist der häufigste Stolperstein.
|
||||
5. Optional `location` (`header`/`footer`) **und** `order` setzen, damit die Seite
|
||||
ins Menü kommt; optional `embed` für eingebettete Blöcke (siehe unten).
|
||||
|
||||
- **Slug bleibt englisch und ohne Umlaute.** `imprint`, `privacy`, `contact`,
|
||||
`board`, `patrons`. Der `title` darf deutsch mit Umlauten sein (`Förderverein`).
|
||||
- **Reservierte Slugs.** `posts`, `events`, `faqs` und `/` (die Startseite `home`)
|
||||
gehören den festen Listen-/Sonderansichten. Eine `pages`-Seite mit einem dieser
|
||||
Slugs wird von der jeweiligen Ansicht verdeckt und **nicht** angezeigt; in der
|
||||
Produktion dienen solche Records nur als Menü-Platzhalter.
|
||||
- **Menü:** Ob eine Seite ins Menü kommt, steht an der Seite selbst — die Felder
|
||||
`location` (`header` oder `footer`) und `order` (Reihenfolge, ab 1). Die App
|
||||
baut Kopf- und Fußnavigation daraus; nichts wird im Markup angefasst.
|
||||
|
||||
Reference in new issue
Block a user