mirror of
https://github.com/djdevin/recflare.git
synced 2026-09-08 14:41:28 -07:00
testing generic deploy
This commit is contained in:
@@ -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 }`.
|
||||
|
||||
@@ -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,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,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).
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"compatibility_flags": ["nodejs_compat"],
|
||||
"routes": [
|
||||
{
|
||||
"pattern": "accounts.rec.djdevin.net",
|
||||
"pattern": "accounts.rec.example.com",
|
||||
"custom_domain": true
|
||||
}
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user