feat: update configuration and README for improved authentication and database support
This commit is contained in:
62
.env.example
62
.env.example
@@ -2,82 +2,64 @@
|
||||
# K-Template Configuration
|
||||
# ============================================================================
|
||||
# Copy this file to .env and adjust values for your environment.
|
||||
# All values shown are defaults or examples.
|
||||
|
||||
# ============================================================================
|
||||
# Server Configuration
|
||||
# Server
|
||||
# ============================================================================
|
||||
HOST=127.0.0.1
|
||||
PORT=3000
|
||||
|
||||
# ============================================================================
|
||||
# Database Configuration
|
||||
# Database
|
||||
# ============================================================================
|
||||
# SQLite (default)
|
||||
DATABASE_URL=sqlite:data.db?mode=rwc
|
||||
|
||||
# PostgreSQL (alternative - requires postgres feature)
|
||||
# PostgreSQL (requires postgres feature flag)
|
||||
# DATABASE_URL=postgres://user:password@localhost:5432/mydb
|
||||
|
||||
# Connection pool settings
|
||||
DB_MAX_CONNECTIONS=5
|
||||
DB_MIN_CONNECTIONS=1
|
||||
|
||||
# ============================================================================
|
||||
# Authentication Mode
|
||||
# Cookie Secret
|
||||
# ============================================================================
|
||||
# Options: session, jwt, both
|
||||
# - session: Cookie-based sessions (requires auth-axum-login feature)
|
||||
# - jwt: Bearer token authentication (requires auth-jwt feature)
|
||||
# - both: Support both methods (try JWT first, fall back to session)
|
||||
AUTH_MODE=jwt
|
||||
# Used to encrypt the OIDC state cookie (CSRF token, PKCE verifier, nonce).
|
||||
# Must be at least 64 characters in production.
|
||||
COOKIE_SECRET=your-cookie-secret-key-must-be-at-least-64-characters-long-for-security!!
|
||||
|
||||
# ============================================================================
|
||||
# Session Configuration (for session/both modes)
|
||||
# ============================================================================
|
||||
# Must be at least 64 characters in production
|
||||
SESSION_SECRET=your-super-secret-key-must-be-at-least-64-characters-long-for-security
|
||||
|
||||
# Set to true in production for HTTPS-only cookies
|
||||
# Set to true when serving over HTTPS
|
||||
SECURE_COOKIE=false
|
||||
|
||||
# ============================================================================
|
||||
# JWT Configuration (for jwt/both modes)
|
||||
# JWT
|
||||
# ============================================================================
|
||||
# Must be at least 32 characters in production
|
||||
# Must be at least 32 characters in production.
|
||||
JWT_SECRET=your-jwt-secret-key-at-least-32-chars
|
||||
|
||||
# Optional: JWT issuer and audience for token validation
|
||||
JWT_ISSUER=your-app-name
|
||||
JWT_AUDIENCE=your-app-audience
|
||||
# Optional: embed issuer/audience claims in tokens
|
||||
# JWT_ISSUER=your-app-name
|
||||
# JWT_AUDIENCE=your-app-audience
|
||||
|
||||
# Token expiry in hours (default: 24)
|
||||
# Token lifetime in hours (default: 24)
|
||||
JWT_EXPIRY_HOURS=24
|
||||
|
||||
# ============================================================================
|
||||
# OIDC Configuration (optional - requires auth-oidc feature)
|
||||
# OIDC (optional — requires auth-oidc feature flag)
|
||||
# ============================================================================
|
||||
# Your OIDC provider's issuer URL (e.g., Keycloak, Auth0, Zitadel)
|
||||
OIDC_ISSUER=https://your-oidc-provider.com
|
||||
|
||||
# Client credentials from your OIDC provider
|
||||
OIDC_CLIENT_ID=your-client-id
|
||||
OIDC_CLIENT_SECRET=your-client-secret
|
||||
|
||||
# Callback URL (must match what's configured in your OIDC provider)
|
||||
OIDC_REDIRECT_URL=http://localhost:3000/api/v1/auth/callback
|
||||
|
||||
# Optional: Resource ID for audience verification
|
||||
# OIDC_RESOURCE_ID=your-resource-id
|
||||
# OIDC_ISSUER=https://your-oidc-provider.com
|
||||
# OIDC_CLIENT_ID=your-client-id
|
||||
# OIDC_CLIENT_SECRET=your-client-secret
|
||||
# OIDC_REDIRECT_URL=http://localhost:3000/api/v1/auth/callback
|
||||
# OIDC_RESOURCE_ID=your-resource-id # optional audience claim to verify
|
||||
|
||||
# ============================================================================
|
||||
# CORS Configuration
|
||||
# CORS
|
||||
# ============================================================================
|
||||
# Comma-separated list of allowed origins
|
||||
CORS_ALLOWED_ORIGINS=http://localhost:5173,http://localhost:3000
|
||||
|
||||
# ============================================================================
|
||||
# Production Mode
|
||||
# ============================================================================
|
||||
# Set to true/production/1 to enable production checks (secret length, etc.)
|
||||
# Set to true/production/1 to enforce minimum secret lengths and other checks.
|
||||
PRODUCTION=false
|
||||
|
||||
128
Cargo.lock
generated
128
Cargo.lock
generated
@@ -803,20 +803,6 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.31"
|
||||
@@ -1395,8 +1381,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "k-core"
|
||||
version = "0.1.10"
|
||||
source = "git+https://git.gabrielkaszewski.dev/GKaszewski/k-core#7a72f5f54ad45ba82f451e90c44c0581d13194d9"
|
||||
version = "0.1.11"
|
||||
source = "git+https://git.gabrielkaszewski.dev/GKaszewski/k-core#0ea9aa7870d73b5f665241a4183ffd899e628b9c"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-nats",
|
||||
@@ -1408,12 +1394,9 @@ dependencies = [
|
||||
"serde",
|
||||
"sqlx",
|
||||
"thiserror 2.0.17",
|
||||
"time",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"tower-sessions",
|
||||
"tower-sessions-sqlx-store",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"uuid",
|
||||
@@ -1475,7 +1458,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1639,7 +1621,7 @@ version = "5.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"base64 0.21.7",
|
||||
"chrono",
|
||||
"getrandom 0.2.16",
|
||||
"http",
|
||||
@@ -2189,25 +2171,6 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rmp"
|
||||
version = "0.8.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rmp-serde"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155"
|
||||
dependencies = [
|
||||
"rmp",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "0.9.9"
|
||||
@@ -2707,7 +2670,6 @@ dependencies = [
|
||||
"sha2",
|
||||
"smallvec",
|
||||
"thiserror 2.0.17",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tracing",
|
||||
@@ -2792,7 +2754,6 @@ dependencies = [
|
||||
"sqlx-core",
|
||||
"stringprep",
|
||||
"thiserror 2.0.17",
|
||||
"time",
|
||||
"tracing",
|
||||
"uuid",
|
||||
"whoami",
|
||||
@@ -2832,7 +2793,6 @@ dependencies = [
|
||||
"sqlx-core",
|
||||
"stringprep",
|
||||
"thiserror 2.0.17",
|
||||
"time",
|
||||
"tracing",
|
||||
"uuid",
|
||||
"whoami",
|
||||
@@ -2859,7 +2819,6 @@ dependencies = [
|
||||
"serde_urlencoded",
|
||||
"sqlx-core",
|
||||
"thiserror 2.0.17",
|
||||
"time",
|
||||
"tracing",
|
||||
"url",
|
||||
"uuid",
|
||||
@@ -3129,22 +3088,6 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-cookies"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "151b5a3e3c45df17466454bb74e9ecedecc955269bdedbf4d150dfa393b55a36"
|
||||
dependencies = [
|
||||
"axum-core",
|
||||
"cookie",
|
||||
"futures-util",
|
||||
"http",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.6.8"
|
||||
@@ -3176,71 +3119,6 @@ version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
||||
|
||||
[[package]]
|
||||
name = "tower-sessions"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43a05911f23e8fae446005fe9b7b97e66d95b6db589dc1c4d59f6a2d4d4927d3"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"http",
|
||||
"time",
|
||||
"tokio",
|
||||
"tower-cookies",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tower-sessions-core",
|
||||
"tower-sessions-memory-store",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-sessions-core"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce8cce604865576b7751b7a6bc3058f754569a60d689328bb74c52b1d87e355b"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
"base64 0.22.1",
|
||||
"futures",
|
||||
"http",
|
||||
"parking_lot",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.17",
|
||||
"time",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-sessions-memory-store"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb05909f2e1420135a831dd5df9f5596d69196d0a64c3499ca474c4bd3d33242"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"time",
|
||||
"tokio",
|
||||
"tower-sessions-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-sessions-sqlx-store"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e054622079f57fc1a7d6a6089c9334f963d62028fe21dc9eddd58af9a78480b3"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"rmp-serde",
|
||||
"sqlx",
|
||||
"thiserror 1.0.69",
|
||||
"time",
|
||||
"tower-sessions-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.44"
|
||||
|
||||
148
README.md
148
README.md
@@ -5,10 +5,10 @@ A production-ready, modular Rust API template for K-Suite applications, followin
|
||||
## Features
|
||||
|
||||
- **Hexagonal Architecture**: Clear separation between Domain, Infrastructure, and API layers
|
||||
- **Multiple Auth Modes**: Session-based, JWT, or both - fully configurable
|
||||
- **OIDC Integration**: Connect to any OpenID Connect provider (Keycloak, Auth0, Zitadel, etc.)
|
||||
- **JWT-Only Authentication**: Stateless Bearer token auth — no server-side sessions
|
||||
- **OIDC Integration**: Connect to any OpenID Connect provider (Keycloak, Auth0, Zitadel, etc.) with stateless cookie-based flow state
|
||||
- **Database Flexibility**: SQLite (default) or PostgreSQL via feature flags
|
||||
- **Type-Safe Configuration**: Newtypes with built-in validation for all security-sensitive values
|
||||
- **Type-Safe Domain**: Newtypes with built-in validation for emails, passwords, secrets, and OIDC values
|
||||
- **Cargo Generate Ready**: Pre-configured for scaffolding new services
|
||||
|
||||
## Quick Start
|
||||
@@ -22,7 +22,6 @@ cargo generate --git https://github.com/GKaszewski/k-template.git
|
||||
You'll be prompted to choose:
|
||||
- **Project name**: Your new service name
|
||||
- **Database**: `sqlite` or `postgres`
|
||||
- **Session auth**: Enable cookie-based sessions
|
||||
- **JWT auth**: Enable Bearer token authentication
|
||||
- **OIDC**: Enable OpenID Connect integration
|
||||
|
||||
@@ -33,15 +32,6 @@ git clone https://github.com/GKaszewski/k-template.git my-api
|
||||
cd my-api
|
||||
cp .env.example .env
|
||||
# Edit .env with your configuration
|
||||
```
|
||||
|
||||
### Run
|
||||
|
||||
```bash
|
||||
# Development (with hot reload via cargo-watch)
|
||||
cargo watch -x run
|
||||
|
||||
# Or simply
|
||||
cargo run
|
||||
```
|
||||
|
||||
@@ -51,51 +41,51 @@ The API will be available at `http://localhost:3000/api/v1/`.
|
||||
|
||||
All configuration is done via environment variables. See [.env.example](.env.example) for all options.
|
||||
|
||||
### Authentication Modes
|
||||
### Key Variables
|
||||
|
||||
Set `AUTH_MODE` to one of:
|
||||
|
||||
| Mode | Description | Required Features |
|
||||
|------|-------------|-------------------|
|
||||
| `session` | Cookie-based sessions | `auth-axum-login` |
|
||||
| `jwt` | Bearer token authentication | `auth-jwt` |
|
||||
| `both` | Try JWT first, fall back to session | `auth-axum-login`, `auth-jwt` |
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `DATABASE_URL` | `sqlite:data.db?mode=rwc` | Database connection string |
|
||||
| `COOKIE_SECRET` | *(insecure dev default)* | Secret for encrypting OIDC state cookie (≥64 bytes in production) |
|
||||
| `JWT_SECRET` | *(insecure dev default)* | Secret for signing JWT tokens (≥32 bytes in production) |
|
||||
| `JWT_EXPIRY_HOURS` | `24` | Token lifetime in hours |
|
||||
| `CORS_ALLOWED_ORIGINS` | `http://localhost:5173` | Comma-separated allowed origins |
|
||||
| `SECURE_COOKIE` | `false` | Set `true` when serving over HTTPS |
|
||||
| `PRODUCTION` | `false` | Enforces minimum secret lengths |
|
||||
|
||||
### OIDC Integration
|
||||
|
||||
To enable OIDC login (e.g., "Login with Google"):
|
||||
To enable "Login with Google/Keycloak/etc.":
|
||||
|
||||
1. Enable the `auth-oidc` feature (enabled by default)
|
||||
2. Configure your OIDC provider in `.env`:
|
||||
1. Enable the `auth-oidc` feature (on by default in cargo-generate)
|
||||
2. Set these environment variables:
|
||||
```env
|
||||
OIDC_ISSUER=https://your-provider.com
|
||||
OIDC_CLIENT_ID=your-client-id
|
||||
OIDC_CLIENT_SECRET=your-secret
|
||||
OIDC_REDIRECT_URL=http://localhost:3000/api/v1/auth/callback
|
||||
```
|
||||
3. Users can login via `GET /api/v1/auth/login/oidc`
|
||||
3. Users start the flow at `GET /api/v1/auth/login/oidc`
|
||||
|
||||
OIDC state (CSRF token, PKCE verifier, nonce) is stored in a short-lived encrypted cookie — no database session table required.
|
||||
|
||||
## Feature Flags
|
||||
|
||||
Features are configured in `api/Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[features]
|
||||
default = ["sqlite", "auth-axum-login", "auth-oidc", "auth-jwt"]
|
||||
default = ["sqlite", "auth-jwt"]
|
||||
```
|
||||
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| `sqlite` | SQLite database support (default) |
|
||||
| `postgres` | PostgreSQL database support |
|
||||
| `auth-axum-login` | Session-based authentication |
|
||||
| `sqlite` | SQLite database (default) |
|
||||
| `postgres` | PostgreSQL database |
|
||||
| `auth-jwt` | JWT Bearer token authentication |
|
||||
| `auth-oidc` | OpenID Connect integration |
|
||||
| `auth-jwt` | JWT token authentication |
|
||||
| `auth-full` | All auth features combined |
|
||||
|
||||
### Common Configurations
|
||||
|
||||
**JWT-only API (stateless)**:
|
||||
**JWT-only (minimal, default)**:
|
||||
```toml
|
||||
default = ["sqlite", "auth-jwt"]
|
||||
```
|
||||
@@ -105,48 +95,74 @@ default = ["sqlite", "auth-jwt"]
|
||||
default = ["sqlite", "auth-oidc", "auth-jwt"]
|
||||
```
|
||||
|
||||
**Full-featured (all auth methods)**:
|
||||
**PostgreSQL + OIDC + JWT**:
|
||||
```toml
|
||||
default = ["sqlite", "auth-full"]
|
||||
default = ["postgres", "auth-oidc", "auth-jwt"]
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Authentication
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `POST` | `/api/v1/auth/login` | Login with email/password |
|
||||
| `POST` | `/api/v1/auth/register` | Register new user |
|
||||
| `POST` | `/api/v1/auth/logout` | Logout (session mode) |
|
||||
| `GET` | `/api/v1/auth/me` | Get current user |
|
||||
| `POST` | `/api/v1/auth/token` | Get JWT for session user |
|
||||
| `GET` | `/api/v1/auth/login/oidc` | Start OIDC login flow |
|
||||
| `GET` | `/api/v1/auth/callback` | OIDC callback |
|
||||
| Method | Endpoint | Auth | Description |
|
||||
|--------|----------|------|-------------|
|
||||
| `POST` | `/api/v1/auth/register` | — | Register with email + password → JWT |
|
||||
| `POST` | `/api/v1/auth/login` | — | Login with email + password → JWT |
|
||||
| `POST` | `/api/v1/auth/logout` | — | Returns 200; client drops the token |
|
||||
| `GET` | `/api/v1/auth/me` | Bearer | Current user info |
|
||||
| `POST` | `/api/v1/auth/token` | Bearer | Issue a fresh JWT (`auth-jwt`) |
|
||||
| `GET` | `/api/v1/auth/login/oidc` | — | Start OIDC flow, sets encrypted state cookie (`auth-oidc`) |
|
||||
| `GET` | `/api/v1/auth/callback` | — | Complete OIDC flow → JWT, clears cookie (`auth-oidc`) |
|
||||
|
||||
### Example: Register and use a token
|
||||
|
||||
```bash
|
||||
# Register
|
||||
curl -X POST http://localhost:3000/api/v1/auth/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email": "user@example.com", "password": "mypassword"}'
|
||||
# → {"access_token": "eyJ...", "token_type": "Bearer", "expires_in": 86400}
|
||||
|
||||
# Use the token
|
||||
curl http://localhost:3000/api/v1/auth/me \
|
||||
-H "Authorization: Bearer eyJ..."
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
k-template/
|
||||
├── domain/ # Core business logic (no I/O dependencies)
|
||||
├── domain/ # Pure business logic — zero I/O dependencies
|
||||
│ └── src/
|
||||
│ ├── entities.rs # User entity
|
||||
│ ├── value_objects.rs # Email, Password, OIDC newtypes
|
||||
│ ├── value_objects.rs # Email, Password, JwtSecret, OIDC newtypes
|
||||
│ ├── repositories.rs # Repository interfaces (ports)
|
||||
│ └── services.rs # Domain services
|
||||
│ ├── services.rs # Domain services
|
||||
│ └── errors.rs # DomainError (Unauthenticated 401, Forbidden 403, …)
|
||||
│
|
||||
├── infra/ # Infrastructure adapters
|
||||
│ └── src/
|
||||
│ ├── auth/ # Auth backends (OIDC, JWT)
|
||||
│ └── user_repository.rs
|
||||
│ ├── auth/
|
||||
│ │ ├── jwt.rs # JwtValidator — create + verify tokens
|
||||
│ │ └── oidc.rs # OidcService + OidcState (cookie-serializable)
|
||||
│ ├── user_repository.rs # SQLite / PostgreSQL adapter
|
||||
│ ├── db.rs # DatabasePool re-export
|
||||
│ └── factory.rs # build_user_repository()
|
||||
│
|
||||
├── api/ # HTTP API layer
|
||||
├── api/ # HTTP layer
|
||||
│ └── src/
|
||||
│ ├── routes/ # API endpoints
|
||||
│ ├── config.rs # Configuration
|
||||
│ └── state.rs # Application state
|
||||
│ ├── routes/
|
||||
│ │ ├── auth.rs # Login, register, logout, me, OIDC flow
|
||||
│ │ └── config.rs # /config endpoint
|
||||
│ ├── config.rs # Config::from_env()
|
||||
│ ├── state.rs # AppState (user_service, cookie_key, jwt_validator, …)
|
||||
│ ├── extractors.rs # CurrentUser (JWT Bearer extractor)
|
||||
│ ├── error.rs # ApiError → HTTP status mapping
|
||||
│ └── dto.rs # LoginRequest, RegisterRequest, TokenResponse, …
|
||||
│
|
||||
├── .env.example # Configuration template
|
||||
├── migrations_sqlite/
|
||||
├── migrations_postgres/
|
||||
├── .env.example
|
||||
└── compose.yml # Docker Compose for local dev
|
||||
```
|
||||
|
||||
@@ -156,10 +172,13 @@ k-template/
|
||||
|
||||
```bash
|
||||
# All tests
|
||||
cargo test --all-features
|
||||
cargo test
|
||||
|
||||
# Domain tests only
|
||||
# Domain only
|
||||
cargo test -p domain
|
||||
|
||||
# Infra only (SQLite integration tests)
|
||||
cargo test -p infra
|
||||
```
|
||||
|
||||
### Database Migrations
|
||||
@@ -168,10 +187,23 @@ cargo test -p domain
|
||||
# SQLite
|
||||
sqlx migrate run --source migrations_sqlite
|
||||
|
||||
# PostgreSQL
|
||||
# PostgreSQL
|
||||
sqlx migrate run --source migrations_postgres
|
||||
```
|
||||
|
||||
### Building with specific features
|
||||
|
||||
```bash
|
||||
# Minimal: SQLite + JWT only
|
||||
cargo build -F sqlite,auth-jwt
|
||||
|
||||
# Full: SQLite + JWT + OIDC
|
||||
cargo build -F sqlite,auth-jwt,auth-oidc
|
||||
|
||||
# PostgreSQL variant
|
||||
cargo build --no-default-features -F postgres,auth-jwt,auth-oidc
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
@@ -34,7 +34,23 @@ async fn main() -> anyhow::Result<()> {
|
||||
|
||||
// Setup database
|
||||
tracing::info!("Connecting to database: {}", config.database_url);
|
||||
|
||||
#[cfg(all(feature = "sqlite", not(feature = "postgres")))]
|
||||
let db_type = k_core::db::DbType::Sqlite;
|
||||
|
||||
#[cfg(all(feature = "postgres", not(feature = "sqlite")))]
|
||||
let db_type = k_core::db::DbType::Postgres;
|
||||
|
||||
// Both features enabled: fall back to URL inspection at runtime
|
||||
#[cfg(all(feature = "sqlite", feature = "postgres"))]
|
||||
let db_type = if config.database_url.starts_with("postgres") {
|
||||
k_core::db::DbType::Postgres
|
||||
} else {
|
||||
k_core::db::DbType::Sqlite
|
||||
};
|
||||
|
||||
let db_config = k_core::db::DatabaseConfig {
|
||||
db_type,
|
||||
url: config.database_url.clone(),
|
||||
max_connections: config.db_max_connections,
|
||||
min_connections: config.db_min_connections,
|
||||
@@ -51,8 +67,6 @@ async fn main() -> anyhow::Result<()> {
|
||||
|
||||
let server_config = ServerConfig {
|
||||
cors_origins: config.cors_allowed_origins.clone(),
|
||||
// session_secret is unused (sessions removed); kept for k-core API compat
|
||||
session_secret: None,
|
||||
};
|
||||
|
||||
let app = Router::new()
|
||||
|
||||
Reference in New Issue
Block a user