Code-Review: tote Stellen entfernt, Umwege vereinfacht
- SQLite-Import, migrate.sh, notes/migrations.md und altes jQuery-Skript entfernt - Backend: scanEntry/queryEntries statt doppelter Scan-Liste, Feed-Queries direkt statt feedWhere, Thread ohne Map-Umweg, gemeinsame Helfer für Cookie-Löschen und Mediennamen, usernameTaken - Frontend: PostForm für Beitrag und Antwort, EntryCard in Deleted/LiveCard aufgeteilt, tote CSS-Regeln und Typen entfernt, Strich-Regeln zusammengelegt - Rechtstexte verlinken /stats; Kommentare ohne Vorgeschichte Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TiXsPUqw7oeomZ8wZrQW5q
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
d1bd0f5f1d
commit
df0cfec132
@@ -95,23 +95,6 @@ REVOKE CONNECT, TEMPORARY ON DATABASE kontrollverlust FROM PUBLIC;
|
||||
Tabellen und Indizes legt die App beim Start selbst an (`CREATE ... IF NOT
|
||||
EXISTS`, als `kver` → gehören `kver`). Nicht als Admin anlegen.
|
||||
|
||||
## Umzug SQLite → Postgres (einmalig)
|
||||
|
||||
Dienst stoppen, dann mit dem neuen Image die alte Datei aus dem Volume
|
||||
importieren. Der Import läuft in einer Transaktion, prüft die Zeilenzahlen und
|
||||
bricht ab, wenn in Postgres schon Daten liegen:
|
||||
|
||||
```sh
|
||||
systemctl --user stop kver
|
||||
podman run --rm --env-file /etc/kver/kver.env \
|
||||
-v kver-data:/app/data:ro \
|
||||
kver import-sqlite /app/data/kver.db
|
||||
systemctl --user start kver
|
||||
```
|
||||
|
||||
Der Container braucht dabei (und im Betrieb) dieselbe Netzwerkanbindung an
|
||||
Postgres wie der Dienst selbst.
|
||||
|
||||
## Bestehende Medien übernehmen
|
||||
|
||||
Medien einmalig ins Volume kopieren (Volumes müssen existieren;
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
# Datenbank-Migrationen
|
||||
|
||||
Manuelle Schritte, die beim Deploy gegen eine **bestehende** Datenbank nötig sind.
|
||||
Eine frische DB (Schema aus `db.go`) braucht keine davon.
|
||||
|
||||
---
|
||||
|
||||
## `avatar`-Spalte in `user`
|
||||
|
||||
**Wann:** beim Deploy der Profilbild-Version gegen eine bestehende `kver.db`.
|
||||
|
||||
**Hintergrund:** Profilbilder werden als Pfad (`static/media/...`) in der neuen Spalte
|
||||
`user.avatar` gespeichert (leerer String = kein Bild, Frontend zeigt dann das
|
||||
Platzhalterbild). `CREATE TABLE IF NOT EXISTS` ergänzt die Spalte bei einer
|
||||
vorhandenen Tabelle nicht.
|
||||
|
||||
**Migration:**
|
||||
```sql
|
||||
ALTER TABLE user ADD COLUMN avatar TEXT NOT NULL DEFAULT '';
|
||||
```
|
||||
|
||||
Bestehende Nutzer haben damit kein Bild (`''`). Nur einmalig ausführen.
|
||||
|
||||
---
|
||||
|
||||
## `filepath` NULL → leerer String
|
||||
|
||||
**Wann:** beim Umstieg auf die Go-Version, falls `entry.filepath` NULL-Werte enthält.
|
||||
|
||||
**Hintergrund:** `scanEntries` scannt `filepath` in ein Go-`string`-Feld (kein Pointer).
|
||||
SQLite NULL in ein `string` scannen schlägt fehl; `scanEntries` überspringt diese Zeilen
|
||||
still mit `continue`. Ergebnis: Einträge ohne Bild fehlen im Feed komplett.
|
||||
Das aktuelle Go-Schema definiert `filepath TEXT NOT NULL DEFAULT ''`. `migrate.sh`
|
||||
normalisiert NULL → `''` beim Kopieren aus der Quelldatenbank.
|
||||
|
||||
```sql
|
||||
UPDATE entry SET filepath = '' WHERE filepath IS NULL;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `filepath`-Sentinel `"none"` → leerer String
|
||||
|
||||
**Wann:** beim Umstieg auf die Go-Version, falls eine alte `kver.db` aus der
|
||||
Python-Zeit weiterverwendet wird.
|
||||
|
||||
**Hintergrund:** Die Python-Version schrieb für Beiträge ohne Bild den
|
||||
String `"none"` in `entry.filepath`. Die Go-Version nutzt stattdessen den
|
||||
leeren String (Go-Zero-Value) und prüft nur noch auf `filepath == ""`.
|
||||
Alte `"none"`-Zeilen würden sonst im Feed als Bildpfad interpretiert
|
||||
(`<img src="/none">` → kaputtes Bild).
|
||||
|
||||
**Migration:**
|
||||
```sql
|
||||
UPDATE entry SET filepath = '' WHERE filepath = 'none';
|
||||
```
|
||||
|
||||
Idempotent — kann gefahrlos mehrfach laufen.
|
||||
|
||||
---
|
||||
|
||||
## Threading-Spalten in `entry`
|
||||
|
||||
**Wann:** beim Deploy der Threading-Version gegen eine bestehende `kver.db`, die
|
||||
die Tabelle `entry` noch ohne die Threading-Spalten hat.
|
||||
|
||||
**Hintergrund:** Das Threading führt `reply_to` (pid des Eltern-Posts, `0` = Root),
|
||||
`reply_count` (Anzahl Antworten im gesamten Teilbaum) und `last_activity`
|
||||
(Sortierschlüssel des Hauptfeeds) ein. `CREATE TABLE IF NOT EXISTS` ergänzt diese
|
||||
Spalten bei einer vorhandenen Tabelle nicht.
|
||||
|
||||
**Migration:**
|
||||
```sql
|
||||
ALTER TABLE entry ADD COLUMN reply_to INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE entry ADD COLUMN reply_count INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE entry ADD COLUMN last_activity INTEGER;
|
||||
UPDATE entry SET last_activity = created_at WHERE last_activity IS NULL;
|
||||
```
|
||||
|
||||
Bestehende Zeilen werden so zu Root-Beiträgen (`reply_to = 0`, `reply_count = 0`)
|
||||
mit `last_activity = created_at`. Nicht idempotent — die `ALTER TABLE`-Zeilen nur
|
||||
einmalig ausführen.
|
||||
|
||||
---
|
||||
|
||||
## Soft-Delete-Spalte in `entry`
|
||||
|
||||
**Wann:** beim Deploy der Soft-Delete-Version gegen eine bestehende `kver.db`.
|
||||
|
||||
**Hintergrund:** Statt Beiträge hart zu löschen werden sie als `[deleted]`-Platzhalter
|
||||
markiert (`deleted = 1`, Inhalt/Bild/Autor geleert), damit Antworten nicht verwaisen.
|
||||
Das Frontend und die Feeds erkennen gelöschte Beiträge an dieser Spalte.
|
||||
|
||||
**Migration:**
|
||||
```sql
|
||||
ALTER TABLE entry ADD COLUMN deleted INTEGER NOT NULL DEFAULT 0;
|
||||
```
|
||||
|
||||
Bestehende Beiträge bleiben damit sichtbar (`deleted = 0`). Nur einmalig ausführen.
|
||||
|
||||
---
|
||||
|
||||
## `asn`-Spalte in `impression`
|
||||
|
||||
**Wann:** beim Deploy der ASN-Auswertung gegen eine bestehende `impression`-Tabelle.
|
||||
|
||||
**Hintergrund:** Pro Impression wird das autonome System (Netzbetreiber) gespeichert,
|
||||
aufgelöst aus der vollen Client-IP *vor* dem Maskieren (siehe `lookupASN`). Die Spalte
|
||||
liegt bewusst **nicht** im Primärschlüssel — pro `anon_ip` ist das ASN deterministisch,
|
||||
also entstehen keine Zeilen-Duplikate, und ein simples `ADD COLUMN` genügt (kein
|
||||
Tabellen-Rebuild). `CREATE TABLE IF NOT EXISTS` ergänzt die Spalte bei einer
|
||||
vorhandenen Tabelle nicht.
|
||||
|
||||
**Migration:**
|
||||
```sql
|
||||
ALTER TABLE impression ADD COLUMN asn TEXT NOT NULL DEFAULT '';
|
||||
```
|
||||
|
||||
Bestehende Zeilen haben damit kein ASN (`''`, in der Auswertung „(unbekannt)"). Nur
|
||||
einmalig ausführen.
|
||||
Reference in New Issue
Block a user