Procházet zdrojové kódy

fix(db): dedupe legacy settings rows and add missing UNIQUE(key) index

  Legacy SQLite installs created the `settings` table without a UNIQUE
  constraint on `key`. The seed loop's `INSERT OR IGNORE` silently
  degraded to a plain INSERT, so every `systemctl restart` added another
  row of `advanced_auth_enabled` / `smtp_auth_enabled`. After a handful
  of restarts, `scalar_one_or_none()` in is_advanced_auth_enabled() and
  similar sites blew up with `MultipleResultsFound`, 500'ing the login
  flow.

  Run-migrations now deletes dup rows (keeping MIN(id) per key) and
  creates the missing `ix_settings_key` unique index before the seed
  loop. Both ops are idempotent — fresh installs and Postgres already
  have the index, so they no-op.
maziggy před 1 měsícem
rodič
revize
b99ceb26ed
1 změnil soubory, kde provedl 11 přidání a 0 odebrání
  1. 11 0
      backend/app/core/database.py

+ 11 - 0
backend/app/core/database.py

@@ -1530,6 +1530,17 @@ async def run_migrations(conn):
         "CREATE INDEX IF NOT EXISTS ix_library_files_deleted_at ON library_files(deleted_at)",
         "CREATE INDEX IF NOT EXISTS ix_library_files_deleted_at ON library_files(deleted_at)",
     )
     )
 
 
+    # Legacy SQLite installs created `settings` without a UNIQUE constraint on `key`,
+    # so `INSERT OR IGNORE` below silently degrades to a plain INSERT and dupes rows on
+    # every restart. Dedupe (keep lowest id per key) and add the missing unique index
+    # before seeding. Safe/idempotent on both dialects — fresh installs already have
+    # no dupes and `create_all` already emits the index.
+    await _safe_execute(
+        conn,
+        "DELETE FROM settings WHERE id NOT IN (SELECT MIN(id) FROM settings GROUP BY key)",
+    )
+    await _safe_execute(conn, "CREATE UNIQUE INDEX IF NOT EXISTS ix_settings_key ON settings(key)")
+
     # Seed default settings keys that must exist on fresh install
     # Seed default settings keys that must exist on fresh install
     default_settings = [
     default_settings = [
         ("advanced_auth_enabled", "false"),
         ("advanced_auth_enabled", "false"),