Files
gt-ai-os-community/apps/control-panel-backend/alembic/versions/010_add_system_management_tables.py
HackWeasel b9dfb86260 GT AI OS Community Edition v2.0.33
Security hardening release addressing CodeQL and Dependabot alerts:

- Fix stack trace exposure in error responses
- Add SSRF protection with DNS resolution checking
- Implement proper URL hostname validation (replaces substring matching)
- Add centralized path sanitization to prevent path traversal
- Fix ReDoS vulnerability in email validation regex
- Improve HTML sanitization in validation utilities
- Fix capability wildcard matching in auth utilities
- Update glob dependency to address CVE
- Add CodeQL suppression comments for verified false positives

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-12 17:04:45 -05:00

104 lines
4.3 KiB
Python

"""Add system management tables (versions, updates, backups)
Revision ID: 010_add_system_management_tables
Revises: 009_add_tfa_session_fields
Create Date: 2025-11-25
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects.postgresql import JSON
# revision identifiers, used by Alembic.
revision = '010_add_system_management_tables'
down_revision = '009_add_tfa_session_fields'
branch_labels = None
depends_on = None
def upgrade():
# Create system_versions table
op.create_table(
'system_versions',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('uuid', sa.String(36), nullable=False),
sa.Column('version', sa.String(50), nullable=False),
sa.Column('installed_at', sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
sa.Column('installed_by', sa.String(255), nullable=True),
sa.Column('is_current', sa.Boolean(), nullable=False, default=True),
sa.Column('release_notes', sa.Text(), nullable=True),
sa.Column('git_commit', sa.String(40), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('uuid')
)
op.create_index('ix_system_versions_id', 'system_versions', ['id'])
op.create_index('ix_system_versions_version', 'system_versions', ['version'])
# Create update_jobs table
op.create_table(
'update_jobs',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('uuid', sa.String(36), nullable=False),
sa.Column('target_version', sa.String(50), nullable=False),
sa.Column('status', sa.Enum('pending', 'in_progress', 'completed', 'failed', 'rolled_back', name='updatestatus'), nullable=False),
sa.Column('started_at', sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
sa.Column('completed_at', sa.DateTime(timezone=True), nullable=True),
sa.Column('current_stage', sa.String(100), nullable=True),
sa.Column('logs', JSON, nullable=False, default=[]),
sa.Column('error_message', sa.Text(), nullable=True),
sa.Column('backup_id', sa.Integer(), nullable=True),
sa.Column('started_by', sa.String(255), nullable=True),
sa.Column('rollback_reason', sa.Text(), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('uuid')
)
op.create_index('ix_update_jobs_id', 'update_jobs', ['id'])
op.create_index('ix_update_jobs_uuid', 'update_jobs', ['uuid'])
op.create_index('ix_update_jobs_status', 'update_jobs', ['status'])
# Create backup_records table
op.create_table(
'backup_records',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('uuid', sa.String(36), nullable=False),
sa.Column('backup_type', sa.Enum('manual', 'pre_update', 'scheduled', name='backuptype'), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
sa.Column('size_bytes', sa.BigInteger(), nullable=True),
sa.Column('location', sa.String(500), nullable=False),
sa.Column('version', sa.String(50), nullable=True),
sa.Column('components', JSON, nullable=False, default={}),
sa.Column('checksum', sa.String(64), nullable=True),
sa.Column('created_by', sa.String(255), nullable=True),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('is_valid', sa.Boolean(), nullable=False, default=True),
sa.Column('expires_at', sa.DateTime(timezone=True), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('uuid')
)
op.create_index('ix_backup_records_id', 'backup_records', ['id'])
op.create_index('ix_backup_records_uuid', 'backup_records', ['uuid'])
# Insert initial system version (v2.0.31 as per current deployment)
op.execute("""
INSERT INTO system_versions (uuid, version, installed_by, is_current, installed_at)
VALUES (
'initial-version-uuid',
'v2.0.31',
'system',
true,
NOW()
)
""")
def downgrade():
# Drop tables
op.drop_table('backup_records')
op.drop_table('update_jobs')
op.drop_table('system_versions')
# Drop enum types
op.execute('DROP TYPE IF EXISTS updatestatus')
op.execute('DROP TYPE IF EXISTS backuptype')