|
| 1 | +"""normalize user tables |
| 2 | +
|
| 3 | +Revision ID: e73c215d7221 |
| 4 | +Revises: d9a7c3f2b6e1 |
| 5 | +Create Date: 2026-06-22 11:45:27.978571 |
| 6 | +
|
| 7 | +""" |
| 8 | +from typing import Sequence, Union |
| 9 | + |
| 10 | +from alembic import op |
| 11 | +import sqlalchemy as sa |
| 12 | +from sqlalchemy.dialects import postgresql |
| 13 | + |
| 14 | +# revision identifiers, used by Alembic. |
| 15 | +revision: str = 'e73c215d7221' |
| 16 | +down_revision: Union[str, Sequence[str], None] = 'd9a7c3f2b6e1' |
| 17 | +branch_labels: Union[str, Sequence[str], None] = None |
| 18 | +depends_on: Union[str, Sequence[str], None] = None |
| 19 | + |
| 20 | + |
| 21 | +def upgrade() -> None: |
| 22 | + """Upgrade schema.""" |
| 23 | + # ### commands auto generated by Alembic - please adjust! ### |
| 24 | + op.create_table('user_addresses', |
| 25 | + sa.Column('id', sa.Uuid(), nullable=False), |
| 26 | + sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), |
| 27 | + sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), |
| 28 | + sa.Column('deleted_at', sa.DateTime(), nullable=True), |
| 29 | + sa.Column('user_id', sa.String(length=36), nullable=False), |
| 30 | + sa.Column('label', sa.String(length=100), nullable=False), |
| 31 | + sa.Column('line1', sa.String(length=255), nullable=False), |
| 32 | + sa.Column('line2', sa.String(length=255), nullable=True), |
| 33 | + sa.Column('line3', sa.String(length=255), nullable=True), |
| 34 | + sa.Column('city', sa.String(length=100), nullable=False), |
| 35 | + sa.Column('state', sa.String(length=100), nullable=True), |
| 36 | + sa.Column('postal_code', sa.String(length=20), nullable=False), |
| 37 | + sa.Column('country', sa.String(length=2), nullable=False), |
| 38 | + sa.Column('is_default', sa.Boolean(), nullable=False), |
| 39 | + sa.PrimaryKeyConstraint('id') |
| 40 | + ) |
| 41 | + op.create_index('ix_user_addresses_country', 'user_addresses', ['country'], unique=False) |
| 42 | + op.create_index('ix_user_addresses_is_default', 'user_addresses', ['is_default'], unique=False) |
| 43 | + op.create_index('ix_user_addresses_label', 'user_addresses', ['label'], unique=False) |
| 44 | + op.create_index('ix_user_addresses_user_id', 'user_addresses', ['user_id'], unique=False) |
| 45 | + op.create_table('user_contacts', |
| 46 | + sa.Column('id', sa.Uuid(), nullable=False), |
| 47 | + sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), |
| 48 | + sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), |
| 49 | + sa.Column('deleted_at', sa.DateTime(), nullable=True), |
| 50 | + sa.Column('user_id', sa.String(length=36), nullable=False), |
| 51 | + sa.Column('contact_type', sa.String(length=50), nullable=False), |
| 52 | + sa.Column('value', sa.String(length=255), nullable=False), |
| 53 | + sa.Column('is_primary', sa.Boolean(), nullable=False), |
| 54 | + sa.Column('is_verified', sa.Boolean(), nullable=False), |
| 55 | + sa.PrimaryKeyConstraint('id') |
| 56 | + ) |
| 57 | + op.create_index('ix_user_contacts_is_primary', 'user_contacts', ['is_primary'], unique=False) |
| 58 | + op.create_index('ix_user_contacts_type', 'user_contacts', ['contact_type'], unique=False) |
| 59 | + op.create_index('ix_user_contacts_user_id', 'user_contacts', ['user_id'], unique=False) |
| 60 | + op.create_table('user_profiles', |
| 61 | + sa.Column('id', sa.Uuid(), nullable=False), |
| 62 | + sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), |
| 63 | + sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), |
| 64 | + sa.Column('deleted_at', sa.DateTime(), nullable=True), |
| 65 | + sa.Column('user_id', sa.String(length=36), nullable=False), |
| 66 | + sa.Column('first_name', sa.String(length=100), nullable=True), |
| 67 | + sa.Column('last_name', sa.String(length=100), nullable=True), |
| 68 | + sa.Column('display_name', sa.String(length=255), nullable=True), |
| 69 | + sa.Column('avatar_url', sa.String(length=500), nullable=True), |
| 70 | + sa.Column('bio', sa.Text(), nullable=True), |
| 71 | + sa.Column('birth_date', sa.Date(), nullable=True), |
| 72 | + sa.PrimaryKeyConstraint('id'), |
| 73 | + sa.UniqueConstraint('user_id') |
| 74 | + ) |
| 75 | + op.create_index('ix_user_profiles_user_id', 'user_profiles', ['user_id'], unique=True) |
| 76 | + op.create_table('user_security', |
| 77 | + sa.Column('id', sa.Uuid(), nullable=False), |
| 78 | + sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), |
| 79 | + sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), |
| 80 | + sa.Column('deleted_at', sa.DateTime(), nullable=True), |
| 81 | + sa.Column('user_id', sa.String(length=36), nullable=False), |
| 82 | + sa.Column('failed_login_attempts', sa.Integer(), nullable=False), |
| 83 | + sa.Column('locked_until', sa.DateTime(timezone=True), nullable=True), |
| 84 | + sa.Column('password_changed_at', sa.DateTime(timezone=True), nullable=True), |
| 85 | + sa.Column('two_factor_enabled', sa.Boolean(), nullable=False), |
| 86 | + sa.Column('two_factor_secret', sa.String(length=255), nullable=True), |
| 87 | + sa.Column('two_factor_backup_codes', sa.String(length=1000), nullable=True), |
| 88 | + sa.PrimaryKeyConstraint('id'), |
| 89 | + sa.UniqueConstraint('user_id') |
| 90 | + ) |
| 91 | + op.create_index('ix_user_security_locked_until', 'user_security', ['locked_until'], unique=False) |
| 92 | + op.create_index('ix_user_security_two_factor_enabled', 'user_security', ['two_factor_enabled'], unique=False) |
| 93 | + op.create_index('ix_user_security_user_id', 'user_security', ['user_id'], unique=True) |
| 94 | + op.create_table('user_sessions', |
| 95 | + sa.Column('id', sa.Uuid(), nullable=False), |
| 96 | + sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), |
| 97 | + sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), |
| 98 | + sa.Column('deleted_at', sa.DateTime(), nullable=True), |
| 99 | + sa.Column('user_id', sa.String(length=36), nullable=False), |
| 100 | + sa.Column('refresh_token_hash', sa.String(length=255), nullable=False), |
| 101 | + sa.Column('expires_at', sa.DateTime(timezone=True), nullable=False), |
| 102 | + sa.Column('device_info', sa.String(length=500), nullable=True), |
| 103 | + sa.Column('ip_address', sa.String(length=45), nullable=True), |
| 104 | + sa.Column('user_agent', sa.String(length=1000), nullable=True), |
| 105 | + sa.Column('is_revoked', sa.Boolean(), nullable=False), |
| 106 | + sa.Column('revoked_at', sa.DateTime(timezone=True), nullable=True), |
| 107 | + sa.Column('revoked_reason', sa.String(length=255), nullable=True), |
| 108 | + sa.PrimaryKeyConstraint('id') |
| 109 | + ) |
| 110 | + op.create_index('ix_user_sessions_device_info', 'user_sessions', ['device_info'], unique=False) |
| 111 | + op.create_index('ix_user_sessions_expires_at', 'user_sessions', ['expires_at'], unique=False) |
| 112 | + op.create_index('ix_user_sessions_is_revoked', 'user_sessions', ['is_revoked'], unique=False) |
| 113 | + op.create_index('ix_user_sessions_token_hash', 'user_sessions', ['refresh_token_hash'], unique=True) |
| 114 | + op.create_index('ix_user_sessions_user_id', 'user_sessions', ['user_id'], unique=False) |
| 115 | + op.create_table('user_settings', |
| 116 | + sa.Column('id', sa.Uuid(), nullable=False), |
| 117 | + sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), |
| 118 | + sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), |
| 119 | + sa.Column('deleted_at', sa.DateTime(), nullable=True), |
| 120 | + sa.Column('user_id', sa.String(length=36), nullable=False), |
| 121 | + sa.Column('preferences', postgresql.JSONB(astext_type=sa.Text()), nullable=False), |
| 122 | + sa.PrimaryKeyConstraint('id'), |
| 123 | + sa.UniqueConstraint('user_id') |
| 124 | + ) |
| 125 | + op.create_index('ix_user_settings_preferences', 'user_settings', ['preferences'], unique=False, postgresql_using='gin') |
| 126 | + op.create_index('ix_user_settings_user_id', 'user_settings', ['user_id'], unique=True) |
| 127 | + op.create_table('user_verifications', |
| 128 | + sa.Column('id', sa.Uuid(), nullable=False), |
| 129 | + sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), |
| 130 | + sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), |
| 131 | + sa.Column('deleted_at', sa.DateTime(), nullable=True), |
| 132 | + sa.Column('user_id', sa.String(length=36), nullable=False), |
| 133 | + sa.Column('channel', sa.String(length=50), nullable=False), |
| 134 | + sa.Column('is_verified', sa.Boolean(), nullable=False), |
| 135 | + sa.Column('verified_at', sa.DateTime(timezone=True), nullable=True), |
| 136 | + sa.Column('verification_token', sa.String(length=255), nullable=True), |
| 137 | + sa.Column('token_expires_at', sa.DateTime(timezone=True), nullable=True), |
| 138 | + sa.PrimaryKeyConstraint('id') |
| 139 | + ) |
| 140 | + op.create_index('ix_user_verifications_channel', 'user_verifications', ['channel'], unique=False) |
| 141 | + op.create_index('ix_user_verifications_is_verified', 'user_verifications', ['is_verified'], unique=False) |
| 142 | + op.create_index('ix_user_verifications_token', 'user_verifications', ['verification_token'], unique=False) |
| 143 | + op.create_index('ix_user_verifications_user_id', 'user_verifications', ['user_id'], unique=False) |
| 144 | + op.drop_index(op.f('ix_refresh_tokens_token_hash'), table_name='refresh_tokens') |
| 145 | + op.drop_index(op.f('ix_refresh_tokens_user_id'), table_name='refresh_tokens') |
| 146 | + op.drop_table('refresh_tokens') |
| 147 | + op.create_unique_constraint(None, 'authorization_resources', ['key']) |
| 148 | + op.alter_column('permissions', 'resource_id', |
| 149 | + existing_type=sa.UUID(), |
| 150 | + nullable=False) |
| 151 | + op.create_unique_constraint(None, 'permissions', ['key']) |
| 152 | + op.drop_constraint(op.f('fk_permissions_resource_id_authorization_resources'), 'permissions', type_='foreignkey') |
| 153 | + op.drop_constraint(op.f('role_permissions_permission_id_fkey'), 'role_permissions', type_='foreignkey') |
| 154 | + op.drop_constraint(op.f('role_permissions_role_id_fkey'), 'role_permissions', type_='foreignkey') |
| 155 | + op.create_unique_constraint(None, 'roles', ['name']) |
| 156 | + op.drop_constraint(op.f('todos_user_id_fkey'), 'todos', type_='foreignkey') |
| 157 | + op.drop_constraint(op.f('user_has_roles_user_id_fkey'), 'user_has_roles', type_='foreignkey') |
| 158 | + op.drop_constraint(op.f('user_has_roles_role_id_fkey'), 'user_has_roles', type_='foreignkey') |
| 159 | + op.add_column('users', sa.Column('password_hash', sa.String(length=255), nullable=True)) |
| 160 | + op.add_column('users', sa.Column('auth_provider', sa.String(length=50), nullable=False)) |
| 161 | + op.add_column('users', sa.Column('external_id', sa.String(length=255), nullable=True)) |
| 162 | + op.add_column('users', sa.Column('status', sa.String(length=50), nullable=False)) |
| 163 | + op.alter_column('users', 'username', |
| 164 | + existing_type=sa.VARCHAR(length=255), |
| 165 | + type_=sa.String(length=100), |
| 166 | + existing_nullable=True) |
| 167 | + op.create_index('ix_users_auth_provider', 'users', ['auth_provider'], unique=False) |
| 168 | + op.create_index('ix_users_status', 'users', ['status'], unique=False) |
| 169 | + op.create_index(op.f('ix_users_username'), 'users', ['username'], unique=True) |
| 170 | + op.drop_column('users', 'password') |
| 171 | + op.drop_column('users', 'fullname') |
| 172 | + op.drop_column('users', 'birthday') |
| 173 | + # ### end Alembic commands ### |
| 174 | + |
| 175 | + |
| 176 | +def downgrade() -> None: |
| 177 | + """Downgrade schema.""" |
| 178 | + # ### commands auto generated by Alembic - please adjust! ### |
| 179 | + op.add_column('users', sa.Column('birthday', sa.DATE(), autoincrement=False, nullable=True)) |
| 180 | + op.add_column('users', sa.Column('fullname', sa.VARCHAR(length=255), autoincrement=False, nullable=True)) |
| 181 | + op.add_column('users', sa.Column('password', sa.VARCHAR(length=255), autoincrement=False, nullable=False)) |
| 182 | + op.drop_index(op.f('ix_users_username'), table_name='users') |
| 183 | + op.drop_index('ix_users_status', table_name='users') |
| 184 | + op.drop_index('ix_users_auth_provider', table_name='users') |
| 185 | + op.alter_column('users', 'username', |
| 186 | + existing_type=sa.String(length=100), |
| 187 | + type_=sa.VARCHAR(length=255), |
| 188 | + existing_nullable=True) |
| 189 | + op.drop_column('users', 'status') |
| 190 | + op.drop_column('users', 'external_id') |
| 191 | + op.drop_column('users', 'auth_provider') |
| 192 | + op.drop_column('users', 'password_hash') |
| 193 | + op.create_foreign_key(op.f('user_has_roles_role_id_fkey'), 'user_has_roles', 'roles', ['role_id'], ['id']) |
| 194 | + op.create_foreign_key(op.f('user_has_roles_user_id_fkey'), 'user_has_roles', 'users', ['user_id'], ['id']) |
| 195 | + op.create_foreign_key(op.f('todos_user_id_fkey'), 'todos', 'users', ['user_id'], ['id']) |
| 196 | + op.drop_constraint(None, 'roles', type_='unique') |
| 197 | + op.create_foreign_key(op.f('role_permissions_role_id_fkey'), 'role_permissions', 'roles', ['role_id'], ['id']) |
| 198 | + op.create_foreign_key(op.f('role_permissions_permission_id_fkey'), 'role_permissions', 'permissions', ['permission_id'], ['id']) |
| 199 | + op.create_foreign_key(op.f('fk_permissions_resource_id_authorization_resources'), 'permissions', 'authorization_resources', ['resource_id'], ['id']) |
| 200 | + op.drop_constraint(None, 'permissions', type_='unique') |
| 201 | + op.alter_column('permissions', 'resource_id', |
| 202 | + existing_type=sa.UUID(), |
| 203 | + nullable=True) |
| 204 | + op.drop_constraint(None, 'authorization_resources', type_='unique') |
| 205 | + op.create_table('refresh_tokens', |
| 206 | + sa.Column('id', sa.UUID(), autoincrement=False, nullable=False), |
| 207 | + sa.Column('created_at', postgresql.TIMESTAMP(timezone=True), server_default=sa.text('now()'), autoincrement=False, nullable=False), |
| 208 | + sa.Column('updated_at', postgresql.TIMESTAMP(timezone=True), server_default=sa.text('now()'), autoincrement=False, nullable=False), |
| 209 | + sa.Column('deleted_at', postgresql.TIMESTAMP(), autoincrement=False, nullable=True), |
| 210 | + sa.Column('user_id', sa.UUID(), autoincrement=False, nullable=False), |
| 211 | + sa.Column('token_hash', sa.VARCHAR(length=255), autoincrement=False, nullable=False), |
| 212 | + sa.Column('expires_at', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=False), |
| 213 | + sa.Column('is_revoked', sa.BOOLEAN(), autoincrement=False, nullable=False), |
| 214 | + sa.ForeignKeyConstraint(['user_id'], ['users.id'], name=op.f('refresh_tokens_user_id_fkey')), |
| 215 | + sa.PrimaryKeyConstraint('id', name=op.f('refresh_tokens_pkey')) |
| 216 | + ) |
| 217 | + op.create_index(op.f('ix_refresh_tokens_user_id'), 'refresh_tokens', ['user_id'], unique=False) |
| 218 | + op.create_index(op.f('ix_refresh_tokens_token_hash'), 'refresh_tokens', ['token_hash'], unique=True) |
| 219 | + op.drop_index('ix_user_verifications_user_id', table_name='user_verifications') |
| 220 | + op.drop_index('ix_user_verifications_token', table_name='user_verifications') |
| 221 | + op.drop_index('ix_user_verifications_is_verified', table_name='user_verifications') |
| 222 | + op.drop_index('ix_user_verifications_channel', table_name='user_verifications') |
| 223 | + op.drop_table('user_verifications') |
| 224 | + op.drop_index('ix_user_settings_user_id', table_name='user_settings') |
| 225 | + op.drop_index('ix_user_settings_preferences', table_name='user_settings', postgresql_using='gin') |
| 226 | + op.drop_table('user_settings') |
| 227 | + op.drop_index('ix_user_sessions_user_id', table_name='user_sessions') |
| 228 | + op.drop_index('ix_user_sessions_token_hash', table_name='user_sessions') |
| 229 | + op.drop_index('ix_user_sessions_is_revoked', table_name='user_sessions') |
| 230 | + op.drop_index('ix_user_sessions_expires_at', table_name='user_sessions') |
| 231 | + op.drop_index('ix_user_sessions_device_info', table_name='user_sessions') |
| 232 | + op.drop_table('user_sessions') |
| 233 | + op.drop_index('ix_user_security_user_id', table_name='user_security') |
| 234 | + op.drop_index('ix_user_security_two_factor_enabled', table_name='user_security') |
| 235 | + op.drop_index('ix_user_security_locked_until', table_name='user_security') |
| 236 | + op.drop_table('user_security') |
| 237 | + op.drop_index('ix_user_profiles_user_id', table_name='user_profiles') |
| 238 | + op.drop_table('user_profiles') |
| 239 | + op.drop_index('ix_user_contacts_user_id', table_name='user_contacts') |
| 240 | + op.drop_index('ix_user_contacts_type', table_name='user_contacts') |
| 241 | + op.drop_index('ix_user_contacts_is_primary', table_name='user_contacts') |
| 242 | + op.drop_table('user_contacts') |
| 243 | + op.drop_index('ix_user_addresses_user_id', table_name='user_addresses') |
| 244 | + op.drop_index('ix_user_addresses_label', table_name='user_addresses') |
| 245 | + op.drop_index('ix_user_addresses_is_default', table_name='user_addresses') |
| 246 | + op.drop_index('ix_user_addresses_country', table_name='user_addresses') |
| 247 | + op.drop_table('user_addresses') |
| 248 | + # ### end Alembic commands ### |
0 commit comments