# Campaign Manager Campaign Manager is split into two Rust backend services, a SvelteKit web app, and a TaleSpire Symbiote frontend. ## Architecture - `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. ## Data layout All databases run on one Postgres instance with separate logical databases: - `cm_users`: users/auth/session data. - `cm_campaign`: campaign and character data. - `cm_content`: rulesets/content data. ## Environment Copy `.env.example` to `.env` and set values. Important variables: - `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) ## Local development ```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 ```