From 8e513c1275258b64bd5566dd09c8d444d51d5ea6 Mon Sep 17 00:00:00 2001 From: Devin Zuczek Date: Tue, 30 Jun 2026 23:44:57 -0400 Subject: [PATCH] bio --- apps/accounts/src/accounts-db.ts | 2 ++ apps/accounts/src/accounts.app.ts | 10 ++++++---- apps/accounts/src/test/integration/api.test.ts | 17 +++++++++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/apps/accounts/src/accounts-db.ts b/apps/accounts/src/accounts-db.ts index 822ef48..bd884b1 100644 --- a/apps/accounts/src/accounts-db.ts +++ b/apps/accounts/src/accounts-db.ts @@ -35,6 +35,8 @@ export interface Account { createdAt: string /** Set via POST /account/me/email; absent until the player provides one. */ email?: string + /** Set via PUT /account/me/bio; read back via GET /account/:id/bio. */ + bio?: string } interface AccountRow { diff --git a/apps/accounts/src/accounts.app.ts b/apps/accounts/src/accounts.app.ts index 6f9d92f..ac66001 100644 --- a/apps/accounts/src/accounts.app.ts +++ b/apps/accounts/src/accounts.app.ts @@ -117,11 +117,12 @@ const app = new Hono() return c.json(ids.map((id) => toAccountDto(stored.get(id) ?? defaultAccount(id)))) }) - .get('/account/:id/bio', (c) => { + .get('/account/:id/bio', async (c) => { const accountId = Number.parseInt(c.req.param('id'), 10) if (Number.isNaN(accountId)) return c.body(null, 400) - // TODO: query PlayerBios; no binding yet so the bio is always empty. - return c.json({ accountId, bio: '' }) + // Bio is stored on the account JSON (set via PUT /account/me/bio). + const account = await getAccount(c.env.DB, accountId) + return c.json({ accountId, bio: account?.bio ?? '' }) }) .get('/account/:id', async (c) => { @@ -192,7 +193,8 @@ const app = new Hono() .put('/account/me/bio', async (c) => { const id = await authedId(c) if (id === null) return unauthorized(c) - await formField(c, 'bio') // TODO: upsert into PlayerBios. + const bio = await formField(c, 'bio') + await updateAccount(c.env.DB, id, { bio }) return c.json({ success: true }) }) diff --git a/apps/accounts/src/test/integration/api.test.ts b/apps/accounts/src/test/integration/api.test.ts index e170fa4..64151d0 100644 --- a/apps/accounts/src/test/integration/api.test.ts +++ b/apps/accounts/src/test/integration/api.test.ts @@ -232,6 +232,23 @@ describe('auth-gated endpoints', () => { expect(((await me.json()) as { identityFlags: number }).identityFlags).toBe(384) }) + test('PUT /account/me/bio 401s without a token', async () => { + const res = await exports.default.fetch(`${ORIGIN}/account/me/bio`, { ...form({ bio: 'x' }) }) + expect(res.status).toBe(401) + }) + + test('PUT /account/me/bio persists the bio, read back via GET /account/:id/bio', async () => { + const res = await exports.default.fetch(`${ORIGIN}/account/me/bio`, { + ...form({ bio: 'Devin!' }), + headers: { ...(await bearer('890')), 'Content-Type': 'application/x-www-form-urlencoded' }, + }) + expect(res.status).toBe(200) + expect(await res.json()).toEqual({ success: true }) + + const bio = await exports.default.fetch(`${ORIGIN}/account/890/bio`) + expect(await bio.json()).toEqual({ accountId: 890, bio: 'Devin!' }) + }) + test('POST /account/me/email 401s without a token', async () => { const res = await exports.default.fetch(`${ORIGIN}/account/me/email`, { ...form({ email: 'a@b.com' }),