mirror of
https://github.com/djdevin/recflare.git
synced 2026-09-08 14:41:28 -07:00
update some naming conventions
This commit is contained in:
+3
-2
@@ -5,7 +5,8 @@ RECFLARE_DOMAIN=rec.example.com
|
||||
# worker's directory name. Defaults to the directory name when unset.
|
||||
# RECFLARE_SUBDOMAINS={"playersettings":"settings"}
|
||||
|
||||
# D1 database ids, as a compact JSON object keyed by the worker's directory name.
|
||||
# Id of the shared `recflare` D1 database (create it manually with
|
||||
# `wrangler d1 create recflare`). All D1-backed workers bind this one database.
|
||||
# Kept out of the committed wrangler.jsonc (which uses a "local" placeholder) and
|
||||
# spliced in at deploy time. Required to deploy any worker that uses D1.
|
||||
# RECFLARE_D1={"rooms":"d44083e1-5bfe-4467-aa9a-f13c5c2496d5"}
|
||||
# RECFLARE_D1=d44083e1-5bfe-4467-aa9a-f13c5c2496d5
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Account storage on the shared `rec-rooms` D1 database. Each account is a single
|
||||
* Account storage on the shared `recflare` D1 database. Each account is a single
|
||||
* JSON blob in the `data` column; queryable fields (AccountId, Username) are
|
||||
* SQLite generated (virtual) columns extracted from that JSON and indexed —
|
||||
* the same JSON-blob pattern the `rooms` worker uses.
|
||||
|
||||
@@ -4,13 +4,15 @@
|
||||
"main": "src/accounts.app.ts",
|
||||
"compatibility_date": "2025-09-20",
|
||||
"compatibility_flags": ["nodejs_compat"],
|
||||
// Shared D1 database. The `accounts` table schema/migrations are owned by the
|
||||
// `auth` worker; this worker binds it read/write to look up and create accounts.
|
||||
// Shared `recflare` D1 database. The `accounts` table schema/migrations are owned
|
||||
// by the `auth` worker; this worker binds it read/write to look up and create
|
||||
// accounts. The "local" placeholder is replaced with the real id from RECFLARE_D1
|
||||
// (see .env) at deploy time.
|
||||
"d1_databases": [
|
||||
{
|
||||
"binding": "DB",
|
||||
"database_name": "rec-rooms",
|
||||
"database_id": "d44083e1-5bfe-4467-aa9a-f13c5c2496d5"
|
||||
"database_name": "recflare",
|
||||
"database_id": "local"
|
||||
}
|
||||
],
|
||||
"logpush": false,
|
||||
|
||||
@@ -518,7 +518,7 @@ const app = new Hono<App>({ strict: false })
|
||||
)
|
||||
|
||||
// ---- Room server ----------------------------------------------------------
|
||||
// Room data is read from the shared `rec-rooms` D1 (owned by the rooms worker).
|
||||
// Room data is read from the shared `recflare` D1 (owned by the rooms worker).
|
||||
// Register specific paths before the `/:id` param route.
|
||||
.get('/roomserver/rooms/bulk', async (c) => {
|
||||
const idParam = c.req.query('id')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Read helpers for the shared `rec-rooms` D1 database. The schema, migrations,
|
||||
* Read helpers for the shared `recflare` D1 database. The schema, migrations,
|
||||
* and seed are owned by the `rooms` worker (apps/rooms/src/rooms-db.ts +
|
||||
* migrations); this worker binds the same database read-only for its
|
||||
* `/roomserver/rooms/*` endpoints. Keep these queries in sync with the rooms
|
||||
|
||||
@@ -14,7 +14,7 @@ declare module 'cloudflare:test' {
|
||||
|
||||
const ORIGIN = 'https://example.com'
|
||||
|
||||
// The /roomserver/rooms/* routes read from the shared rec-rooms D1. Set up the
|
||||
// The /roomserver/rooms/* routes read from the shared recflare D1. Set up the
|
||||
// schema (matching the rooms worker's migration) + a couple of rooms for tests.
|
||||
const TEST_ROOMS = [
|
||||
{
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
"main": "src/api.app.ts",
|
||||
"compatibility_date": "2025-09-20",
|
||||
"compatibility_flags": ["nodejs_compat"],
|
||||
// Shared rooms DB (created + migrated by the `rooms` worker; bound read-only here).
|
||||
// Shared `recflare` DB (bound read-only here). Created manually with
|
||||
// `wrangler d1 create recflare`; its id lives in RECFLARE_D1 (see .env) and the
|
||||
// "local" placeholder below is spliced out at deploy time.
|
||||
"d1_databases": [
|
||||
{
|
||||
"binding": "DB",
|
||||
"database_name": "rec-rooms",
|
||||
"database_id": "d44083e1-5bfe-4467-aa9a-f13c5c2496d5"
|
||||
"database_name": "recflare",
|
||||
"database_id": "local"
|
||||
}
|
||||
],
|
||||
// Image bucket shared with the `img` worker (which serves objects back by key).
|
||||
@@ -17,7 +19,7 @@
|
||||
"r2_buckets": [
|
||||
{
|
||||
"binding": "IMAGES",
|
||||
"bucket_name": "rec-img"
|
||||
"bucket_name": "recflare-img"
|
||||
}
|
||||
],
|
||||
"logpush": false,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Account storage on the shared `rec-rooms` D1 database. Each account is a single
|
||||
* Account storage on the shared `recflare` D1 database. Each account is a single
|
||||
* JSON blob in the `data` column; queryable fields (AccountId, Username) are
|
||||
* SQLite generated (virtual) columns extracted from that JSON and indexed —
|
||||
* the same JSON-blob pattern the `rooms` worker uses.
|
||||
|
||||
@@ -87,7 +87,7 @@ async function placeNewPlayerInOrientation(env: App['Bindings'], accountId: numb
|
||||
platform: 0,
|
||||
appVersion: '20230302',
|
||||
}
|
||||
await env.MATCH_PRESENCE.put(`presence:${accountId}`, JSON.stringify(presence), {
|
||||
await env.RECFLARE_MATCH_PRESENCE.put(`presence:${accountId}`, JSON.stringify(presence), {
|
||||
expirationTtl: PRESENCE_TTL,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ export type Env = SharedHonoEnv & {
|
||||
// Shared match-presence KV (owned by the `match` worker). On account creation
|
||||
// the new player's presence is seeded to the Orientation room so the match
|
||||
// heartbeat keeps them there instead of bouncing them to the dorm.
|
||||
MATCH_PRESENCE: KVNamespace
|
||||
RECFLARE_MATCH_PRESENCE: KVNamespace
|
||||
}
|
||||
|
||||
/** Variables can be extended */
|
||||
|
||||
@@ -139,7 +139,7 @@ describe('auth worker routes', () => {
|
||||
test('POST /connect/token create_account seeds the new player into Orientation', async () => {
|
||||
const payload = await tokenFor('grant_type=create_account&platform_id=steam-456')
|
||||
const sub = payload.sub as string
|
||||
const presence = await env.MATCH_PRESENCE.get<{
|
||||
const presence = await env.RECFLARE_MATCH_PRESENCE.get<{
|
||||
roomInstance: { roomInstanceId: number; roomId: number; location: string; name: string }
|
||||
}>(`presence:${sub}`, 'json')
|
||||
expect(presence).not.toBeNull()
|
||||
|
||||
@@ -4,14 +4,16 @@
|
||||
"main": "src/auth.app.ts",
|
||||
"compatibility_date": "2025-09-20",
|
||||
"compatibility_flags": ["nodejs_compat"],
|
||||
// Shared D1 database (also used by the rooms worker). The `auth` worker owns
|
||||
// the `accounts` table; a dedicated migrations_table keeps its migration
|
||||
// history separate from the rooms worker's migrations on the same database.
|
||||
// Shared `recflare` D1 database (also used by the rooms worker). The `auth` worker
|
||||
// owns the `accounts` table; a dedicated migrations_table keeps its migration
|
||||
// history separate from the rooms worker's migrations on the same database. The
|
||||
// "local" placeholder is replaced with the real id from RECFLARE_D1 (see .env) at
|
||||
// deploy time.
|
||||
"d1_databases": [
|
||||
{
|
||||
"binding": "DB",
|
||||
"database_name": "rec-rooms",
|
||||
"database_id": "d44083e1-5bfe-4467-aa9a-f13c5c2496d5",
|
||||
"database_name": "recflare",
|
||||
"database_id": "local",
|
||||
"migrations_dir": "migrations",
|
||||
"migrations_table": "d1_migrations_auth"
|
||||
}
|
||||
@@ -20,7 +22,7 @@
|
||||
// accounts into the Orientation room on signup.
|
||||
"kv_namespaces": [
|
||||
{
|
||||
"binding": "MATCH_PRESENCE",
|
||||
"binding": "RECFLARE_MATCH_PRESENCE",
|
||||
"id": "9f53f04b7dd244658d59f515a14748b6"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"r2_buckets": [
|
||||
{
|
||||
"binding": "CDN_ASSETS",
|
||||
"bucket_name": "rec-cdn"
|
||||
"bucket_name": "recflare-cdn"
|
||||
}
|
||||
],
|
||||
"upload_source_maps": true,
|
||||
|
||||
+3
-3
@@ -2,7 +2,7 @@
|
||||
|
||||
Image-delivery worker served on the `img` subdomain.
|
||||
|
||||
Images are stored as objects in the **`rec-img` R2 bucket** and streamed back by
|
||||
Images are stored as objects in the **`recflare-img` R2 bucket** and streamed back by
|
||||
key:
|
||||
|
||||
- `GET /` — service status `{ "service": "img", "status": "ok" }`.
|
||||
@@ -37,7 +37,7 @@ node -e "const{generateKeyPairSync}=require('crypto');const{publicKey,privateKey
|
||||
## One-time bucket setup
|
||||
|
||||
```sh
|
||||
wrangler r2 bucket create rec-img
|
||||
wrangler r2 bucket create recflare-img
|
||||
```
|
||||
|
||||
## Seeding / uploading images
|
||||
@@ -45,7 +45,7 @@ wrangler r2 bucket create rec-img
|
||||
A starter image lives in `static/` so it can be uploaded to R2:
|
||||
|
||||
```sh
|
||||
wrangler r2 object put rec-img/DefaultProfileImage.jpg \
|
||||
wrangler r2 object put recflare-img/DefaultProfileImage.jpg \
|
||||
--file=apps/img/static/DefaultProfileImage.jpg \
|
||||
--content-type=image/jpeg --remote
|
||||
```
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"r2_buckets": [
|
||||
{
|
||||
"binding": "IMAGES",
|
||||
"bucket_name": "rec-img"
|
||||
"bucket_name": "recflare-img"
|
||||
}
|
||||
],
|
||||
"upload_source_maps": true,
|
||||
|
||||
@@ -5,7 +5,7 @@ export type Env = SharedHonoEnv & {
|
||||
// Per-player presence (the room instance they're currently in). Written by
|
||||
// matchmake/goto, read by the heartbeat, cleared on login — mirrors the
|
||||
// reference server's HeartbeatDB.
|
||||
MATCH_PRESENCE: KVNamespace
|
||||
RECFLARE_MATCH_PRESENCE: KVNamespace
|
||||
// Shared rooms DB (owned by the `rooms` worker). Read-only here to resolve a
|
||||
// room's real scene/subroom when matchmaking into it.
|
||||
DB: D1Database
|
||||
|
||||
@@ -98,14 +98,14 @@ const presenceKey = (id: number) => `presence:${id}`
|
||||
|
||||
/** Persist the player's presence (room instance + status), refreshing the TTL. */
|
||||
async function setPresence(c: Context<App>, id: number, presence: Presence): Promise<void> {
|
||||
await c.env.MATCH_PRESENCE.put(presenceKey(id), JSON.stringify(presence), {
|
||||
await c.env.RECFLARE_MATCH_PRESENCE.put(presenceKey(id), JSON.stringify(presence), {
|
||||
expirationTtl: PRESENCE_TTL,
|
||||
})
|
||||
}
|
||||
|
||||
/** Read the player's stored presence, or null when they aren't in a room. */
|
||||
async function getPresence(c: Context<App>, id: number): Promise<Presence | null> {
|
||||
return c.env.MATCH_PRESENCE.get<Presence>(presenceKey(id), 'json')
|
||||
return c.env.RECFLARE_MATCH_PRESENCE.get<Presence>(presenceKey(id), 'json')
|
||||
}
|
||||
|
||||
/** Store the room instance the player just matchmade into, preserving status. */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Read helpers for the shared `rec-rooms` D1 database. The schema, migrations,
|
||||
* Read helpers for the shared `recflare` D1 database. The schema, migrations,
|
||||
* and seed are owned by the `rooms` worker (apps/rooms/src/rooms-db.ts +
|
||||
* migrations); this worker binds the same database read-only to resolve a room's
|
||||
* real scene/subroom when building a matchmake instance. Keep these queries in
|
||||
|
||||
@@ -12,7 +12,7 @@ declare module 'cloudflare:test' {
|
||||
|
||||
const ORIGIN = 'https://example.com'
|
||||
|
||||
// Matchmaking into a room resolves its real scene from the shared rec-rooms D1.
|
||||
// Matchmaking into a room resolves its real scene from the shared recflare D1.
|
||||
// Seed the schema + a couple of rooms (matching the rooms worker's migration).
|
||||
const RECCENTER_SCENE = 'cbad71af-0831-44d8-b8ef-69edafa841f6'
|
||||
const TEST_ROOMS = [
|
||||
|
||||
@@ -5,19 +5,20 @@
|
||||
"compatibility_date": "2025-09-20",
|
||||
"compatibility_flags": ["nodejs_compat"],
|
||||
// Per-player presence store (room instance the player is currently in).
|
||||
// Create with `wrangler kv namespace create MATCH_PRESENCE` and set the id.
|
||||
// Create with `wrangler kv namespace create RECFLARE_MATCH_PRESENCE` and set the id.
|
||||
"kv_namespaces": [
|
||||
{
|
||||
"binding": "MATCH_PRESENCE",
|
||||
"binding": "RECFLARE_MATCH_PRESENCE",
|
||||
"id": "9f53f04b7dd244658d59f515a14748b6"
|
||||
}
|
||||
],
|
||||
// Shared rooms DB (created + migrated by the `rooms` worker; bound read-only here).
|
||||
// Shared `recflare` DB (bound read-only here). The "local" placeholder is replaced
|
||||
// with the real id from RECFLARE_D1 (see .env) at deploy time.
|
||||
"d1_databases": [
|
||||
{
|
||||
"binding": "DB",
|
||||
"database_name": "rec-rooms",
|
||||
"database_id": "d44083e1-5bfe-4467-aa9a-f13c5c2496d5"
|
||||
"database_name": "recflare",
|
||||
"database_id": "local"
|
||||
}
|
||||
],
|
||||
"logpush": false,
|
||||
|
||||
@@ -9,7 +9,7 @@ Player-settings worker served on the `playersettings` subdomain.
|
||||
- `PUT /playersettings` — `[Authorize]`. Accepts a form-urlencoded
|
||||
`key=…&value=…` (or a JSON `{key,value}` / array) and **upserts** it into the
|
||||
player's settings, keyed by the `sub` claim of the Bearer JWT. Returns `200`.
|
||||
Persisted in Workers KV (`PLAYER_SETTINGS`, key `player:<id>`).
|
||||
Persisted in Workers KV (`RECFLARE_PLAYER_SETTINGS`, key `player:<id>`).
|
||||
|
||||
> A full settings PUT would replace the player's _entire_ settings set on each
|
||||
> call; we merge instead, so a single-key PUT (e.g. `key=PlayerSessionCount`)
|
||||
@@ -18,7 +18,7 @@ Player-settings worker served on the `playersettings` subdomain.
|
||||
## KV namespace
|
||||
|
||||
```sh
|
||||
wrangler kv namespace create PLAYER_SETTINGS # then put the id in wrangler.jsonc
|
||||
wrangler kv namespace create RECFLARE_PLAYER_SETTINGS # then put the id in wrangler.jsonc
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { SharedHonoEnv, SharedHonoVariables } from '@repo/hono-helpers/src/
|
||||
|
||||
export type Env = SharedHonoEnv & {
|
||||
/** Per-player settings store. Key `player:<id>` → JSON map of `{ key: value }`. */
|
||||
PLAYER_SETTINGS: KVNamespace
|
||||
RECFLARE_PLAYER_SETTINGS: KVNamespace
|
||||
}
|
||||
|
||||
/** Variables can be extended */
|
||||
|
||||
@@ -89,10 +89,10 @@ const app = new Hono<App>()
|
||||
if (id === null) return unauthorized(c)
|
||||
|
||||
const kvKey = `player:${id}`
|
||||
let stored = await c.env.PLAYER_SETTINGS.get<Record<string, string>>(kvKey, 'json')
|
||||
let stored = await c.env.RECFLARE_PLAYER_SETTINGS.get<Record<string, string>>(kvKey, 'json')
|
||||
if (!stored || Object.keys(stored).length === 0) {
|
||||
stored = Object.fromEntries(DEFAULT_SETTINGS.map((s) => [s.Key, s.Value]))
|
||||
await c.env.PLAYER_SETTINGS.put(kvKey, JSON.stringify(stored))
|
||||
await c.env.RECFLARE_PLAYER_SETTINGS.put(kvKey, JSON.stringify(stored))
|
||||
}
|
||||
|
||||
return c.json(Object.entries(stored).map(([Key, Value]) => ({ PlayerId: id, Key, Value })))
|
||||
@@ -109,11 +109,11 @@ const app = new Hono<App>()
|
||||
if (incoming.length === 0) return c.body(null, 200)
|
||||
|
||||
const kvKey = `player:${id}`
|
||||
const existing = await c.env.PLAYER_SETTINGS.get<Record<string, string>>(kvKey, 'json')
|
||||
const existing = await c.env.RECFLARE_PLAYER_SETTINGS.get<Record<string, string>>(kvKey, 'json')
|
||||
const merged: Record<string, string> = { ...existing }
|
||||
for (const { key, value } of incoming) merged[key] = value
|
||||
|
||||
await c.env.PLAYER_SETTINGS.put(kvKey, JSON.stringify(merged))
|
||||
await c.env.RECFLARE_PLAYER_SETTINGS.put(kvKey, JSON.stringify(merged))
|
||||
return c.body(null, 200)
|
||||
})
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ describe('playersettings endpoints', () => {
|
||||
expect(settings.find((s) => s.Key === 'PlayerSessionCount')?.Value).toBe('13')
|
||||
|
||||
// Defaults were persisted to KV.
|
||||
const stored = await env.PLAYER_SETTINGS.get<Record<string, string>>('player:100', 'json')
|
||||
const stored = await env.RECFLARE_PLAYER_SETTINGS.get<Record<string, string>>('player:100', 'json')
|
||||
expect(stored?.['Recroom.OOBE']).toBe('77')
|
||||
})
|
||||
|
||||
@@ -97,7 +97,7 @@ describe('playersettings endpoints', () => {
|
||||
)
|
||||
expect(res.status).toBe(200)
|
||||
|
||||
const stored = await env.PLAYER_SETTINGS.get<Record<string, string>>('player:7', 'json')
|
||||
const stored = await env.RECFLARE_PLAYER_SETTINGS.get<Record<string, string>>('player:7', 'json')
|
||||
expect(stored).toEqual({ PlayerSessionCount: '1' })
|
||||
})
|
||||
|
||||
@@ -111,7 +111,7 @@ describe('playersettings endpoints', () => {
|
||||
putForm({ key: 'B', value: '2' }, await bearer('8'))
|
||||
)
|
||||
|
||||
const stored = await env.PLAYER_SETTINGS.get<Record<string, string>>('player:8', 'json')
|
||||
const stored = await env.RECFLARE_PLAYER_SETTINGS.get<Record<string, string>>('player:8', 'json')
|
||||
expect(stored).toEqual({ A: '1', B: '2' })
|
||||
})
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
// Per-player settings persistence.
|
||||
"kv_namespaces": [
|
||||
{
|
||||
"binding": "PLAYER_SETTINGS",
|
||||
"binding": "RECFLARE_PLAYER_SETTINGS",
|
||||
"id": "d33a90014e904b0eac720bddcbe0b036"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -4,16 +4,17 @@
|
||||
"main": "src/rooms.app.ts",
|
||||
"compatibility_date": "2025-09-20",
|
||||
"compatibility_flags": ["nodejs_compat"],
|
||||
// Room storage. Create with `wrangler d1 create rec-rooms`, then put the real
|
||||
// id in the gitignored root .env under RECFLARE_D1 (see .env.example) — it is
|
||||
// spliced in at deploy time by run-wrangler-deploy. The "local" placeholder
|
||||
// below is only a key for the local SQLite db used by `wrangler dev`, so the
|
||||
// committed file needs no per-developer edits. Apply migrations with
|
||||
// `wrangler d1 migrations apply rec-rooms --remote`.
|
||||
// Shared `recflare` DB (room storage lives here). Create it manually with
|
||||
// `wrangler d1 create recflare`, then put the real id in the gitignored root
|
||||
// .env under RECFLARE_D1 (see .env.example) — it is spliced in at deploy time
|
||||
// by run-wrangler-deploy. The "local" placeholder below is only a key for the
|
||||
// local SQLite db used by `wrangler dev`, so the committed file needs no
|
||||
// per-developer edits. Apply migrations with
|
||||
// `wrangler d1 migrations apply recflare --remote`.
|
||||
"d1_databases": [
|
||||
{
|
||||
"binding": "DB",
|
||||
"database_name": "rec-rooms",
|
||||
"database_name": "recflare",
|
||||
"database_id": "local",
|
||||
"migrations_dir": "migrations"
|
||||
}
|
||||
|
||||
@@ -37,18 +37,16 @@ DIR=$(basename "$PWD")
|
||||
SUBDOMAIN=$(printf '%s' "$SUBDOMAINS_JSON" | jq -r --arg d "$DIR" '.[$d] // $d')
|
||||
HOST="$SUBDOMAIN.$DOMAIN"
|
||||
|
||||
# Resolve this worker's D1 database id from RECFLARE_D1 (a JSON object keyed by
|
||||
# the worker's directory name) and splice it into a generated config. The
|
||||
# committed wrangler.jsonc carries a "local" placeholder so it needs no
|
||||
# Splice the shared `recflare` D1 database id from RECFLARE_D1 into a generated
|
||||
# config. All D1-backed workers bind the same database, so RECFLARE_D1 is a single
|
||||
# id. The committed wrangler.jsonc carries a "local" placeholder so it needs no
|
||||
# per-developer edits; the real id stays in the gitignored .env / CI secrets.
|
||||
# Workers without a D1 binding simply have no placeholder to replace.
|
||||
CONFIG="wrangler.jsonc"
|
||||
if grep -q '"database_id": *"local"' wrangler.jsonc 2>/dev/null; then
|
||||
D1_JSON=${RECFLARE_D1:-}
|
||||
[ -n "$D1_JSON" ] || D1_JSON='{}'
|
||||
DB_ID=$(printf '%s' "$D1_JSON" | jq -r --arg d "$DIR" '.[$d] // empty')
|
||||
DB_ID=${RECFLARE_D1:-}
|
||||
if [ -z "$DB_ID" ]; then
|
||||
echo "error: no D1 id for '$DIR' in RECFLARE_D1 — add it to .env (see .env.example)" >&2
|
||||
echo "error: RECFLARE_D1 is not set — add the recflare D1 id to .env (see .env.example)" >&2
|
||||
exit 1
|
||||
fi
|
||||
CONFIG="wrangler.generated.jsonc"
|
||||
|
||||
Reference in New Issue
Block a user