diff --git a/README.md b/README.md index 7f5312a..cfd2172 100644 --- a/README.md +++ b/README.md @@ -1,169 +1,222 @@ # Dead Pigeons 🐦 (3rd Semester Project) - [Dead Pigeons](#dead-pigeons) + - [Live demo](#live-demo) + - [Run API](#run-api) + - [Run Client](#run-client) + - [Run Tests](#run-tests) + - [Prerequisites](#prerequisites) - [Environment](#environment) - - [Authorization](#authorization) - - [Anonymous](#anonymous) - - [Authenticated user](#authenticated-user) - - [Player only](#player-only) - - [Admin only](#admin-only) - [Configuration](#configuration) + - [API configuration](#api-configuration) + - [Client configuration](#client-configuration) + - [Testing](#testing) + - [How we test](#how-we-test) - [Linting](#linting) - - [Filtering / sorting / paging](#filtering--sorting--paging) - - [Testing](#testing) + - [Authentication](#authentication) + - [JWT](#jwt) + - [Authorization](#authorization) + - [Anonymous (not logged in)](#anonymous-not-logged-in) + - [Authenticated user (role = Player or Admin)](#authenticated-user-role--player-or-admin) + - [Player only (role = Player)](#player-only-role--player) + - [Admin only (role = Admin)](#admin-only-role--admin) + - [Architecture / Data model](#architecture--data-model) + - [Deployment](#deployment) + - [Proxy & Frontend Integration](#proxy--frontend-integration) - [Limitations](#limitations) +## Dead Pigeons +This project was developed for the local sports club, Jerne IF. +It delivers a web-based solution that digitizes the game workflow, +making it easier to operate the game, manage players and boards, +and scale as participation grows. +Our project has Mobile-friendly UI (responsive layout for phone/tablet). + +## Live demo +- https://deadpigeons-frontend.fly.dev + email and password are stored in Wiseflow. + +### Run API +From repo root (adjust paths if yours differ): +```bash +cd server/api +dotnet run +``` +### Run Client +```bash +cd client +npm install +npm run dev +``` +### Run Tests +```bash +cd server/tests +dotnet test +``` + ## Prerequisites - .NET SDK **9.x** - Node.js -- PostgreSQL -- Fly.io CLI (for deployment) +- PostgreSQL (Neon db) +- Fly.io CLI + Fly.io account (deployment) - Docker (for running PostgreSQL locally and for deployment) - GitHub account (for CI/CD) -- Fly.io account (for hosting) ## Environment Full-stack lottery web application with: - api: ASP.NET Core (.NET 9), EF Core + PostgreSQL - client: React + TypeScript + Tailwind CSS - tests: xUnit + Testcontainers + XUnit.DependencyInjection -- CI/CD: GitHub Actions + Docker + Fly.io -- infrastructure: Fly.io + PostgreSQL +- CI: GitHub Actions (build + tests) +- deployment: Docker + Fly.io +- infrastructure: Fly.io + PostgreSQL (Neon) - querying: sieve (filter/sort/paging) - authentication: JWT access tokens - documentation: Swagger / OpenAPI ## Configuration ### API configuration -Configuration (connection string and Jwt secret) are read from `server/api/appsettings.json` -Swagger enabled in development mode, see Program.cs -Sieve is enabled for selected list endpoints, filter/sort/paging via query parameters +- Configuration values (connection string and Jwt secret) are read from `server/api/appsettings.json`. +- Swagger is enabled (please see `server/api/Program.cs`). +- Sieve is enabled for selected list endpoints (filter/sort/paging via query parameters using `SieveModel`). -## Client configuration -VITE_API_URL is stored in .env file. -Build mode (dev/prod) settings -Proxy settings for local development in vite.config.ts +### Client configuration +- `VITE_API_URL` is stored in `client/.env`. +- Build modes: development vs production (Vite). +- Proxy settings for local development are configured in `client/vite.config.ts`. ### Testing -## How we test -xUnit tests for service methods: -Happy path + unhappy path -Testcontainers for isolated Postgres persistence -XUnit.DependencyInjection for test setup - -### Linting -Client linting is handled with ESLint (TypeScript + React rules). eslint.config.js contains the configuration. +#### How we test +- xUnit tests for service methods (happy path + unhappy path). +- Testcontainers for isolated PostgreSQL persistence. +- XUnit.DependencyInjection for test setup / DI wiring. + +## Linting +Client linting is handled with ESLint (TypeScript + React rules). +`eslint.config.js` contains the configuration. The client uses ESLint (v9 “flat config”) with TypeScript + React rules. - Config: `client/eslint.config.js` - Run lint: + - `cd client` - `npm run lint` -- `dist/` is ignored +- Ignored: + - `dist/` - Rules include: - - `@eslint/js` - - `typescript-eslint` - - `eslint-plugin-react-hooks` + - `@eslint/js` + - `typescript-eslint` + - `eslint-plugin-react-hooks` - `eslint-plugin-react-refresh` (Vite) +## Authentication +The API uses email + password login. +- Users log in with **email** and **password**. +- Passwords are hashed with **Argon2id** and never stored in plain text. +- On successful login, the API issues a **JWT access token**. +- The client must send the JWT with subsequent requests (typically in the `Authorization: Bearer ` header). + +## JWT +JWT access token details: +- Contains claims: + - `sub` = user ID + - `role` = user role (Admin / Player) +- Signed using **HMAC-SHA256** +- Valid for **8 hours** +- Automatically validated by ASP.NET middleware: + - signature validation + - expiration validation + ## Authorization -We use role-based authorization policies. - -### Anonymous -(not logged in): no token required -- POST /api/auth/login - -### Authenticated user -(Admin + Player) -- Board: GET /api/board/user/{userId} -- Board price: GET /api/BoardPrice -- Transactions: GET /api/Transaction/user/{userId} -- User: GET /api/user + GET /api/user/me -- Game result: GET /api/games/draw/history - -### Player only -- Transactions: POST /api/Transaction/deposit -- Board: POST /api/board/user/purchase + PUT /api/board/{boardId}/auto-repeat + GET /api/board/purchase/status - -### Admin only -- Transactions: PUT /api/Transaction/{id}/status + GET /api/Transaction/pending -- User: POST /api/user + PATCH /api/user/{id}/activate + PATCH /api/user/{id}/deactivate -- Board: GET /api/board/admin/all - -### Limitations -- **No rate limiting / brute-force protection.** - Login endpoint does not implement throttling, lockout, or captcha, so it’s not hardened against repeated attempts. -- **No email verification / password reset.** - User registration does not include email verification or password reset flows. -- **No internationalization (i18n).** - The client does not support multiple languages. +We use role-based authorization policies (Admin / Player) and in some cases +resource-based rules (a Player can only access their own data). + +### Anonymous (not logged in) +No token required: +- `POST /api/auth/login` (login) -- +### Authenticated user (role = Player or Admin) +Users must be logged in (JWT required). -- ** with mobile optimization.** - The client is specifically optimized for mobile devices. -- -### Authentication +**Boards** +- `GET /api/board/user/{userId}` (player = only own boards; admin = any user) - ** The API uses JWT-based authentication with Argon2id password hashing. -- Users log in using email and password -- Passwords are securely hashed with Argon2id and never stored in plain text -- On successful login, the API issues a JWT -- The JWT contains the user ID (sub) and role (role) and must be sent with subsequent requests - ** JWT details -- Signed using HMAC-SHA256 -- Valid for 8 hours -- Validated automatically by middleware (signature and expiration) +**Board prices** +- `GET /api/BoardPrice` (accessible when logged in) +**Game results** +- `GET /api/games/draw/history` (view past winning draws) +### Player only (role = Player) +Players must be authenticated. -### Deployment +**Boards** +- `GET /api/board/user/{userId}` (view own boards only) +- `POST /api/board/user/purchase` (purchase boards for self only) +- `PUT /api/board/{boardId}/auto-repeat` (set auto-repeat for own board only) +- `GET /api/board/purchase/status` (view own purchase status only) - ** The backend is deployed using Docker and Fly.io. +**Repeats** +- `GET /api/repeat/me` (own repeats only) +- `POST /api/repeat` (create repeat for self only) +- `POST /api/repeat/{id}/stop` (stop own repeat only) + +**Transactions** +- `POST /api/Transaction/deposit` (create deposit for self only) +- `GET /api/Transaction/user/{userId}` (view own transactions only) + +### Admin only (role = Admin) +Admins must be authenticated. + +**Admin game management** +- `POST /api/admin/games/draw` (trigger draw) +- `GET /api/admin/games/draw/status` (view draw status) +- `GET /api/admin/games/winners/summary` (view winners summary) +- `GET /api/admin/games/draw/history` (view draw history) + +**Users** +- `POST /api/user` (create user) +- `PATCH /api/user/{id}/activate` (activate user) +- `PATCH /api/user/{id}/deactivate` (deactivate user) +- `GET /api/user` (view all users) + +**Boards** +- `GET /api/board/admin/all` (view all boards) +- `GET /api/board/user/{userId}` (admin can view any user’s boards) + +**Transactions** +- `GET /api/Transaction/pending` (view pending transactions) +- `PUT /api/Transaction/{id}/status` (approve/reject transaction) + +## Architecture / Data model +> Data model diagram: +![DBdeadpigeons.png](client/public/DBdeadpigeons.png) + +## Deployment +**The backend is deployed using Docker and Fly.io.** - The API is built with a multi-stage Docker build - A lightweight ASP.NET runtime image is used in production - Deployed to Fly.io in the fra region - Runs on port 8080 with HTTPS enforced -- Fly.io manages TLS, scaling, and machine lifecycle - ** Configuration +- Fly.io manages TLS, scaling, and machine lifecycle + ** Configuration - Database connection and JWT secret are provided via AppSettings - In production, secrets are supplied using environment variables / Fly.io secrets - ### Proxy & Frontend Integration - - Frontend runs on http://localhost:5173 - API requests are proxied to the backend - CORS allows the local frontend origin - ** Production +**Production** - Frontend and backend are hosted separately - Backend is accessed over HTTPS via Fly.io - CORS allows only the production frontend domain -This setup ensures secure and smooth communication in both development and production environments. - -### Run API -From repo root (adjust paths if yours differ): -```bash -cd server/api -dotnet run -``` -### Run Client -```bash -cd client -npm install -npm run dev -``` - -### Run Tests -```bash -cd server/tests -dotnet test - - - -``` -![DBdeadpigeons.png](client/public/DBdeadpigeons.png) - - - + This setup ensures secure and smooth communication in both development and production environments. +## Limitations +- **No rate limiting / brute-force protection.** + Login endpoint does not implement throttling, lockout, or captcha, so it’s not hardened against repeated attempts. +- **No email verification / password reset.** + User registration does not include email verification or “forgot password” functionality. +- **No internationalization (i18n).** + The client UI does not support multiple languages.