76 lines
3.4 KiB
Python
76 lines
3.4 KiB
Python
# #region Alembic.AddSessionActivityTable [C:2] [TYPE Module] [SEMANTICS alembic,migration,session,activity,auth]
|
|
# @defgroup Alembic Create the session_activity table for session timeout enforcement.
|
|
# @PRE Previous migration (f7a8b9c0d1e2) has been applied.
|
|
# @POST session_activity table is created when users exists; otherwise ORM create_all()
|
|
# creates it after the fresh Alembic upgrade.
|
|
# @SIDE_EFFECT DDL execution — creates table, index, foreign key constraint.
|
|
# @RELATION DEPENDS_ON -> [Models.Auth.SessionActivity]
|
|
# @RELATION DEPENDS_ON -> [Models.Auth.User]
|
|
# @RATIONALE users is ORM-owned and is created by Base.metadata.create_all() after
|
|
# Alembic during a fresh install, so the FK migration must be safely skipped first.
|
|
# @REJECTED Unconditionally creating the FK table was rejected — fresh installs fail
|
|
# before application startup because users does not yet exist.
|
|
"""add session_activity table
|
|
|
|
Revision ID: 8e9f0a1b2c3d
|
|
Revises: f7a8b9c0d1e2
|
|
Create Date: 2026-07-23 07:45:00.000000
|
|
|
|
"""
|
|
from collections.abc import Sequence
|
|
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
from sqlalchemy import inspect
|
|
|
|
|
|
# revision identifiers, used by Alembic.
|
|
revision: str = "8e9f0a1b2c3d"
|
|
down_revision: str | Sequence[str] | None = "f7a8b9c0d1e2"
|
|
branch_labels: str | Sequence[str] | None = None
|
|
depends_on: str | Sequence[str] | None = None
|
|
|
|
|
|
# #region Alembic.AddSessionActivityTable.TableExists [C:1] [TYPE Function] [SEMANTICS alembic,helper,table]
|
|
# @BRIEF Check if a table already exists in the database.
|
|
def _table_exists(table_name: str) -> bool:
|
|
conn = op.get_bind()
|
|
inspector = inspect(conn)
|
|
return table_name in inspector.get_table_names()
|
|
# #endregion Alembic.AddSessionActivityTable.TableExists
|
|
|
|
|
|
# #region Alembic.AddSessionActivityTable.Upgrade [C:2] [TYPE Function] [SEMANTICS alembic,upgrade]
|
|
# @BRIEF Create session_activity table for idle/absolute timeout enforcement.
|
|
# @PRE auth.users table exists in the database.
|
|
# @POST session_activity table created with jti PK, user_id FK->users.id, indexes.
|
|
# @SIDE_EFFECT Executes CREATE TABLE DDL.
|
|
def upgrade() -> None:
|
|
"""Create session_activity table for idle/absolute timeout enforcement."""
|
|
if _table_exists("session_activity") or not _table_exists("users"):
|
|
return
|
|
op.create_table(
|
|
"session_activity",
|
|
sa.Column("jti", sa.String(), nullable=False),
|
|
sa.Column("user_id", sa.String(), nullable=False, index=True),
|
|
sa.Column("issued_at", sa.DateTime(), nullable=False),
|
|
sa.Column("expires_at", sa.DateTime(), nullable=False),
|
|
sa.Column("last_activity_at", sa.DateTime(), nullable=False, server_default=sa.func.now()),
|
|
sa.Column("ip_address", sa.String(), nullable=True),
|
|
sa.Column("user_agent", sa.String(), nullable=True),
|
|
sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"),
|
|
sa.PrimaryKeyConstraint("jti"),
|
|
)
|
|
# #endregion Alembic.AddSessionActivityTable.Upgrade
|
|
|
|
|
|
# #region Alembic.AddSessionActivityTable.Downgrade [C:1] [TYPE Function] [SEMANTICS alembic,downgrade]
|
|
# @BRIEF Drop session_activity table, reverting the upgrade.
|
|
# @POST session_activity table dropped.
|
|
# @SIDE_EFFECT Executes DROP TABLE DDL.
|
|
def downgrade() -> None:
|
|
"""Drop session_activity table."""
|
|
op.drop_table("session_activity")
|
|
# #endregion Alembic.AddSessionActivityTable.Downgrade
|
|
# #endregion Alembic.AddSessionActivityTable
|