"""create all tables Revision ID: 001 Revises: Create Date: 2026-03-14 """ from alembic import op import sqlalchemy as sa revision = '001' down_revision = None branch_labels = None depends_on = None def upgrade() -> None: # ── tenants ────────────────────────────────────────────── op.create_table( 'tenants', sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True), sa.Column('code', sa.String(50), nullable=False), sa.Column('prefix', sa.String(20), nullable=False, server_default=''), sa.Column('name', sa.String(200), nullable=False), sa.Column('name_eng', sa.String(200)), sa.Column('tax_id', sa.String(20)), sa.Column('domain', sa.String(200), nullable=False), sa.Column('address', sa.String(500)), sa.Column('tel', sa.String(50)), sa.Column('contact', sa.String(100)), sa.Column('contact_mobile', sa.String(50)), sa.Column('contact_email', sa.String(200)), sa.Column('keycloak_realm', sa.String(100)), sa.Column('plan_code', sa.String(50)), sa.Column('employee_limit', sa.Integer()), sa.Column('trial_start_date', sa.Date()), sa.Column('trial_end_date', sa.Date()), sa.Column('quota_per_user', sa.Integer(), nullable=False, server_default='20'), sa.Column('total_quota', sa.Integer(), nullable=False, server_default='200'), sa.Column('is_manager', sa.Boolean(), nullable=False, server_default='false'), sa.Column('is_active', sa.Boolean(), nullable=False, server_default='true'), sa.Column('status', sa.String(20), nullable=False, server_default='trial'), sa.Column('note', sa.Text()), sa.Column('created_at', sa.DateTime(), nullable=False, server_default=sa.text('NOW()')), sa.Column('updated_at', sa.DateTime(), nullable=False, server_default=sa.text('NOW()')), sa.UniqueConstraint('code', name='uq_tenants_code'), sa.UniqueConstraint('domain', name='uq_tenants_domain'), ) op.create_index('ix_tenants_id', 'tenants', ['id']) op.create_index('ix_tenants_code', 'tenants', ['code']) op.create_index('ix_tenants_domain', 'tenants', ['domain']) # ── accounts ───────────────────────────────────────────── op.create_table( 'accounts', sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True), sa.Column('tenant_id', sa.Integer(), sa.ForeignKey('tenants.id', ondelete='CASCADE'), nullable=False), sa.Column('account_code', sa.String(20), nullable=False), sa.Column('sso_account', sa.String(100), nullable=False), sa.Column('sso_uuid', sa.String(100)), sa.Column('notification_email', sa.String(200), nullable=False), sa.Column('email', sa.String(200)), sa.Column('legal_name', sa.String(200)), sa.Column('english_name', sa.String(200)), sa.Column('quota_limit', sa.Integer(), nullable=False, server_default='20'), sa.Column('is_active', sa.Boolean(), nullable=False, server_default='true'), sa.Column('default_password', sa.String(200)), sa.Column('seq_no', sa.Integer(), nullable=False), sa.Column('created_at', sa.DateTime(), nullable=False, server_default=sa.text('NOW()')), sa.Column('updated_at', sa.DateTime(), nullable=False, server_default=sa.text('NOW()')), sa.UniqueConstraint('account_code', name='uq_accounts_code'), sa.UniqueConstraint('sso_uuid', name='uq_accounts_sso_uuid'), ) op.create_index('ix_accounts_id', 'accounts', ['id']) op.create_index('ix_accounts_tenant_id', 'accounts', ['tenant_id']) op.create_index('ix_accounts_account_code', 'accounts', ['account_code']) # ── schedules ──────────────────────────────────────────── op.create_table( 'schedules', sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True), sa.Column('name', sa.String(100), nullable=False), sa.Column('cron_timer', sa.String(50), nullable=False), sa.Column('status', sa.String(10), nullable=False, server_default='Waiting'), sa.Column('last_run_at', sa.DateTime()), sa.Column('next_run_at', sa.DateTime()), sa.Column('last_status', sa.String(10)), sa.Column('recorded_at', sa.DateTime(), nullable=False, server_default=sa.text('NOW()')), sa.UniqueConstraint('name', name='uq_schedules_name'), ) op.create_index('ix_schedules_id', 'schedules', ['id']) # ── schedule_logs ──────────────────────────────────────── op.create_table( 'schedule_logs', sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True), sa.Column('schedule_id', sa.Integer(), sa.ForeignKey('schedules.id'), nullable=False), sa.Column('schedule_name', sa.String(100), nullable=False), sa.Column('started_at', sa.DateTime(), nullable=False), sa.Column('ended_at', sa.DateTime()), sa.Column('status', sa.String(10), nullable=False, server_default='running'), ) op.create_index('ix_schedule_logs_id', 'schedule_logs', ['id']) op.create_index('ix_schedule_logs_schedule_id', 'schedule_logs', ['schedule_id']) # ── tenant_schedule_results ────────────────────────────── op.create_table( 'tenant_schedule_results', sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True), sa.Column('schedule_log_id', sa.Integer(), sa.ForeignKey('schedule_logs.id'), nullable=False), sa.Column('tenant_id', sa.Integer(), sa.ForeignKey('tenants.id'), nullable=False), sa.Column('traefik_status', sa.Boolean()), sa.Column('traefik_done_at', sa.DateTime()), sa.Column('sso_result', sa.Boolean()), sa.Column('sso_done_at', sa.DateTime()), sa.Column('mailbox_result', sa.Boolean()), sa.Column('mailbox_done_at', sa.DateTime()), sa.Column('nc_result', sa.Boolean()), sa.Column('nc_done_at', sa.DateTime()), sa.Column('office_result', sa.Boolean()), sa.Column('office_done_at', sa.DateTime()), sa.Column('fail_reason', sa.Text()), sa.Column('quota_usage', sa.Float()), sa.Column('recorded_at', sa.DateTime(), nullable=False, server_default=sa.text('NOW()')), ) op.create_index('ix_tenant_schedule_results_id', 'tenant_schedule_results', ['id']) op.create_index('ix_tenant_schedule_results_log_id', 'tenant_schedule_results', ['schedule_log_id']) op.create_index('ix_tenant_schedule_results_tenant_id', 'tenant_schedule_results', ['tenant_id']) # ── account_schedule_results ───────────────────────────── op.create_table( 'account_schedule_results', sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True), sa.Column('schedule_log_id', sa.Integer(), sa.ForeignKey('schedule_logs.id'), nullable=False), sa.Column('account_id', sa.Integer(), sa.ForeignKey('accounts.id'), nullable=False), sa.Column('sso_uuid', sa.String(100)), sa.Column('sso_account', sa.String(100)), sa.Column('sso_result', sa.Boolean()), sa.Column('sso_done_at', sa.DateTime()), sa.Column('mailbox_result', sa.Boolean()), sa.Column('mailbox_done_at', sa.DateTime()), sa.Column('nc_result', sa.Boolean()), sa.Column('nc_done_at', sa.DateTime()), sa.Column('fail_reason', sa.Text()), sa.Column('quota_usage', sa.Float()), sa.Column('recorded_at', sa.DateTime(), nullable=False, server_default=sa.text('NOW()')), ) op.create_index('ix_account_schedule_results_id', 'account_schedule_results', ['id']) op.create_index('ix_account_schedule_results_log_id', 'account_schedule_results', ['schedule_log_id']) op.create_index('ix_account_schedule_results_account_id', 'account_schedule_results', ['account_id']) # ── servers ────────────────────────────────────────────── op.create_table( 'servers', sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True), sa.Column('name', sa.String(100), nullable=False), sa.Column('ip_address', sa.String(50), nullable=False), sa.Column('description', sa.String(200)), sa.Column('sort_order', sa.Integer(), nullable=False, server_default='0'), sa.Column('is_active', sa.Boolean(), nullable=False, server_default='true'), sa.Column('recorded_at', sa.DateTime(), nullable=False, server_default=sa.text('NOW()')), sa.UniqueConstraint('ip_address', name='uq_servers_ip'), ) op.create_index('ix_servers_id', 'servers', ['id']) # ── server_status_logs ─────────────────────────────────── op.create_table( 'server_status_logs', sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True), sa.Column('schedule_log_id', sa.Integer(), sa.ForeignKey('schedule_logs.id'), nullable=False), sa.Column('server_id', sa.Integer(), sa.ForeignKey('servers.id'), nullable=False), sa.Column('result', sa.Boolean(), nullable=False), sa.Column('response_time', sa.Float()), sa.Column('fail_reason', sa.Text()), sa.Column('recorded_at', sa.DateTime(), nullable=False, server_default=sa.text('NOW()')), ) op.create_index('ix_server_status_logs_id', 'server_status_logs', ['id']) op.create_index('ix_server_status_logs_schedule_log_id', 'server_status_logs', ['schedule_log_id']) op.create_index('ix_server_status_logs_server_id', 'server_status_logs', ['server_id']) # ── system_status_logs ─────────────────────────────────── op.create_table( 'system_status_logs', sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True), sa.Column('schedule_log_id', sa.Integer(), sa.ForeignKey('schedule_logs.id'), nullable=False), sa.Column('environment', sa.String(10), nullable=False), sa.Column('service_name', sa.String(50), nullable=False), sa.Column('service_desc', sa.String(100)), sa.Column('result', sa.Boolean(), nullable=False), sa.Column('fail_reason', sa.Text()), sa.Column('recorded_at', sa.DateTime(), nullable=False, server_default=sa.text('NOW()')), ) op.create_index('ix_system_status_logs_id', 'system_status_logs', ['id']) op.create_index('ix_system_status_logs_schedule_log_id', 'system_status_logs', ['schedule_log_id']) def downgrade() -> None: op.drop_table('system_status_logs') op.drop_table('server_status_logs') op.drop_table('servers') op.drop_table('account_schedule_results') op.drop_table('tenant_schedule_results') op.drop_table('schedule_logs') op.drop_table('schedules') op.drop_table('accounts') op.drop_table('tenants')