From d56c5edae3bb8ba164eb439732fdb54ccbe323a6 Mon Sep 17 00:00:00 2001 From: Lulu-The-Narwhal Date: Thu, 5 Feb 2026 15:16:52 +0200 Subject: [PATCH 1/3] base-starter --- .env | 4 +-- backend/Dockerfile | 6 ++--- backend/app/core/config.py | 2 +- compose.override.yml | 20 +++++++-------- compose.traefik.yml | 4 +-- development.md | 36 +++++++++++++-------------- frontend/README.md | 2 +- frontend/playwright.config.ts | 4 +-- frontend/tests/reset-password.spec.ts | 4 +-- frontend/vite.config.ts | 3 +++ 10 files changed, 43 insertions(+), 42 deletions(-) diff --git a/.env b/.env index 1d44286e25..b1f8064353 100644 --- a/.env +++ b/.env @@ -6,7 +6,7 @@ DOMAIN=localhost # DOMAIN=localhost.tiangolo.com # Used by the backend to generate links in emails to the frontend -FRONTEND_HOST=http://localhost:5173 +FRONTEND_HOST=http://localhost:5174 # In staging and production, set this env var to the frontend host, e.g. # FRONTEND_HOST=https://dashboard.example.com @@ -17,7 +17,7 @@ PROJECT_NAME="Full Stack FastAPI Project" STACK_NAME=full-stack-fastapi-project # Backend -BACKEND_CORS_ORIGINS="http://localhost,http://localhost:5173,https://localhost,https://localhost:5173,http://localhost.tiangolo.com" +BACKEND_CORS_ORIGINS="http://localhost,http://localhost:5174,https://localhost,https://localhost:5174,http://localhost.tiangolo.com" SECRET_KEY=changethis FIRST_SUPERUSER=admin@example.com FIRST_SUPERUSER_PASSWORD=changethis diff --git a/backend/Dockerfile b/backend/Dockerfile index 9f31dcd78a..765df6ff6d 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -22,9 +22,9 @@ ENV PATH="/app/.venv/bin:$PATH" # Install dependencies # Ref: https://docs.astral.sh/uv/guides/integration/docker/#intermediate-layers +COPY uv.lock pyproject.toml /app/ + RUN --mount=type=cache,target=/root/.cache/uv \ - --mount=type=bind,source=uv.lock,target=uv.lock \ - --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ uv sync --frozen --no-install-workspace --package app COPY ./backend/scripts /app/backend/scripts @@ -36,8 +36,6 @@ COPY ./backend/app /app/backend/app # Sync the project # Ref: https://docs.astral.sh/uv/guides/integration/docker/#intermediate-layers RUN --mount=type=cache,target=/root/.cache/uv \ - --mount=type=bind,source=uv.lock,target=uv.lock \ - --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ uv sync --frozen --package app WORKDIR /app/backend/ diff --git a/backend/app/core/config.py b/backend/app/core/config.py index 650b9f7910..56c71fa5d5 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -34,7 +34,7 @@ class Settings(BaseSettings): SECRET_KEY: str = secrets.token_urlsafe(32) # 60 minutes * 24 hours * 8 days = 8 days ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 8 - FRONTEND_HOST: str = "http://localhost:5173" + FRONTEND_HOST: str = "http://localhost:5174" ENVIRONMENT: Literal["local", "staging", "production"] = "local" BACKEND_CORS_ORIGINS: Annotated[ diff --git a/compose.override.yml b/compose.override.yml index 779cc8238d..c52d92dff4 100644 --- a/compose.override.yml +++ b/compose.override.yml @@ -10,8 +10,8 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock ports: - - "80:80" - - "8090:8080" + - "81:80" + - "8091:8080" # Duplicate the command from compose.yml to add --api.insecure=true command: # Enable Docker in Traefik, so that it reads labels from Docker services @@ -48,17 +48,17 @@ services: db: restart: "no" ports: - - "5432:5432" + - "5433:5432" adminer: restart: "no" ports: - - "8080:8080" + - "8082:8080" backend: restart: "no" ports: - - "8000:8000" + - "8001:8000" build: context: . dockerfile: backend/Dockerfile @@ -90,18 +90,18 @@ services: mailcatcher: image: schickling/mailcatcher ports: - - "1080:1080" - - "1025:1025" + - "1081:1080" + - "1026:1025" frontend: restart: "no" ports: - - "5173:80" + - "5174:80" build: context: . dockerfile: frontend/Dockerfile args: - - VITE_API_URL=http://localhost:8000 + - VITE_API_URL=http://localhost:8001 - NODE_ENV=development playwright: @@ -127,7 +127,7 @@ services: - ./frontend/blob-report:/app/frontend/blob-report - ./frontend/test-results:/app/frontend/test-results ports: - - 9323:9323 + - 9324:9323 networks: traefik-public: diff --git a/compose.traefik.yml b/compose.traefik.yml index bcd7d142ca..8c0a333c1d 100644 --- a/compose.traefik.yml +++ b/compose.traefik.yml @@ -3,9 +3,9 @@ services: image: traefik:3.6 ports: # Listen on port 80, default for HTTP, necessary to redirect to HTTPS - - 80:80 + - 81:80 # Listen on port 443, default for HTTPS - - 443:443 + - 444:443 restart: always labels: # Enable Traefik for this service, to make it available in the public network diff --git a/development.md b/development.md index 7879ffcdbc..15b43caa93 100644 --- a/development.md +++ b/development.md @@ -10,15 +10,15 @@ docker compose watch * Now you can open your browser and interact with these URLs: -Frontend, built with Docker, with routes handled based on the path: +Frontend, built with Docker, with routes handled based on the path: -Backend, JSON based web API based on OpenAPI: +Backend, JSON based web API based on OpenAPI: -Automatic interactive documentation with Swagger UI (from the OpenAPI backend): +Automatic interactive documentation with Swagger UI (from the OpenAPI backend): -Adminer, database web administration: +Adminer, database web administration: -Traefik UI, to see how the routes are being handled by the proxy: +Traefik UI, to see how the routes are being handled by the proxy: **Note**: The first time you start your stack, it might take a minute for it to be ready. While the backend waits for the database to be ready and configures everything. You can check the logs to monitor it. @@ -44,13 +44,13 @@ This is useful for: * Verifying email content and formatting * Debugging email-related functionality without sending real emails -The backend is automatically configured to use Mailcatcher when running with Docker Compose locally (SMTP on port 1025). All captured emails can be viewed at . +The backend is automatically configured to use Mailcatcher when running with Docker Compose locally (SMTP on port 1025). All captured emails can be viewed at . ## Local Development The Docker Compose files are configured so that each of the services is available in a different port in `localhost`. -For the backend and frontend, they use the same port that would be used by their local development server, so, the backend is at `http://localhost:8000` and the frontend at `http://localhost:5173`. +For the backend and frontend, they use the same port that would be used by their local development server, so, the backend is at `http://localhost:8001` and the frontend at `http://localhost:5174`. This way, you could turn off a Docker Compose service and start its local development service, and everything would keep working, because it all uses the same ports. @@ -76,7 +76,7 @@ And then you can run the local development server for the backend: ```bash cd backend -fastapi dev app/main.py +fastapi dev app/main.py --port 8001 ``` ## Docker Compose in `localhost.tiangolo.com` @@ -188,19 +188,19 @@ The production or staging URLs would use these same paths, but with your own dom Development URLs, for local development. -Frontend: +Frontend: -Backend: +Backend: -Automatic Interactive Docs (Swagger UI): +Automatic Interactive Docs (Swagger UI): -Automatic Alternative Docs (ReDoc): +Automatic Alternative Docs (ReDoc): -Adminer: +Adminer: -Traefik UI: +Traefik UI: -MailCatcher: +MailCatcher: ### Development URLs with `localhost.tiangolo.com` Configured @@ -214,8 +214,8 @@ Automatic Interactive Docs (Swagger UI): -Adminer: +Adminer: -Traefik UI: +Traefik UI: -MailCatcher: +MailCatcher: diff --git a/frontend/README.md b/frontend/README.md index 7b50d58b3f..0fa59f76b8 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -13,7 +13,7 @@ bun install bun run dev ``` -* Then open your browser at http://localhost:5173/. +* Then open your browser at http://localhost:5174/. Notice that this live server is not running inside Docker, it's for local development, and that is the recommended workflow. Once you are happy with your frontend, you can build the frontend Docker image and start it, to test it in a production-like environment. But building the image at every change will not be as productive as running the local development server with live reload. diff --git a/frontend/playwright.config.ts b/frontend/playwright.config.ts index 36f03d9919..e66d64b6df 100644 --- a/frontend/playwright.config.ts +++ b/frontend/playwright.config.ts @@ -24,7 +24,7 @@ export default defineConfig({ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Base URL to use in actions like `await page.goto('/')`. */ - baseURL: 'http://localhost:5173', + baseURL: 'http://localhost:5174', /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ trace: 'on-first-retry', @@ -85,7 +85,7 @@ export default defineConfig({ /* Run your local dev server before starting the tests */ webServer: { command: 'bun run dev', - url: 'http://localhost:5173', + url: 'http://localhost:5174', reuseExistingServer: !process.env.CI, }, }); diff --git a/frontend/tests/reset-password.spec.ts b/frontend/tests/reset-password.spec.ts index 88a2fc277c..239770a1c5 100644 --- a/frontend/tests/reset-password.spec.ts +++ b/frontend/tests/reset-password.spec.ts @@ -59,7 +59,7 @@ test("User can reset password successfully using the link", async ({ let url = await page.getAttribute(selector, "href") // TODO: update var instead of doing a replace - url = url!.replace("http://localhost/", "http://localhost:5173/") + url = url!.replace("http://localhost/", "http://localhost:5174/") // Set the new password and confirm it await page.goto(url) @@ -111,7 +111,7 @@ test("Weak new password validation", async ({ page, request }) => { const selector = 'a[href*="/reset-password?token="]' let url = await page.getAttribute(selector, "href") - url = url!.replace("http://localhost/", "http://localhost:5173/") + url = url!.replace("http://localhost/", "http://localhost:5174/") // Set a weak new password await page.goto(url) diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 874db9071f..a8fa5a0aba 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -19,4 +19,7 @@ export default defineConfig({ react(), tailwindcss(), ], + server: { + port: 5174, + }, }) From 68d3ad245d4e64df86ebfde8b9978293e6b1a05d Mon Sep 17 00:00:00 2001 From: Lulu-The-Narwhal Date: Thu, 5 Feb 2026 15:27:35 +0200 Subject: [PATCH 2/3] user avatar + api --- .../a1b2c3d4e5f6_add_avatar_url_to_user.py | 25 +++++++ backend/app/api/routes/users.py | 36 +++++++++- backend/app/main.py | 9 +++ backend/app/models.py | 1 + ...8_c2917da5-1ca2-48bb-b8e9-5bb2ee73f909.JPG | Bin 0 -> 68488 bytes compose.override.yml | 1 + compose.yml | 5 ++ frontend/src/client/types.gen.ts | 1 + .../UserSettings/UserInformation.tsx | 67 +++++++++++++++++- 9 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 backend/app/alembic/versions/a1b2c3d4e5f6_add_avatar_url_to_user.py create mode 100644 backend/app/static/uploads/5eaaec5c-84d0-480a-958c-d786840b12b8_c2917da5-1ca2-48bb-b8e9-5bb2ee73f909.JPG diff --git a/backend/app/alembic/versions/a1b2c3d4e5f6_add_avatar_url_to_user.py b/backend/app/alembic/versions/a1b2c3d4e5f6_add_avatar_url_to_user.py new file mode 100644 index 0000000000..bfc25b44fb --- /dev/null +++ b/backend/app/alembic/versions/a1b2c3d4e5f6_add_avatar_url_to_user.py @@ -0,0 +1,25 @@ +"""Add avatar_url to User + +Revision ID: a1b2c3d4e5f6 +Revises: fe56fa70289e +Create Date: 2026-02-05 10:00:00.000000 + +""" +from alembic import op +import sqlalchemy as sa +import sqlmodel.sql.sqltypes + + +# revision identifiers, used by Alembic. +revision = 'a1b2c3d4e5f6' +down_revision = 'fe56fa70289e' +branch_labels = None +depends_on = None + + +def upgrade(): + op.add_column('user', sa.Column('avatar_url', sa.String(length=500), nullable=True)) + + +def downgrade(): + op.drop_column('user', 'avatar_url') diff --git a/backend/app/api/routes/users.py b/backend/app/api/routes/users.py index 35f64b626e..73a15dd36e 100644 --- a/backend/app/api/routes/users.py +++ b/backend/app/api/routes/users.py @@ -1,7 +1,9 @@ import uuid +import shutil +from pathlib import Path from typing import Any -from fastapi import APIRouter, Depends, HTTPException +from fastapi import APIRouter, Depends, HTTPException, UploadFile, File from sqlmodel import col, delete, func, select from app import crud @@ -99,6 +101,38 @@ def update_user_me( return current_user +@router.post("/me/avatar", response_model=UserPublic) +def update_user_avatar( + *, + session: SessionDep, + current_user: CurrentUser, + file: UploadFile = File(...) +) -> Any: + """ + Upload user avatar. + """ + # Ensure upload directory exists + upload_dir = Path("app/static/uploads") + upload_dir.mkdir(parents=True, exist_ok=True) + + # Generate unique filename + file_ext = Path(file.filename).suffix if file.filename else "" + file_name = f"{current_user.id}_{uuid.uuid4()}{file_ext}" + file_path = upload_dir / file_name + + with file_path.open("wb") as buffer: + shutil.copyfileobj(file.file, buffer) + + # Update user profile + # Assuming served at /static/uploads/ + avatar_url = f"/static/uploads/{file_name}" + current_user.avatar_url = avatar_url + session.add(current_user) + session.commit() + session.refresh(current_user) + return current_user + + @router.patch("/me/password", response_model=Message) def update_password_me( *, session: SessionDep, body: UpdatePassword, current_user: CurrentUser diff --git a/backend/app/main.py b/backend/app/main.py index 9a95801e74..1d467669fe 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1,5 +1,7 @@ import sentry_sdk +import os from fastapi import FastAPI +from fastapi.staticfiles import StaticFiles from fastapi.routing import APIRoute from starlette.middleware.cors import CORSMiddleware @@ -30,4 +32,11 @@ def custom_generate_unique_id(route: APIRoute) -> str: allow_headers=["*"], ) +# Mount static files +upload_dir = "app/static" +if not os.path.exists(upload_dir): + os.makedirs(upload_dir) + +app.mount("/static", StaticFiles(directory=upload_dir), name="static") + app.include_router(api_router, prefix=settings.API_V1_STR) diff --git a/backend/app/models.py b/backend/app/models.py index b5132e0e2c..c4635c92c1 100644 --- a/backend/app/models.py +++ b/backend/app/models.py @@ -16,6 +16,7 @@ class UserBase(SQLModel): is_active: bool = True is_superuser: bool = False full_name: str | None = Field(default=None, max_length=255) + avatar_url: str | None = Field(default=None, max_length=500) # Properties to receive via API on creation diff --git a/backend/app/static/uploads/5eaaec5c-84d0-480a-958c-d786840b12b8_c2917da5-1ca2-48bb-b8e9-5bb2ee73f909.JPG b/backend/app/static/uploads/5eaaec5c-84d0-480a-958c-d786840b12b8_c2917da5-1ca2-48bb-b8e9-5bb2ee73f909.JPG new file mode 100644 index 0000000000000000000000000000000000000000..7d26d796b7562715c6270a67180aa10cb9d89b1d GIT binary patch literal 68488 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 z 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 { const [editMode, setEditMode] = useState(false) const { user: currentUser } = useAuth() + const fileInputRef = useRef(null) + + const handleAvatarClick = () => { + fileInputRef.current?.click() + } + + const handleFileChange = async (event: ChangeEvent) => { + const file = event.target.files?.[0] + if (!file) return + + const formData = new FormData() + formData.append("file", file) + + try { + const token = localStorage.getItem("access_token") + const response = await fetch(`${import.meta.env.VITE_API_URL}/api/v1/users/me/avatar`, { + method: "POST", + headers: { + Authorization: `Bearer ${token}`, + }, + body: formData, + }) + + if (!response.ok) { + throw new Error("Failed to upload avatar") + } + + showSuccessToast("Avatar updated successfully") + queryClient.invalidateQueries({ queryKey: ["currentUser"] }) + } catch (error) { + showErrorToast("Error uploading avatar") + } + } + const form = useForm({ resolver: zodResolver(formSchema), mode: "onBlur", @@ -83,6 +117,37 @@ const UserInformation = () => { return (

User Information

+ +
+
+ {currentUser?.avatar_url ? ( + Avatar + ) : ( +
+ {currentUser?.full_name?.charAt(0) || currentUser?.email?.charAt(0) || "?"} +
+ )} +
+
+ + +
+
+
Date: Thu, 5 Feb 2026 13:28:46 +0000 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=8E=A8=20Auto=20format=20and=20update?= =?UTF-8?q?=20with=20pre-commit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/routes/users.py | 15 ++- backend/app/main.py | 5 +- compose.yml | 2 +- frontend/src/client/schemas.gen.ts | 49 ++++++++++ frontend/src/client/sdk.gen.ts | 22 ++++- frontend/src/client/types.gen.ts | 12 +++ .../UserSettings/UserInformation.tsx | 93 ++++++++++--------- 7 files changed, 140 insertions(+), 58 deletions(-) diff --git a/backend/app/api/routes/users.py b/backend/app/api/routes/users.py index 73a15dd36e..6fb83eaee0 100644 --- a/backend/app/api/routes/users.py +++ b/backend/app/api/routes/users.py @@ -1,9 +1,9 @@ -import uuid import shutil +import uuid from pathlib import Path from typing import Any -from fastapi import APIRouter, Depends, HTTPException, UploadFile, File +from fastapi import APIRouter, Depends, File, HTTPException, UploadFile from sqlmodel import col, delete, func, select from app import crud @@ -103,10 +103,7 @@ def update_user_me( @router.post("/me/avatar", response_model=UserPublic) def update_user_avatar( - *, - session: SessionDep, - current_user: CurrentUser, - file: UploadFile = File(...) + *, session: SessionDep, current_user: CurrentUser, file: UploadFile = File(...) ) -> Any: """ Upload user avatar. @@ -114,15 +111,15 @@ def update_user_avatar( # Ensure upload directory exists upload_dir = Path("app/static/uploads") upload_dir.mkdir(parents=True, exist_ok=True) - + # Generate unique filename file_ext = Path(file.filename).suffix if file.filename else "" file_name = f"{current_user.id}_{uuid.uuid4()}{file_ext}" file_path = upload_dir / file_name - + with file_path.open("wb") as buffer: shutil.copyfileobj(file.file, buffer) - + # Update user profile # Assuming served at /static/uploads/ avatar_url = f"/static/uploads/{file_name}" diff --git a/backend/app/main.py b/backend/app/main.py index 1d467669fe..041a166a6f 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1,8 +1,9 @@ -import sentry_sdk import os + +import sentry_sdk from fastapi import FastAPI -from fastapi.staticfiles import StaticFiles from fastapi.routing import APIRoute +from fastapi.staticfiles import StaticFiles from starlette.middleware.cors import CORSMiddleware from app.api.main import api_router diff --git a/compose.yml b/compose.yml index b556766f37..5dae6f3848 100644 --- a/compose.yml +++ b/compose.yml @@ -115,7 +115,7 @@ services: interval: 10s timeout: 5s retries: 5 - + volumes: - app-static-data:/app/backend/app/static diff --git a/frontend/src/client/schemas.gen.ts b/frontend/src/client/schemas.gen.ts index 5c0c9c4a4e..aedacd8da2 100644 --- a/frontend/src/client/schemas.gen.ts +++ b/frontend/src/client/schemas.gen.ts @@ -57,6 +57,19 @@ export const Body_login_login_access_tokenSchema = { title: 'Body_login-login_access_token' } as const; +export const Body_users_update_user_avatarSchema = { + properties: { + file: { + type: 'string', + format: 'binary', + title: 'File' + } + }, + type: 'object', + required: ['file'], + title: 'Body_users-update_user_avatar' +} as const; + export const HTTPValidationErrorSchema = { properties: { detail: { @@ -318,6 +331,18 @@ export const UserCreateSchema = { ], title: 'Full Name' }, + avatar_url: { + anyOf: [ + { + type: 'string', + maxLength: 500 + }, + { + type: 'null' + } + ], + title: 'Avatar Url' + }, password: { type: 'string', maxLength: 128, @@ -360,6 +385,18 @@ export const UserPublicSchema = { ], title: 'Full Name' }, + avatar_url: { + anyOf: [ + { + type: 'string', + maxLength: 500 + }, + { + type: 'null' + } + ], + title: 'Avatar Url' + }, id: { type: 'string', format: 'uuid', @@ -452,6 +489,18 @@ export const UserUpdateSchema = { ], title: 'Full Name' }, + avatar_url: { + anyOf: [ + { + type: 'string', + maxLength: 500 + }, + { + type: 'null' + } + ], + title: 'Avatar Url' + }, password: { anyOf: [ { diff --git a/frontend/src/client/sdk.gen.ts b/frontend/src/client/sdk.gen.ts index ba79e3f726..c23100c29b 100644 --- a/frontend/src/client/sdk.gen.ts +++ b/frontend/src/client/sdk.gen.ts @@ -3,7 +3,7 @@ import type { CancelablePromise } from './core/CancelablePromise'; import { OpenAPI } from './core/OpenAPI'; import { request as __request } from './core/request'; -import type { ItemsReadItemsData, ItemsReadItemsResponse, ItemsCreateItemData, ItemsCreateItemResponse, ItemsReadItemData, ItemsReadItemResponse, ItemsUpdateItemData, ItemsUpdateItemResponse, ItemsDeleteItemData, ItemsDeleteItemResponse, LoginLoginAccessTokenData, LoginLoginAccessTokenResponse, LoginTestTokenResponse, LoginRecoverPasswordData, LoginRecoverPasswordResponse, LoginResetPasswordData, LoginResetPasswordResponse, LoginRecoverPasswordHtmlContentData, LoginRecoverPasswordHtmlContentResponse, PrivateCreateUserData, PrivateCreateUserResponse, UsersReadUsersData, UsersReadUsersResponse, UsersCreateUserData, UsersCreateUserResponse, UsersReadUserMeResponse, UsersDeleteUserMeResponse, UsersUpdateUserMeData, UsersUpdateUserMeResponse, UsersUpdatePasswordMeData, UsersUpdatePasswordMeResponse, UsersRegisterUserData, UsersRegisterUserResponse, UsersReadUserByIdData, UsersReadUserByIdResponse, UsersUpdateUserData, UsersUpdateUserResponse, UsersDeleteUserData, UsersDeleteUserResponse, UtilsTestEmailData, UtilsTestEmailResponse, UtilsHealthCheckResponse } from './types.gen'; +import type { ItemsReadItemsData, ItemsReadItemsResponse, ItemsCreateItemData, ItemsCreateItemResponse, ItemsReadItemData, ItemsReadItemResponse, ItemsUpdateItemData, ItemsUpdateItemResponse, ItemsDeleteItemData, ItemsDeleteItemResponse, LoginLoginAccessTokenData, LoginLoginAccessTokenResponse, LoginTestTokenResponse, LoginRecoverPasswordData, LoginRecoverPasswordResponse, LoginResetPasswordData, LoginResetPasswordResponse, LoginRecoverPasswordHtmlContentData, LoginRecoverPasswordHtmlContentResponse, PrivateCreateUserData, PrivateCreateUserResponse, UsersReadUsersData, UsersReadUsersResponse, UsersCreateUserData, UsersCreateUserResponse, UsersReadUserMeResponse, UsersDeleteUserMeResponse, UsersUpdateUserMeData, UsersUpdateUserMeResponse, UsersUpdateUserAvatarData, UsersUpdateUserAvatarResponse, UsersUpdatePasswordMeData, UsersUpdatePasswordMeResponse, UsersRegisterUserData, UsersRegisterUserResponse, UsersReadUserByIdData, UsersReadUserByIdResponse, UsersUpdateUserData, UsersUpdateUserResponse, UsersDeleteUserData, UsersDeleteUserResponse, UtilsTestEmailData, UtilsTestEmailResponse, UtilsHealthCheckResponse } from './types.gen'; export class ItemsService { /** @@ -325,6 +325,26 @@ export class UsersService { }); } + /** + * Update User Avatar + * Upload user avatar. + * @param data The data for the request. + * @param data.formData + * @returns UserPublic Successful Response + * @throws ApiError + */ + public static updateUserAvatar(data: UsersUpdateUserAvatarData): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/api/v1/users/me/avatar', + formData: data.formData, + mediaType: 'multipart/form-data', + errors: { + 422: 'Validation Error' + } + }); + } + /** * Update Password Me * Update own password. diff --git a/frontend/src/client/types.gen.ts b/frontend/src/client/types.gen.ts index b53d169e19..e1e2c8dcf0 100644 --- a/frontend/src/client/types.gen.ts +++ b/frontend/src/client/types.gen.ts @@ -9,6 +9,10 @@ export type Body_login_login_access_token = { client_secret?: (string | null); }; +export type Body_users_update_user_avatar = { + file: (Blob | File); +}; + export type HTTPValidationError = { detail?: Array; }; @@ -67,6 +71,7 @@ export type UserCreate = { is_active?: boolean; is_superuser?: boolean; full_name?: (string | null); + avatar_url?: (string | null); password: string; }; @@ -96,6 +101,7 @@ export type UserUpdate = { is_active?: boolean; is_superuser?: boolean; full_name?: (string | null); + avatar_url?: (string | null); password?: (string | null); }; @@ -197,6 +203,12 @@ export type UsersUpdateUserMeData = { export type UsersUpdateUserMeResponse = (UserPublic); +export type UsersUpdateUserAvatarData = { + formData: Body_users_update_user_avatar; +}; + +export type UsersUpdateUserAvatarResponse = (UserPublic); + export type UsersUpdatePasswordMeData = { requestBody: UpdatePassword; }; diff --git a/frontend/src/components/UserSettings/UserInformation.tsx b/frontend/src/components/UserSettings/UserInformation.tsx index 2f35f8d3d9..05e4919a27 100644 --- a/frontend/src/components/UserSettings/UserInformation.tsx +++ b/frontend/src/components/UserSettings/UserInformation.tsx @@ -1,6 +1,6 @@ import { zodResolver } from "@hookform/resolvers/zod" import { useMutation, useQueryClient } from "@tanstack/react-query" -import { useState, useRef, type ChangeEvent } from "react" +import { type ChangeEvent, useRef, useState } from "react" import { useForm } from "react-hook-form" import { z } from "zod" @@ -48,23 +48,26 @@ const UserInformation = () => { formData.append("file", file) try { - const token = localStorage.getItem("access_token") - const response = await fetch(`${import.meta.env.VITE_API_URL}/api/v1/users/me/avatar`, { - method: "POST", - headers: { - Authorization: `Bearer ${token}`, - }, - body: formData, - }) - - if (!response.ok) { - throw new Error("Failed to upload avatar") - } - - showSuccessToast("Avatar updated successfully") - queryClient.invalidateQueries({ queryKey: ["currentUser"] }) - } catch (error) { - showErrorToast("Error uploading avatar") + const token = localStorage.getItem("access_token") + const response = await fetch( + `${import.meta.env.VITE_API_URL}/api/v1/users/me/avatar`, + { + method: "POST", + headers: { + Authorization: `Bearer ${token}`, + }, + body: formData, + }, + ) + + if (!response.ok) { + throw new Error("Failed to upload avatar") + } + + showSuccessToast("Avatar updated successfully") + queryClient.invalidateQueries({ queryKey: ["currentUser"] }) + } catch (_error) { + showErrorToast("Error uploading avatar") } } @@ -117,35 +120,35 @@ const UserInformation = () => { return (

User Information

- +
-
- {currentUser?.avatar_url ? ( - Avatar - ) : ( -
- {currentUser?.full_name?.charAt(0) || currentUser?.email?.charAt(0) || "?"} -
- )} -
-
- - + {currentUser?.avatar_url ? ( + Avatar -
+ ) : ( +
+ {currentUser?.full_name?.charAt(0) || + currentUser?.email?.charAt(0) || + "?"} +
+ )} +
+
+ + +