#!/usr/bin/env bash # # migrate.sh — liest kver-original.db und erzeugt daraus eine frische kver.db # mit dem aktuellen Go-Schema. Die Quelldatenbank wird nie verändert. # Idempotent: kver.db wird bei jedem Lauf neu erstellt. # # Verwendung: # ./migrate.sh [quelldatei [zieldatei]] # Standard: kver-original.db -> kver.db # # Schalter (als Umgebungsvariablen): # REPLIES=keep|drop Antworten (reply_to != 0) übernehmen oder verwerfen (Default: keep) # set -euo pipefail SRC="${1:-original-kver.db}" DST="${2:-kver.db}" REPLIES="${REPLIES:-keep}" if [[ ! -f "$SRC" ]]; then echo "FEHLER: Quelldatenbank '$SRC' nicht gefunden." >&2 exit 1 fi if [[ "$REPLIES" != "keep" && "$REPLIES" != "drop" ]]; then echo "FEHLER: REPLIES muss 'keep' oder 'drop' sein (war: '$REPLIES')." >&2 exit 1 fi if [[ "$SRC" -ef "$DST" ]]; then echo "FEHLER: Quelle und Ziel sind dieselbe Datei." >&2 exit 1 fi qs() { sqlite3 "$SRC" "$1"; } qd() { sqlite3 "$DST" "$1"; } echo "== kver-Migration ==" echo "Quelle: $SRC" echo "Ziel: $DST" echo "REPLIES: $REPLIES" echo echo "Quelle:" echo " Einträge gesamt: $(qs 'SELECT COUNT(*) FROM entry;')" echo " davon deleted=1: $(qs 'SELECT COUNT(*) FROM entry WHERE deleted=1;')" echo " davon Antworten: $(qs 'SELECT COUNT(*) FROM entry WHERE reply_to!=0;')" echo " User: $(qs 'SELECT COUNT(*) FROM user;')" # Zieldatenbank immer frisch anlegen rm -f "$DST" echo echo "Erstelle $DST mit aktuellem Schema..." sqlite3 "$DST" <<'SCHEMA' .bail on CREATE TABLE user ( id INTEGER PRIMARY KEY AUTOINCREMENT, uid INTEGER UNIQUE, username TEXT UNIQUE, password BLOB, created_at INTEGER, last_login INTEGER, avatar TEXT NOT NULL DEFAULT '' ); CREATE TABLE session ( id INTEGER PRIMARY KEY AUTOINCREMENT, uid INTEGER, value TEXT UNIQUE, created_at INTEGER, expires INTEGER, description TEXT ); CREATE TABLE entry ( id INTEGER PRIMARY KEY AUTOINCREMENT, pid INTEGER UNIQUE NOT NULL, uid INTEGER NOT NULL, created_at INTEGER NOT NULL, content TEXT NOT NULL DEFAULT '', filepath TEXT NOT NULL DEFAULT '', reply_to INTEGER NOT NULL DEFAULT 0, reply_count INTEGER NOT NULL DEFAULT 0, last_activity INTEGER NOT NULL DEFAULT 0, deleted INTEGER NOT NULL DEFAULT 0 ); CREATE TABLE vote ( id INTEGER PRIMARY KEY AUTOINCREMENT, uid INTEGER, pid INTEGER, mode TEXT, UNIQUE(uid, pid) ); SCHEMA # Prüfen, welche optionalen Spalten in der Quelle vorhanden sind. # Ältere Python-DBs haben reply_count und last_activity noch nicht. has_col() { sqlite3 "$SRC" "SELECT COUNT(*) FROM pragma_table_info('$1') WHERE name='$2';" } reply_count_expr="0" [[ "$(has_col entry reply_count)" == "1" ]] && reply_count_expr="COALESCE(e.reply_count, 0)" last_activity_expr="e.created_at" [[ "$(has_col entry last_activity)" == "1" ]] && last_activity_expr="COALESCE(e.last_activity, e.created_at)" # Profilbild: alte DBs haben die Spalte nicht -> leerer String. avatar_expr="''" [[ "$(has_col user avatar)" == "1" ]] && avatar_expr="COALESCE(u.avatar, '')" replies_filter="" [[ "$REPLIES" == "drop" ]] && replies_filter="AND e.reply_to = 0" has_vote_table=$(qs "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='vote';") # Quell-Pfad für SQL-String-Literal escapen (einfache Anführungszeichen verdoppeln) src_sql="${SRC//\'/\'\'}" sqlite3 "$DST" < '' (Go-Zero-Value; scanEntries erwartet string, nicht *string) -- content NULL -> '' -- deleted: nur nicht-gelöschte Einträge werden übernommen -- orphans: Einträge ohne bekannten User werden verworfen INSERT INTO entry (pid, uid, created_at, content, filepath, reply_to, reply_count, last_activity, deleted) SELECT e.pid, e.uid, e.created_at, COALESCE(e.content, ''), COALESCE(NULLIF(e.filepath, 'none'), ''), COALESCE(e.reply_to, 0), $reply_count_expr, $last_activity_expr, 0 FROM src.entry e WHERE e.deleted = 0 AND e.uid IN (SELECT uid FROM src.user) $replies_filter; -- Sessions leer lassen: Alt-Tokens sind Integer (Python-Format), -- Go erzeugt crypto/rand-Hex-Tokens -> alle Alt-Sessions sind wertlos. COMMIT; DETACH src; VACUUM; SQL # Votes separat, da die Tabelle in alten DBs fehlen kann if [[ "$has_vote_table" == "1" ]]; then sqlite3 "$DST" < static/media/ aus der Produktion kopieren, sonst liefern diese Bilder 404." fi echo echo "Fertig."