121 lines
3.1 KiB
Python
121 lines
3.1 KiB
Python
"""add roles and user_roles tables
|
|
|
|
Revision ID: add_roles_tables
|
|
Revises: add_article_views
|
|
Create Date: 2025-11-21
|
|
"""
|
|
|
|
from typing import Tuple
|
|
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
from sqlalchemy import func, text
|
|
from sqlalchemy.dialects.postgresql import JSONB
|
|
|
|
revision = "add_roles_tables"
|
|
down_revision = "add_article_views"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def timestamps() -> Tuple[sa.Column, sa.Column]:
|
|
return (
|
|
sa.Column(
|
|
"created_at",
|
|
sa.TIMESTAMP(timezone=True),
|
|
nullable=False,
|
|
server_default=func.now(),
|
|
),
|
|
sa.Column(
|
|
"updated_at",
|
|
sa.TIMESTAMP(timezone=True),
|
|
nullable=False,
|
|
server_default=func.now(),
|
|
onupdate=func.current_timestamp(),
|
|
),
|
|
)
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.create_table(
|
|
"roles",
|
|
sa.Column("id", sa.Integer, primary_key=True),
|
|
sa.Column("name", sa.String(length=64), nullable=False, unique=True),
|
|
sa.Column("description", sa.Text, nullable=False, server_default=""),
|
|
sa.Column(
|
|
"permissions",
|
|
JSONB,
|
|
nullable=False,
|
|
server_default=text("'[]'::jsonb"),
|
|
),
|
|
*timestamps(),
|
|
)
|
|
op.execute(
|
|
"""
|
|
CREATE TRIGGER update_role_modtime
|
|
BEFORE UPDATE
|
|
ON roles
|
|
FOR EACH ROW
|
|
EXECUTE PROCEDURE update_updated_at_column();
|
|
""",
|
|
)
|
|
|
|
op.create_table(
|
|
"user_roles",
|
|
sa.Column(
|
|
"user_id",
|
|
sa.Integer,
|
|
sa.ForeignKey("users.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
),
|
|
sa.Column(
|
|
"role_id",
|
|
sa.Integer,
|
|
sa.ForeignKey("roles.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
),
|
|
sa.Column(
|
|
"assigned_at",
|
|
sa.TIMESTAMP(timezone=True),
|
|
nullable=False,
|
|
server_default=func.now(),
|
|
),
|
|
)
|
|
op.create_primary_key(
|
|
"pk_user_roles",
|
|
"user_roles",
|
|
["user_id", "role_id"],
|
|
)
|
|
op.create_index("ix_user_roles_role_id", "user_roles", ["role_id"])
|
|
|
|
op.execute(
|
|
"""
|
|
INSERT INTO roles (name, description, permissions)
|
|
VALUES ('admin', 'System administrator with full privileges', '["*"]')
|
|
ON CONFLICT (name) DO NOTHING;
|
|
""",
|
|
)
|
|
op.execute(
|
|
"""
|
|
DO $$
|
|
DECLARE
|
|
admin_role_id INTEGER;
|
|
first_user_id INTEGER;
|
|
BEGIN
|
|
SELECT id INTO admin_role_id FROM roles WHERE name = 'admin';
|
|
SELECT id INTO first_user_id FROM users ORDER BY id ASC LIMIT 1;
|
|
IF admin_role_id IS NOT NULL AND first_user_id IS NOT NULL THEN
|
|
INSERT INTO user_roles (user_id, role_id)
|
|
VALUES (first_user_id, admin_role_id)
|
|
ON CONFLICT DO NOTHING;
|
|
END IF;
|
|
END $$;
|
|
""",
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_index("ix_user_roles_role_id", table_name="user_roles")
|
|
op.drop_table("user_roles")
|
|
op.drop_table("roles")
|