mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-05-31 17:53:59 +00:00
Format all markdown files in active directories
This commit is contained in:
22
.github/ISSUE_TEMPLATE/feature_request.md
vendored
22
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -7,6 +7,7 @@ assignees: []
|
||||
---
|
||||
|
||||
## User Story
|
||||
|
||||
**As a** (who wants to accomplish something)
|
||||
**I want to** (what they want to accomplish)
|
||||
**So that** (why they want to accomplish that thing)
|
||||
@@ -15,29 +16,18 @@ assignees: []
|
||||
|
||||
### Scenario 1
|
||||
|
||||
|
||||
Given ...
|
||||
When ...
|
||||
Then ...
|
||||
|
||||
Given ... When ... Then ...
|
||||
|
||||
### Scenario 2
|
||||
|
||||
|
||||
Given ...
|
||||
When ...
|
||||
Then ...
|
||||
|
||||
Given ... When ... Then ...
|
||||
|
||||
### Scenario 3
|
||||
|
||||
|
||||
Given ...
|
||||
When ...
|
||||
Then ...
|
||||
|
||||
Given ... When ... Then ...
|
||||
|
||||
## Subtasks
|
||||
|
||||
- [ ] Task 1
|
||||
- [ ] Task 2
|
||||
- [ ] Task 3
|
||||
- [ ] Task 3
|
||||
|
||||
962
LICENSE.md
962
LICENSE.md
File diff suppressed because it is too large
Load Diff
46
README.md
46
README.md
@@ -1,25 +1,33 @@
|
||||
# The Biergarten App
|
||||
|
||||
The Biergarten App is a multi-project monorepo with a .NET backend and an active React
|
||||
Router frontend in `src/Website`. The current website focuses on account flows, theme
|
||||
switching, shared UI components, Storybook coverage, and integration with the API.
|
||||
The Biergarten App is a multi-project monorepo with a .NET backend and an active
|
||||
React Router frontend in `src/Website`. The current website focuses on account
|
||||
flows, theme switching, shared UI components, Storybook coverage, and
|
||||
integration with the API.
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Getting Started](docs/getting-started.md) - Local setup for backend and active website
|
||||
- [Architecture](docs/architecture.md) - Current backend and frontend architecture
|
||||
- [Docker Guide](docs/docker.md) - Container-based backend development and testing
|
||||
- [Getting Started](docs/getting-started.md) - Local setup for backend and
|
||||
active website
|
||||
- [Architecture](docs/architecture.md) - Current backend and frontend
|
||||
architecture
|
||||
- [Docker Guide](docs/docker.md) - Container-based backend development and
|
||||
testing
|
||||
- [Testing](docs/testing.md) - Backend and frontend test commands
|
||||
- [Environment Variables](docs/environment-variables.md) - Active configuration reference
|
||||
- [Environment Variables](docs/environment-variables.md) - Active configuration
|
||||
reference
|
||||
- [Token Validation](docs/token-validation.md) - JWT validation architecture
|
||||
- [Legacy Website Archive](docs/archive/legacy-website-v1.md) - Archived notes for the old Next.js frontend
|
||||
- [Legacy Website Archive](docs/archive/legacy-website-v1.md) - Archived notes
|
||||
for the old Next.js frontend
|
||||
|
||||
## Diagrams
|
||||
|
||||
- [Architecture](docs/diagrams-out/architecture.svg) - Layered architecture
|
||||
- [Deployment](docs/diagrams-out/deployment.svg) - Docker topology
|
||||
- [Authentication Flow](docs/diagrams-out/authentication-flow.svg) - Auth sequence
|
||||
- [Database Schema](docs/diagrams-out/database-schema.svg) - Entity relationships
|
||||
- [Authentication Flow](docs/diagrams-out/authentication-flow.svg) - Auth
|
||||
sequence
|
||||
- [Database Schema](docs/diagrams-out/database-schema.svg) - Entity
|
||||
relationships
|
||||
|
||||
## Current Status
|
||||
|
||||
@@ -34,7 +42,8 @@ Active areas in the repository:
|
||||
|
||||
Legacy area retained for reference:
|
||||
|
||||
- `src/Website-v1` contains the archived Next.js frontend and is no longer the active website
|
||||
- `src/Website-v1` contains the archived Next.js frontend and is no longer the
|
||||
active website
|
||||
|
||||
## Tech Stack
|
||||
|
||||
@@ -43,7 +52,8 @@ Legacy area retained for reference:
|
||||
- **UI Documentation**: Storybook 10, Vitest browser mode, Playwright
|
||||
- **Testing**: xUnit, Reqnroll (BDD), FluentAssertions, Moq
|
||||
- **Infrastructure**: Docker, Docker Compose
|
||||
- **Security**: Argon2id password hashing, JWT access/refresh/confirmation tokens
|
||||
- **Security**: Argon2id password hashing, JWT access/refresh/confirmation
|
||||
tokens
|
||||
|
||||
## Quick Start
|
||||
|
||||
@@ -133,7 +143,8 @@ See [Testing](docs/testing.md) for the full command list.
|
||||
|
||||
Common active variables:
|
||||
|
||||
- Backend: `DB_SERVER`, `DB_NAME`, `DB_USER`, `DB_PASSWORD`, `ACCESS_TOKEN_SECRET`, `REFRESH_TOKEN_SECRET`, `CONFIRMATION_TOKEN_SECRET`
|
||||
- Backend: `DB_SERVER`, `DB_NAME`, `DB_USER`, `DB_PASSWORD`,
|
||||
`ACCESS_TOKEN_SECRET`, `REFRESH_TOKEN_SECRET`, `CONFIRMATION_TOKEN_SECRET`
|
||||
- Frontend: `API_BASE_URL`, `SESSION_SECRET`, `NODE_ENV`
|
||||
|
||||
See [Environment Variables](docs/environment-variables.md) for details.
|
||||
@@ -148,10 +159,13 @@ See [Environment Variables](docs/environment-variables.md) for details.
|
||||
|
||||
### Development Workflow
|
||||
|
||||
1. Start development environment: `docker compose -f docker-compose.dev.yaml up -d`
|
||||
1. Start development environment:
|
||||
`docker compose -f docker-compose.dev.yaml up -d`
|
||||
2. Make changes to code
|
||||
3. Run tests: `docker compose -f docker-compose.test.yaml up --abort-on-container-exit`
|
||||
4. Rebuild if needed: `docker compose -f docker-compose.dev.yaml up -d --build api.core`
|
||||
3. Run tests:
|
||||
`docker compose -f docker-compose.test.yaml up --abort-on-container-exit`
|
||||
4. Rebuild if needed:
|
||||
`docker compose -f docker-compose.dev.yaml up -d --build api.core`
|
||||
|
||||
## Support
|
||||
|
||||
|
||||
@@ -4,24 +4,28 @@ This document describes the active architecture of The Biergarten App.
|
||||
|
||||
## High-Level Overview
|
||||
|
||||
The Biergarten App is a monorepo with a clear split between the backend and the active
|
||||
website:
|
||||
The Biergarten App is a monorepo with a clear split between the backend and the
|
||||
active website:
|
||||
|
||||
- **Backend**: .NET 10 Web API with SQL Server and a layered architecture
|
||||
- **Frontend**: React 19 + React Router 7 website in `src/Website`
|
||||
- **Architecture Style**: Layered backend plus server-rendered React frontend
|
||||
|
||||
The legacy Next.js frontend has been retained in `src/Website-v1` for reference only and is
|
||||
documented in [archive/legacy-website-v1.md](archive/legacy-website-v1.md).
|
||||
The legacy Next.js frontend has been retained in `src/Website-v1` for reference
|
||||
only and is documented in
|
||||
[archive/legacy-website-v1.md](archive/legacy-website-v1.md).
|
||||
|
||||
## Diagrams
|
||||
|
||||
For visual representations, see:
|
||||
|
||||
- [architecture.svg](diagrams-out/architecture.svg) - Layered architecture diagram
|
||||
- [architecture.svg](diagrams-out/architecture.svg) - Layered architecture
|
||||
diagram
|
||||
- [deployment.svg](diagrams-out/deployment.svg) - Docker deployment diagram
|
||||
- [authentication-flow.svg](diagrams-out/authentication-flow.svg) - Authentication workflow
|
||||
- [database-schema.svg](diagrams-out/database-schema.svg) - Database relationships
|
||||
- [authentication-flow.svg](diagrams-out/authentication-flow.svg) -
|
||||
Authentication workflow
|
||||
- [database-schema.svg](diagrams-out/database-schema.svg) - Database
|
||||
relationships
|
||||
|
||||
## Backend Architecture
|
||||
|
||||
@@ -218,7 +222,8 @@ public interface IAuthRepository
|
||||
|
||||
### Active Website (`src/Website`)
|
||||
|
||||
The current website is a React Router 7 application with server-side rendering enabled.
|
||||
The current website is a React Router 7 application with server-side rendering
|
||||
enabled.
|
||||
|
||||
```text
|
||||
src/Website/
|
||||
@@ -244,20 +249,22 @@ src/Website/
|
||||
|
||||
### Theme System
|
||||
|
||||
The active website uses semantic DaisyUI theme tokens backed by four Biergarten themes:
|
||||
The active website uses semantic DaisyUI theme tokens backed by four Biergarten
|
||||
themes:
|
||||
|
||||
- Biergarten Lager
|
||||
- Biergarten Stout
|
||||
- Biergarten Cassis
|
||||
- Biergarten Weizen
|
||||
|
||||
All component styling should prefer semantic tokens such as `primary`, `success`,
|
||||
`surface`, and `highlight` instead of hard-coded color values.
|
||||
All component styling should prefer semantic tokens such as `primary`,
|
||||
`success`, `surface`, and `highlight` instead of hard-coded color values.
|
||||
|
||||
### Legacy Frontend
|
||||
|
||||
The previous Next.js frontend has been archived at `src/Website-v1`. Active product and
|
||||
engineering documentation should point to `src/Website`, while legacy notes live in
|
||||
The previous Next.js frontend has been archived at `src/Website-v1`. Active
|
||||
product and engineering documentation should point to `src/Website`, while
|
||||
legacy notes live in
|
||||
[archive/legacy-website-v1.md](archive/legacy-website-v1.md).
|
||||
|
||||
## Security Architecture
|
||||
@@ -387,8 +394,8 @@ For details, see [Docker Guide](docker.md).
|
||||
|
||||
### Health Checks
|
||||
|
||||
**SQL Server**: Validates database connectivity **API**: Checks service health and
|
||||
dependencies
|
||||
**SQL Server**: Validates database connectivity **API**: Checks service health
|
||||
and dependencies
|
||||
|
||||
**Configuration**:
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Docker Guide
|
||||
|
||||
This document covers Docker deployment, configuration, and troubleshooting for The
|
||||
Biergarten App.
|
||||
This document covers Docker deployment, configuration, and troubleshooting for
|
||||
The Biergarten App.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -13,7 +13,8 @@ The project uses Docker Compose to orchestrate multiple services:
|
||||
- .NET API
|
||||
- Test runners
|
||||
|
||||
See the [deployment diagram](diagrams/pdf/deployment.pdf) for visual representation.
|
||||
See the [deployment diagram](diagrams/pdf/deployment.pdf) for visual
|
||||
representation.
|
||||
|
||||
## Docker Compose Environments
|
||||
|
||||
@@ -144,7 +145,11 @@ api.core / tests (start when ready)
|
||||
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', "sqlcmd -S localhost -U sa -P '${DB_PASSWORD}' -C -Q 'SELECT 1'"]
|
||||
test:
|
||||
[
|
||||
"CMD-SHELL",
|
||||
"sqlcmd -S localhost -U sa -P '${DB_PASSWORD}' -C -Q 'SELECT 1'",
|
||||
]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
@@ -209,16 +214,16 @@ Each environment uses isolated bridge networks:
|
||||
All containers are configured via environment variables from `.env` files:
|
||||
|
||||
```yaml
|
||||
env_file: '.env.dev' # or .env.test, .env.prod
|
||||
env_file: ".env.dev" # or .env.test, .env.prod
|
||||
|
||||
environment:
|
||||
ASPNETCORE_ENVIRONMENT: 'Development'
|
||||
DOTNET_RUNNING_IN_CONTAINER: 'true'
|
||||
DB_SERVER: '${DB_SERVER}'
|
||||
DB_NAME: '${DB_NAME}'
|
||||
DB_USER: '${DB_USER}'
|
||||
DB_PASSWORD: '${DB_PASSWORD}'
|
||||
JWT_SECRET: '${JWT_SECRET}'
|
||||
ASPNETCORE_ENVIRONMENT: "Development"
|
||||
DOTNET_RUNNING_IN_CONTAINER: "true"
|
||||
DB_SERVER: "${DB_SERVER}"
|
||||
DB_NAME: "${DB_NAME}"
|
||||
DB_USER: "${DB_USER}"
|
||||
DB_PASSWORD: "${DB_PASSWORD}"
|
||||
JWT_SECRET: "${JWT_SECRET}"
|
||||
```
|
||||
|
||||
For complete list, see [Environment Variables](environment-variables.md).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Environment Variables
|
||||
|
||||
This document covers the active environment variables used by the current Biergarten
|
||||
stack.
|
||||
This document covers the active environment variables used by the current
|
||||
Biergarten stack.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -19,8 +19,8 @@ Direct environment variable access via `Environment.GetEnvironmentVariable()`.
|
||||
|
||||
### Frontend (`src/Website`)
|
||||
|
||||
The active website reads runtime values from the server environment for its auth and API
|
||||
integration.
|
||||
The active website reads runtime values from the server environment for its auth
|
||||
and API integration.
|
||||
|
||||
### Docker
|
||||
|
||||
@@ -54,14 +54,15 @@ Provide complete connection string:
|
||||
DB_CONNECTION_STRING="Server=localhost,1433;Database=Biergarten;User Id=sa;Password=YourStrong!Passw0rd;TrustServerCertificate=True;"
|
||||
```
|
||||
|
||||
**Priority**: `DB_CONNECTION_STRING` is checked first. If not found, connection string is
|
||||
built from components.
|
||||
**Priority**: `DB_CONNECTION_STRING` is checked first. If not found, connection
|
||||
string is built from components.
|
||||
|
||||
**Implementation**: See `DefaultSqlConnectionFactory.cs`
|
||||
|
||||
### JWT Authentication Secrets (Backend)
|
||||
|
||||
The backend uses separate secrets for different token types to enable independent key rotation and validation isolation.
|
||||
The backend uses separate secrets for different token types to enable
|
||||
independent key rotation and validation isolation.
|
||||
|
||||
```bash
|
||||
# Access token secret (1-hour tokens)
|
||||
@@ -131,8 +132,8 @@ DOTNET_RUNNING_IN_CONTAINER=true # Flag for container execution
|
||||
|
||||
## Frontend Variables (`src/Website`)
|
||||
|
||||
The active website does not use the old Next.js/Prisma environment model. Its core runtime
|
||||
variables are:
|
||||
The active website does not use the old Next.js/Prisma environment model. Its
|
||||
core runtime variables are:
|
||||
|
||||
```bash
|
||||
API_BASE_URL=http://localhost:8080 # Base URL for the .NET API
|
||||
@@ -208,9 +209,10 @@ cp .env.example .env.dev
|
||||
|
||||
## Legacy Frontend Variables
|
||||
|
||||
Variables for the archived Next.js frontend (`src/Website-v1`) have been removed from this
|
||||
active reference. See [archive/legacy-website-v1.md](archive/legacy-website-v1.md) if you
|
||||
need the legacy Prisma, Cloudinary, Mapbox, or SparkPost notes.
|
||||
Variables for the archived Next.js frontend (`src/Website-v1`) have been removed
|
||||
from this active reference. See
|
||||
[archive/legacy-website-v1.md](archive/legacy-website-v1.md) if you need the
|
||||
legacy Prisma, Cloudinary, Mapbox, or SparkPost notes.
|
||||
|
||||
**Docker Compose Mapping**:
|
||||
|
||||
@@ -243,8 +245,8 @@ need the legacy Prisma, Cloudinary, Mapbox, or SparkPost notes.
|
||||
| `MSSQL_PID` | | | ✓ | No | SQL Server edition |
|
||||
| `DOTNET_RUNNING_IN_CONTAINER` | ✓ | | ✓ | No | Container flag |
|
||||
|
||||
\* Either `DB_CONNECTION_STRING` OR the component variables (`DB_SERVER`, `DB_NAME`,
|
||||
`DB_USER`, `DB_PASSWORD`) must be provided.
|
||||
\* Either `DB_CONNECTION_STRING` OR the component variables (`DB_SERVER`,
|
||||
`DB_NAME`, `DB_USER`, `DB_PASSWORD`) must be provided.
|
||||
|
||||
## Validation
|
||||
|
||||
@@ -258,8 +260,8 @@ Variables are validated at startup:
|
||||
|
||||
### Frontend Validation
|
||||
|
||||
The active website relies on runtime defaults for local development and the surrounding
|
||||
server environment in deployed environments.
|
||||
The active website relies on runtime defaults for local development and the
|
||||
surrounding server environment in deployed environments.
|
||||
|
||||
- `API_BASE_URL` defaults to `http://localhost:8080`
|
||||
- `SESSION_SECRET` falls back to a development-only local secret
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Getting Started
|
||||
|
||||
This guide covers local setup for the current Biergarten stack: the .NET backend in
|
||||
`src/Core` and the active React Router frontend in `src/Website`.
|
||||
This guide covers local setup for the current Biergarten stack: the .NET backend
|
||||
in `src/Core` and the active React Router frontend in `src/Website`.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -128,8 +128,9 @@ dotnet run --project API/API.Core/API.Core.csproj
|
||||
|
||||
## Legacy Frontend Note
|
||||
|
||||
The previous Next.js frontend now lives in `src/Website-v1` and is not the active website.
|
||||
Legacy setup details have been moved to [docs/archive/legacy-website-v1.md](archive/legacy-website-v1.md).
|
||||
The previous Next.js frontend now lives in `src/Website-v1` and is not the
|
||||
active website. Legacy setup details have been moved to
|
||||
[docs/archive/legacy-website-v1.md](archive/legacy-website-v1.md).
|
||||
|
||||
## Next Steps
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Testing
|
||||
|
||||
This document describes the testing strategy and how to run tests for The Biergarten App.
|
||||
This document describes the testing strategy and how to run tests for The
|
||||
Biergarten App.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -9,13 +10,15 @@ The project uses a multi-layered testing approach across backend and frontend:
|
||||
- **API.Specs** - BDD integration tests using Reqnroll (Gherkin)
|
||||
- **Infrastructure.Repository.Tests** - Unit tests for data access layer
|
||||
- **Service.Auth.Tests** - Unit tests for authentication business logic
|
||||
- **Storybook Vitest project** - Browser-based interaction tests for shared website stories
|
||||
- **Storybook Playwright suite** - Browser checks against Storybook-rendered components
|
||||
- **Storybook Vitest project** - Browser-based interaction tests for shared
|
||||
website stories
|
||||
- **Storybook Playwright suite** - Browser checks against Storybook-rendered
|
||||
components
|
||||
|
||||
## Running Tests with Docker (Recommended)
|
||||
|
||||
The easiest way to run all tests is using Docker Compose, which sets up an isolated test
|
||||
environment:
|
||||
The easiest way to run all tests is using Docker Compose, which sets up an
|
||||
isolated test environment:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.test.yaml up --abort-on-container-exit
|
||||
@@ -98,7 +101,8 @@ npm run test:storybook
|
||||
|
||||
**Purpose**:
|
||||
|
||||
- Verifies shared stories such as form fields, submit buttons, navbar states, toasts, and the theme gallery
|
||||
- Verifies shared stories such as form fields, submit buttons, navbar states,
|
||||
toasts, and the theme gallery
|
||||
- Runs in browser mode via Vitest and Storybook integration
|
||||
|
||||
### Frontend Playwright Storybook Tests
|
||||
@@ -113,7 +117,8 @@ npm run test:storybook:playwright
|
||||
|
||||
- Storybook dependencies installed
|
||||
- Playwright browser dependencies installed
|
||||
- The command will start or reuse the Storybook server defined in `playwright.storybook.config.ts`
|
||||
- The command will start or reuse the Storybook server defined in
|
||||
`playwright.storybook.config.ts`
|
||||
|
||||
## Test Coverage
|
||||
|
||||
@@ -278,7 +283,8 @@ Scenario: User login with valid credentials
|
||||
|
||||
## Continuous Integration
|
||||
|
||||
Tests run automatically in CI/CD pipelines using the test Docker Compose configuration:
|
||||
Tests run automatically in CI/CD pipelines using the test Docker Compose
|
||||
configuration:
|
||||
|
||||
```bash
|
||||
# CI/CD command
|
||||
@@ -292,7 +298,8 @@ Exit codes:
|
||||
- `0` - All tests passed
|
||||
- Non-zero - Test failures occurred
|
||||
|
||||
Frontend UI checks should also be included in CI for the active website workspace:
|
||||
Frontend UI checks should also be included in CI for the active website
|
||||
workspace:
|
||||
|
||||
```bash
|
||||
cd src/Website
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
|
||||
## Overview
|
||||
|
||||
The Core project implements comprehensive JWT token validation across three token types:
|
||||
The Core project implements comprehensive JWT token validation across three
|
||||
token types:
|
||||
|
||||
- **Access Tokens**: Short-lived (1 hour) tokens for API authentication
|
||||
- **Refresh Tokens**: Long-lived (21 days) tokens for obtaining new access tokens
|
||||
- **Confirmation Tokens**: Short-lived (30 minutes) tokens for email confirmation
|
||||
- **Refresh Tokens**: Long-lived (21 days) tokens for obtaining new access
|
||||
tokens
|
||||
- **Confirmation Tokens**: Short-lived (30 minutes) tokens for email
|
||||
confirmation
|
||||
|
||||
## Components
|
||||
|
||||
@@ -17,10 +20,13 @@ The Core project implements comprehensive JWT token validation across three toke
|
||||
Low-level JWT operations.
|
||||
|
||||
**Methods:**
|
||||
|
||||
- `GenerateJwt()` - Creates signed JWT tokens
|
||||
- `ValidateJwtAsync()` - Validates token signature, expiration, and format
|
||||
|
||||
**Implementation:** [JwtInfrastructure.cs](Infrastructure.Jwt/JwtInfrastructure.cs)
|
||||
**Implementation:**
|
||||
[JwtInfrastructure.cs](Infrastructure.Jwt/JwtInfrastructure.cs)
|
||||
|
||||
- Uses Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler
|
||||
- Algorithm: HS256 (HMAC-SHA256)
|
||||
- Validates token lifetime, signature, and well-formedness
|
||||
@@ -32,16 +38,20 @@ Low-level JWT operations.
|
||||
High-level token validation with context (token type, user extraction).
|
||||
|
||||
**Methods:**
|
||||
|
||||
- `ValidateAccessTokenAsync(string token)` - Validates access tokens
|
||||
- `ValidateRefreshTokenAsync(string token)` - Validates refresh tokens
|
||||
- `ValidateConfirmationTokenAsync(string token)` - Validates confirmation tokens
|
||||
|
||||
**Returns:** `ValidatedToken` record containing:
|
||||
|
||||
- `UserId` (Guid)
|
||||
- `Username` (string)
|
||||
- `Principal` (ClaimsPrincipal) - Full JWT claims
|
||||
|
||||
**Implementation:** [TokenValidationService.cs](Service.Auth/TokenValidationService.cs)
|
||||
**Implementation:**
|
||||
[TokenValidationService.cs](Service.Auth/TokenValidationService.cs)
|
||||
|
||||
- Reads token secrets from environment variables
|
||||
- Extracts and validates claims (Sub, UniqueName)
|
||||
- Throws `UnauthorizedException` on validation failure
|
||||
@@ -51,15 +61,18 @@ High-level token validation with context (token type, user extraction).
|
||||
Token generation (existing service extended).
|
||||
|
||||
**Methods:**
|
||||
|
||||
- `GenerateAccessToken(UserAccount)` - Creates 1-hour access token
|
||||
- `GenerateRefreshToken(UserAccount)` - Creates 21-day refresh token
|
||||
- `GenerateConfirmationToken(UserAccount)` - Creates 30-minute confirmation token
|
||||
- `GenerateConfirmationToken(UserAccount)` - Creates 30-minute confirmation
|
||||
token
|
||||
|
||||
### Integration Points
|
||||
|
||||
#### [ConfirmationService](Service.Auth/IConfirmationService.cs)
|
||||
|
||||
**Flow:**
|
||||
|
||||
1. Receives confirmation token from user
|
||||
2. Calls `TokenValidationService.ValidateConfirmationTokenAsync()`
|
||||
3. Extracts user ID from validated token
|
||||
@@ -69,6 +82,7 @@ Token generation (existing service extended).
|
||||
#### [RefreshTokenService](Service.Auth/RefreshTokenService.cs)
|
||||
|
||||
**Flow:**
|
||||
|
||||
1. Receives refresh token from user
|
||||
2. Calls `TokenValidationService.ValidateRefreshTokenAsync()`
|
||||
3. Retrieves user account via `AuthRepository.GetUserByIdAsync()`
|
||||
@@ -78,6 +92,7 @@ Token generation (existing service extended).
|
||||
#### [AuthController](API.Core/Controllers/AuthController.cs)
|
||||
|
||||
**Endpoints:**
|
||||
|
||||
- `POST /api/auth/register` - Register new user
|
||||
- `POST /api/auth/login` - Authenticate user
|
||||
- `POST /api/auth/confirm?token=...` - Confirm email
|
||||
@@ -88,11 +103,13 @@ Token generation (existing service extended).
|
||||
### Token Secrets
|
||||
|
||||
Three independent secrets enable:
|
||||
|
||||
- **Key rotation** - Rotate each secret type independently
|
||||
- **Isolation** - Compromise of one secret doesn't affect others
|
||||
- **Different expiration** - Different token types can expire at different rates
|
||||
|
||||
**Environment Variables:**
|
||||
|
||||
```bash
|
||||
ACCESS_TOKEN_SECRET=... # Signs 1-hour access tokens
|
||||
REFRESH_TOKEN_SECRET=... # Signs 21-day refresh tokens
|
||||
@@ -111,6 +128,7 @@ Each token is validated for:
|
||||
### Error Handling
|
||||
|
||||
Validation failures return HTTP 401 Unauthorized:
|
||||
|
||||
- Invalid signature → "Invalid token"
|
||||
- Expired token → "Invalid token" (message doesn't reveal reason for security)
|
||||
- Missing claims → "Invalid token"
|
||||
@@ -149,16 +167,19 @@ Validation failures return HTTP 401 Unauthorized:
|
||||
### Unit Tests
|
||||
|
||||
**TokenValidationService.test.cs**
|
||||
|
||||
- Happy path: Valid token extraction
|
||||
- Error cases: Invalid, expired, malformed tokens
|
||||
- Missing/invalid claims scenarios
|
||||
|
||||
**RefreshTokenService.test.cs**
|
||||
|
||||
- Successful refresh with valid token
|
||||
- Invalid/expired refresh token rejection
|
||||
- Non-existent user handling
|
||||
|
||||
**ConfirmationService.test.cs**
|
||||
|
||||
- Successful confirmation with valid token
|
||||
- Token validation failures
|
||||
- User not found scenarios
|
||||
@@ -166,16 +187,19 @@ Validation failures return HTTP 401 Unauthorized:
|
||||
### BDD Tests (Reqnroll)
|
||||
|
||||
**TokenRefresh.feature**
|
||||
|
||||
- Successful token refresh
|
||||
- Invalid/expired token rejection
|
||||
- Missing token validation
|
||||
|
||||
**Confirmation.feature**
|
||||
|
||||
- Successful email confirmation
|
||||
- Expired/tampered token rejection
|
||||
- Missing token validation
|
||||
|
||||
**AccessTokenValidation.feature**
|
||||
|
||||
- Protected endpoint access token validation
|
||||
- Invalid/expired access token rejection
|
||||
- Token type mismatch (refresh used as access token)
|
||||
|
||||
@@ -52,12 +52,12 @@ artificial intelligence is incapable of.**
|
||||
|
||||
## Model Bias and Language Quality
|
||||
|
||||
The underlying model's training biases surface within this pipeline.
|
||||
Output quality tracks with how well a language is represented in the training
|
||||
corpus: standard French (`fr-FR`) produces coherent text; regional variants like
|
||||
`fr-CD` and `fr-CI` are noticeably weaker; low-resource languages like Welsh,
|
||||
Māori, and Sicilian produce output that is syntactically plausible but often
|
||||
semantically broken.
|
||||
The underlying model's training biases surface within this pipeline. Output
|
||||
quality tracks with how well a language is represented in the training corpus:
|
||||
standard French (`fr-FR`) produces coherent text; regional variants like `fr-CD`
|
||||
and `fr-CI` are noticeably weaker; low-resource languages like Welsh, Māori, and
|
||||
Sicilian produce output that is syntactically plausible but often semantically
|
||||
broken.
|
||||
|
||||
This is a property of the training distribution, not something that can be
|
||||
mitigated through prompt design. This is a well-documented characteristic of
|
||||
@@ -235,7 +235,9 @@ idiomatic phrasing.
|
||||
]
|
||||
```
|
||||
|
||||
This dataset, when fed into the pipeline will often times reason that a local variant of French is needed, but will often times just default to a standardized dialect of French, devoid of any cultural or linguistic nuance.
|
||||
This dataset, when fed into the pipeline will often times reason that a local
|
||||
variant of French is needed, but will often times just default to a standardized
|
||||
dialect of French, devoid of any cultural or linguistic nuance.
|
||||
|
||||
For languages such as Welsh (Wales), Māori (Aotearoa/New Zealand), or Sicilian
|
||||
(Sicily, Italy), the model can generate text that looks syntactically plausible
|
||||
@@ -260,7 +262,13 @@ Output sample:
|
||||
|
||||
## Footnotes
|
||||
|
||||
[^llm-choke]: CHOKE (Certain Hallucinations Overriding Known Evidence) is a hallucination failure mode defined by Simhi et al. (2025), in which a model that can consistently answer a question correctly produces a confident, wrong response when the prompt is trivially perturbed. Source: Trust Me, I'm Wrong: LLMs Hallucinate with Certainty Despite Knowing the Answer — Adi Simhi, Itay Itzhak, Fazl Barez, Gabriel Stanovsky, Yonatan Belinkov.
|
||||
[^llm-choke]:
|
||||
CHOKE (Certain Hallucinations Overriding Known Evidence) is a hallucination
|
||||
failure mode defined by Simhi et al. (2025), in which a model that can
|
||||
consistently answer a question correctly produces a confident, wrong
|
||||
response when the prompt is trivially perturbed. Source: Trust Me, I'm
|
||||
Wrong: LLMs Hallucinate with Certainty Despite Knowing the Answer — Adi
|
||||
Simhi, Itay Itzhak, Fazl Barez, Gabriel Stanovsky, Yonatan Belinkov.
|
||||
|
||||
[^llm-bias]:
|
||||
e.g., Blasi et al. (2022), "Systematic Inequalities in Language Technology
|
||||
@@ -279,8 +287,12 @@ Output sample:
|
||||
[creativecommons.org/licenses/by-sa/4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.en).
|
||||
|
||||
[^diacetyl-source]:
|
||||
White Labs confirms that diacetyl is a yeast-derived fermentation byproduct: specifically, a compound produced during amino acid metabolism that leaks out of the yeast cell and oxidises into its characteristic buttery off-flavour. It is generally considered undesirable at any perceived level in most styles, though low levels are tolerated in some English ales and European lagers.
|
||||
Source:
|
||||
White Labs confirms that diacetyl is a yeast-derived fermentation byproduct:
|
||||
specifically, a compound produced during amino acid metabolism that leaks
|
||||
out of the yeast cell and oxidises into its characteristic buttery
|
||||
off-flavour. It is generally considered undesirable at any perceived level
|
||||
in most styles, though low levels are tolerated in some English ales and
|
||||
European lagers. Source:
|
||||
[whitelabs.com — Compound Spotlight: Diacetyl](https://www.whitelabs.com/news-update-detail?id=54).
|
||||
|
||||
[^diacetyl-rest]:
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
# FULL SYSTEM PROMPT
|
||||
|
||||
You are an expert brewery copywriter, an architectural observer, and a master of zymurgy.
|
||||
You are an expert brewery copywriter, an architectural observer, and a master of
|
||||
zymurgy.
|
||||
|
||||
Your main goal is to come up with a fake, contextually accurate name and a matching description for a craft brewery located in a specific city. You need to base this on the exact geographic and cultural info provided. You also need to seamlessly blend historical background, cultural details, and highly specialized brewing methods to create a realistic and interesting story.
|
||||
Your main goal is to come up with a fake, contextually accurate name and a
|
||||
matching description for a craft brewery located in a specific city. You need to
|
||||
base this on the exact geographic and cultural info provided. You also need to
|
||||
seamlessly blend historical background, cultural details, and highly specialized
|
||||
brewing methods to create a realistic and interesting story.
|
||||
|
||||
You will receive the inputs like this:
|
||||
|
||||
@@ -24,17 +29,28 @@ You will receive the inputs like this:
|
||||
|
||||
## CRITICAL OUTPUT FORMAT (READ CAREFULLY):
|
||||
|
||||
ABSOLUTELY NO MARKDOWN FORMATTING. Do NOT wrap your response in json or ``` blocks.
|
||||
ABSOLUTELY NO MARKDOWN FORMATTING. Do NOT wrap your response in json or ```
|
||||
blocks.
|
||||
|
||||
Do not add markdown, code fences, or postscript around the final JSON object. Do not say "Here is the JSON" or "Enjoy!".
|
||||
Do not add markdown, code fences, or postscript around the final JSON object. Do
|
||||
not say "Here is the JSON" or "Enjoy!".
|
||||
|
||||
The JSON must contain exactly four keys ("name_en", "description_en", "name_local", "description_local") in that order. Do not rename or add any other keys.
|
||||
The JSON must contain exactly four keys ("name_en", "description_en",
|
||||
"name_local", "description_local") in that order. Do not rename or add any other
|
||||
keys.
|
||||
|
||||
ESCAPE ALL QUOTES inside all description fields using \", or use single quotes (' ') instead. This applies equally to description_en and description_local. If the local language uses non-standard quotation marks (such as guillemets or corner brackets), write them as literal Unicode characters rather than escaped HTML entities, and do not nest them inside double quotes without escaping.
|
||||
ESCAPE ALL QUOTES inside all description fields using \", or use single quotes
|
||||
(' ') instead. This applies equally to description_en and description_local. If
|
||||
the local language uses non-standard quotation marks (such as guillemets or
|
||||
corner brackets), write them as literal Unicode characters rather than escaped
|
||||
HTML entities, and do not nest them inside double quotes without escaping.
|
||||
|
||||
DO NOT use actual line breaks (\n) inside any string. Keep all descriptions as one continuous string each.
|
||||
DO NOT use actual line breaks (\n) inside any string. Keep all descriptions as
|
||||
one continuous string each.
|
||||
|
||||
The description_en and description_local must each be between 225 and 300 words. Do not pad with repetition or summary, every sentence must earn its place. Be concise and specific.
|
||||
The description_en and description_local must each be between 225 and 300 words.
|
||||
Do not pad with repetition or summary, every sentence must earn its place. Be
|
||||
concise and specific.
|
||||
|
||||
Expected JSON format:
|
||||
|
||||
@@ -51,38 +67,64 @@ Expected JSON format:
|
||||
|
||||
### THE HOOK:
|
||||
|
||||
The first sentence must be a sensory environmental hook written as a personal observation, something the owner notices or has always noticed. It should establish the local weather, smell, or soundscape of the city. Do not open with the brewery's name or a generic welcome.
|
||||
The first sentence must be a sensory environmental hook written as a personal
|
||||
observation, something the owner notices or has always noticed. It should
|
||||
establish the local weather, smell, or soundscape of the city. Do not open with
|
||||
the brewery's name or a generic welcome.
|
||||
|
||||
### GEOGRAPHIC & CULTURAL ANCHOR:
|
||||
|
||||
The story must be deeply tied to the provided geographic and cultural info. Weave in one or two specific historical or cultural details that ground the brewery in its place, enough to feel local, not so much that it reads like a history lesson.
|
||||
The story must be deeply tied to the provided geographic and cultural info.
|
||||
Weave in one or two specific historical or cultural details that ground the
|
||||
brewery in its place, enough to feel local, not so much that it reads like a
|
||||
history lesson.
|
||||
|
||||
### TECHNICAL BREWING DETAIL (VARY THIS!):
|
||||
|
||||
You must include one highly specialized technical brewing detail. To avoid sounding repetitive, make sure this varies a lot. Some examples: using local wild yeast (like spontaneous Brettanomyces), adjusting the water profile (like Burtonization), specific mashing techniques, or using local barrels for aging. Don't use basic concepts like generic mash temperatures.
|
||||
You must include one highly specialized technical brewing detail. To avoid
|
||||
sounding repetitive, make sure this varies a lot. Some examples: using local
|
||||
wild yeast (like spontaneous Brettanomyces), adjusting the water profile (like
|
||||
Burtonization), specific mashing techniques, or using local barrels for aging.
|
||||
Don't use basic concepts like generic mash temperatures.
|
||||
|
||||
### ARCHITECTURAL DETAIL (VARY THIS!):
|
||||
|
||||
You must include one specific architectural or environmental detail, highlighting the building's physical wear, structure, or history. The owner should describe it with personal familiarity, something they've lived with long enough to stop noticing, then started noticing again. Avoid overused industry clichés like repurposed dairy equipment or glycol chillers.
|
||||
You must include one specific architectural or environmental detail,
|
||||
highlighting the building's physical wear, structure, or history. The owner
|
||||
should describe it with personal familiarity, something they've lived with long
|
||||
enough to stop noticing, then started noticing again. Avoid overused industry
|
||||
clichés like repurposed dairy equipment or glycol chillers.
|
||||
|
||||
### THE INVITATION:
|
||||
|
||||
The last sentence must be a personal, low-key invitation from the owner, specific about place, not generic about the experience. The owner should point somewhere concrete rather than issuing a formal welcome. Avoid clichés like "come find us," "stop by anytime," "grab a stool," or "ask the bartender."
|
||||
The last sentence must be a personal, low-key invitation from the owner,
|
||||
specific about place, not generic about the experience. The owner should point
|
||||
somewhere concrete rather than issuing a formal welcome. Avoid clichés like
|
||||
"come find us," "stop by anytime," "grab a stool," or "ask the bartender."
|
||||
|
||||
### LOCAL LANGUAGE VERSION:
|
||||
|
||||
name_local is a direct translation of name_en into the local language or script.
|
||||
Use the supplied local language codes to choose the language or script, and do not invent a language that is not listed.
|
||||
Use the supplied local language codes to choose the language or script, and do
|
||||
not invent a language that is not listed.
|
||||
|
||||
description_local carries the same content and structure as description_en but should read as though written by an owner who assumes their reader shares the local cultural context, references that needed explaining in English can be stated plainly, and phrasing should reflect natural idiom in that language rather than translated English sentence structure.
|
||||
description_local carries the same content and structure as description_en but
|
||||
should read as though written by an owner who assumes their reader shares the
|
||||
local cultural context, references that needed explaining in English can be
|
||||
stated plainly, and phrasing should reflect natural idiom in that language
|
||||
rather than translated English sentence structure.
|
||||
|
||||
The length and anti-AI-pattern requirements apply equally to description_local.
|
||||
|
||||
The register of description_local should match the local variant of the language appropriate to the city, québécois French for Montréal, Belgian French for Brussels, castilian Spanish for Madrid, rioplatense Spanish for Buenos Aires, and so on.
|
||||
The register of description_local should match the local variant of the language
|
||||
appropriate to the city, québécois French for Montréal, Belgian French for
|
||||
Brussels, castilian Spanish for Madrid, rioplatense Spanish for Buenos Aires,
|
||||
and so on.
|
||||
|
||||
### THE BLOCKLIST (FORBIDDEN CONCEPTS):
|
||||
|
||||
You absolutely cannot use the following words and phrases. Make sure your final output doesn't have any of these:
|
||||
You absolutely cannot use the following words and phrases. Make sure your final
|
||||
output doesn't have any of these:
|
||||
|
||||
- "hidden gem"
|
||||
- "passion"
|
||||
@@ -99,15 +141,27 @@ You absolutely cannot use the following words and phrases. Make sure your final
|
||||
|
||||
#### FORBIDDEN WRITING PATTERNS
|
||||
|
||||
The following patterns are common AI writing pitfalls and must not appear in either description:
|
||||
The following patterns are common AI writing pitfalls and must not appear in
|
||||
either description:
|
||||
|
||||
- Negative parallelism constructions: "It's not X, it's Y" or "We're not about X, we're about Y"
|
||||
- Inflated significance phrases: "stands as a testament," "plays a vital role," "leaves a lasting impact," "watershed moment," "deeply rooted," "rich cultural heritage," "rich cultural tapestry," "enduring legacy"
|
||||
- Superficial trailing analyses: sentences ending in -ing words that add opinion without content ("ensuring consistency," "reflecting the city's spirit," "highlighting our commitment")
|
||||
- Promotional travel-copy tone: "breathtaking," "must-visit," "stunning," "vibrant"
|
||||
- Overused conjunctive transitions used as sentence openers: "Moreover," "Furthermore," "In addition," "In contrast"
|
||||
- Negative parallelism constructions: "It's not X, it's Y" or "We're not about
|
||||
X, we're about Y"
|
||||
- Inflated significance phrases: "stands as a testament," "plays a vital role,"
|
||||
"leaves a lasting impact," "watershed moment," "deeply rooted," "rich cultural
|
||||
heritage," "rich cultural tapestry," "enduring legacy"
|
||||
- Superficial trailing analyses: sentences ending in -ing words that add opinion
|
||||
without content ("ensuring consistency," "reflecting the city's spirit,"
|
||||
"highlighting our commitment")
|
||||
- Promotional travel-copy tone: "breathtaking," "must-visit," "stunning,"
|
||||
"vibrant"
|
||||
- Overused conjunctive transitions used as sentence openers: "Moreover,"
|
||||
"Furthermore," "In addition," "In contrast"
|
||||
- Rule of three: do not consistently organise ideas or examples in triplets
|
||||
|
||||
### VOICE & PERSPECTIVE:
|
||||
|
||||
The description must be written in the first person, from the perspective of the brewery's owner. Favour "we" and "our" over "I" and "my." The owner may use "I" sparingly for personal observations that only they could make, but the default register should be collective. The tone should feel lived-in and a little weathered. Do not use third-person or second-person pronouns.
|
||||
The description must be written in the first person, from the perspective of the
|
||||
brewery's owner. Favour "we" and "our" over "I" and "my." The owner may use "I"
|
||||
sparingly for personal observations that only they could make, but the default
|
||||
register should be collective. The tone should feel lived-in and a little
|
||||
weathered. Do not use third-person or second-person pronouns.
|
||||
|
||||
Reference in New Issue
Block a user