diff --git a/README.md b/README.md index ce83a51a..9cb41d9a 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Data generation pipeline (C++): Active areas in the repository: - .NET 10 backend (vertical-slice architecture with MediatR) + SQL Server -- React 19 website (React Router 7 + Vite) +- React 19 website (React Router 8 + Vite) - Shared Biergarten theme system + Storybook coverage - Auth flows and account/email integration (local Mailpit in dev compose) - Photo upload to S3-compatible object storage (local SeaweedFS in dev compose) @@ -54,7 +54,7 @@ Archived/reference areas: ## Tech stack - **Backend**: .NET 10, ASP.NET Core, SQL Server 2022, DbUp -- **Frontend**: React 19, React Router 7, Vite 7, Tailwind CSS 4, DaisyUI 5, +- **Frontend**: React 19, React Router 8, Vite 7, Tailwind CSS 4, DaisyUI 5, Leaflet/React Leaflet (brewery maps) - **UI Documentation**: Storybook 10, Vitest browser mode, Playwright - **Testing**: xUnit, Reqnroll (BDD), FluentAssertions, Moq @@ -66,7 +66,7 @@ Archived/reference areas: ## Requirements - .NET SDK 10 or later -- Node.js 18 or later +- Node.js 22 or later - Docker Desktop, or an equivalent Docker Engine setup See [Getting Started](docs/web/getting-started.md) for the full @@ -83,10 +83,15 @@ This section is the shortest path to a working dev environment. git clone https://github.com/aaronpo97/the-biergarten-app cd the-biergarten-app -cp web/.env.example web/.env.dev +cd web && ./generate-env.sh .env.dev && cd .. docker compose --env-file web/.env.dev -f web/docker-compose.dev.yaml up --build -d ``` +`generate-env.sh` copies `.env.example` and replaces the secret-bearing values +with freshly randomized ones; a plain `cp` leaves placeholder secrets in +place. See [Environment Variables](docs/web/environment-variables.md#generating-an-env-file) +for details. + Access: - Website: http://localhost:3000 @@ -137,7 +142,7 @@ Create a test environment file, then run the full test stack (backend and frontend) with Docker: ```bash -cp web/.env.example web/.env.test +cd web && ./generate-env.sh .env.test && cd .. docker compose --env-file web/.env.test -f web/docker-compose.test.yaml up --abort-on-container-exit ``` diff --git a/docs/web/architecture.md b/docs/web/architecture.md index c43dcab6..ad6faabf 100644 --- a/docs/web/architecture.md +++ b/docs/web/architecture.md @@ -8,7 +8,7 @@ The Biergarten App is a monorepo split between the backend and the website: - **Backend**: .NET 10 Web API with SQL Server, organized as vertical feature slices with MediatR -- **Frontend**: React 19 + React Router 7 website in `web/frontend` +- **Frontend**: React 19 + React Router 8 website in `web/frontend` - **Architecture Style**: Vertical-slice backend plus server-rendered React frontend @@ -96,6 +96,7 @@ flowchart TB HOST -- "discovers controllers via
AddApplicationPart" --> SLICES S1 -- "SendRegistrationEmailCommand
(via Shared.Application contract)" --> S3 + S1 -- "ProjectReference
(avatar upload)" --> S5 SLICES --> SHARED SLICES --> DOMAIN @@ -108,11 +109,13 @@ flowchart TB style S5 stroke-dasharray: 4 3 ``` -Slices never reference each other's project. The one cross-slice interaction -(`Features.Users` triggering a confirmation email handled by `Features.Emails`) -goes through a MediatR command (`SendRegistrationEmailCommand`) whose contract -lives in `Shared.Application`, so neither slice takes a project reference on the -other. +Slices generally don't reference each other's project. Most cross-slice +interactions go through a MediatR command instead — for example, +`Features.Users` triggering a confirmation email handled by `Features.Emails` +via a `SendRegistrationEmailCommand` whose contract lives in +`Shared.Application`, so neither slice takes a project reference on the other. +The one exception is `Features.Users`, which takes a direct `ProjectReference` +on `Features.PhotoUpload` to send `UploadPhotoCommand` for avatar uploads. ### Layer responsibilities @@ -173,13 +176,14 @@ brewery photo upload command) rather than bound to an HTTP route directly. **Dependencies**: - `Domain.Entities`, `Domain.Exceptions` -- `Infrastructure.Sql` (generic ADO.NET plumbing) plus whichever infrastructure - project the slice needs (`Infrastructure.Jwt`/ +- `Database.Connection` (generic ADO.NET connection/command plumbing) plus + whichever infrastructure project the slice needs (`Infrastructure.Jwt`/ `Infrastructure.PasswordHashing` for Users, `Infrastructure.Email`/ `Infrastructure.Email.Templates` for Emails, `Infrastructure.FileUpload` for PhotoUpload) - `Shared.Contracts`, `Shared.Application` -- **Never** another `Features.*` project +- No other `Features.*` project, with one exception: `Features.Users` + references `Features.PhotoUpload` directly for avatar uploads **Rules**: @@ -375,7 +379,7 @@ error-handling approach in more detail. ### Website (`web/frontend`) -The website is a React Router 7 application with server-side rendering enabled. +The website is a React Router 8 application with server-side rendering enabled. ```text web/frontend/ @@ -395,7 +399,7 @@ web/frontend/ │ │ - home landing page │ │ - theme theme switcher/guide │ ├── hooks/ Shared hooks not tied to one feature, for example useMediaQuery -│ ├── routes.ts Route table (React Router 7 config-based routing) +│ ├── routes.ts Route table (React Router 8 config-based routing) │ ├── root.tsx App shell and global providers │ └── app.css Theme tokens and global styling ├── .storybook/ Storybook config and preview setup @@ -471,7 +475,7 @@ reference. Product and engineering documentation points to `web/frontend`. - Memory: 64MB - Iterations: 4 -- Parallelism: CPU core count +- Parallelism: 2 (hardcoded, to avoid thread-pool exhaustion) - Salt: 128-bit (16 bytes) - Hash: 256-bit (32 bytes) @@ -499,6 +503,12 @@ reference. Product and engineering documentation points to `web/frontend`. } ``` +This block exists in `appsettings.json`, but nothing in the code currently +reads it: actual token lifetimes come from the hardcoded +`TokenServiceExpirationHours` constants, and `JwtInfrastructure` hardcodes +`ValidateIssuer`/`ValidateAudience` to `false`, so `Issuer`/`Audience` are not +enforced. + ## Database architecture For the table-by-table schema and the custom SQL error code scheme, see diff --git a/docs/web/environment-variables.md b/docs/web/environment-variables.md index 09dea3ac..232f0fde 100644 --- a/docs/web/environment-variables.md +++ b/docs/web/environment-variables.md @@ -302,10 +302,16 @@ web/.env.prod # Production config (gitignored) **Setup**: ```bash -cp web/.env.example web/.env.dev -# Edit web/.env.dev with your values +cd web +./generate-env.sh .env.dev ``` +See [Generating an env file](#generating-an-env-file) above — `generate-env.sh` +randomizes the secret-bearing values, so it's the preferred way to create any +`.env.dev`/`.env.test`/`.env.prod` file. A plain `cp web/.env.example +web/.env.dev` works too, but leaves the placeholder secrets from the template +in place; edit them manually if you go that route. + **Docker Compose Mapping**: - `web/docker-compose.dev.yaml` → `web/.env.dev` diff --git a/docs/web/getting-started.md b/docs/web/getting-started.md index ad5027f3..26aee44c 100644 --- a/docs/web/getting-started.md +++ b/docs/web/getting-started.md @@ -13,7 +13,7 @@ This guide covers local setup for the Biergarten stack: the .NET backend in ## Prerequisites - **.NET SDK 10+** -- **Node.js 18+** +- **Node.js 22+** - **Docker Desktop** or equivalent Docker Engine setup - **Java 8+** if you want to regenerate PlantUML diagrams