From b576a490d24f04a7672ffe11acb16e6e9ed01a6d Mon Sep 17 00:00:00 2001 From: Padreug Date: Fri, 22 May 2026 08:41:44 +0200 Subject: [PATCH] refactor: move fork-only migrations to migrations_fork.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `migrations.py` now matches upstream v1.3.0 exactly. Every aio-only schema delta (the old m007-m011: user_id, status, nostr_event_id + created_at, settings table, location + categories) moves into a single `m001_aio_event_schema` function in `migrations_fork.py`, tracked under `events_fork` in `dbversions` by the loader added in aiolabs/lnbits@ae997181. Idempotency guards on every ADD COLUMN / CREATE TABLE let the squashed migration no-op cleanly on dev DBs that already ran the old m007-m011 — schema lands identical from either path. Why now: aiolabs/lnbits#8. We're about to rebase events onto upstream v1.6.1 which adds its own m007_add_allow_fiat. With this move done first, migrations.py stays a fast-forward on rebase and our fork-only schema lives in a separate file that never collides. Requires aiolabs/lnbits @ ae997181 or later for the extension_fork loader. Running on an upstream lnbits without the loader patch will NOT apply the fork schema — but the aiolabs deploy fleet already pulls from aiolabs/lnbits, so this is the only host we ship to. Co-Authored-By: Claude Opus 4.7 (1M context) --- migrations_fork.py | 105 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 migrations_fork.py diff --git a/migrations_fork.py b/migrations_fork.py new file mode 100644 index 0000000..365d259 --- /dev/null +++ b/migrations_fork.py @@ -0,0 +1,105 @@ +""" +Fork-specific database migrations for the aiolabs events extension. + +These migrations are tracked separately under `events_fork` in the +`dbversions` table (loaded by `lnbits/core/helpers.py:migrate_extension_database`), +so they do not collide with upstream's `m{NNN}_*` numbering in +`migrations.py`. Keeping the upstream-tracked file untouched means +`git pull upstream` stays rebase-clean for schema changes. + +Conventions: + - Sequential numbering starting from m001. + - Each migration is `async def m{NNN}_(db)`. + - DDL must be idempotent: a fresh install runs every migration; an + install that previously ran the OLD versions of these as + `m007-m011` in `migrations.py` has the columns/tables already. + Use `_alter_add_column_safe` / `_create_table_safe` so re-runs are + no-ops instead of crashes. + +History compressed into m001 (was m007-m011 in migrations.py pre-v1.6 +rebase): + - m007 add_user_id_support (ticket.user_id column) + - m008 add_event_status (events.status column) + - m009 add_nostr_columns (events.nostr_event_id + created_at) + - m010 add_events_settings (events.settings singleton table) + - m011 add_location_and_categories (events.location + categories) +""" + + +async def _alter_add_column_safe(db, sql: str) -> None: + """ALTER TABLE ADD COLUMN that swallows duplicate-column errors. + + Re-running the squashed migration on a database that already has + these columns (from the pre-squash `m007-m011` in migrations.py) + must be a silent no-op. Same swallow we used in the old migrations. + """ + try: + await db.execute(sql) + except Exception as exc: + msg = str(exc).lower() + if "duplicate column" in msg or "already exists" in msg: + return + raise + + +async def m001_aio_event_schema(db): + """ + Apply every aiolabs schema delta on top of upstream events v1.3.0. + + This is the squashed equivalent of the pre-v1.6 sequence + m007 → m011. Order matters for the settings table seed insert + but the individual column adds are independent and idempotent. + """ + + # --- ticket.user_id ---------------------------------------------- + # Lets a ticket reference an LNbits user id instead of (name, email). + # Application logic enforces that exactly one identifier scheme is + # used per ticket. + await _alter_add_column_safe( + db, "ALTER TABLE events.ticket ADD COLUMN user_id TEXT" + ) + + # --- events.status ----------------------------------------------- + # Proposal / approval workflow. Existing rows default to 'approved' + # so they stay visible after upgrade. + await _alter_add_column_safe( + db, + "ALTER TABLE events.events ADD COLUMN status TEXT NOT NULL DEFAULT 'approved'", + ) + + # --- events.nostr_event_id, nostr_event_created_at --------------- + # Track the most recent NIP-52 calendar event we published, so + # subsequent edits can issue replaceable updates and NIP-09 deletes + # against the right addressable coordinate. + await _alter_add_column_safe( + db, "ALTER TABLE events.events ADD COLUMN nostr_event_id TEXT" + ) + await _alter_add_column_safe( + db, "ALTER TABLE events.events ADD COLUMN nostr_event_created_at INTEGER" + ) + + # --- events.settings --------------------------------------------- + # Singleton settings row used by the admin UI to toggle e.g. + # auto_approve. CREATE TABLE IF NOT EXISTS + a guarded seed keeps + # this idempotent. + await db.execute(""" + CREATE TABLE IF NOT EXISTS events.settings ( + id INTEGER PRIMARY KEY DEFAULT 1, + auto_approve BOOLEAN NOT NULL DEFAULT FALSE + ) + """) + await db.execute( + "INSERT INTO events.settings (id, auto_approve) " + "SELECT 1, FALSE WHERE NOT EXISTS " + "(SELECT 1 FROM events.settings WHERE id = 1)" + ) + + # --- events.location, events.categories -------------------------- + # NIP-52 calendar metadata. `categories` carries a JSON-encoded + # list of hashtags (the NIP-52 `t` tags). + await _alter_add_column_safe( + db, "ALTER TABLE events.events ADD COLUMN location TEXT" + ) + await _alter_add_column_safe( + db, "ALTER TABLE events.events ADD COLUMN categories TEXT" + )