testing generic deploy

This commit is contained in:
Devin Zuczek
2026-06-29 21:52:50 -04:00
parent 3affabd280
commit 69b89e2dc4
79 changed files with 399 additions and 307 deletions
+5 -6
View File
@@ -1,16 +1,15 @@
# accounts
Accounts Worker served at `accounts.rec.djdevin.net`. A Hono app ported from the
C# `AccountsController`. EF Core (`AppDbContext`) queries are stubbed for now —
no real bindings yet.
Accounts Worker served on the `accounts` subdomain. A Hono app for accounts.
Database queries are stubbed for now — no real bindings yet.
## Behavior
- **Auth-gated routes** validate the Bearer JWT issued by the `auth` worker
(same dev secret, see `src/jwt.ts`) and 401 when it's missing/invalid.
- **DB-backed reads** return synthesized default accounts. The C# already fills
every column with a fallback (`Player{id}`, `DefaultProfileImage.jpg`, etc.),
so the stubs return those defaults rather than 404ing on a missing row.
- **DB-backed reads** return synthesized default accounts. Every column gets a
fallback (`Player{id}`, `DefaultProfileImage.jpg`, etc.), so the stubs return
those defaults rather than 404ing on a missing row.
- **DB-backed writes** (`create`, the `PUT /account/me/*` mutations) accept the
request and ack without persisting. `create` mints a random account id and
returns it wrapped in the RecNet result envelope `{ success, value }`.
+2 -2
View File
@@ -21,7 +21,7 @@ export const SCHEMA_DDL: string[] = [
`CREATE INDEX IF NOT EXISTS idx_accounts_username_lower ON accounts (username_lower)`,
]
/** Client-facing account shape (PascalCase, matches the C# `Account`). */
/** Client-facing account shape (PascalCase, as the client expects). */
export interface Account {
AccountId: number
Username: string
@@ -61,7 +61,7 @@ export function randomUsername(): string {
}
/**
* Build a full account object from an id, applying the C# fallbacks for any
* Build a full account object from an id, applying default fallbacks for any
* column the caller doesn't override. Used both to synthesize accounts that
* aren't in the DB and as the base for a freshly created account.
*/
+10 -11
View File
@@ -10,17 +10,17 @@ import type { Context } from 'hono'
import type { App } from './context'
/**
* Ported from the C# `AccountsController`. Account reads/writes are backed by the
* shared `accounts` table in D1 (schema owned by the `auth` worker). Accounts not
* in the table fall back to a synthesized default (the C# fills every column with
* a fallback anyway). Profile mutations still accept-and-ack (marked `TODO`).
* Account reads/writes are backed by the shared `accounts` table in D1 (schema
* owned by the `auth` worker). Accounts not in the table fall back to a
* synthesized default (every column has a fallback anyway). Profile mutations
* still accept-and-ack (marked `TODO`).
*
* Auth-gated routes still validate the Bearer JWT issued by the `auth` worker.
*/
/**
* Resolve the account id from a Bearer token, mirroring the repeated
* auth-header check in the C#. Returns `null` when the header is missing,
* auth-header check. Returns `null` when the header is missing,
* the token is invalid, or the `sub` claim isn't an integer.
*/
async function authedId(c: Context<App>): Promise<number | null> {
@@ -61,7 +61,7 @@ const app = new Hono<App>()
.onError(withOnError())
.notFound(withNotFound())
// Root health check (the C# source returned a placeholder string here).
// Root health check.
.get('/', (c) => c.json({ service: 'accounts', status: 'ok' }))
// ---- Self account --------------------------------------------------------
@@ -70,11 +70,10 @@ const app = new Hono<App>()
if (id === null) return unauthorized(c)
// Load the stored account, falling back to a synthesized default.
const account = (await getAccount(c.env.DB, id)) ?? defaultAccount(id)
// The C# `SelfAccount` marks `JuniorState` (an enum) and `ParentAccountId`
// with `[JsonIgnore(WhenWritingNull)]`, so they're OMITTED when null —
// `JuniorState` (an enum) and `ParentAccountId` are OMITTED when null —
// emitting `"juniorState":null` makes the client's enum parser throw
// ("Can't parse JSON to Enum format"). `Email`/`Phone`/`Birthday` are kept
// as null (the C# has no JsonIgnore on those, and they aren't enums).
// as null (they aren't enums, so null is fine).
return c.json({
...account,
Email: null,
@@ -87,7 +86,7 @@ const app = new Hono<App>()
// ---- Bulk / single lookup ------------------------------------------------
// Register the static `bulk` path before the `/account/:id` param route.
.get('/account/bulk', async (c) => {
// C# reads repeated `id` query params; also accept a comma-separated list.
// Reads repeated `id` query params; also accept a comma-separated list.
const ids =
c.req
.queries('id')
@@ -95,7 +94,7 @@ const app = new Hono<App>()
.map((s) => Number.parseInt(s.trim(), 10))
.filter((n) => !Number.isNaN(n)) ?? []
// Resolve stored accounts, synthesizing a default for any id not in the DB
// so every requested id is present in the response (matches the C#).
// so every requested id is present in the response.
const stored = new Map((await getAccountsByIds(c.env.DB, ids)).map((a) => [a.AccountId, a]))
return c.json(ids.map((id) => stored.get(id) ?? defaultAccount(id)))
})
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Minimal HS256 JWT validation, mirroring the C# `JwtTokenService.ValidateAndGetAccountId`.
* Minimal HS256 JWT validation.
*
* Uses the same placeholder dev secret as the `auth` worker (`apps/auth/src/jwt.ts`).
* Swap both for a shared secret binding before this is used for anything real.
@@ -12,7 +12,7 @@ declare module 'cloudflare:test' {
interface ProvidedEnv extends Env {}
}
const ORIGIN = 'https://accounts.rec.djdevin.net'
const ORIGIN = 'https://example.com'
// Apply the accounts schema + seed the system (uid 0) and Coach (uid 1) accounts
// into the test D1 (mirrors apps/auth/migrations/0001_accounts.sql).
+1 -1
View File
@@ -6,7 +6,7 @@
"compatibility_flags": ["nodejs_compat"],
"routes": [
{
"pattern": "accounts.rec.djdevin.net",
"pattern": "accounts.rec.example.com",
"custom_domain": true
}
],