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
This commit is contained in:
2026-05-26 15:28:03 +03:00
parent 8dcc0f86f8
commit 6b960b0eb3
3 changed files with 64 additions and 0 deletions

View File

@@ -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

View File

@@ -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")

View File

@@ -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.