diff --git a/alembic/versions/030_add_onboarding_completed.py b/alembic/versions/030_add_onboarding_completed.py new file mode 100644 index 0000000..96f7eb4 --- /dev/null +++ b/alembic/versions/030_add_onboarding_completed.py @@ -0,0 +1,31 @@ +"""add_onboarding_completed + +Revision ID: 030_onboarding +Revises: 029 +Create Date: 2026-04-04 +""" + +from alembic import op +import sqlalchemy as sa + +# revision identifiers +revision = "030_onboarding" +down_revision = "029_add_email_digest" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + op.add_column( + "users", + sa.Column( + "onboarding_completed", + sa.Boolean(), + server_default="false", + nullable=False, + ), + ) + + +def downgrade() -> None: + op.drop_column("users", "onboarding_completed") diff --git a/backend/models.py b/backend/models.py index 4c6ffc1..4fbd236 100644 --- a/backend/models.py +++ b/backend/models.py @@ -169,6 +169,9 @@ class User(Base): is_active: Mapped[bool] = mapped_column( Boolean, default=True, server_default="true" ) + onboarding_completed: Mapped[bool] = mapped_column( + Boolean, default=False, server_default="false" + ) notification_preferences: Mapped[dict] = mapped_column( JSONB, nullable=False, server_default='{"email_digests": true, "digest_frequency": "daily"}', diff --git a/backend/routers/auth.py b/backend/routers/auth.py index 7380bc7..5727d9d 100644 --- a/backend/routers/auth.py +++ b/backend/routers/auth.py @@ -171,3 +171,19 @@ async def seed_invite_codes(session: AsyncSession) -> None: )) await session.commit() logger.info("Seeded default invite code: CHRYSOPEDIA-ALPHA-2026") + + +# ── Onboarding ─────────────────────────────────────────────────────────────── + + +@router.post("/onboarding-complete", response_model=UserResponse) +async def complete_onboarding( + current_user: Annotated[User, Depends(get_current_user)], + session: Annotated[AsyncSession, Depends(get_session)], +): + """Mark the current user's onboarding as completed.""" + current_user.onboarding_completed = True + await session.commit() + await session.refresh(current_user) + logger.info("Onboarding completed: %s", current_user.id) + return UserResponse.model_validate(current_user) diff --git a/backend/schemas.py b/backend/schemas.py index b4a57e2..96fd908 100644 --- a/backend/schemas.py +++ b/backend/schemas.py @@ -568,6 +568,7 @@ class UserResponse(BaseModel): role: str creator_id: uuid.UUID | None = None is_active: bool = True + onboarding_completed: bool = False created_at: datetime impersonating: bool = False