|
14 | 14 | validate_copier_project, |
15 | 15 | validate_git_repository, |
16 | 16 | ) |
17 | | -from ..constants import AnswerKeys, ComponentNames, Messages, StorageBackends |
| 17 | +from ..constants import ( |
| 18 | + AnswerKeys, |
| 19 | + AuthLevels, |
| 20 | + ComponentNames, |
| 21 | + Messages, |
| 22 | + StorageBackends, |
| 23 | +) |
| 24 | +from ..core.auth_service_parser import ( |
| 25 | + is_auth_service_with_options, |
| 26 | + parse_auth_service_config, |
| 27 | +) |
18 | 28 | from ..core.component_utils import ( |
19 | 29 | extract_base_component_name, |
20 | 30 | extract_base_service_name, |
|
26 | 36 | MIGRATION_SPECS, |
27 | 37 | bootstrap_alembic, |
28 | 38 | generate_migration, |
| 39 | + get_services_needing_migrations, |
29 | 40 | service_has_migration, |
30 | 41 | ) |
31 | 42 | from ..core.project_map import render_project_map |
@@ -162,6 +173,24 @@ def add_service_command( |
162 | 173 | base_service = service_base_map[service] |
163 | 174 | include_key = AnswerKeys.include_key(base_service) |
164 | 175 | if existing_answers.get(include_key) is True: |
| 176 | + # Special case: auth level upgrades (basic → rbac → org) |
| 177 | + if base_service == AnswerKeys.SERVICE_AUTH and is_auth_service_with_options( |
| 178 | + service |
| 179 | + ): |
| 180 | + auth_config = parse_auth_service_config(service) |
| 181 | + current_level = existing_answers.get( |
| 182 | + AnswerKeys.AUTH_LEVEL, AuthLevels.BASIC |
| 183 | + ) |
| 184 | + level_order = { |
| 185 | + AuthLevels.BASIC: 0, |
| 186 | + AuthLevels.RBAC: 1, |
| 187 | + AuthLevels.ORG: 2, |
| 188 | + } |
| 189 | + if level_order.get(auth_config.level, 0) > level_order.get( |
| 190 | + current_level, 0 |
| 191 | + ): |
| 192 | + # Upgrading auth level — don't skip |
| 193 | + continue |
165 | 194 | already_enabled.append(service) |
166 | 195 |
|
167 | 196 | if already_enabled: |
@@ -362,6 +391,18 @@ def add_service_command( |
362 | 391 | # Get base service name (strips variant syntax like [langchain,sqlite]) |
363 | 392 | base_service = service_base_map[service] |
364 | 393 |
|
| 394 | + # For auth service, pass auth level data |
| 395 | + if base_service == AnswerKeys.SERVICE_AUTH and is_auth_service_with_options( |
| 396 | + service |
| 397 | + ): |
| 398 | + auth_config = parse_auth_service_config(service) |
| 399 | + service_data[AnswerKeys.AUTH_LEVEL] = auth_config.level |
| 400 | + service_data[AnswerKeys.AUTH_RBAC] = auth_config.level in ( |
| 401 | + AuthLevels.RBAC, |
| 402 | + AuthLevels.ORG, |
| 403 | + ) |
| 404 | + service_data[AnswerKeys.AUTH_ORG] = auth_config.level == AuthLevels.ORG |
| 405 | + |
365 | 406 | # For AI service, use the captured configuration |
366 | 407 | if base_service == AnswerKeys.SERVICE_AI: |
367 | 408 | # Use providers from interactive config, or default to openai |
@@ -441,6 +482,21 @@ def add_service_command( |
441 | 482 | fg="green", |
442 | 483 | ) |
443 | 484 |
|
| 485 | + # For auth level upgrades, generate any missing level-specific migrations |
| 486 | + # (e.g., auth_rbac, auth_org, auth_tokens) |
| 487 | + updated_answers = updater.answers |
| 488 | + needed_migrations = get_services_needing_migrations(updated_answers) |
| 489 | + for migration_service in needed_migrations: |
| 490 | + if migration_service in MIGRATION_SPECS and not service_has_migration( |
| 491 | + target_path, migration_service |
| 492 | + ): |
| 493 | + migration_path = generate_migration(target_path, migration_service) |
| 494 | + if migration_path: |
| 495 | + typer.secho( |
| 496 | + f" {t('add_service.generated_migration', name=migration_path.name)}", |
| 497 | + fg="green", |
| 498 | + ) |
| 499 | + |
444 | 500 | # Auto-run migrations for services that need them |
445 | 501 | # Exclude AI service with memory backend (doesn't need migrations) |
446 | 502 | ai_needs_migrations = ( |
|
0 commit comments