Skip to content

Conversation

@codastream
Copy link
Owner

@codastream codastream commented Jan 19, 2026

Main changes

React Profile page

Done

cf #21

  • File structure for components
  • Basic reusable components : Avatar, Button, Background, ... + Page layout
  • Basic of internationalization
  • Basic of responsiveness
  • Basic of accessibility (not all components)
  • Doc in wiki for Tailwind, React
  • Import DTO from @transcendence/core, but no systematic validation

cf #34 and #39

  • PATCH /users/username/:username/avatar
  • DELETE /users/username/:username (not used in PR)
  • changed returns of GET /users/username/:username/ according to x-user-name header
  • better unit test coverage (with use of Copilot for data and services)
new dependencies for React
Name Role Documentation
react and react-dom React for component building. React DOM = entry point for DOM and server rendering react.dev/reference/react-dom
react-router-dom declarative routing : SPA navigation) reactrouter.com
@tanstack/react-query state management for API queries (fetching, caching, and server-sync) tanstack.com/query
axios Promise-based HTTP client axios-http.com
flubber animation : help for svg morphing in menu repo
framer-motion animation library for React framer.com/motion
lucide-react SVG icons. lucide.dev
i18next and react-i18next internationalization i18next.com

Temporary

  • Dev buttons for register and login

Out of scope

  • Frontend display for errors (modals or toaster, in case of 409 conflict for instance) => to do in FRONT Register and login #40
  • Role-based access control : no access to extra parts of profile if admin
  • Unit tests for components (wait a bit till design is hand tested and consistent)
  • Delete profile button (RGPD compliance)
  • better check on what is returned by API : ProfileDTO still has a authId

How to test

make
cd srcs/users && npm run dev
  • go to vite dev server, at an existing route (localhost:5173/profile/Toto) => should display 404 if db is empty
  • register then login => default avatar should show up
  • change avatar
  • log out => no extra info

Others

Gateway

Nginx server config

  • Tried to merge previous improvements into a smaller and more readable config (reusable header config file)
  • Adjusted cookie options in authentication to use SameSite: 'strict' in production and lax in development

Auth service

  • Adapted logger.config.ts to skip health check logs and serialize custom ServiceError types. A bit out of scope, but I had to debug register and login calls

Francois Petit added 30 commits December 20, 2025 21:12
- adapt dev-docker-compose (env, template, other services)
- mount upload volume (for avatars)
- envalid + react router dom + types
- adapt tsconfig for vite
- nginx conf fallback on index (spa)
- export appenv with envalid
- use app.log as logger
- temporarily move old index as admin.html
- folders api, components, pages, styles
- with vite (not tested on prod)
- need form, buttons and styling
- add components tested in Stackblitz
- add font Quantico
- add lint lib for react
- flubber dependency for svg morphing
- component PageContainer
- adapt Navbar
- adjust svg and animation for menu elements
- adjust layering for responsiveness
- using i118next and related libs
- json for en and fr locales

chore: rename core as types

- reuse DTOs and schemas from core package
- auth Provider and useAuth()
- dev buttons for login with roles and logout
- ProfileAuthDTO intersection type
- route /me redirecting to /profile/<username>
- isOwner check for display of sensitive fields (no role check)
- queries using Axios and ReactQuery
- register and login from dev buttons
- user info stored in LocalStorage (temporary solution, not secure)
- nginx config + add Vite dev port to CORS

fix(GATE): types of user
chore(UM): change path to GET /username/:username
chore(AUTH): less verbose logs for /health
- DELETE
- PATCH avatar
- update route for doc
- gateway : use fastify/reply-from to transfer multipart body as stream
- friends : remove authPlugin and tests for 401 (this is already managed by gateway)
- profile : change route to GET /users/username/:username
- UM : absolute path for binding volumes - move db to data root folder
- bind upload to users and nginx
- adapt Makefile + Docker compose to reset permissions on volumes
- proxy in vite config for CORS issue
- nginx route for uploads
- nginx ensure that cookies are sent back
- api clients : auth + profile
- authProvider : single source of truth for user
- Avatar : better handling of default (yet still buggy)
- FileUploader : handle drag-drop events, prevalidation
- ProfilePage : handle upload using react-query + basic bar
- fix: copy extra nginx conf file in Dockerfile
- adapt dev compose and nginx Dockerfile.dev
Francois Petit added 13 commits January 19, 2026 17:48
- POST : 201, 400
- PATCH : 200, 400
- DELETE : 200, 404
- update schema with 2 kinds of nicknames
- removed extra container
- restored old commands in Makefile

misc(NGINX): rename dashboard.html

Makefile
srcs/docker-compose.yml
srcs/gateway/src/utils/constants.ts
srcs/nginx/conf.d/default.conf
srcs/nginx/src/html/dashboard.html
srcs/users/Dockerfile
srcs/users/prisma.config.ts
srcs/users/src/config/env.ts
srcs/users/src/controllers/profiles.controller.ts
- serve dashboard at /dashboard in prod
- default route / temporarily serves profile
- menu stats instead of profile
- navbar avatar redirects to /me on click

srcs/nginx/Dockerfile
srcs/nginx/conf.d/default.conf
srcs/nginx/src/App.tsx
srcs/nginx/src/components/atoms/MenuElement.tsx
srcs/nginx/src/components/molecules/NavBar.tsx
srcs/nginx/src/locales/en/common.json
srcs/nginx/src/locales/fr/common.json
srcs/nginx/src/pages/ProfilePage.tsx
srcs/nginx/src/types/react-types.ts
srcs/nginx/tsconfig.json
srcs/users/tsconfig.json
- get /users/username returns a ProfileSimpleDTO if x-user-name header differs from requested one
- no get /me route in API yet (faked in the client)

srcs/nginx/src/api/profile-api.ts
srcs/nginx/src/components/atoms/DevLogin.tsx
srcs/shared/core/src/index.ts
srcs/shared/core/src/schemas/profile.schema.ts
srcs/users/src/controllers/profiles.controller.ts
srcs/users/src/routes/profiles.routes.ts
- fixed ESLint in PR scope (185 to go)
- fixed test
- add default {} to prevent Fastify 400 error
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request introduces a React-based frontend for the user profile page with avatar upload functionality, along with significant backend improvements across multiple microservices.

Changes:

  • Implements React frontend with profile page, avatar upload, and reusable components
  • Adds PATCH /users/username/:username/avatar and DELETE /users/username/:username endpoints
  • Enhances gateway to handle multipart form-data streaming using @fastify/reply-from
  • Improves test coverage for profiles and friends services
  • Updates Nginx configuration for better proxy handling and static file serving
  • Migrates from vanilla JS/HTML to React with Vite build system

Reviewed changes

Copilot reviewed 135 out of 142 changed files in this pull request and generated 18 comments.

Show a summary per file
File Description
srcs/users/src/services/profiles.service.ts Added avatar upload logic with file validation and storage
srcs/users/src/controllers/profiles.controller.ts Added updateProfileAvatar and deleteProfile endpoints
srcs/users/src/data/profiles.data.ts Implemented file storage and avatar URL updates
srcs/users/Dockerfile Modified user permissions (security concern - running as root)
srcs/gateway/src/utils/proxy.ts Added fastStreamProxy for efficient multipart proxying
srcs/gateway/src/index.ts Enhanced multipart and JSON content type parsers
srcs/nginx/conf.d/default.conf Refactored proxy configuration with reusable headers
srcs/nginx/src/components/* New React component library for profile UI
srcs/nginx/src/api/profile-api.ts Client-side API for profile operations
Test files Comprehensive unit tests for profiles and friends services
Files not reviewed (1)
  • srcs/nginx/package-lock.json: Language not supported

proxy_pass_header Set-Cookie; # laisser passer le cookie du backend au frontend
proxy_cookie_path / /; # le cookie depose par auth/login sera bien renvoye sur un appel a users/xxx
proxy_pass_request_body on;
client_max_body_size 50M;
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The client_max_body_size is set to 50M, which seems excessive for avatar uploads. The multipart plugin is configured with a 5MB limit, creating an inconsistency. Consider aligning both limits to a reasonable value (e.g., 5-10MB) to prevent abuse and ensure consistency.

Copilot uses AI. Check for mistakes.
Comment on lines +59 to +64
# ENV USER_ID=1001
# ENV GROUP_ID=204

# RUN apk add --no-cache wget python3 make g++ \
# && addgroup -g ${GROUP_ID} -S appgroup \
# && adduser -u ${USER_ID} -S -G appgroup appuser
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Docker security settings have been commented out, leaving the service running as root. This is a security risk in production. The commented lines show user/group creation was intentional but disabled.

Consider re-enabling these lines or documenting why they were disabled. Running as root violates the principle of least privilege.

Copilot uses AI. Check for mistakes.
Comment on lines +59 to +62
const allowedMimeTypes = ['image/png', 'image/jpeg', 'image/webp'];
if (!allowedMimeTypes.includes(file.mimetype)) {
throw new AppError(ERR_DEFS.RESSOURCE_INVALID_TYPE, { details: 'Invalid file type' });
}
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file validation only checks MIME type from the client, which can be easily spoofed. Consider adding server-side validation by checking file magic numbers (file signature) to ensure the uploaded file is actually an image.

For robust validation, use a library that checks the actual file content, not just the extension or MIME type.

Copilot uses AI. Check for mistakes.
}

const fileExtension = path.extname(file.filename);
const uniqueName = `avatar-${username}-${Date.now()}${fileExtension}`;
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The avatar filename includes the username, which could expose user information through the file system. Additionally, using Date.now() alone for uniqueness can lead to collisions with concurrent uploads.

Consider using a cryptographically secure random identifier (like UUID) instead of username+timestamp for better security and uniqueness.

Copilot uses AI. Check for mistakes.
const friendProfile =
friendship.receiver.authId === currentUserId ? friendship.requester : friendship.receiver;
const friendProfile = f.receiver.authId === currentUserId ? f.requester : f.receiver;
const nickname = f.receiver.authId == currentUserId ? f.nicknameRequester : f.nicknameReceiver;
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparison using loose equality (==) instead of strict equality (===). This can lead to unexpected type coercion behavior. Use === for type-safe comparisons.

Copilot uses AI. Check for mistakes.
const response = await fetch('/api/redis')
const data = await response.json()
const response = await fetch('/api/redis');
const data = await response.json();
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable data.

Copilot uses AI. Check for mistakes.
const response = await fetch('/api/health')
const data = await response.json()
const response = await fetch('/api/health');
const data = await response.json();
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable data.

Copilot uses AI. Check for mistakes.
const response = await fetch('/api/game/health')
const data = await response.json()
const response = await fetch('/api/game/health');
const data = await response.json();
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable data.

Copilot uses AI. Check for mistakes.
const response = await fetch('/api/block/health')
const data = await response.json()
const response = await fetch('/api/block/health');
const data = await response.json();
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable data.

Copilot uses AI. Check for mistakes.
@@ -1,4 +1,4 @@
import { Scores, GameState, ServerMessage, ClientMessage, Vector2D } from '../core/types.js';
import { Scores, GameState, ServerMessage, ClientMessage, Vector2D } from '../types/types.js';
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused import Vector2D.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants