fix: add project_manager migration + add migration step to full-deploy.sh

- New migration updates MongoDB users collection validator to accept
  project_manager role and pm_client_ids field
- full-deploy.sh was missing the run_migrations step entirely; added it
  after rebuild_containers so new role/field validators apply on every deploy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vadym Samoilenko 2026-04-27 16:05:17 +01:00
parent bbd324e3c7
commit 723bbbc695
2 changed files with 160 additions and 0 deletions

View file

@ -0,0 +1,142 @@
"""Add project_manager role to user collection schema validator."""
from app.migrations.migrator import Migration
class Migration(Migration):
"""Update MongoDB schema validator to support project_manager role."""
def __init__(self):
super().__init__()
self.version = "2026-04-27-000000"
self.description = "Add project_manager role to user schema validator"
async def up(self) -> None:
"""Update the users collection validator."""
validator = {
"$jsonSchema": {
"bsonType": "object",
"required": ["email", "full_name", "role", "auth_provider", "is_active"],
"properties": {
"email": {
"bsonType": "string",
"pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
"description": "Must be a valid email address"
},
"hashed_password": {
"bsonType": ["string", "null"],
"description": "Hashed password (null for Microsoft users)"
},
"full_name": {
"bsonType": "string",
"minLength": 1,
"description": "User's full name"
},
"role": {
"enum": ["client", "reviewer", "linguist", "production", "project_manager", "admin"],
"description": "User role"
},
"auth_provider": {
"enum": ["local", "microsoft"],
"description": "Authentication provider"
},
"is_active": {
"bsonType": "bool",
"description": "Whether user account is active"
},
"pm_client_ids": {
"bsonType": ["array"],
"items": {"bsonType": "string"},
"description": "Client IDs where user is assigned as Project Manager"
},
"created_at": {
"bsonType": "date",
"description": "Account creation timestamp"
},
"updated_at": {
"bsonType": "date",
"description": "Last update timestamp"
}
}
}
}
try:
await self.db.command({
"collMod": "users",
"validator": validator,
"validationLevel": "moderate",
"validationAction": "error"
})
print(f"✅ Updated users collection validator")
except Exception as e:
print(f"⚠️ Could not update validator: {e}")
try:
await self.db.create_collection(
"users",
validator=validator,
validationLevel="moderate",
validationAction="error"
)
print(f"✅ Created users collection with validator")
except Exception as e2:
print(f"⚠️ Could not create collection: {e2}")
print(f"✅ Applied migration {self.version}: {self.description}")
async def down(self) -> None:
"""Revert to validator without project_manager role."""
validator = {
"$jsonSchema": {
"bsonType": "object",
"required": ["email", "full_name", "role", "auth_provider", "is_active"],
"properties": {
"email": {
"bsonType": "string",
"pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
"description": "Must be a valid email address"
},
"hashed_password": {
"bsonType": ["string", "null"],
"description": "Hashed password (null for Microsoft users)"
},
"full_name": {
"bsonType": "string",
"minLength": 1,
"description": "User's full name"
},
"role": {
"enum": ["client", "reviewer", "linguist", "production", "admin"],
"description": "User role"
},
"auth_provider": {
"enum": ["local", "microsoft"],
"description": "Authentication provider"
},
"is_active": {
"bsonType": "bool",
"description": "Whether user account is active"
},
"created_at": {
"bsonType": "date",
"description": "Account creation timestamp"
},
"updated_at": {
"bsonType": "date",
"description": "Last update timestamp"
}
}
}
}
await self.db.command({
"collMod": "users",
"validator": validator,
"validationLevel": "moderate",
"validationAction": "error"
})
print(f"⚠️ Rolled back migration {self.version}: {self.description}")
print(f"⚠️ WARNING: project_manager role users will fail validation!")

View file

@ -246,6 +246,23 @@ build_and_deploy_frontend() {
echo ""
}
# =============================================================================
# Run Database Migrations
# =============================================================================
run_migrations() {
print_header "Running Database Migrations"
print_info "Checking migration status..."
docker compose $COMPOSE_FILES exec -T api python migrate.py status
print_info "Applying pending migrations..."
docker compose $COMPOSE_FILES exec -T api python migrate.py up
print_success "Migrations completed"
echo ""
}
# =============================================================================
# Display Deployment Summary
# =============================================================================
@ -335,6 +352,7 @@ main() {
preflight_checks
verify_code_updated
rebuild_containers
run_migrations
build_and_deploy_frontend
display_summary
fi