Container-Setup, Backups und Dokumentation
Mehrstufiges Image: gebaut wird mit allen Abhaengigkeiten, ausgeliefert wird nur die Laufzeit. Migrationen laufen beim Containerstart. Der App-Port wird bewusst nicht nach aussen gemappt - davor gehoert ein Reverse Proxy, der HTTPS terminiert. Der Backup-Service schreibt taeglich einen pg_dump und raeumt Dumps aelter als 14 Tage weg.
This commit is contained in:
@@ -1,3 +1,178 @@
|
||||
# CC-Manager
|
||||
|
||||
For managing Custom Characters on a Server
|
||||
Selbst gehostete Web-App zur Verwaltung von Custom Characters (CCs) auf einem SCP-RP-Server.
|
||||
Sie führt pro Spieler ein Konto über gekaufte Ansprüche und deren Verbrauch, damit die Frage
|
||||
„darf der gerade einen neuen CC?" nicht mehr von Hand abgeglichen werden muss.
|
||||
|
||||
## Das Prinzip
|
||||
|
||||
Jeder Spieler hat drei Guthaben-Töpfe:
|
||||
|
||||
| Topf | Wird gefüllt durch | Wird verbraucht durch |
|
||||
|------|--------------------|-----------------------|
|
||||
| CC-Guthaben | jedes CC-Paket (+1) | ein neuer CC |
|
||||
| Redesign-Guthaben (bezahlt) | jedes CC-Paket (+1, immer inklusive) und jedes Redesign-Paket (+1) | ein Redesign |
|
||||
| Kulanz-Redesign | manuell eingetragene Kulanz (+1) | ein Redesign |
|
||||
|
||||
Zwei Regeln sind fest verdrahtet:
|
||||
|
||||
1. **Ein CC-Paket erzeugt beide Guthaben.** Der inklusive Redesign wird automatisch mitgebucht,
|
||||
in derselben Transaktion und mit gemeinsamer Gruppen-ID — ein Storno nimmt beide zurück.
|
||||
2. **Inaktive CCs kosten unterschiedlich.** Wurde ein CC nur wegen Spieler-Inaktivität vom Server
|
||||
entfernt, kommt er gratis zurück. Wurde er redesignt und existiert im Original nicht mehr,
|
||||
kostet die Reaktivierung ein Redesign- oder CC-Guthaben.
|
||||
|
||||
Guthaben werden als **Kontobuch** geführt: jede Bewegung ist eine eigene Zeile, der Stand eines
|
||||
Topfes ist deren Summe. Das macht jeden Stand nachvollziehbar und Korrekturen sauber — storniert
|
||||
wird per Gegenbuchung, nie durch Löschen. Ein überzogenes Konto ist erlaubt (kommt vor, wenn ein
|
||||
Redesign vor dem zugehörigen Paket erfasst wird), wird aber rot markiert.
|
||||
|
||||
Ein Redesign legt einen **neuen** CC-Datensatz an und verkettet ihn mit dem alten. So bleibt die
|
||||
Kette „A → B → C" sichtbar und die Reaktivierungsregel greift automatisch richtig.
|
||||
|
||||
## Schnellstart
|
||||
|
||||
Vorausgesetzt wird Docker mit Compose-Plugin (oder DockGe).
|
||||
|
||||
```bash
|
||||
git clone https://git.harlelux.de/Harlekin/CC-Manager.git
|
||||
cd CC-Manager
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
In der `.env` mindestens `DB_PASSWORD` und `SESSION_SECRET` setzen:
|
||||
|
||||
```bash
|
||||
openssl rand -base64 48 # für SESSION_SECRET
|
||||
```
|
||||
|
||||
Dann starten:
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
docker compose logs -f app
|
||||
```
|
||||
|
||||
Beim ersten Start legt die App einen Administrator an. Ist `BOOTSTRAP_ADMIN_PASSWORD` leer,
|
||||
erzeugt sie ein Einmal-Passwort und schreibt es **einmalig** ins Log — dort steht es in der
|
||||
Ausgabe oben. Beim ersten Login ist ein Passwortwechsel Pflicht; bis dahin sind alle anderen
|
||||
Seiten gesperrt.
|
||||
|
||||
Für DockGe: Repo-Inhalt als Stack anlegen, `.env` daneben, fertig. Datenbank-Migrationen laufen
|
||||
bei jedem Containerstart automatisch mit.
|
||||
|
||||
## HTTPS
|
||||
|
||||
Der App-Port wird bewusst **nicht** nach außen gemappt. Davor gehört ein Reverse Proxy
|
||||
(Nginx Proxy Manager, Traefik oder Caddy), der HTTPS terminiert und intern auf `app:3000`
|
||||
weiterleitet. So ist nur Port 443 offen und die Zertifikate laufen über Let's Encrypt.
|
||||
|
||||
Läuft die App hinter einem Proxy, muss `TRUST_PROXY=true` gesetzt bleiben — sonst landen im
|
||||
Audit-Log die Proxy-IPs statt der echten. In Produktion werden Cookies automatisch auf `Secure`
|
||||
gesetzt; nur zum Testen ohne TLS lässt sich das über `COOKIE_SECURE=false` abschalten.
|
||||
|
||||
## Backups
|
||||
|
||||
Der `backup`-Service im Stack schreibt beim Start und danach alle 24 Stunden einen
|
||||
`pg_dump` nach `./backups/` und räumt Dumps älter als 14 Tage weg. Die Daten bilden bezahlte
|
||||
Ansprüche ab — das Verzeichnis gehört auf eine Sicherung außerhalb des Hosts.
|
||||
|
||||
Wiederherstellen:
|
||||
|
||||
```bash
|
||||
gunzip -c backups/ccmanager-2025-01-15-0300.sql.gz | \
|
||||
docker compose exec -T db psql -U ccuser -d ccmanager
|
||||
```
|
||||
|
||||
## Import
|
||||
|
||||
Kaufdaten lassen sich als CSV oder JSON hochladen; das Format ist nicht festgelegt. Der Ablauf:
|
||||
|
||||
1. Datei hochladen — Spalten und Beispielzeilen werden angezeigt.
|
||||
2. Spalten zuordnen (Spieler-Kennung, Produktname, optional Bestell-ID und Datum). Die üblichen
|
||||
Spaltennamen werden vorausgewählt.
|
||||
3. **Vorschau** prüfen: für jede Zeile steht da, was passieren würde.
|
||||
4. Buchen.
|
||||
|
||||
Welcher Produktname welches Guthaben ergibt, steht in der Mapping-Tabelle unter *Verwaltung →
|
||||
Produkt-Mappings* (Teiltreffer, Groß-/Kleinschreibung egal). Neue Produkte lassen sich dort
|
||||
jederzeit ergänzen, ohne den Code anzufassen.
|
||||
|
||||
Wird eine Bestell-ID zugeordnet, bucht ein zweiter Import dieselben Käufe **nicht** erneut —
|
||||
Dubletten werden sowohl gegen bereits gebuchte Bestellungen als auch innerhalb der Datei erkannt.
|
||||
Ohne Bestell-ID entfällt dieser Schutz; darauf weist das Formular hin.
|
||||
|
||||
Kulanz-Redesigns tauchen in keinem Shop-Export auf und werden immer von Hand eingetragen.
|
||||
|
||||
## Rollen
|
||||
|
||||
Es gibt **keine öffentliche Registrierung**. Konten legt ausschließlich ein Administrator an.
|
||||
|
||||
- **Viewer** — darf nur lesen.
|
||||
- **Editor** — Spieler anlegen, Guthaben eintragen, CCs erstellen, redesignen, reaktivieren.
|
||||
- **Admin** — zusätzlich Import, Nutzerverwaltung, Produkt-Mappings, Audit-Log und Korrekturbuchungen.
|
||||
|
||||
Jede ändernde Aktion landet im Audit-Log. Für Administratoren lässt sich unter *Mein Konto*
|
||||
eine Zwei-Faktor-Anmeldung per TOTP einrichten.
|
||||
|
||||
## Entwicklung
|
||||
|
||||
Gebraucht werden Node 20+ und eine erreichbare PostgreSQL-Instanz.
|
||||
|
||||
```bash
|
||||
npm install
|
||||
cp server/.env.example server/.env # DATABASE_URL auf die lokale DB zeigen lassen
|
||||
npm run migrate:dev
|
||||
npm run dev # API auf :3000, Frontend auf :5173
|
||||
```
|
||||
|
||||
Das Frontend leitet `/api` im Entwicklungsbetrieb an den API-Port weiter, Anmeldung und Cookies
|
||||
funktionieren also wie in Produktion.
|
||||
|
||||
Die Tests brauchen **keine** laufende Datenbank — sie starten eine Postgres-Instanz im Speicher:
|
||||
|
||||
```bash
|
||||
npm test
|
||||
```
|
||||
|
||||
Abgedeckt sind die Guthaben-Regeln, der CC-Lebenszyklus samt Reaktivierungslogik, Korrekturen
|
||||
und Stornos, Anmeldung mit CSRF- und Rollenprüfung sowie der Import mit Dedup.
|
||||
|
||||
### Aufbau
|
||||
|
||||
```
|
||||
server/
|
||||
prisma/schema.prisma Datenmodell und Migrationen
|
||||
src/services/ Fachlogik: Ledger, CC-Lebenszyklus, Import
|
||||
src/routes/ HTTP-Endpunkte
|
||||
src/auth/ Passwörter, Sitzungen, CSRF, Rollen, TOTP
|
||||
test/ Integrationstests gegen eine echte Postgres-Instanz
|
||||
web/
|
||||
src/pages/ Login, Übersicht, Spieler-Detail, Import, Verwaltung
|
||||
src/components/ Wiederverwendete Bausteine
|
||||
```
|
||||
|
||||
Backend ist Fastify mit Prisma, Frontend eine React-SPA. Gebaut wird beides in ein einziges
|
||||
Image: Fastify liefert die API und die fertigen Frontend-Dateien aus.
|
||||
|
||||
Nach Änderungen am Datenmodell:
|
||||
|
||||
```bash
|
||||
npm run migrate:dev -- --name beschreibung-der-aenderung
|
||||
```
|
||||
|
||||
## Sicherheit
|
||||
|
||||
- Passwörter mit Argon2id gehasht, Mindestlänge 12 Zeichen.
|
||||
- Sitzungen serverseitig; im Cookie steht nur ein Token, in der Datenbank dessen Hash.
|
||||
Cookie ist `httpOnly`, `SameSite=Lax` und in Produktion `Secure`.
|
||||
- CSRF-Schutz für alle ändernden Anfragen über ein aus der Sitzung abgeleitetes Token.
|
||||
- Alle Eingaben werden validiert, alle Datenbankzugriffe laufen parametrisiert über das ORM.
|
||||
- Rate-Limit auf dem Login, generische Fehlermeldung ohne Hinweis auf existierende Konten.
|
||||
- Sicherheits-Header inklusive Content-Security-Policy.
|
||||
- Ein gesperrtes Konto verliert seine offenen Sitzungen sofort; der letzte aktive Administrator
|
||||
kann sich nicht selbst entfernen.
|
||||
|
||||
## Lizenz
|
||||
|
||||
GPL-3.0-or-later, siehe [LICENSE](LICENSE).
|
||||
|
||||
Reference in New Issue
Block a user