import { Hono } from 'hono' import * as z from 'zod/mini' import type * as App from '../../../App.js' import * as Auth from '../../../internal/Auth.js' import * as Db from '../../../db/Db.js' import * as OpenApi from '../../../internal/OpenApi.js' import * as Response from '../../../internal/Response.js' import * as Schema from '../../../internal/Schema.js' import * as Users from '../../../db/tables/users.js' /** Zod schemas owned by the current-user resource. */ export namespace schema { /** The signed-in developer. */ export const User = OpenApi.component( Schema.describe( z.object({ address: z .optional(z.templateLiteral(['0x', z.string().check(z.regex(/^[0-9a-fA-F]{40}$/))])) .check( z.describe('Wallet address bound to this user, when wallet sign-in established it.'), z.meta({ examples: ['0x0000000000000000000000000000000000000001'] }), ), createdAt: z.iso .datetime() .check( z.describe('When the user was created (ISO 8601).'), z.meta({ examples: ['2026-01-01T00:00:00.000Z'] }), ), email: z .optional(z.email()) .check( z.describe('Verified email from the wallet identity token, when present.'), z.meta({ examples: ['dev@example.com'] }), ), id: z .string() .check( z.describe('Opaque user id (`usr_…`).'), z.meta({ examples: ['usr_1a2b3c4d5e6f7g8h9j0k1m2n'] }), ), updatedAt: z.iso .datetime() .check( z.describe('When the user was last updated (ISO 8601).'), z.meta({ examples: ['2026-01-01T00:00:00.000Z'] }), ), }), 'The signed-in developer.', ), 'User', ) } /** * Mounts the `/me` resource: the session's user. Session lane only — API keys * identify workloads, not developers. */ export function me() { return new Hono().get( '/v1/me', Auth.policy({ session: true }), OpenApi.describeRoute({ operationId: 'getMe', responses: OpenApi.responses({ errors: { 400: { codes: [], description: 'Malformed API key.' }, 403: 'The caller is not a signed-in user.', 404: { codes: ['user_not_found'], description: 'No user exists for this session.', }, }, success: { description: 'The authenticated user.', schema: schema.User }, }), summary: 'Get current user', tags: ['Users'], }), async (c) => { if (Auth.narrowAccess) return Auth.superAdminAccessError(c) // The session lane is the only lane here; `super_admin` bypasses lanes // but has no user row, so it is rejected too. const principal = Auth.getPrincipal(c) if (principal?.type !== 'session') return Response.error(c, { code: 'forbidden', message: 'Session required', status: 403, }) const db = Db.get(c.get('db')) try { const user = await Users.get(db, principal.id) if (!user) return Response.error(c, { code: 'user_not_found', message: 'User not found', status: 404, }) return c.json( Response.validated(schema.User, { ...(user.address ? { address: user.address } : {}), createdAt: user.createdAt, ...(user.email ? { email: user.email } : {}), id: user.id, updatedAt: user.updatedAt, }), 200, ) } catch (cause) { return Response.upstream(c, cause) } }, ) }