mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-02-16 02:39:03 +00:00
919 lines
27 KiB
Markdown
919 lines
27 KiB
Markdown
# The Biergarten App
|
|
|
|
A social platform for craft beer enthusiasts to discover breweries, share reviews, and connect with fellow beer lovers.
|
|
|
|
## Project Status
|
|
|
|
This project is in active development, transitioning from a full-stack Next.js application to a **multi-project monorepo** with:
|
|
- **Backend**: .NET 10 Web API with SQL Server
|
|
- **Frontend**: Next.js with TypeScript
|
|
- **Architecture**: SQL-first approach using stored procedures
|
|
|
|
**Current State** (February 2026):
|
|
- Core authentication and user management APIs functional
|
|
- Database schema and migrations established
|
|
- Domain, Infrastructure, Repository, and Service layers implemented
|
|
- Frontend integration with .NET API in progress
|
|
- Migrating remaining features from Next.js serverless functions
|
|
|
|
**The Next.js app currently runs standalone with its original Prisma/Neon Postgres backend. It will be fully integrated with the .NET API once feature parity is achieved.**
|
|
|
|
---
|
|
|
|
## Repository Structure
|
|
|
|
```
|
|
src/Core/
|
|
├── API/
|
|
│ ├── API.Core/ # ASP.NET Core Web API with Swagger/OpenAPI
|
|
│ └── API.Specs/ # Integration tests using Reqnroll (BDD)
|
|
├── Database/
|
|
│ ├── Database.Migrations/ # DbUp migrations (embedded SQL scripts)
|
|
│ └── Database.Seed/ # Database seeding for development/testing
|
|
├── Domain/
|
|
│ └── Domain.csproj # Domain entities and models
|
|
│ └── Entities/ # Core domain entities (UserAccount, UserCredential, etc.)
|
|
├── Infrastructure/
|
|
│ ├── Infrastructure.Jwt/ # JWT token generation and validation
|
|
│ ├── Infrastructure.PasswordHashing/ # Argon2id password hashing
|
|
│ └── Infrastructure.Repository/
|
|
│ ├── Infrastructure.Repository/ # Data access layer (stored procedure-based)
|
|
│ └── Infrastructure.Repository.Tests/ # Unit tests for repositories
|
|
└── Service/
|
|
└── Service.Core/ # Business logic layer
|
|
|
|
Website/ # Next.js frontend application
|
|
misc/
|
|
└── raw-data/ # Sample data files (breweries, beers)
|
|
```
|
|
|
|
### Key Components
|
|
|
|
**API Layer** (`API.Core`)
|
|
- RESTful endpoints for authentication, users, and breweries
|
|
- Controllers: `AuthController`, `UserController`
|
|
- Configured with Swagger UI for API exploration
|
|
- Health checks and structured logging
|
|
- Middleware for error handling and request processing
|
|
|
|
**Database Layer**
|
|
- SQL Server with stored procedures for all data operations
|
|
- DbUp for version-controlled migrations
|
|
- Comprehensive schema including users, breweries, beers, locations, and social features
|
|
- Seeders for development data (users, locations across US/Canada/Mexico)
|
|
|
|
**Domain Layer** (`Domain`)
|
|
- Core business entities and models
|
|
- Entities: `UserAccount`, `UserCredential`, `UserVerification`
|
|
- Shared domain logic and value objects
|
|
- No external dependencies - pure domain model
|
|
|
|
**Infrastructure Layer**
|
|
- **Infrastructure.Jwt**: JWT token generation, validation, and configuration
|
|
- **Infrastructure.PasswordHashing**: Argon2id password hashing with configurable parameters
|
|
- **Infrastructure.Password**: Password utilities and validation
|
|
- **Infrastructure.Repository**: Repository pattern infrastructure and base classes
|
|
|
|
**Repository Layer** (`Infrastructure.Repository`)
|
|
- Abstraction over SQL Server using ADO.NET
|
|
- `ISqlConnectionFactory` for connection management
|
|
- Repositories: `AuthRepository`, `UserAccountRepository`
|
|
- All data access via stored procedures (no inline SQL)
|
|
|
|
**Service Layer** (`Service.Core`)
|
|
- Business logic and orchestration
|
|
- Services: `AuthService`, `UserService`
|
|
- Integration with infrastructure components
|
|
- Transaction management and business rule enforcement
|
|
|
|
**Frontend** (`Website`)
|
|
- Next.js 14+ with TypeScript
|
|
- TailwindCSS, Headless UI, DaisyUI for UI components
|
|
- Integrations: Mapbox (maps), Cloudinary (image hosting)
|
|
- Progressive migration from serverless API routes to .NET API
|
|
|
|
---
|
|
|
|
## Technology Stack
|
|
|
|
### Backend
|
|
- **.NET 10** - Latest C# and runtime features
|
|
- **ASP.NET Core** - Web API framework
|
|
- **SQL Server 2022** - Primary database
|
|
- **DbUp** - Database migration tool
|
|
- **Argon2id** - Password hashing
|
|
- **JWT** - Authentication tokens
|
|
|
|
### Frontend
|
|
- **Next.js 14+** - React framework
|
|
- **TypeScript** - Type safety
|
|
- **TailwindCSS** - Utility-first CSS
|
|
- **Mapbox GL** - Interactive maps
|
|
- **Cloudinary** - Image management
|
|
|
|
### Testing
|
|
- **xUnit** - Unit testing framework
|
|
- **Reqnroll** - BDD/Gherkin integration testing
|
|
- **FluentAssertions** - Assertion library
|
|
- **DbMocker** - Database mocking
|
|
|
|
### DevOps & Infrastructure
|
|
- **Docker** - Containerization for all services
|
|
- **Docker Compose** - Multi-container orchestration
|
|
- **Multi-stage builds** - Optimized image sizes
|
|
- **Health checks** - Container readiness and liveness probes
|
|
- **Separate environments** - Development, testing, and production configurations
|
|
|
|
---
|
|
|
|
## Getting Started
|
|
|
|
### Prerequisites
|
|
|
|
- **.NET SDK 10+** ([Download](https://dotnet.microsoft.com/download))
|
|
- **Node.js 18+** ([Download](https://nodejs.org/))
|
|
- **Docker Desktop** ([Download](https://www.docker.com/products/docker-desktop))
|
|
|
|
### Quick Start (Development Environment)
|
|
|
|
1. **Clone the repository**
|
|
```bash
|
|
git clone <repository-url>
|
|
cd biergarten-app
|
|
```
|
|
|
|
2. **Configure environment variables**
|
|
|
|
Copy the example file and customize:
|
|
```bash
|
|
cp .env.example .env.dev
|
|
```
|
|
|
|
Required variables in `.env.dev`:
|
|
```bash
|
|
# Database (component-based for Docker)
|
|
DB_SERVER=sqlserver,1433
|
|
DB_NAME=Biergarten
|
|
DB_USER=sa
|
|
DB_PASSWORD=YourStrong!Passw0rd
|
|
|
|
# JWT Authentication
|
|
JWT_SECRET=your-secret-key-minimum-32-characters-required
|
|
```
|
|
|
|
For a complete list of all backend and frontend environment variables, see the [Environment Variables](#environment-variables) section.
|
|
|
|
3. **Start the development environment**
|
|
```bash
|
|
docker compose -f docker-compose.dev.yaml up -d
|
|
```
|
|
|
|
This will:
|
|
- Start SQL Server
|
|
- Run database migrations
|
|
- Seed initial data
|
|
- Start the API on http://localhost:8080
|
|
|
|
4. **Access Swagger UI**
|
|
|
|
Navigate to http://localhost:8080/swagger to explore and test API endpoints.
|
|
|
|
5. **Run the frontend** (optional)
|
|
|
|
The frontend requires additional environment variables. See [Frontend Variables](#frontend-variables-nextjs) section.
|
|
|
|
```bash
|
|
cd Website
|
|
|
|
# Create .env.local with frontend variables
|
|
# (see Environment Variables section)
|
|
|
|
npm install
|
|
npm run dev
|
|
```
|
|
|
|
For complete environment variable documentation, see the [Environment Variables](#environment-variables) section below.
|
|
|
|
### Manual Setup (Without Docker)
|
|
|
|
#### Backend Setup
|
|
|
|
1. **Start SQL Server locally** or use a hosted instance
|
|
|
|
2. **Set environment variables**
|
|
|
|
See [Backend Variables](#backend-variables-net-api) for details.
|
|
|
|
```bash
|
|
# macOS/Linux
|
|
export DB_CONNECTION_STRING="Server=localhost,1433;Database=Biergarten;User Id=sa;Password=YourStrong!Passw0rd;TrustServerCertificate=True;"
|
|
export JWT_SECRET="your-secret-key-minimum-32-characters-required"
|
|
|
|
# Windows PowerShell
|
|
$env:DB_CONNECTION_STRING="Server=localhost,1433;Database=Biergarten;User Id=sa;Password=YourStrong!Passw0rd;TrustServerCertificate=True;"
|
|
$env:JWT_SECRET="your-secret-key-minimum-32-characters-required"
|
|
```
|
|
|
|
3. **Run migrations**
|
|
```bash
|
|
cd src/Core
|
|
dotnet run --project Database/Database.Migrations/Database.Migrations.csproj
|
|
```
|
|
|
|
4. **Seed the database**
|
|
```bash
|
|
dotnet run --project Database/Database.Seed/Database.Seed.csproj
|
|
```
|
|
|
|
5. **Start the API**
|
|
```bash
|
|
dotnet run --project API/API.Core/API.Core.csproj
|
|
```
|
|
|
|
#### Frontend Setup
|
|
|
|
1. **Navigate to Website directory**
|
|
```bash
|
|
cd Website
|
|
```
|
|
|
|
2. **Create environment file**
|
|
|
|
Create `.env.local` with required frontend variables. See [Frontend Variables](#frontend-variables-nextjs) for the complete list.
|
|
|
|
```bash
|
|
# Example minimal setup
|
|
BASE_URL=http://localhost:3000
|
|
NODE_ENV=development
|
|
|
|
# Generate secrets
|
|
CONFIRMATION_TOKEN_SECRET=$(openssl rand -base64 127)
|
|
RESET_PASSWORD_TOKEN_SECRET=$(openssl rand -base64 127)
|
|
SESSION_SECRET=$(openssl rand -base64 127)
|
|
|
|
# Add external service credentials
|
|
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=your-cloud-name
|
|
CLOUDINARY_KEY=your-api-key
|
|
CLOUDINARY_SECRET=your-api-secret
|
|
# ... (see Environment Variables section for complete list)
|
|
```
|
|
|
|
3. **Install dependencies**
|
|
```bash
|
|
npm install
|
|
```
|
|
|
|
4. **Run Prisma migrations** (current frontend database)
|
|
```bash
|
|
npx prisma generate
|
|
npx prisma migrate dev
|
|
```
|
|
|
|
5. **Start the development server**
|
|
```bash
|
|
npm run dev
|
|
```
|
|
|
|
The frontend will be available at http://localhost:3000
|
|
|
|
---
|
|
|
|
## Environment Variables
|
|
|
|
### Overview
|
|
|
|
The Biergarten App uses environment variables for configuration across both backend (.NET API) and frontend (Next.js) services. This section provides complete documentation for all required and optional variables.
|
|
|
|
**Configuration Patterns:**
|
|
- **Backend**: Direct environment variable access via `Environment.GetEnvironmentVariable()`
|
|
- **Frontend**: Centralized configuration module at [src/Website/src/config/env/index.ts](src/Website/src/config/env/index.ts) with Zod validation
|
|
- **Docker**: Environment-specific `.env` files (`.env.dev`, `.env.test`, `.env.prod`)
|
|
|
|
### Backend Variables (.NET API)
|
|
|
|
The .NET API requires environment variables for database connectivity and JWT authentication. These can be set directly in your shell or via `.env` files when using Docker.
|
|
|
|
#### Database Connection
|
|
|
|
**Option 1: Component-Based (Recommended for Docker)**
|
|
|
|
Use individual components to build the connection string:
|
|
|
|
```bash
|
|
DB_SERVER=sqlserver,1433 # SQL Server address and port
|
|
DB_NAME=Biergarten # Database name
|
|
DB_USER=sa # SQL Server username
|
|
DB_PASSWORD=YourStrong!Passw0rd # SQL Server password
|
|
DB_TRUST_SERVER_CERTIFICATE=True # Optional, defaults to True
|
|
```
|
|
|
|
**Option 2: Full Connection String (Local Development)**
|
|
|
|
Provide a complete SQL Server connection string:
|
|
|
|
```bash
|
|
DB_CONNECTION_STRING="Server=localhost,1433;Database=Biergarten;User Id=sa;Password=YourStrong!Passw0rd;TrustServerCertificate=True;"
|
|
```
|
|
|
|
The connection factory checks for `DB_CONNECTION_STRING` first, then falls back to building from components. See [DefaultSqlConnectionFactory.cs](src/Core/Infrastructure/Infrastructure.Repository/Infrastructure.Repository/Sql/DefaultSqlConnectionFactory.cs).
|
|
|
|
#### JWT Authentication
|
|
|
|
```bash
|
|
JWT_SECRET=your-secret-key-minimum-32-characters-required
|
|
```
|
|
|
|
- **Required**: Yes
|
|
- **Minimum Length**: 32 characters
|
|
- **Used For**: Signing JWT tokens for user authentication
|
|
- **Location**: [JwtService.cs](src/Core/Service/Service.Core/Services/JwtService.cs)
|
|
|
|
**Additional JWT Configuration** (in `appsettings.json`):
|
|
- `Jwt:ExpirationMinutes` - Token lifetime (default: 60)
|
|
- `Jwt:Issuer` - Token issuer (default: "biergarten-api")
|
|
- `Jwt:Audience` - Token audience (default: "biergarten-users")
|
|
|
|
#### Migration Control
|
|
|
|
```bash
|
|
CLEAR_DATABASE=true # Development/Testing only
|
|
```
|
|
|
|
- **Required**: No
|
|
- **Effect**: If set to "true", drops and recreates the database during migrations
|
|
- **Usage**: Development and testing environments only
|
|
- **Warning**: Never use in production
|
|
|
|
### Frontend Variables (Next.js)
|
|
|
|
The Next.js frontend requires environment variables for external services, authentication, and database connectivity. Create a `.env` or `.env.local` file in the `Website/` directory.
|
|
|
|
All variables are validated at runtime using Zod schemas. See [src/Website/src/config/env/index.ts](src/Website/src/config/env/index.ts).
|
|
|
|
#### Base Configuration
|
|
|
|
```bash
|
|
BASE_URL=http://localhost:3000 # Application base URL
|
|
NODE_ENV=development # Environment: development, production, test
|
|
```
|
|
|
|
#### Authentication & Sessions
|
|
|
|
```bash
|
|
# Token signing secrets (generate with: openssl rand -base64 127)
|
|
CONFIRMATION_TOKEN_SECRET=<generated-secret> # Email confirmation tokens
|
|
RESET_PASSWORD_TOKEN_SECRET=<generated-secret> # Password reset tokens
|
|
SESSION_SECRET=<generated-secret> # Session cookie signing
|
|
|
|
# Session configuration
|
|
SESSION_TOKEN_NAME=biergarten # Cookie name
|
|
SESSION_MAX_AGE=604800 # Cookie max age in seconds (604800 = 1 week)
|
|
```
|
|
|
|
#### Database (Prisma/Postgres)
|
|
|
|
**Current State**: The frontend currently uses Neon Postgres with Prisma. This will migrate to the SQL Server backend once feature parity is achieved.
|
|
|
|
```bash
|
|
POSTGRES_PRISMA_URL=postgresql://user:pass@host/db?pgbouncer=true # Pooled connection
|
|
POSTGRES_URL_NON_POOLING=postgresql://user:pass@host/db # Direct connection (migrations)
|
|
SHADOW_DATABASE_URL=postgresql://user:pass@host/shadow_db # Prisma shadow DB
|
|
```
|
|
|
|
#### Admin Account
|
|
|
|
```bash
|
|
ADMIN_PASSWORD=SecureAdminPassword123! # Initial admin account password for seeding
|
|
```
|
|
|
|
### Docker Variables
|
|
|
|
When running services in Docker, additional environment variables control container behavior:
|
|
|
|
#### ASP.NET Core
|
|
|
|
```bash
|
|
ASPNETCORE_ENVIRONMENT=Development # Development, Production
|
|
ASPNETCORE_URLS=http://0.0.0.0:8080 # Binding address
|
|
DOTNET_RUNNING_IN_CONTAINER=true # Container execution flag
|
|
```
|
|
|
|
#### SQL Server (Docker Container)
|
|
|
|
```bash
|
|
SA_PASSWORD=YourStrong!Passw0rd # SQL Server SA password (maps to DB_PASSWORD)
|
|
ACCEPT_EULA=Y # Accept SQL Server EULA
|
|
MSSQL_PID=Express # SQL Server edition (Express, Developer, etc.)
|
|
```
|
|
|
|
**Note**: `SA_PASSWORD` in the SQL Server container maps to `DB_PASSWORD` for the API application.
|
|
|
|
### External Services
|
|
|
|
The frontend integrates with several third-party services. Sign up for accounts and retrieve API credentials:
|
|
|
|
#### Cloudinary (Image Hosting)
|
|
|
|
```bash
|
|
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=your-cloud-name # Public, client-accessible
|
|
CLOUDINARY_KEY=your-api-key # Server-side API key
|
|
CLOUDINARY_SECRET=your-api-secret # Server-side secret
|
|
```
|
|
|
|
**Setup**:
|
|
1. Sign up at [cloudinary.com](https://cloudinary.com)
|
|
2. Navigate to Dashboard
|
|
3. Copy Cloud Name, API Key, and API Secret
|
|
|
|
**Note**: The `NEXT_PUBLIC_` prefix makes the cloud name accessible in client-side code.
|
|
|
|
#### Mapbox (Maps & Geocoding)
|
|
|
|
```bash
|
|
MAPBOX_ACCESS_TOKEN=pk.your-public-token
|
|
```
|
|
|
|
**Setup**:
|
|
1. Create account at [mapbox.com](https://mapbox.com)
|
|
2. Navigate to Account → Tokens
|
|
3. Create a new token with public scopes
|
|
4. Copy the access token
|
|
|
|
#### SparkPost (Email Service)
|
|
|
|
```bash
|
|
SPARKPOST_API_KEY=your-api-key
|
|
SPARKPOST_SENDER_ADDRESS=noreply@yourdomain.com
|
|
```
|
|
|
|
**Setup**:
|
|
1. Sign up at [sparkpost.com](https://sparkpost.com)
|
|
2. Verify your sending domain or use sandbox
|
|
3. Create an API key with "Send via SMTP" permission
|
|
4. Configure sender address (must match verified domain)
|
|
|
|
### Generating Secrets
|
|
|
|
For authentication secrets (`JWT_SECRET`, `CONFIRMATION_TOKEN_SECRET`, etc.), generate cryptographically secure random values:
|
|
|
|
**macOS/Linux:**
|
|
```bash
|
|
openssl rand -base64 127
|
|
```
|
|
|
|
**Windows PowerShell:**
|
|
```powershell
|
|
[Convert]::ToBase64String((1..127 | ForEach-Object { Get-Random -Maximum 256 }))
|
|
```
|
|
|
|
**Requirements**:
|
|
- `JWT_SECRET`: Minimum 32 characters
|
|
- Session/token secrets: Recommend 127+ characters for maximum security
|
|
|
|
### Environment File Structure
|
|
|
|
The project uses multiple environment files depending on the context:
|
|
|
|
#### Backend/Docker (Root Directory)
|
|
|
|
- **`.env.example`** - Template file (tracked in Git)
|
|
- **`.env.dev`** - Development environment (gitignored)
|
|
- **`.env.test`** - Testing environment (gitignored)
|
|
- **`.env.prod`** - Production environment (gitignored)
|
|
|
|
**Setup**:
|
|
```bash
|
|
# Copy template and customize
|
|
cp .env.example .env.dev
|
|
# Edit .env.dev with your values
|
|
```
|
|
|
|
Docker Compose files reference these:
|
|
- `docker-compose.dev.yaml` → `.env.dev`
|
|
- `docker-compose.test.yaml` → `.env.test`
|
|
- `docker-compose.prod.yaml` → `.env.prod`
|
|
|
|
#### Frontend (Website Directory)
|
|
|
|
- **`.env`** or **`.env.local`** - Local development (gitignored)
|
|
|
|
**Setup**:
|
|
```bash
|
|
cd Website
|
|
# Create .env file with frontend variables
|
|
touch .env.local
|
|
```
|
|
|
|
### Variable Reference Table
|
|
|
|
| Variable | Backend | Frontend | Docker | Required | Notes |
|
|
|----------|---------|----------|--------|----------|-------|
|
|
| **Database** |
|
|
| `DB_SERVER` | ✓ | | ✓ | Yes* | SQL Server address |
|
|
| `DB_NAME` | ✓ | | ✓ | Yes* | Database name |
|
|
| `DB_USER` | ✓ | | ✓ | Yes* | SQL username |
|
|
| `DB_PASSWORD` | ✓ | | ✓ | Yes* | SQL password |
|
|
| `DB_CONNECTION_STRING` | ✓ | | | Yes* | Alternative to components |
|
|
| `DB_TRUST_SERVER_CERTIFICATE` | ✓ | | ✓ | No | Defaults to True |
|
|
| `SA_PASSWORD` | | | ✓ | Yes | SQL Server container only |
|
|
| **Authentication (Backend)** |
|
|
| `JWT_SECRET` | ✓ | | ✓ | Yes | Min 32 chars |
|
|
| **Authentication (Frontend)** |
|
|
| `CONFIRMATION_TOKEN_SECRET` | | ✓ | | Yes | Email confirmation |
|
|
| `RESET_PASSWORD_TOKEN_SECRET` | | ✓ | | Yes | Password reset |
|
|
| `SESSION_SECRET` | | ✓ | | Yes | Session signing |
|
|
| `SESSION_TOKEN_NAME` | | ✓ | | No | Default: "biergarten" |
|
|
| `SESSION_MAX_AGE` | | ✓ | | No | Default: 604800 |
|
|
| **Base Configuration** |
|
|
| `BASE_URL` | | ✓ | | Yes | App base URL |
|
|
| `NODE_ENV` | | ✓ | | Yes | development/production |
|
|
| `ASPNETCORE_ENVIRONMENT` | ✓ | | ✓ | Yes | Development/Production |
|
|
| `ASPNETCORE_URLS` | ✓ | | ✓ | Yes | Binding address |
|
|
| **Database (Frontend - Current)** |
|
|
| `POSTGRES_PRISMA_URL` | | ✓ | | Yes | Pooled connection |
|
|
| `POSTGRES_URL_NON_POOLING` | | ✓ | | Yes | Direct connection |
|
|
| `SHADOW_DATABASE_URL` | | ✓ | | No | Prisma shadow DB |
|
|
| **External Services** |
|
|
| `NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME` | | ✓ | | Yes | Public, client-side |
|
|
| `CLOUDINARY_KEY` | | ✓ | | Yes | Server-side |
|
|
| `CLOUDINARY_SECRET` | | ✓ | | Yes | Server-side |
|
|
| `MAPBOX_ACCESS_TOKEN` | | ✓ | | Yes | Maps/geocoding |
|
|
| `SPARKPOST_API_KEY` | | ✓ | | Yes | Email service |
|
|
| `SPARKPOST_SENDER_ADDRESS` | | ✓ | | Yes | From address |
|
|
| **Other** |
|
|
| `ADMIN_PASSWORD` | | ✓ | | No | Seeding only |
|
|
| `CLEAR_DATABASE` | ✓ | | ✓ | No | Dev/test only |
|
|
| `ACCEPT_EULA` | | | ✓ | Yes | SQL Server EULA |
|
|
| `MSSQL_PID` | | | ✓ | No | SQL Server edition |
|
|
|
|
\* Either `DB_CONNECTION_STRING` OR the four component variables (`DB_SERVER`, `DB_NAME`, `DB_USER`, `DB_PASSWORD`) are required.
|
|
|
|
---
|
|
|
|
## Testing
|
|
|
|
### Run All Tests (Docker)
|
|
```bash
|
|
docker compose -f docker-compose.test.yaml up --abort-on-container-exit
|
|
```
|
|
|
|
This runs:
|
|
- **API.Specs** - BDD integration tests
|
|
- **Infrastructure.Repository.Tests** - Unit tests for data access
|
|
|
|
Test results are output to `./test-results/`.
|
|
|
|
### Run Tests Locally
|
|
|
|
**Integration Tests (API.Specs)**
|
|
```bash
|
|
cd src/Core
|
|
dotnet test API/API.Specs/API.Specs.csproj
|
|
```
|
|
|
|
**Unit Tests (Infrastructure.Repository.Tests)**
|
|
```bash
|
|
cd src/Core
|
|
dotnet test Infrastructure/Infrastructure.Repository/Infrastructure.Repository.Tests/Repository.Tests.csproj
|
|
```
|
|
|
|
### Test Features
|
|
|
|
Current test coverage includes:
|
|
- User authentication (login, registration)
|
|
- JWT token generation
|
|
- Password validation
|
|
- 404 error handling
|
|
- User repository operations
|
|
|
|
---
|
|
|
|
## Database Schema
|
|
|
|
The database uses a SQL-first approach with comprehensive normalization and referential integrity.
|
|
|
|
### Key Tables
|
|
|
|
**User Management**
|
|
- `UserAccount` - User profiles
|
|
- `UserCredential` - Password hashes (Argon2id)
|
|
- `UserVerification` - Account verification status
|
|
- `UserAvatar` - Profile pictures
|
|
- `UserFollow` - Social following relationships
|
|
|
|
**Location Data**
|
|
- `Country` - ISO 3166-1 country codes
|
|
- `StateProvince` - ISO 3166-2 subdivisions
|
|
- `City` - City/municipality data
|
|
|
|
**Content**
|
|
- `BreweryPost` - Brewery information
|
|
- `BreweryPostLocation` - Geographic data with `GEOGRAPHY` type
|
|
- `BeerStyle` - Beer style taxonomy
|
|
- `BeerPost` - Individual beers with ABV/IBU
|
|
- `BeerPostComment` - User reviews and ratings
|
|
- `Photo` - Image metadata
|
|
|
|
**Stored Procedures** (examples)
|
|
- `USP_RegisterUser` - Create user account with credential
|
|
- `USP_GetUserAccountByUsername` - Retrieve user by username
|
|
- `USP_RotateUserCredential` - Update password
|
|
- `USP_CreateCountry/StateProvince/City` - Location management
|
|
|
|
---
|
|
|
|
## Authentication & Security
|
|
|
|
- **Password Hashing**: Argon2id with configurable parameters
|
|
- Salt: 128-bit (16 bytes)
|
|
- Hash: 256-bit (32 bytes)
|
|
- Memory: 64MB
|
|
- Iterations: 4
|
|
- Parallelism: Based on CPU cores
|
|
|
|
- **JWT Tokens**: HS256 signing
|
|
- Claims: User ID (sub), Username (unique_name), JTI
|
|
- Configurable expiration (60-120 minutes)
|
|
- Secret key from environment variable
|
|
|
|
- **Credential Management**:
|
|
- Credential rotation/invalidation supported
|
|
- Expiry tracking (90-day default)
|
|
- Revocation timestamps
|
|
|
|
---
|
|
|
|
## Architecture Patterns
|
|
|
|
### Layered Architecture
|
|
```
|
|
API (Controllers)
|
|
|
|
|
Service Layer (Business Logic)
|
|
|
|
|
Repository Layer (Data Access)
|
|
|
|
|
Database (SQL Server)
|
|
```
|
|
|
|
### Design Patterns
|
|
- **Repository Pattern**: Abstraction over data access
|
|
- **Dependency Injection**: Constructor injection throughout
|
|
- **Factory Pattern**: `ISqlConnectionFactory` for database connections
|
|
- **Service Pattern**: Encapsulated business logic
|
|
|
|
### SQL-First Approach
|
|
- All CRUD operations via stored procedures
|
|
- No ORM (Entity Framework, Dapper, etc.)
|
|
- Direct ADO.NET for maximum control
|
|
- Version-controlled schema via DbUp
|
|
|
|
---
|
|
|
|
## Docker & Containerization
|
|
|
|
|
|
### Container Architecture
|
|
|
|
### Docker Compose Environments
|
|
|
|
Three separate compose files manage different environments:
|
|
|
|
#### 1. **Development** (`docker-compose.dev.yaml`)
|
|
- **Purpose**: Local development with live data
|
|
- **Features**:
|
|
- SQL Server with persistent volume
|
|
- Database migrations with `CLEAR_DATABASE=true` (drops/recreates schema)
|
|
- Seed data for testing
|
|
- API accessible on `localhost:8080`
|
|
- Hot reload support via volume mounts
|
|
|
|
**Services**:
|
|
```yaml
|
|
sqlserver # SQL Server 2022 (port 1433)
|
|
database.migrations # Runs DbUp migrations
|
|
database.seed # Seeds initial data
|
|
api.core # Web API (ports 8080, 8081)
|
|
```
|
|
|
|
**Usage**:
|
|
```bash
|
|
docker compose -f docker-compose.dev.yaml up -d
|
|
docker compose -f docker-compose.dev.yaml logs -f # View logs
|
|
docker compose -f docker-compose.dev.yaml down # Stop all services
|
|
```
|
|
|
|
#### 2. **Testing** (`docker-compose.test.yaml`)
|
|
- **Purpose**: Automated CI/CD testing
|
|
- **Features**:
|
|
- Isolated test database
|
|
- Runs integration and unit tests
|
|
- Test results exported to `./test-results/`
|
|
- Containers exit after tests complete
|
|
|
|
**Services**:
|
|
```yaml
|
|
sqlserver # Test database instance
|
|
database.migrations # Fresh schema each run
|
|
database.seed # Test data
|
|
api.specs # Integration tests (Reqnroll)
|
|
repository.tests # Unit tests (xUnit)
|
|
```
|
|
|
|
**Usage**:
|
|
```bash
|
|
docker compose -f docker-compose.test.yaml up --abort-on-container-exit;
|
|
docker compose -f docker-compose.test.yaml down -v;
|
|
# View results in ./test-results/
|
|
```
|
|
|
|
#### 3. **Production** (`docker-compose.prod.yaml`)
|
|
- **Purpose**: Production-like deployment
|
|
- **Features**:
|
|
- Production logging levels
|
|
- No database clearing
|
|
- Optimized builds
|
|
- Health checks enabled
|
|
- Restart policies configured
|
|
|
|
**Services**:
|
|
```yaml
|
|
sqlserver # Production SQL Server
|
|
database.migrations # Schema updates only (no drops)
|
|
api.core # Production API
|
|
```
|
|
|
|
### Service Dependencies
|
|
|
|
Docker Compose manages service startup order using **health checks** and **depends_on** conditions:
|
|
|
|
```yaml
|
|
database.migrations:
|
|
depends_on:
|
|
sqlserver:
|
|
condition: service_healthy # Waits for SQL Server to be ready
|
|
|
|
database.seed:
|
|
depends_on:
|
|
database.migrations:
|
|
condition: service_completed_successfully # Waits for migrations
|
|
```
|
|
|
|
**Flow**:
|
|
1. `sqlserver` starts and runs health check (SQL query)
|
|
2. `database.migrations` starts when SQL Server is healthy
|
|
3. `database.seed` starts when migrations complete successfully
|
|
4. `api.core` starts when seeding completes
|
|
|
|
### Health Checks
|
|
|
|
SQL Server container includes a health check to ensure it's ready:
|
|
|
|
```yaml
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P '${SA_PASSWORD}' -C -Q 'SELECT 1' || exit 1"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 12
|
|
start_period: 30s
|
|
```
|
|
|
|
This prevents downstream services from attempting connections before SQL Server is ready.
|
|
|
|
### Volumes
|
|
|
|
**Persistent Storage**:
|
|
- `sqlserverdata-dev` - Development database data
|
|
- `sqlserverdata-test` - Test database data
|
|
- `sqlserverdata-prod` - Production database data
|
|
- `nuget-cache-dev/prod` - NuGet package cache (speeds up builds)
|
|
|
|
**Mounted Volumes**:
|
|
```yaml
|
|
volumes:
|
|
- ./test-results:/app/test-results # Export test results to host
|
|
- nuget-cache-dev:/root/.nuget/packages # Cache dependencies
|
|
```
|
|
|
|
### Networks
|
|
|
|
Each environment uses isolated bridge networks:
|
|
- `devnet` - Development network
|
|
- `testnet` - Testing network (fully isolated)
|
|
- `prodnet` - Production network
|
|
|
|
This prevents cross-environment communication and enhances security.
|
|
|
|
### Environment Variables
|
|
|
|
All containers receive configuration via environment variables:
|
|
|
|
```yaml
|
|
environment:
|
|
ASPNETCORE_ENVIRONMENT: "Development"
|
|
DOTNET_RUNNING_IN_CONTAINER: "true"
|
|
DB_CONNECTION_STRING: "${DB_CONNECTION_STRING}"
|
|
JWT_SECRET: "${JWT_SECRET}"
|
|
```
|
|
|
|
Values are populated from the `.env` file in the project root.
|
|
|
|
### Container Lifecycle
|
|
|
|
**Development Workflow**:
|
|
```bash
|
|
# Start environment
|
|
docker compose -f docker-compose.dev.yaml up -d
|
|
|
|
# View logs
|
|
docker compose -f docker-compose.dev.yaml logs -f api.core
|
|
|
|
# Restart a service
|
|
docker compose -f docker-compose.dev.yaml restart api.core
|
|
|
|
# Rebuild after code changes
|
|
docker compose -f docker-compose.dev.yaml up -d --build api.core
|
|
|
|
# Clean shutdown
|
|
docker compose -f docker-compose.dev.yaml down
|
|
|
|
# Remove volumes (fresh start)
|
|
docker compose -f docker-compose.dev.yaml down -v
|
|
```
|
|
|
|
**Testing Workflow**:
|
|
```bash
|
|
# Run tests (containers auto-exit)
|
|
docker compose -f docker-compose.test.yaml up --abort-on-container-exit
|
|
|
|
# Check test results
|
|
cat test-results/test-results.trx
|
|
cat test-results/repository-tests.trx
|
|
|
|
# Clean up
|
|
docker compose -f docker-compose.test.yaml down -v
|
|
```
|
|
|
|
## Docker Tips & Troubleshooting
|
|
|
|
### Common Commands
|
|
|
|
**View running containers**:
|
|
```bash
|
|
docker ps
|
|
```
|
|
|
|
**View all containers (including stopped)**:
|
|
```bash
|
|
docker ps -a
|
|
```
|
|
|
|
**View logs for a specific service**:
|
|
```bash
|
|
docker compose -f docker-compose.dev.yaml logs -f api.core
|
|
```
|
|
|
|
**Execute commands in a running container**:
|
|
```bash
|
|
docker exec -it dev-env-api-core bash
|
|
```
|
|
|
|
**Connect to SQL Server from host**:
|
|
```bash
|
|
# Using sqlcmd (if installed)
|
|
sqlcmd -S localhost,1433 -U sa -P 'YourStrong!Passw0rd' -C
|
|
|
|
# Config
|
|
Server: localhost,1433
|
|
Authentication: SQL Login
|
|
Username: sa
|
|
Password: (from .env)
|
|
```
|
|
|
|
---
|
|
|
|
## Roadmap
|
|
|
|
### Near-term
|
|
- [ ] Complete API endpoints for breweries and beers
|
|
- [ ] Integrate frontend with .NET API
|
|
- [ ] Implement image upload service
|
|
- [ ] Add comprehensive API documentation
|
|
|
|
### Medium-term
|
|
- [ ] Geospatial queries for nearby breweries
|
|
- [ ] Advanced authentication (OAuth, 2FA)
|
|
---
|
|
|
|
## License
|
|
|
|
See [LICENSE.md](LICENSE.md) for details.
|
|
|
|
---
|
|
|
|
## Contact & Support
|
|
|
|
For questions about this project, please open an issue in the repository.
|
|
|
|
|