setup local dev env

This commit is contained in:
2026-03-10 17:13:23 +01:00
parent b27db93a90
commit f4d3fade9b
61 changed files with 6695 additions and 228 deletions

139
README.md
View File

@@ -1,66 +1,97 @@
Root
- .gitignore, pnpm-workspace.yaml, package.json (workspace scripts), Cargo.toml (workspace), .npmrc
rulesets/ — @campaign-manager/rulesets workspace package
- types.ts — Ruleset, SheetData, ValidationResult, DiceAction, StatBlock, VNode interfaces
- dsa5e/index.ts — DSA5e stub (typed default sheet with attributes, talents, combat, spells)
- generic/index.ts — Generic stub (name, HP, notes)
- index.ts — re-exports everything + RULESETS registry map
# Campaign Manager
symbiote/ — Plain Svelte + Vite (target es2022 for top-level await support)
- vite.config.ts, tsconfig.json, index.html
- public/manifest.json — valid TaleSpire manifest with colorStyles, fonts, diceFinder, icons extras
- src/lib/ts-api.d.ts — ambient TS global (storage, dice, players, sync)
- src/lib/store.ts — accessToken, refreshToken, currentUser, currentGroup, isBoardTransition stores +
loadTokens/saveTokens/clearTokens via TS.storage
- src/lib/api.ts — auto-refresh on 401, suppresses calls during board transition
- src/App.svelte — currentView store drives view switching
- src/main.ts — board transition listeners, awaits TS.hasInitialized, mounts App
- src/views/ — Login, GroupList, GroupDetail, CharacterSheet, RollHistory stubs
Campaign Manager is split into two Rust backend services, a SvelteKit web app, and a TaleSpire Symbiote frontend.
web/ — SvelteKit + adapter-static
- svelte.config.js, vite.config.ts, tsconfig.json, src/app.html
- src/lib/api.ts — access token in memory, refresh via HttpOnly cookie
- src/routes/+layout.svelte — silent token refresh on mount, redirects to /login if unauthenticated
- All routes stubbed: /, /login, /groups, /groups/[id], /groups/[id]/settings, /characters, /characters/[id]
## Architecture
backend/ — Rust + Axum
- Cargo.toml with all specified dependencies
- src/main.rs — reads DATABASE_URL/JWT_SECRET, builds Axum router, CORS, tracing, binds :3000
- src/lib.rs — module entrypoint
- `backend/common`: shared Rust crate for JWT and API error primitives.
- `backend/campaign-service`: auth + campaign domain service.
- `backend/content-service`: ruleset/content domain service.
- `web`: SvelteKit Node server that uses server-side BFF proxy routes (`/api/*`).
- `symbiote`: static Svelte app for TaleSpire.
Verification results: ✅ symbiote build → dist/ with index.html + manifest.json ✅ web build → SvelteKit static output
✅ cargo check → no errors
## Data layout
All databases run on one Postgres instance with separate logical databases:
Local dev workflow is now:
# Start Postgres
docker compose up -d
# Copy and fill in secrets
cp .env.example .env
- `cm_users`: users/auth/session data.
- `cm_campaign`: campaign and character data.
- `cm_content`: rulesets/content data.
# Run backend
cd backend && cargo run
## Environment
# Symbiote (build-watch mode — see below)
pnpm dev:symbiote
Copy `.env.example` to `.env` and set values.
# Web app
pnpm dev:web Local dev workflow is now:
# Start Postgres
docker compose up -d
# Copy and fill in secrets
cp .env.example .env
Important variables:
# Run backend
cd backend && cargo run
- `USERS_DATABASE_URL`
- `CAMPAIGN_DATABASE_URL`
- `CONTENT_DATABASE_URL`
- `JWT_SECRET`
- `CAMPAIGN_API_BASE` / `CONTENT_API_BASE` (web BFF upstreams)
- `VITE_CAMPAIGN_API_BASE` / `VITE_CONTENT_API_BASE` (symbiote build-time)
# Symbiote (build-watch mode — see below)
pnpm dev:symbiote
## Local development
# Web app
pnpm dev:web
```bash
# Install JS dependencies
pnpm install
# Run campaign service
pnpm dev:campaign
# Run content service
pnpm dev:content
# Run web app (SvelteKit dev server)
pnpm dev:web
# Run symbiote app
pnpm dev:symbiote
```
Build commands:
```bash
pnpm build:campaign
pnpm build:content
pnpm build:backend
pnpm build:web
pnpm build:symbiote
```
Run the same checks as CI locally:
```bash
pnpm ci:local
```
## Web BFF routes
The web app proxies backend calls via SvelteKit server routes:
- `/api/auth/*` -> campaign service (`CAMPAIGN_API_BASE`)
- `/api/campaign/*` -> campaign service (`CAMPAIGN_API_BASE`)
- `/api/content/*` -> content service (`CONTENT_API_BASE`)
The web app is intended to run as a server process (Node adapter) in production.
## Docker compose
`docker-compose.yml` runs both backend services plus the web server and expects an external `dev-infra` network for Postgres connectivity.
## Woodpecker without Public Ingress
If your Gitea is remote and your Woodpecker server runs locally, push/PR webhooks cannot reach your machine without a public endpoint.
Recommended workflow until ingress/tunnel exists:
- Run `pnpm ci:local` before push.
- Use local Woodpecker only for manual runs.
Local stack files are in `../../infra/woodpecker` from this directory:
```bash
cp ../../infra/woodpecker/.env.example ../../infra/woodpecker/.env
docker compose -f ../../infra/woodpecker/docker-compose.yml up -d
```