From 6e6e141d26476444c0441fd7b243daf5f7e1939e Mon Sep 17 00:00:00 2001 From: Barcode Betty Date: Sat, 4 Apr 2026 17:10:29 +0000 Subject: [PATCH] fix(api): bootstrap users table in migration 007 + harden create_all MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create migration 007 to raw-SQL CREATE TABLE IF NOT EXISTS the users table as a safety net for fresh databases where Base.metadata.create_all() may fail due to import errors before the table is created. Wrap the create_all call in env.py with try/except so alembic never crashes due to create_all failures — migrations already handle table creation. Co-Authored-By: Paperclip --- alembic/env.py | 8 +++- alembic/versions/007_bootstrap_users_table.py | 47 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 alembic/versions/007_bootstrap_users_table.py diff --git a/alembic/env.py b/alembic/env.py index 504da75..b781b2f 100644 --- a/alembic/env.py +++ b/alembic/env.py @@ -50,7 +50,13 @@ def run_migrations_online() -> None: # Create any tables defined in models but not yet created by migrations. # This bootstraps fresh databases that have no legacy schema. # checkfirst=True ensures this is a no-op on existing databases. - Base.metadata.create_all(bind=connection, checkfirst=True) + try: + Base.metadata.create_all(bind=connection, checkfirst=True) + except Exception as exc: + import logging + logging.getLogger("alembic.env").warning( + "create_all failed (non-fatal, migrations should handle table creation): %s", exc + ) if context.is_offline_mode(): diff --git a/alembic/versions/007_bootstrap_users_table.py b/alembic/versions/007_bootstrap_users_table.py new file mode 100644 index 0000000..e9695c0 --- /dev/null +++ b/alembic/versions/007_bootstrap_users_table.py @@ -0,0 +1,47 @@ +"""Bootstrap users table on fresh databases. + +On fresh databases, migrations 001-006 skip users-table operations because +the table does not exist yet. Base.metadata.create_all() in env.py is meant +to handle this, but if it fails (import errors, etc.) the table is never +created. This migration creates the users table with raw SQL as a safety net. + +Revision ID: 007_bootstrap_users_table +Revises: 006_email_inbound_token_server_default +Create Date: 2026-04-04 +""" + +import sqlalchemy as sa +from sqlalchemy import text + +from alembic import op + +revision = "007_bootstrap_users_table" +down_revision = "006_email_inbound_token_server_default" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + conn = op.get_bind() + inspector = sa.inspect(conn) + if inspector.has_table("users"): + return # Table already exists (non-fresh DB or create_all already ran) + + conn.execute(text(""" + CREATE TABLE users ( + id TEXT PRIMARY KEY, + email VARCHAR(255) NOT NULL UNIQUE, + hashed_password VARCHAR(255), + display_name VARCHAR(100), + email_verified BOOLEAN NOT NULL DEFAULT false, + image TEXT, + email_inbound_token VARCHAR(22) NOT NULL UNIQUE + DEFAULT replace(replace(trim(trailing '=' from encode(gen_random_bytes(16), 'base64')), '+', '-'), '/', '_'), + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() + ) + """)) + + +def downgrade() -> None: + op.execute(text("DROP TABLE IF EXISTS users"))