@@ -101,6 +101,10 @@ class ServiceMigrationSpec:
101101 ),
102102 ColumnSpec ("hashed_password" , "sa.String()" , nullable = False ),
103103 ColumnSpec ("last_login" , "sa.DateTime()" , nullable = True ),
104+ ColumnSpec (
105+ "failed_login_attempts" , "sa.Integer()" , nullable = False , default = "0"
106+ ),
107+ ColumnSpec ("locked_until" , "sa.DateTime()" , nullable = True ),
104108 ColumnSpec ("created_at" , "sa.DateTime()" , nullable = False ),
105109 ColumnSpec ("updated_at" , "sa.DateTime()" , nullable = True ),
106110 ],
@@ -138,7 +142,10 @@ class ServiceMigrationSpec:
138142 ColumnSpec ("created_at" , "sa.DateTime()" , nullable = False ),
139143 ColumnSpec ("updated_at" , "sa.DateTime()" , nullable = True ),
140144 ],
141- indexes = [IndexSpec ("ix_organization_slug" , ["slug" ], unique = True )],
145+ indexes = [
146+ IndexSpec ("ix_organization_name" , ["name" ]),
147+ IndexSpec ("ix_organization_slug" , ["slug" ], unique = True ),
148+ ],
142149 ),
143150 TableSpec (
144151 name = "organization_member" ,
@@ -169,6 +176,30 @@ class ServiceMigrationSpec:
169176 ForeignKeySpec (["user_id" ], "user" , ["id" ]),
170177 ],
171178 ),
179+ TableSpec (
180+ name = "org_invite" ,
181+ columns = [
182+ ColumnSpec ("id" , "sa.Integer()" , nullable = False , primary_key = True ),
183+ ColumnSpec ("organization_id" , "sa.Integer()" , nullable = False ),
184+ ColumnSpec ("email" , "sa.String()" , nullable = False ),
185+ ColumnSpec ("role" , "sa.String()" , nullable = False , default = "'member'" ),
186+ ColumnSpec ("invited_by" , "sa.Integer()" , nullable = False ),
187+ ColumnSpec (
188+ "status" , "sa.String()" , nullable = False , default = "'pending'"
189+ ),
190+ ColumnSpec ("token" , "sa.String()" , nullable = False ),
191+ ColumnSpec ("created_at" , "sa.DateTime()" , nullable = False ),
192+ ],
193+ indexes = [
194+ IndexSpec ("ix_org_invite_email" , ["email" ]),
195+ IndexSpec ("ix_org_invite_org_id" , ["organization_id" ]),
196+ IndexSpec ("ix_org_invite_token" , ["token" ], unique = True ),
197+ ],
198+ foreign_keys = [
199+ ForeignKeySpec (["organization_id" ], "organization" , ["id" ]),
200+ ForeignKeySpec (["invited_by" ], "user" , ["id" ]),
201+ ],
202+ ),
172203 ],
173204)
174205
@@ -378,6 +409,47 @@ class ServiceMigrationSpec:
378409 ],
379410)
380411
412+ AUTH_TOKENS_MIGRATION = ServiceMigrationSpec (
413+ service_name = "auth_tokens" ,
414+ description = "Auth token tables (password reset, email verification)" ,
415+ tables = [
416+ TableSpec (
417+ name = "password_reset_token" ,
418+ columns = [
419+ ColumnSpec ("id" , "sa.Integer()" , nullable = False , primary_key = True ),
420+ ColumnSpec ("user_id" , "sa.Integer()" , nullable = False ),
421+ ColumnSpec ("token" , "sa.String()" , nullable = False ),
422+ ColumnSpec ("created_at" , "sa.DateTime()" , nullable = False ),
423+ ColumnSpec ("used" , "sa.Boolean()" , nullable = False , default = "'false'" ),
424+ ],
425+ indexes = [
426+ IndexSpec ("ix_password_reset_token_user_id" , ["user_id" ]),
427+ IndexSpec ("ix_password_reset_token_token" , ["token" ], unique = True ),
428+ ],
429+ foreign_keys = [
430+ ForeignKeySpec (["user_id" ], "user" , ["id" ]),
431+ ],
432+ ),
433+ TableSpec (
434+ name = "email_verification_token" ,
435+ columns = [
436+ ColumnSpec ("id" , "sa.Integer()" , nullable = False , primary_key = True ),
437+ ColumnSpec ("user_id" , "sa.Integer()" , nullable = False ),
438+ ColumnSpec ("token" , "sa.String()" , nullable = False ),
439+ ColumnSpec ("created_at" , "sa.DateTime()" , nullable = False ),
440+ ColumnSpec ("used" , "sa.Boolean()" , nullable = False , default = "'false'" ),
441+ ],
442+ indexes = [
443+ IndexSpec ("ix_email_verification_token_user_id" , ["user_id" ]),
444+ IndexSpec ("ix_email_verification_token_token" , ["token" ], unique = True ),
445+ ],
446+ foreign_keys = [
447+ ForeignKeySpec (["user_id" ], "user" , ["id" ]),
448+ ],
449+ ),
450+ ],
451+ )
452+
381453VOICE_MIGRATION = ServiceMigrationSpec (
382454 service_name = "ai_voice" ,
383455 description = "AI voice service table (TTS and STT usage tracking)" ,
@@ -442,6 +514,7 @@ class ServiceMigrationSpec:
442514 "auth" : AUTH_MIGRATION ,
443515 "auth_rbac" : AUTH_RBAC_MIGRATION ,
444516 "auth_org" : ORG_MIGRATION ,
517+ "auth_tokens" : AUTH_TOKENS_MIGRATION ,
445518 "ai" : AI_MIGRATION ,
446519 "ai_voice" : VOICE_MIGRATION ,
447520}
@@ -767,6 +840,10 @@ def get_services_needing_migrations(context: dict[str, Any]) -> list[str]:
767840 if include_auth == "yes" or include_auth is True :
768841 services .append ("auth" )
769842
843+ # Auth token tables (password reset, email verification) - always with auth
844+ if include_auth == "yes" or include_auth is True :
845+ services .append ("auth_tokens" )
846+
770847 # Auth RBAC columns (rbac or org level)
771848 include_auth_rbac = context .get ("include_auth_rbac" )
772849 auth_level = context .get ("auth_level" )
0 commit comments