From bc49b7cd5bdcb5f4161bc22bcbc06b7680d7816b Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 22 May 2026 21:47:32 +0200 Subject: [PATCH] fix(db): fix migration failures when upgrading from 0.8.0 schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migration 1: remove idx_sessions_token_hash from _SCHEMA_STATEMENTS. The legacy schema has sessions.token (not token_hash). The IF NOT EXISTS guard only prevents duplicate index names — it still requires the column to exist. Migration 2 drops and rebuilds sessions with token_hash anyway, so creating the index in migration 1 was redundant. Migration 3: replace ALTER TABLE ADD COLUMN with a table rebuild. SQLite rejects ALTER TABLE ADD COLUMN NOT NULL DEFAULT when the table already contains rows. The old DB has ~181k geo_cache rows, so the ALTER always failed. Rebuild copies existing rows with last_seen set to cached_at as a reasonable approximation of last-seen time. --- backend/app/db.py | 27 ++++++++++++++++++++++++--- backend/pyproject.toml | 2 +- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/backend/app/db.py b/backend/app/db.py index e448d44..6f4b74e 100644 --- a/backend/app/db.py +++ b/backend/app/db.py @@ -102,10 +102,15 @@ CREATE TABLE IF NOT EXISTS schema_migrations ( """ # Ordered list of DDL statements to execute on initialisation. +# NOTE: _CREATE_SESSIONS_TOKEN_INDEX is intentionally omitted here. +# The old 0.8.0 schema has a `sessions.token` column (not `token_hash`), so +# running CREATE INDEX … ON sessions (token_hash) in migration 1 would fail +# with "no such column: token_hash" on legacy databases. Migration 2 drops +# and recreates the sessions table with token_hash and also creates the index, +# so there is no need to create it in migration 1. _SCHEMA_STATEMENTS: list[str] = [ _CREATE_SETTINGS, _CREATE_SESSIONS, - _CREATE_SESSIONS_TOKEN_INDEX, _CREATE_BLOCKLIST_SOURCES, _CREATE_IMPORT_LOG, _CREATE_GEO_CACHE, @@ -133,8 +138,24 @@ CREATE UNIQUE INDEX idx_sessions_token_hash ON sessions (token_hash); 3: """ -- Migration 3: Add last_seen timestamp to geo_cache for retention policy. -- Tracks when each IP was last referenced to enable purging of stale entries. --- Default to current timestamp for existing rows. -ALTER TABLE geo_cache ADD COLUMN last_seen TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')); +-- SQLite rejects ALTER TABLE ADD COLUMN with a non-constant NOT NULL default +-- when the table already contains rows, so we rebuild the table instead. +-- Existing rows receive last_seen = cached_at as a reasonable approximation +-- (the IP was at least seen when it was first cached). +DROP TABLE IF EXISTS geo_cache_new; +CREATE TABLE geo_cache_new ( + ip TEXT PRIMARY KEY, + country_code TEXT, + country_name TEXT, + asn TEXT, + org TEXT, + cached_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + last_seen TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) +); +INSERT INTO geo_cache_new (ip, country_code, country_name, asn, org, cached_at, last_seen) + SELECT ip, country_code, country_name, asn, org, cached_at, cached_at FROM geo_cache; +DROP TABLE geo_cache; +ALTER TABLE geo_cache_new RENAME TO geo_cache; """, 4: """ -- Migration 4: Add scheduler_lock table for multi-worker safety. diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 5b268d5..c28ed5e 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "bangui-backend" -version = "0.9.19-rc.1" +version = "0.9.19-rc.3" description = "BanGUI backend — fail2ban web management interface" requires-python = ">=3.12" dependencies = [