Files
ctms/backend/alembic/versions/20260629_03_email_settings_per_domain.py
2026-06-30 09:44:24 +08:00

124 lines
4.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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")