From f6ed383b3a17dfd73d275c614b081f9fd0f0af70 Mon Sep 17 00:00:00 2001 From: roberto Date: Tue, 10 Mar 2026 16:14:00 +0100 Subject: [PATCH] add user name and surname --- ...1dc_add_name_and_surname_to_users_table.py | 30 ++++++++++++++++ app/api/middleware/auth.py | 16 +++++++-- app/api/routes/auth.py | 36 +++++++++++++++++++ app/models.py | 2 ++ app/schemas.py | 2 ++ 5 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 alembic/versions/818478c251dc_add_name_and_surname_to_users_table.py diff --git a/alembic/versions/818478c251dc_add_name_and_surname_to_users_table.py b/alembic/versions/818478c251dc_add_name_and_surname_to_users_table.py new file mode 100644 index 0000000..164c246 --- /dev/null +++ b/alembic/versions/818478c251dc_add_name_and_surname_to_users_table.py @@ -0,0 +1,30 @@ +"""add name and surname to users table + +Revision ID: 818478c251dc +Revises: 004 +Create Date: 2026-03-10 15:10:42.811947 + +""" +from __future__ import annotations + +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '818478c251dc' +down_revision: Union[str, None] = '004' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + op.add_column('users', sa.Column('name', sa.String(length=100), nullable=True)) + op.add_column('users', sa.Column('surname', sa.String(length=100), nullable=True)) + + +def downgrade() -> None: + op.drop_column('users', 'surname') + op.drop_column('users', 'name') diff --git a/app/api/middleware/auth.py b/app/api/middleware/auth.py index 1cd8df0..329ba30 100644 --- a/app/api/middleware/auth.py +++ b/app/api/middleware/auth.py @@ -55,11 +55,23 @@ async def get_current_user( raise credentials_exc # Live tier lookup — subscription row is the authoritative source. - from app.models import Subscription # noqa: PLC0415 + from app.models import Subscription, User # noqa: PLC0415 result = await db.execute( select(Subscription.tier).where(Subscription.user_id == user_id) ) tier: str = result.scalar_one_or_none() or "free" - return UserProfile(id=user_id, email=email, tier=tier) # type: ignore[arg-type] + # Fetch name/surname from user row. + user_result = await db.execute( + select(User.name, User.surname).where(User.id == user_id) + ) + user_row = user_result.one_or_none() + + return UserProfile( + id=user_id, + email=email, + name=user_row.name if user_row else None, + surname=user_row.surname if user_row else None, + tier=tier, + ) # type: ignore[arg-type] diff --git a/app/api/routes/auth.py b/app/api/routes/auth.py index b32925e..1ab10ea 100644 --- a/app/api/routes/auth.py +++ b/app/api/routes/auth.py @@ -66,6 +66,8 @@ def _make_access_token(user_id: str, email: str, tier: str) -> tuple[str, int]: class _RegisterRequest(BaseModel): email: str password: str + name: str | None = None + surname: str | None = None class _LoginRequest(BaseModel): @@ -93,6 +95,8 @@ async def register( user = User( id=str(uuid.uuid4()), email=body.email, + name=body.name, + surname=body.surname, password_hash=_hash_password(body.password), tier="free", encryption_key=Fernet.generate_key().decode(), @@ -193,7 +197,39 @@ async def refresh( ) +class _UpdateProfileRequest(BaseModel): + name: str | None = None + surname: str | None = None + + @router.get("/me", response_model=UserProfile) async def me(current_user: UserProfile = Depends(get_current_user)) -> UserProfile: """Return the profile for the authenticated user.""" return current_user + + +@router.put("/me", response_model=UserProfile) +async def update_profile( + body: _UpdateProfileRequest, + current_user: UserProfile = Depends(get_current_user), + db: AsyncSession = Depends(get_session), +) -> UserProfile: + """Update the authenticated user's name and surname.""" + result = await db.execute(select(User).where(User.id == current_user.id)) + user = result.scalar_one() + + if body.name is not None: + user.name = body.name + if body.surname is not None: + user.surname = body.surname + + await db.commit() + await db.refresh(user) + + return UserProfile( + id=user.id, + email=user.email, + name=user.name, + surname=user.surname, + tier=current_user.tier, + ) diff --git a/app/models.py b/app/models.py index e0e5f7f..93cdfab 100644 --- a/app/models.py +++ b/app/models.py @@ -75,6 +75,8 @@ class User(Base): Uuid(as_uuid=False), primary_key=True, default=_uuid ) email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False, index=True) + name: Mapped[str | None] = mapped_column(String(100), nullable=True) + surname: Mapped[str | None] = mapped_column(String(100), nullable=True) password_hash: Mapped[str] = mapped_column(String(255), nullable=False) tier: Mapped[str] = mapped_column(TierEnum, nullable=False, default="free") stripe_customer_id: Mapped[str | None] = mapped_column(String(255), nullable=True) diff --git a/app/schemas.py b/app/schemas.py index 95ad3e0..2ca50e9 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -27,6 +27,8 @@ class AuthTokens(BaseModel): class UserProfile(BaseModel): id: str email: str + name: str | None = None + surname: str | None = None tier: BillingTier