Initial MinerOps working checkpoint
This commit is contained in:
commit
4c854374e0
103 files changed
+2400932
No files matched your search
@@ -0,0 +1,173 @@
|
||||
"""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")
|
||||
Reference in new issue
Block a user