Files
minerops/backend/alembic/versions/0001_foundation.py
T

174 lines
7.0 KiB
Python

"""create initial foundation tables
Revision ID: 0001_foundation
Revises:
Create Date: 2026-07-08
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql
revision: str = "0001_foundation"
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.execute('CREATE EXTENSION IF NOT EXISTS "pgcrypto";')
op.create_table(
"roles",
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True, server_default=sa.text("gen_random_uuid()")),
sa.Column("name", sa.Text(), nullable=False, unique=True),
sa.Column("description", sa.Text(), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()")),
)
op.create_table(
"permissions",
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True, server_default=sa.text("gen_random_uuid()")),
sa.Column("key", sa.Text(), nullable=False, unique=True),
sa.Column("description", sa.Text(), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()")),
)
op.create_table(
"role_permissions",
sa.Column("role_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("roles.id", ondelete="CASCADE"), nullable=False),
sa.Column("permission_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("permissions.id", ondelete="CASCADE"), nullable=False),
sa.PrimaryKeyConstraint("role_id", "permission_id"),
)
op.create_table(
"users",
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True, server_default=sa.text("gen_random_uuid()")),
sa.Column("username", sa.Text(), nullable=False, unique=True),
sa.Column("display_name", sa.Text(), nullable=False),
sa.Column("email", sa.Text(), nullable=True),
sa.Column("password_hash", sa.Text(), nullable=False),
sa.Column("role_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("roles.id"), nullable=False),
sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.text("true")),
sa.Column("last_login_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()")),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()")),
)
op.create_index("idx_users_role_id", "users", ["role_id"])
op.create_index("idx_users_is_active", "users", ["is_active"])
op.create_table(
"sites",
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True, server_default=sa.text("gen_random_uuid()")),
sa.Column("name", sa.Text(), nullable=False, unique=True),
sa.Column("description", sa.Text(), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()")),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()")),
)
op.create_table(
"rooms",
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True, server_default=sa.text("gen_random_uuid()")),
sa.Column("site_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("sites.id", ondelete="CASCADE"), nullable=False),
sa.Column("name", sa.Text(), nullable=False),
sa.Column("description", sa.Text(), nullable=True),
sa.Column("sort_order", sa.Integer(), nullable=False, server_default=sa.text("0")),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()")),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()")),
sa.UniqueConstraint("site_id", "name", name="uq_rooms_site_name"),
)
op.create_index("idx_rooms_site_id", "rooms", ["site_id"])
op.create_table(
"app_settings",
sa.Column("key", sa.Text(), primary_key=True),
sa.Column("value_json", postgresql.JSONB(), nullable=False),
sa.Column("is_sensitive", sa.Boolean(), nullable=False, server_default=sa.text("false")),
sa.Column("updated_by_user_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("users.id", ondelete="SET NULL"), nullable=True),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()")),
)
op.execute("""
INSERT INTO roles (name, description) VALUES
('Admin', 'Full system administrator'),
('Technician', 'Technician user for repair and operational tasks'),
('Viewer', 'Read-only dashboard and miner viewer'),
('Auditor', 'Read-only audit and reporting user');
""")
op.execute("""
INSERT INTO permissions (key, description) VALUES
('admin.access', 'Access administrator features'),
('users.manage', 'Create and manage users'),
('miners.view', 'View miners'),
('miners.create', 'Create miners'),
('miners.update', 'Update miners'),
('miners.delete', 'Delete miners'),
('scan_ranges.manage', 'Manage scan ranges'),
('scans.run', 'Run scans'),
('credentials.manage', 'Manage miner credentials'),
('backups.create', 'Create backups'),
('exports.create', 'Create exports'),
('audit.view', 'View audit logs');
""")
op.execute("""
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r
CROSS JOIN permissions p
WHERE r.name = 'Admin';
""")
op.execute("""
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r
JOIN permissions p ON p.key IN (
'miners.view',
'miners.update',
'scans.run',
'exports.create'
)
WHERE r.name = 'Technician';
""")
op.execute("""
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r
JOIN permissions p ON p.key IN ('miners.view')
WHERE r.name = 'Viewer';
""")
op.execute("""
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r
JOIN permissions p ON p.key IN ('miners.view', 'audit.view', 'exports.create')
WHERE r.name = 'Auditor';
""")
op.execute("""
INSERT INTO sites (name, description)
VALUES ('Main Mining Site', 'Default mining site');
""")
def downgrade() -> None:
op.drop_table("app_settings")
op.drop_index("idx_rooms_site_id", table_name="rooms")
op.drop_table("rooms")
op.drop_table("sites")
op.drop_index("idx_users_is_active", table_name="users")
op.drop_index("idx_users_role_id", table_name="users")
op.drop_table("users")
op.drop_table("role_permissions")
op.drop_table("permissions")
op.drop_table("roles")