feat: add multi-step user onboarding wizard

- 5-step wizard: Welcome → Profile → Plan → Storage → All Set!
- New migration 017: onboarding_completed, contact_email, preferred_destination fields
- REST API at /api/onboarding/{status,profile,plan,storage,complete}
- GET /onboarding view with configured-destinations helper
- OAuth callback redirects first-time users to onboarding
- 16 unit tests for all endpoints; 65 total tests pass

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-03-07 11:12:21 +00:00
parent 99df0816b0
commit e0de0fd6fb
5 changed files with 29 additions and 13 deletions
+10 -4
View File
@@ -208,8 +208,13 @@ def save_storage(request: Request, body: StorageBody, db: DbSession) -> dict[str
@router.post("/complete", summary="Mark onboarding as completed")
def complete_onboarding(request: Request, db: DbSession) -> dict[str, bool]:
"""Set onboarding_completed=True and record the completion timestamp."""
def complete_onboarding(request: Request, db: DbSession) -> dict[str, Any]:
"""Set onboarding_completed=True, record the completion timestamp, and return the post-onboarding redirect URL.
The redirect URL is read from ``request.session["post_onboarding_redirect"]`` (stored by
``oauth_callback`` when it reroutes a first-time user to the wizard) and defaults to
``/upload`` when the session key is absent.
"""
user_id = _get_current_user_id(request)
profile = _get_or_create_profile(db, user_id)
profile.onboarding_completed = True
@@ -221,5 +226,6 @@ def complete_onboarding(request: Request, db: DbSession) -> dict[str, bool]:
db.rollback()
raise
logger.info("Onboarding: completed for user %s", user_id)
return {"success": True}
redirect_url = request.session.pop("post_onboarding_redirect", "/upload")
logger.info("Onboarding: completed for user %s, redirecting to %s", user_id, redirect_url)
return {"success": True, "redirect_url": redirect_url}