"""add_password_reset_table Revision ID: d103d442dc24 Revises: 0f96348cdfd2 Create Date: 2025-08-22 08:27:58.468119 """ from __future__ import annotations from collections.abc import Sequence from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import mysql # revision identifiers, used by Alembic. revision: str = "d103d442dc24" down_revision: str | Sequence[str] | None = "0f96348cdfd2" branch_labels: str | Sequence[str] | None = None depends_on: str | Sequence[str] | None = None def upgrade() -> None: """Upgrade schema.""" # ### commands auto generated by Alembic - please adjust! ### # 安全创建 password_resets 表(如果不存在) try: op.create_table( "password_resets", sa.Column("id", sa.Integer(), nullable=False), sa.Column("user_id", sa.BigInteger(), nullable=False), sa.Column("email", sa.String(255), nullable=False), sa.Column("reset_code", sa.String(8), nullable=False), sa.Column("created_at", sa.DateTime(), nullable=False), sa.Column("expires_at", sa.DateTime(), nullable=False), sa.Column("is_used", sa.Boolean(), nullable=False), sa.Column("used_at", sa.DateTime(), nullable=True), sa.Column("ip_address", sa.String(255), nullable=True), sa.Column("user_agent", sa.String(255), nullable=True), sa.ForeignKeyConstraint( ["user_id"], ["lazer_users.id"], ), sa.PrimaryKeyConstraint("id"), ) except Exception: # 如果表已存在,继续执行 pass # 安全创建索引 try: op.create_index(op.f("ix_password_resets_email"), "password_resets", ["email"], unique=False) except Exception: pass try: op.create_index( op.f("ix_password_resets_user_id"), "password_resets", ["user_id"], unique=False, ) except Exception: pass # 安全删除 two_factor_auth 表 - 先删除表(这会自动删除外键约束和索引) try: op.drop_table("two_factor_auth") except Exception: # 如果表不存在,继续执行 pass # 安全删除 user_ip_history 表 try: op.drop_table("user_ip_history") except Exception: # 如果表不存在,继续执行 pass # 安全删除 session_verification 表 try: op.drop_table("session_verification") except Exception: # 如果表不存在,继续执行 pass op.alter_column( "beatmapsets", "nsfw", existing_type=mysql.TINYINT(display_width=1), nullable=True, ) op.alter_column( "beatmapsets", "spotlight", existing_type=mysql.TINYINT(display_width=1), nullable=True, ) op.alter_column( "beatmapsets", "video", existing_type=mysql.TINYINT(display_width=1), nullable=True, ) op.alter_column( "beatmapsets", "can_be_hyped", existing_type=mysql.TINYINT(display_width=1), nullable=True, ) op.alter_column( "beatmapsets", "discussion_locked", existing_type=mysql.TINYINT(display_width=1), nullable=True, ) op.alter_column( "beatmapsets", "storyboard", existing_type=mysql.TINYINT(display_width=1), nullable=True, ) op.alter_column( "beatmapsets", "download_disabled", existing_type=mysql.TINYINT(display_width=1), nullable=True, ) # 安全删除索引 try: op.drop_index(op.f("uq_user_achievement"), table_name="lazer_user_achievements") except Exception: # 如果索引不存在或有外键约束,继续执行 pass op.alter_column( "scores", "has_replay", existing_type=mysql.TINYINT(display_width=1), nullable=True, ) op.alter_column("scores", "passed", existing_type=mysql.TINYINT(display_width=1), nullable=True) op.alter_column( "scores", "preserve", existing_type=mysql.TINYINT(display_width=1), nullable=True, ) # ### end Alembic commands ### def downgrade() -> None: """Downgrade schema.""" # ### commands auto generated by Alembic - please adjust! ### op.alter_column( "scores", "preserve", existing_type=mysql.TINYINT(display_width=1), nullable=False, ) op.alter_column("scores", "passed", existing_type=mysql.TINYINT(display_width=1), nullable=False) op.alter_column( "scores", "has_replay", existing_type=mysql.TINYINT(display_width=1), nullable=False, ) op.create_index( op.f("uq_user_achievement"), "lazer_user_achievements", ["user_id", "achievement_id"], unique=True, ) op.alter_column( "beatmapsets", "download_disabled", existing_type=mysql.TINYINT(display_width=1), nullable=False, ) op.alter_column( "beatmapsets", "storyboard", existing_type=mysql.TINYINT(display_width=1), nullable=False, ) op.alter_column( "beatmapsets", "discussion_locked", existing_type=mysql.TINYINT(display_width=1), nullable=False, ) op.alter_column( "beatmapsets", "can_be_hyped", existing_type=mysql.TINYINT(display_width=1), nullable=False, ) op.alter_column( "beatmapsets", "video", existing_type=mysql.TINYINT(display_width=1), nullable=False, ) op.alter_column( "beatmapsets", "spotlight", existing_type=mysql.TINYINT(display_width=1), nullable=False, ) op.alter_column( "beatmapsets", "nsfw", existing_type=mysql.TINYINT(display_width=1), nullable=False, ) op.create_table( "session_verification", sa.Column("id", mysql.BIGINT(), autoincrement=True, nullable=False), sa.Column("user_id", mysql.BIGINT(), autoincrement=False, nullable=False), sa.Column("session_id", mysql.VARCHAR(length=255), nullable=False), sa.Column("ip_address", mysql.VARCHAR(length=45), nullable=False), sa.Column( "is_verified", mysql.TINYINT(display_width=1), autoincrement=False, nullable=False, ), sa.Column("verified_at", mysql.DATETIME(), nullable=True), sa.Column("expires_at", mysql.DATETIME(), nullable=False), sa.ForeignKeyConstraint(["user_id"], ["lazer_users.id"], name=op.f("session_verification_ibfk_1")), sa.PrimaryKeyConstraint("id"), mysql_collate="utf8mb4_0900_ai_ci", mysql_default_charset="utf8mb4", mysql_engine="InnoDB", ) op.create_index( op.f("ix_session_verification_user_id"), "session_verification", ["user_id"], unique=False, ) op.create_table( "user_ip_history", sa.Column("id", mysql.BIGINT(), autoincrement=True, nullable=False), sa.Column("user_id", mysql.BIGINT(), autoincrement=False, nullable=False), sa.Column("ip_address", mysql.VARCHAR(length=45), nullable=False), sa.Column("first_seen", mysql.DATETIME(), nullable=False), sa.Column("last_seen", mysql.DATETIME(), nullable=False), sa.Column("usage_count", mysql.INTEGER(), autoincrement=False, nullable=False), sa.Column( "is_trusted", mysql.TINYINT(display_width=1), autoincrement=False, nullable=False, ), sa.ForeignKeyConstraint(["user_id"], ["lazer_users.id"], name=op.f("user_ip_history_ibfk_1")), sa.PrimaryKeyConstraint("id"), mysql_collate="utf8mb4_0900_ai_ci", mysql_default_charset="utf8mb4", mysql_engine="InnoDB", ) op.create_index(op.f("ix_user_ip_history_user_id"), "user_ip_history", ["user_id"], unique=False) op.create_index( op.f("ix_user_ip_history_ip_address"), "user_ip_history", ["ip_address"], unique=False, ) op.create_index(op.f("ix_user_ip_history_id"), "user_ip_history", ["id"], unique=False) op.create_table( "two_factor_auth", sa.Column("id", mysql.BIGINT(), autoincrement=True, nullable=False), sa.Column("user_id", mysql.BIGINT(), autoincrement=False, nullable=False), sa.Column("verification_code", mysql.VARCHAR(length=8), nullable=False), sa.Column("expires_at", mysql.DATETIME(), nullable=False), sa.Column( "is_used", mysql.TINYINT(display_width=1), autoincrement=False, nullable=False, ), sa.Column("ip_address", mysql.VARCHAR(length=45), nullable=False), sa.Column("trigger_reason", mysql.VARCHAR(length=50), nullable=False), sa.Column("verified_at", mysql.DATETIME(), nullable=True), sa.ForeignKeyConstraint(["user_id"], ["lazer_users.id"], name=op.f("two_factor_auth_ibfk_1")), sa.PrimaryKeyConstraint("id"), mysql_collate="utf8mb4_0900_ai_ci", mysql_default_charset="utf8mb4", mysql_engine="InnoDB", ) op.create_index(op.f("ix_two_factor_auth_user_id"), "two_factor_auth", ["user_id"], unique=False) op.create_index(op.f("ix_two_factor_auth_id"), "two_factor_auth", ["id"], unique=False) op.drop_index(op.f("ix_password_resets_user_id"), table_name="password_resets") op.drop_index(op.f("ix_password_resets_email"), table_name="password_resets") op.drop_table("password_resets") # ### end Alembic commands ###