"""email settings per register domain Revision ID: 20260629_03 Revises: 20260629_02 Create Date: 2026-06-29 14:20:00.000000 """ from typing import Sequence, Union import uuid from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql revision: str = "20260629_03" down_revision: Union[str, None] = "20260629_02" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def _table_exists(inspector: sa.Inspector, table_name: str) -> bool: return table_name in inspector.get_table_names() def _column_exists(inspector: sa.Inspector, table_name: str, column_name: str) -> bool: return any(column["name"] == column_name for column in inspector.get_columns(table_name)) def _normalize_domains(value: str | None) -> list[str]: domains: list[str] = [] for item in (value or "huapont.cn,qq.com").replace(",", ",").split(","): domain = item.strip().lower().lstrip("@") if domain and domain not in domains: domains.append(domain) return domains or ["huapont.cn", "qq.com"] def upgrade() -> None: bind = op.get_bind() inspector = sa.inspect(bind) if not _table_exists(inspector, "system_email_settings"): return if not _column_exists(inspector, "system_email_settings", "register_domain"): op.add_column("system_email_settings", sa.Column("register_domain", sa.String(length=255), nullable=True)) rows = bind.execute(sa.text("SELECT * FROM system_email_settings ORDER BY created_at ASC")).mappings().all() for row in rows: domains = _normalize_domains(row.get("allowed_register_domain")) primary_domain = domains[0] bind.execute( sa.text( "UPDATE system_email_settings " "SET register_domain = :register_domain, allowed_register_domain = :register_domain " "WHERE id = :id" ), {"register_domain": primary_domain, "id": row["id"]}, ) for domain in domains[1:]: exists = bind.execute( sa.text("SELECT 1 FROM system_email_settings WHERE register_domain = :register_domain"), {"register_domain": domain}, ).first() if exists: continue bind.execute( sa.text( """ INSERT INTO system_email_settings ( id, register_domain, smtp_host, smtp_port, smtp_security, smtp_username, smtp_password_encrypted, sender_email, sender_name, allowed_register_domain, verification_code_ttl_minutes, send_cooldown_seconds, max_verify_attempts, updated_by, created_at, updated_at ) VALUES ( :id, :register_domain, :smtp_host, :smtp_port, :smtp_security, :smtp_username, :smtp_password_encrypted, :sender_email, :sender_name, :allowed_register_domain, :verification_code_ttl_minutes, :send_cooldown_seconds, :max_verify_attempts, :updated_by, now(), now() ) """ ), { "id": uuid.uuid4(), "register_domain": domain, "smtp_host": row["smtp_host"], "smtp_port": row["smtp_port"], "smtp_security": row["smtp_security"], "smtp_username": row["smtp_username"], "smtp_password_encrypted": row["smtp_password_encrypted"], "sender_email": row["sender_email"], "sender_name": row["sender_name"], "allowed_register_domain": domain, "verification_code_ttl_minutes": row["verification_code_ttl_minutes"], "send_cooldown_seconds": row["send_cooldown_seconds"], "max_verify_attempts": row["max_verify_attempts"], "updated_by": row["updated_by"], }, ) op.alter_column("system_email_settings", "register_domain", existing_type=sa.String(length=255), nullable=False) op.create_unique_constraint( "uq_system_email_settings_register_domain", "system_email_settings", ["register_domain"], ) def downgrade() -> None: bind = op.get_bind() inspector = sa.inspect(bind) if not _table_exists(inspector, "system_email_settings"): return uniques = {item["name"] for item in inspector.get_unique_constraints("system_email_settings")} if "uq_system_email_settings_register_domain" in uniques: op.drop_constraint( "uq_system_email_settings_register_domain", "system_email_settings", type_="unique", ) if _column_exists(inspector, "system_email_settings", "register_domain"): op.drop_column("system_email_settings", "register_domain")