작업 내용 저장
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
"""remove bot_principals and auth_audit_logs, add service token support
|
||||
|
||||
Revision ID: 20260423_0013
|
||||
Revises: 20260414_0012
|
||||
Create Date: 2026-04-23 00:00:00
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy import inspect
|
||||
|
||||
|
||||
revision = "20260423_0013"
|
||||
down_revision = "20260414_0012"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def _table_exists(inspector, table_name: str) -> bool:
|
||||
return table_name in inspector.get_table_names()
|
||||
|
||||
|
||||
def _column_exists(inspector, table_name: str, column_name: str) -> bool:
|
||||
if not _table_exists(inspector, table_name):
|
||||
return False
|
||||
return any(c["name"] == column_name for c in inspector.get_columns(table_name))
|
||||
|
||||
|
||||
def _index_exists(inspector, table_name: str, index_name: str) -> bool:
|
||||
if not _table_exists(inspector, table_name):
|
||||
return False
|
||||
return any(idx["name"] == index_name for idx in inspector.get_indexes(table_name))
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = inspect(bind)
|
||||
|
||||
# 사전 검증: bot_principals.owner_user_id NULL 행이 있으면 중단
|
||||
if _table_exists(inspector, "bot_principals"):
|
||||
null_count = bind.execute(
|
||||
sa.text("SELECT COUNT(*) FROM bot_principals WHERE owner_user_id IS NULL")
|
||||
).scalar()
|
||||
if null_count and int(null_count) > 0:
|
||||
raise RuntimeError(
|
||||
f"마이그레이션 중단: bot_principals.owner_user_id NULL 행 {null_count}개 발견. "
|
||||
"owner_user_id를 먼저 보정한 후 다시 실행하세요."
|
||||
)
|
||||
|
||||
# auth_tokens.user_id 보정: bot_principal.id → bot_principal.owner_user_id
|
||||
if _column_exists(inspector, "auth_tokens", "bot_principal_id"):
|
||||
bind.execute(
|
||||
sa.text(
|
||||
"""
|
||||
UPDATE auth_tokens t
|
||||
SET user_id = b.owner_user_id
|
||||
FROM bot_principals b
|
||||
WHERE t.bot_principal_id = b.id
|
||||
AND t.bot_principal_id IS NOT NULL
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
# token_type 'bot' → 'service' rename
|
||||
bind.execute(
|
||||
sa.text("UPDATE auth_tokens SET token_type = 'service' WHERE token_type = 'bot'")
|
||||
)
|
||||
|
||||
# service token name unique index 생성
|
||||
inspector = inspect(bind)
|
||||
if not _index_exists(inspector, "auth_tokens", "idx_auth_tokens_service_name_unique"):
|
||||
op.execute(
|
||||
"""
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_auth_tokens_service_name_unique
|
||||
ON auth_tokens (name)
|
||||
WHERE token_type = 'service'
|
||||
"""
|
||||
)
|
||||
|
||||
# auth_tokens.bot_principal_id 컬럼 DROP
|
||||
inspector = inspect(bind)
|
||||
if _column_exists(inspector, "auth_tokens", "bot_principal_id"):
|
||||
# 기존 인덱스 제거 후 컬럼 DROP
|
||||
if _index_exists(inspector, "auth_tokens", "idx_auth_tokens_bot_created"):
|
||||
op.drop_index("idx_auth_tokens_bot_created", table_name="auth_tokens")
|
||||
op.drop_column("auth_tokens", "bot_principal_id")
|
||||
|
||||
# auth_audit_logs 테이블 DROP
|
||||
inspector = inspect(bind)
|
||||
if _table_exists(inspector, "auth_audit_logs"):
|
||||
op.drop_table("auth_audit_logs")
|
||||
|
||||
# bot_principals 테이블 DROP (관련 인덱스·FK 포함)
|
||||
inspector = inspect(bind)
|
||||
if _table_exists(inspector, "bot_principals"):
|
||||
for idx_name in [
|
||||
"idx_bot_principals_active_created",
|
||||
"idx_bot_principals_owner_active_created",
|
||||
]:
|
||||
if _index_exists(inspector, "bot_principals", idx_name):
|
||||
op.drop_index(idx_name, table_name="bot_principals")
|
||||
op.drop_table("bot_principals")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = inspect(bind)
|
||||
|
||||
# bot_principals 테이블 복원
|
||||
if not _table_exists(inspector, "bot_principals"):
|
||||
op.create_table(
|
||||
"bot_principals",
|
||||
sa.Column("id", sa.Text(), nullable=False),
|
||||
sa.Column("name", sa.Text(), nullable=False),
|
||||
sa.Column("owner_user_id", sa.Text(), nullable=True),
|
||||
sa.Column("permission", sa.Text(), nullable=False),
|
||||
sa.Column("description", sa.Text(), nullable=True),
|
||||
sa.Column("is_active", sa.Boolean(), nullable=True, server_default=sa.text("true")),
|
||||
sa.Column("created_at", sa.TIMESTAMP(timezone=True), nullable=True),
|
||||
sa.Column("updated_at", sa.TIMESTAMP(timezone=True), nullable=True),
|
||||
sa.Column("deactivated_at", sa.TIMESTAMP(timezone=True), nullable=True),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
|
||||
# auth_audit_logs 테이블 복원
|
||||
if not _table_exists(inspector, "auth_audit_logs"):
|
||||
op.create_table(
|
||||
"auth_audit_logs",
|
||||
sa.Column("id", sa.Text(), nullable=False),
|
||||
sa.Column("event_type", sa.Text(), nullable=False),
|
||||
sa.Column("action", sa.Text(), nullable=False),
|
||||
sa.Column("actor_id", sa.Text(), nullable=True),
|
||||
sa.Column("bot_id", sa.Text(), nullable=False),
|
||||
sa.Column("token_id_masked", sa.Text(), nullable=True),
|
||||
sa.Column("token_ref_masked", sa.Text(), nullable=True),
|
||||
sa.Column("result", sa.Text(), nullable=True),
|
||||
sa.Column("detail", sa.Text(), nullable=True),
|
||||
sa.Column("payload", sa.JSON(), nullable=True),
|
||||
sa.Column("occurred_at", sa.TIMESTAMP(timezone=True), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
|
||||
# auth_tokens.bot_principal_id 컬럼 복원
|
||||
inspector = inspect(bind)
|
||||
if not _column_exists(inspector, "auth_tokens", "bot_principal_id"):
|
||||
op.add_column("auth_tokens", sa.Column("bot_principal_id", sa.Text(), nullable=True))
|
||||
|
||||
# token_type 'service' → 'bot' 역방향 (구조 복원 목적)
|
||||
bind.execute(
|
||||
sa.text("UPDATE auth_tokens SET token_type = 'bot' WHERE token_type = 'service'")
|
||||
)
|
||||
|
||||
# service token unique index 제거
|
||||
inspector = inspect(bind)
|
||||
if _index_exists(inspector, "auth_tokens", "idx_auth_tokens_service_name_unique"):
|
||||
op.drop_index("idx_auth_tokens_service_name_unique", table_name="auth_tokens")
|
||||
Reference in New Issue
Block a user