First commit, phased 1 and 2 created but untested

This commit is contained in:
bepis
2026-10-20 07:40:35 +11:00
commit 7a3e408ffc
66 changed files with 5639 additions and 0 deletions
+87
View File
@@ -0,0 +1,87 @@
# API.md
Base path: `/api`. JSON in, JSON out. Auth via session cookie.
## Response envelope
Success: `{ "ok": true, "data": { ... } }`
Failure: `{ "ok": false, "error": { "code": "INSUFFICIENT_ENERGY", "message": "Not enough energy." } }`
## Status codes
| Code | Use |
|---|---|
| 200 | success |
| 201 | created (register, create hero) |
| 400 | validation failed (`VALIDATION_ERROR`) |
| 401 | not logged in (`UNAUTHENTICATED`) |
| 403 | logged in but not allowed (`FORBIDDEN`, `BANNED`) |
| 404 | not found (`NOT_FOUND`) |
| 409 | conflict (`USERNAME_TAKEN`, `HERO_EXISTS`, `EXPEDITION_ACTIVE`) |
| 422 | rule violation (`INSUFFICIENT_ENERGY`, `INSUFFICIENT_GOLD`, `LEVEL_TOO_LOW`, `NOT_READY`) |
| 429 | rate limited |
| 500 | unexpected (never leak stack traces) |
## Endpoints
### Auth (public unless noted)
| Method | Path | Body | Returns |
|---|---|---|---|
| POST | `/auth/register` | `{username, password}` | `{user:{id,username}}` + sets session |
| POST | `/auth/login` | `{username, password}` | `{user:{id,username}, hasHero}` |
| POST | `/auth/logout` 🔒 | – | `{}` |
| GET | `/auth/me` 🔒 | – | `{user:{id,username}, hasHero}` |
Validation: username 3–20 chars `[A-Za-z0-9_]`; password 8–72 chars.
Login failure always returns the same generic message (`INVALID_CREDENTIALS`) — don't reveal whether the username exists.
### Character 🔒
| Method | Path | Body | Returns |
|---|---|---|---|
| POST | `/character` | `{name, class}` | `{character}` |
| GET | `/character` | – | `{character, derived, equipment, energy:{current,max,nextTickInSec}}` |
| POST | `/character/allocate` | `{str,agi,int,vit}` (points to add) | `{character, derived}` |
| POST | `/character/rest` | – | heals to full for gold cost (see BALANCE) |
### Hunting 🔒
| Method | Path | Body | Returns |
|---|---|---|---|
| GET | `/zones` | – | zones with `unlocked` flag |
| POST | `/hunt` | `{zoneId}` | `{result:{won,rounds[],xp,gold,loot[],leveledUp,newLevel}, character}` |
| GET | `/combat-log?limit=20` | – | recent fights |
### Inventory 🔒
| Method | Path | Body | Returns |
|---|---|---|---|
| GET | `/inventory` | – | `{items:[{inventoryId,item,quantity,equippedSlot}]}` |
| POST | `/inventory/equip` | `{inventoryId}` | `{equipment, derived}` |
| POST | `/inventory/unequip` | `{slot}` | `{equipment, derived}` |
| POST | `/inventory/use` | `{inventoryId}` | `{character, message}` |
### Shop 🔒
| Method | Path | Body | Returns |
|---|---|---|---|
| GET | `/shop` | – | stock list with prices and `canAfford`, `levelOk` |
| POST | `/shop/buy` | `{itemId, quantity}` (1–99) | `{gold, inventory}` |
| POST | `/shop/sell` | `{inventoryId, quantity}` | `{gold, inventory}` |
### Expeditions 🔒
| Method | Path | Body | Returns |
|---|---|---|---|
| GET | `/expeditions` | – | available defs + active one (with `remainingSec`) |
| POST | `/expeditions/start` | `{defId}` | `{expedition}` |
| POST | `/expeditions/claim` | – | `{rewards, character}` |
### Leaderboard & heroes (public read)
| Method | Path | Query | Returns |
|---|---|---|---|
| GET | `/leaderboard` | `type=level|gold|kills`, `page=1` | top 50 per page |
| GET | `/heroes/:name` | – | public profile (name, class, level, kills, equipped item names) |
### Misc
| GET | `/health` | – | `{ok:true}` |
## Rules for every route
1. Validate with zod; on failure → 400 `VALIDATION_ERROR` with field messages.
2. Never trust client-supplied ids for ownership: always scope by `req.session.userId` → character.
3. Responses never include `password_hash` or session data.
4. State-changing endpoints are POST. GET must never modify state.
5. Return the *updated* character where relevant so the UI can refresh without a second call.