Files
ashen-realms/README.md
Bastian Wagner e0e6db3ae8 chore: move unreferenced source art out of the served web assets
apps/web/public/images was shipping ~90 MB of source art (enemies, npc,
combat status icons, HUD icon originals/difficulty badges, unused
background paintings) that no component, template, or stylesheet
actually references, since Angular copies public/ verbatim into every
browser build. Moved everything not referenced under apps/web/src to a
new art/ directory at the repo root, preserving the original subfolder
layout; only the 11 files actually loaded by the app (runtime HUD/
background derivatives and their PNG fallbacks) remain under
apps/web/public/images. Documented the split in README.md.
2026-08-19 10:50:00 +02:00

177 lines
6.7 KiB
Markdown

# Ashen Realms
A dark-fantasy browser RPG built as an npm-workspace modular monolith:
- `apps/web` — Angular 22 single-page application
- `apps/api` — NestJS 11 + TypeORM + PostgreSQL API
- `packages/*` — reserved shared boundaries (currently unused)
This README documents the first visible vertical slice: a server-authoritative
world/travel loop between two locations (Südtor von Graufurt and Verbrannte
Straße) for one demo character, with no login required.
## Prerequisites
- Node.js and npm (workspace-aware npm, matching the versions used by CI/your
toolchain).
- A reachable PostgreSQL server (PostgreSQL 13 or newer — the schema uses the
built-in `gen_random_uuid()`, which needs no extensions) with a database
named `ashen_realms`. Create it once, e.g.:
```powershell
psql -h localhost -U postgres -c "CREATE DATABASE ashen_realms;"
psql -h localhost -U postgres -c "CREATE USER ashen WITH PASSWORD 'ashen';"
psql -h localhost -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE ashen_realms TO ashen;"
```
Adjust user/password/host to match your own local PostgreSQL instance; just
keep `DATABASE_URL` (below) pointed at it.
## Local setup
Run from the repository root:
```powershell
npm install
Copy-Item .env.example .env
npm run db:migrate
npm run db:seed
npm run dev:api
npm run dev:web
```
Run `dev:api` and `dev:web` in separate terminals — both watch and keep
running. Once both are up:
- API: `http://localhost:3000/api/...` (see routes below)
- Web: `http://localhost:4200`, which proxies `/api/*` requests to
`http://localhost:3000` in development (see `apps/web/proxy.conf.json`)
Open `http://localhost:4200/world` to see Aric Duskwalker at the Südtor von
Graufurt and travel to the Verbrannte Straße and back.
### Configuration (`.env`)
`.env.example` (repo root) documents every variable. Copy it to `.env` at the
repo root — `.env` is git-ignored and must never be committed. The important
one for local setup is:
```
DATABASE_URL=postgresql://ashen:ashen@localhost:5432/ashen_realms
```
This is the default local PostgreSQL connection string: database name
`ashen_realms`, default port `5432`. Edit it if your local PostgreSQL uses a
different user, password, host, or port. All API workspace scripts
(`db:migrate`, `db:seed`, `dev:api`, and `apps/api`'s own `test:e2e`) resolve
this `.env` from the repository root regardless of which workspace directory
npm runs the underlying command in.
`synchronize` is always `false`; schema changes only happen through the
checked-in migration at
`apps/api/src/database/migrations/1787072400000-CreateVisibleVerticalSlice.ts`.
The seed (`apps/api/src/database/seeds/vertical-slice.seed.ts`) is idempotent:
running `npm run db:seed` multiple times upserts the same two locations, two
connections, and one demo character without creating duplicates or resetting
the character's current (live) location.
### Ports
| Service | Port | Notes |
|---|---|---|
| API (NestJS) | `3000` | All routes are under `/api` (e.g. `/api/health`). |
| Web (Angular dev server) | `4200` | Proxies `/api/*` to the API in development. |
## Available routes (first slice)
```
GET /api/health
GET /api/characters/me
GET /api/world/current-location
GET /api/travel/current
POST /api/travel { "targetLocationId": "<uuid>" }
```
`POST /api/travel` accepts exactly `targetLocationId`; the global validation
pipe rejects any other property (e.g. a client-supplied `arrivesAt`) with
`400 Bad Request`, since `arrivesAt` is always server-derived.
## Assets
`apps/web/public/images` holds only the derivative image files the Angular
build actually serves (resized `runtime/*-128.png` HUD icons, `runtime/*-960.jpg`
and `runtime/*-1440.jpg` background variants, and the small set of PNG
fallbacks referenced directly by source/styles). Everything under `apps/web/public`
is copied verbatim into the browser build, so keep that folder limited to
files a component, template, or stylesheet actually references.
Original/unresized source art (enemy, NPC, and combat-status artwork, HUD icon
originals, difficulty badges, etc.) that isn't loaded by the app lives in
`art/` at the repository root instead, mirroring the same subfolder layout
(e.g. `art/enemies`, `art/npc`, `art/hud`). It is not part of any build output.
## Scripts
Run these from the repository root unless noted otherwise.
| Script | Description |
|---|---|
| `npm run dev:web` | Start the Angular dev server (port 4200). |
| `npm run dev:api` | Start the NestJS API in watch mode (port 3000). |
| `npm run build:web` | Production build of the Angular app. |
| `npm run build:api` | Production build of the NestJS app. |
| `npm run build` | Both builds. |
| `npm test` | Unit tests for every workspace. |
| `npm run test:e2e` | API end-to-end/smoke tests (see below). |
| `npm run db:migrate` | Run pending TypeORM migrations against `DATABASE_URL`. |
| `npm run db:revert` | Revert the last migration. |
| `npm run db:seed` | Run the idempotent demo-content seed. |
## Testing
Unit tests never require a database:
```powershell
npm test --workspace=@ashen-realms/api -- --runInBand
```
The API end-to-end smoke test (`apps/api/test/visible-slice.e2e-spec.ts`)
always asserts `GET /api/health`. When `DATABASE_URL` is set and reachable,
it additionally boots the real `AppModule` (real database, entities, and
controllers) and asserts the seeded character/world responses and the
`arrivesAt` validation rejection. Those database-backed assertions are
skipped — not failed — when no database is configured, so `npm run test:e2e`
is safe to run without any local PostgreSQL setup too:
```powershell
npm run test:e2e --workspace=@ashen-realms/api -- --runInBand
```
Both builds:
```powershell
npm run build:api
npm run build:web
```
## Known limitations of this first vertical slice
This slice deliberately excludes (per
`docs/superpowers/specs/2026-08-18-first-visible-vertical-slice-design.md`):
- Authentication, user accounts, JWTs, or any login/registration flow — there
is exactly one hardcoded demo character (Aric Duskwalker).
- Hunting, combat, loot, inventory, equipment, quests, merchants, and
currencies.
- Realtime features: WebSockets, chat, guilds, CMS, object storage, or a
multi-service/microservice architecture.
- Ambush resolution: `ambushChance` is stored on each connection and surfaced
only as a coarse `LOW`/`HIGH` danger rating; it is never rolled.
- Production containerization or NestJS static hosting.
- Client-side clock skew can shift how the travel countdown *displays*, but
arrival is always confirmed by the server (`GET /api/travel/current` /
`GET /api/world/current-location`), never inferred locally.
Only two locations and one directed pair of connections exist
(`south-gate` ⇄ `burned-road`), each with a fixed 10-second travel duration.