From 6b960b0eb3096bfc1f612aa86fe3a75a6ed5ed8e Mon Sep 17 00:00:00 2001 From: busya Date: Tue, 26 May 2026 15:28:03 +0300 Subject: [PATCH] fix: add is_admin column migration + additive safety net Alembic migration 86c7b1d6a710 adds is_admin BOOLEAN DEFAULT FALSE to the roles table. Fixes login crash after M-3 change: psycopg2.errors.UndefinedColumn: column roles.is_admin does not exist Also adds _ensure_roles_is_admin_column() as an additive migration safety net for environments that don't run alembic automatically. To apply: cd backend && source .venv/bin/activate && alembic upgrade head --- backend/.env.example | 1 + ...a710_add_is_admin_column_to_roles_table.py | 28 +++++++++++++++ backend/src/core/database.py | 35 +++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 backend/alembic/versions/86c7b1d6a710_add_is_admin_column_to_roles_table.py diff --git a/backend/.env.example b/backend/.env.example index cf663a1b..712b0b46 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -8,6 +8,7 @@ ENCRYPTION_KEY=change-me-to-a-fernet-key DATABASE_URL=postgresql+psycopg2://postgres:postgres@localhost:5432/ss_tools AUTH_DATABASE_URL=postgresql+psycopg2://postgres:postgres@localhost:5432/ss_tools TASKS_DATABASE_URL=postgresql+psycopg2://postgres:postgres@localhost:5432/ss_tools +# Run after pulling: cd backend && source .venv/bin/activate && alembic upgrade head # ── Optional ── ALLOWED_ORIGINS=http://localhost:5173 diff --git a/backend/alembic/versions/86c7b1d6a710_add_is_admin_column_to_roles_table.py b/backend/alembic/versions/86c7b1d6a710_add_is_admin_column_to_roles_table.py new file mode 100644 index 00000000..dc3c7449 --- /dev/null +++ b/backend/alembic/versions/86c7b1d6a710_add_is_admin_column_to_roles_table.py @@ -0,0 +1,28 @@ +"""add is_admin column to roles table + +Revision ID: 86c7b1d6a710 +Revises: b0c1d2e3f4a5 +Create Date: 2026-05-26 15:27:06.159151 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '86c7b1d6a710' +down_revision: Union[str, Sequence[str], None] = 'b0c1d2e3f4a5' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Add is_admin column to roles table.""" + op.add_column("roles", sa.Column("is_admin", sa.Boolean(), nullable=False, server_default=sa.text("false"))) + + +def downgrade() -> None: + """Remove is_admin column from roles table.""" + op.drop_column("roles", "is_admin") diff --git a/backend/src/core/database.py b/backend/src/core/database.py index 66bfa01f..af4b12b2 100644 --- a/backend/src/core/database.py +++ b/backend/src/core/database.py @@ -364,6 +364,41 @@ def _ensure_auth_users_columns(bind_engine): # #endregion _ensure_auth_users_columns +# #region _ensure_roles_is_admin_column [C:3] [TYPE Function] +# @BRIEF Add is_admin column to roles table if missing (additive migration). +# @PRE Database connection is active. +# @POST roles.is_admin column exists (BOOLEAN, default FALSE). +# @SIDE_EFFECT Executes ALTER TABLE on the auth database. +def _ensure_roles_is_admin_column(bind_engine): + with belief_scope("_ensure_roles_is_admin_column"): + table_name = "roles" + inspector = inspect(bind_engine) + if table_name not in inspector.get_table_names(): + return + + existing_columns = { + str(column.get("name") or "").strip() + for column in inspector.get_columns(table_name) + } + + if "is_admin" in existing_columns: + logger.reason("roles.is_admin column already exists") + return + + alter = "ALTER TABLE roles ADD COLUMN is_admin BOOLEAN NOT NULL DEFAULT FALSE" + try: + with bind_engine.begin() as connection: + connection.execute(text(alter)) + logger.reason("Added roles.is_admin column", extra={"statement": alter}) + except Exception as migration_error: + logger.explore( + "roles.is_admin additive migration failed", + extra={"error": str(migration_error)}, + ) + raise +# #endregion _ensure_roles_is_admin_column + + # #region _ensure_filter_source_enum_values [C:3] [TYPE Function] # @BRIEF Adds missing FilterSource enum values to the PostgreSQL native filtersource type. # @PRE bind_engine points to application database with imported_filters table.