A collaborative, real-time web application for Albion Online guild members to crowdsource and visualise temporary Roads of Avalon portal connections between zones.
Multiple users join a shared, password-protected room and contribute connection data which propagates to all participants over WebSockets in ~250 ms.
| Layer | Technologies |
|---|---|
| Language | TypeScript (strict mode) |
| Backend | Fastify, @fastify/websocket, pg, bcrypt, zod |
| Frontend | Vite + Vue 3 (<script setup>), TailwindCSS, @vue-flow/core, reka-ui, Pinia |
| Testing | vitest, @testing-library/vue, @vue/test-utils, supertest |
| Tooling | pnpm workspaces, tsx |
Create .env files in provisioning and web/server (see below). Then run:
# 1. Start the local database (Postgres in Docker)
pnpm db:up
# 2. Install all dependencies
pnpm install
# 3. Perform a build so shared components are available
pnpm build
# 4. Start both server and client (concurrently)
pnpm devThe client dev server runs on http://localhost:5173 (proxied to the API server on :3001).
albion-mapper/
├── web/
│ ├── client/ # Vue 3 SPA — components, stores, composables
│ │ ├── src/
│ │ └── test/
│ ├── server/ # Fastify API — HTTP routes, WS, PostgreSQL, expiry
│ │ ├── src/
│ │ ├── migrations/ # Database migrations (node-pg-migrate)
│ │ ├── fixtures/ # Data seeding
│ │ └── test/
│ └── shared/ # Domain types, Zod schemas, zones adapter
│ ├── src/
│ ├── data/ # maps.json (committed, updated by sync-maps)
│ └── test/
├── map-parser/ # Standalone parser that populates web/shared/data/maps.json
├── package.json
└── pnpm-workspace.yaml
# All packages at once
pnpm test
# Individual packages
pnpm --filter map-parser test
pnpm --filter shared test
pnpm --filter server test
pnpm --filter client testTest counts (all green):
map-parser— 42 tests (data classification, sync logic)shared— 32 tests (zones adapter, categorization)server— 27 tests (rooms, connections, expiry, WebSocket)client— 31 tests (connectionStyle, roomStore, ZoneCombobox, ReportForm)
Open a room → click or tab into From zone, type to search, pick a zone → tab to To zone → tab to time (enter as H:MM or plain minutes) → Enter to submit.
- Home zone centred at (0,0); direct neighbours at radius 220 px; second-degree at 440 px.
- Edge colours: green (> 30 min), amber (10–30 min), red dashed (< 10 min), grey dashed (stale, within 6 h grace).
- Live countdown on each edge (
MM:SSorHh MMm). - Click a node → sets it as the new home zone (broadcasts to all clients).
- Click an edge → opens a popover with reporter, timestamp, and a Delete button.
WebSocket at /ws/rooms/:id. Authenticated via JWT (sent as first auth message). All writes go through REST; WS fans out connection_added, connection_removed, and room_updated events to every authenticated subscriber in the same room.
- Passwords hashed with
bcrypt(cost 12). - Short-lived JWT (24 h) for API and WS auth.
- Rate limiting:
POST /api/rooms→ 10/hour/IP;POST /api/rooms/:id/auth→ 20/hour/IP. - Zone validation on every connection submission (both IDs must exist in catalogue, must differ).
| Variable | Default | Description |
|---|---|---|
DATABASE_URL |
- | PostgreSQL connection string (required) |
JWT_SECRET |
change-me-in-production |
HMAC secret for JWT signing |
PORT |
3001 |
Server listen port |
HOST |
0.0.0.0 |
Server listen host |