Skip to content

Migrate admin handlers to unified AppContext pattern#1498

Merged
jaypatrick merged 10 commits intomainfrom
claude/add-hono-ecosystem-integrations
Apr 8, 2026
Merged

Migrate admin handlers to unified AppContext pattern#1498
jaypatrick merged 10 commits intomainfrom
claude/add-hono-ecosystem-integrations

Conversation

@Claude
Copy link
Copy Markdown
Contributor

@Claude Claude AI commented Apr 8, 2026

Description

Migrates all admin route handlers from decomposed parameter passing (request: Request, env: Env, authContext: IAuthContext, ...params) to unified AppContext pattern (c: AppContext, ...params). This enables request-scoped Prisma client access via c.get('prisma') and aligns with Hono ecosystem best practices.

Also updates all corresponding Deno unit tests to use the new makeAppContext(req, env, authContext, prisma?) helper and fixes the CI failure caused by a dual-vitest-version conflict introduced by the initial dependency additions.

Changes

Handler Signature Updates

Converted 20 admin handler functions across 5 files to accept AppContext as the first parameter:

worker/handlers/admin-users.ts (7 handlers):

  • handleAdminListUsers(c)
  • handleAdminGetUser(c, userId)
  • handleAdminUpdateUser(c, userId)
  • handleAdminDeleteUser(c, userId)
  • handleAdminBanUser(c, userId)
  • handleAdminUnbanUser(c, userId)

worker/handlers/admin-agents.ts (5 handlers):

  • handleAdminListAgentSessions(c)
  • handleAdminGetAgentSession(c, sessionId)
  • handleAdminListAgentAuditLog(c)
  • handleAdminTerminateAgentSession(c, sessionId)

worker/handlers/auth-config.ts (1 handler):

  • handleAdminAuthConfig(c)

worker/handlers/admin-usage.ts (1 handler):

  • handleAdminGetUserUsage(c, userId)

worker/handlers/admin-neon.ts (8 handlers):

  • handleAdminNeonGetProject(c)
  • handleAdminNeonListBranches(c)
  • handleAdminNeonGetBranch(c, branchId)
  • handleAdminNeonCreateBranch(c)
  • handleAdminNeonDeleteBranch(c, branchId)
  • handleAdminNeonListEndpoints(c)
  • handleAdminNeonListDatabases(c, branchId)
  • handleAdminNeonQuery(c)

Route Call Updates

Updated all 20 corresponding handler invocations in worker/routes/admin.routes.ts:

// Before:
return handleAdminDeleteUser(c.req.raw, c.env, c.get('authContext'), c.req.param('id')!) as any;

// After:
return handleAdminDeleteUser(c, c.req.param('id')!) as any;

Context Access Pattern

Handlers now access request primitives through AppContext:

// Before:
export async function handleAdminNeonQuery(
    request: Request,
    env: Env,
    authContext: IAuthContext,
): Promise<Response> {
    const denied = checkRoutePermission('/admin/neon/query', authContext);
    const url = new URL(request.url);
    const neon = getNeonService(env);
    const body = await request.json();

// After:
export async function handleAdminNeonQuery(c: AppContext): Promise<Response> {
    const denied = checkRoutePermission('/admin/neon/query', c.get('authContext'));
    const url = new URL(c.req.url);
    const neon = getNeonService(c.env);
    const body = await c.req.json();

Helper functions updated to accept AppContext:

// Before:
function resolveProjectId(request: Request, env: Env): string | null {
    const url = new URL(request.url);
    return url.searchParams.get('projectId') ?? env.NEON_PROJECT_ID ?? null;
}

// After:
function resolveProjectId(c: AppContext): string | null {
    const url = new URL(c.req.url);
    return url.searchParams.get('projectId') ?? c.env.NEON_PROJECT_ID ?? null;
}

Test Updates

All 5 admin handler test files updated to use makeAppContext(req, env, authContext, prisma?) from worker/test-helpers.ts:

  • worker/handlers/admin-users.test.ts — replaced (req, env, authContext) calls; eliminated _internals.createPrismaClient stubs by passing mock prisma directly into context
  • worker/handlers/admin-agents.test.ts — same refactor; 503 "no prisma in context" tests replace the makeEnvNoHyperdrive() approach; removed all stub/_internals usage
  • worker/handlers/admin-usage.test.ts — updated all handler invocations
  • worker/handlers/admin-neon.test.ts — updated all 22 handler invocations across guard-rail and happy-path tests
  • worker/handlers/auth-config.test.ts — updated all 13 handler invocations

Dependency Fix

The initial addition of @cloudflare/vitest-pool-workers@^0.5.25 + vitest@^2.1.9 introduced a dual-vitest-version conflict: the frontend workspace already uses vitest@4.1.0, and having vitest@2.1.9 alongside it caused pnpm's auto-install-peers to wire @vitest/ui@2.1.9 as a peer of vitest@4.1.0. This produced a broken vite snapshot that failed with SyntaxError: The requested module 'vite' does not provide an export named 'defaultClientConditions' in the frontend-lint-test CI job.

Resolution:

  • package.json: upgraded @cloudflare/vitest-pool-workers from ^0.5.25^0.14.2 (first version supporting vitest@^4.1.0); changed root vitest from ^2.1.9^4.1.0 to match the frontend, eliminating the dual-vitest conflict; removed @vitest/ui (no longer needed)
  • vitest.worker.config.ts: migrated from the removed defineWorkersConfig/@cloudflare/vitest-pool-workers/config API (removed in 0.13.x) to the new cloudflareTest plugin + defineConfig from vitest/config
  • pnpm-lock.yaml: regenerated with minimal diff — adds @cloudflare/vitest-pool-workers@0.14.2 and workerd@1.20260405.1 snapshots; removes old vitest@2.1.9 entries; better-auth stays on vitest@4.1.0; no Angular builder hash changes

Testing

  • Unit tests added/updated
  • Manual testing performed
  • CI passes

Zero Trust Architecture Checklist

Worker / Backend

  • Every handler verifies auth before executing business logic (N/A - no auth logic changed)
  • CORS origin allowlist enforced (not *) on write/authenticated endpoints (N/A - no CORS changes)
  • All secrets accessed via Worker Secret bindings (not [vars]) (N/A - no secret access changed)
  • All external inputs Zod-validated before use (N/A - validation unchanged)
  • All D1 queries use parameterized .prepare().bind() (no string interpolation) (N/A - no D1 queries changed)
  • Security events emitted to Analytics Engine on auth failures (N/A - no analytics changes)

Frontend / Angular

  • Protected routes have functional CanActivateFn auth guards (N/A - backend only)
  • Auth tokens managed via Clerk SDK (not localStorage) (N/A - backend only)
  • HTTP interceptor attaches ****** (no manual token passing) (N/A - backend only)
  • API responses validated with Zod schemas before consumption (N/A - backend only)

@Claude Claude AI changed the title [WIP] Add Hono ecosystem integrations for Cloudflare Vitest and more Migrate admin handlers to unified AppContext pattern Apr 8, 2026
@Claude Claude AI requested a review from jaypatrick April 8, 2026 03:18
@jaypatrick jaypatrick requested a review from Copilot April 8, 2026 03:20
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Migrates Worker admin endpoints to a unified AppContext handler signature so admin handlers can consistently access request-scoped context (e.g., c.get('prisma'), c.get('authContext')) and align with the rest of the Hono/OpenAPI route ecosystem.

Changes:

  • Updated admin route handler function signatures from (request, env, authContext, ...) to (c: AppContext, ...) across admin handler modules.
  • Updated worker/routes/admin.routes.ts to pass the Hono context (c) directly to the migrated handlers.
  • Added a Vitest Workers config + npm script/deps to support Workers-runtime-style integration testing via @cloudflare/vitest-pool-workers.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
worker/routes/admin.routes.ts Updated admin route invocations to call handlers with c (AppContext) instead of decomposed params.
worker/handlers/auth-config.ts Migrated auth-config admin handler to accept AppContext and use c.env / c.get('authContext').
worker/handlers/admin-users.ts Migrated admin user management handlers to AppContext; switched Prisma access to c.get('prisma') where applicable.
worker/handlers/admin-usage.ts Migrated usage handler to AppContext and used c.req.url / c.env.
worker/handlers/admin-neon.ts Migrated Neon admin handlers + helper to AppContext and used c.req / c.env.
worker/handlers/admin-agents.ts Migrated agent handlers to AppContext and switched to request-scoped Prisma from c.get('prisma').
vitest.worker.config.ts Added Vitest configuration for Workers integration tests via @cloudflare/vitest-pool-workers.
package.json Added test:worker:vitest script and Vitest-related devDependencies.

Comment thread worker/handlers/admin-users.ts
Comment thread worker/handlers/admin-usage.ts
Comment thread worker/handlers/admin-neon.ts
Comment thread worker/handlers/admin-agents.ts
Comment thread worker/handlers/auth-config.ts
@jaypatrick
Copy link
Copy Markdown
Owner

@copilot apply changes based on the comments in this thread

@jaypatrick
Copy link
Copy Markdown
Owner

@copilot Fix CI

jaypatrick and others added 5 commits April 7, 2026 23:35
- Update worker/handlers/admin-users.test.ts: replace (req, env, authContext)
  calls with makeAppContext(req, env, authContext, prisma) — also eliminates
  _internals.createPrismaClient stubs for delete/ban/unban tests
- Update worker/handlers/admin-agents.test.ts: same pattern; 503 tests now
  omit prisma from context instead of using makeEnvNoHyperdrive(); remove
  stub/_internals usage entirely; remove unused makeEnvNoHyperdrive helper
- Update worker/handlers/admin-usage.test.ts: add makeAppContext import and
  update all 6 handler invocations
- Update worker/handlers/admin-neon.test.ts: add makeAppContext import and
  update all 22 handler invocations across guard-rail and happy-path tests
- Update worker/handlers/auth-config.test.ts: add makeAppContext import and
  update all 13 handler invocations
- Fix package.json: correct @vitest/ui and vitest versions from ^2.2.7
  (non-existent) to ^2.1.9 (compatible with @cloudflare/vitest-pool-workers@^0.5.25)

Agent-Logs-Url: https://github.com/jaypatrick/adblock-compiler/sessions/9f75c48a-9d70-4321-937c-6cbc1d0a36ce

Co-authored-by: jaypatrick <1800595+jaypatrick@users.noreply.github.com>
…k conflict

Co-authored-by: jaypatrick <1800595+jaypatrick@users.noreply.github.com>
…-workers)

Co-authored-by: jaypatrick <1800595+jaypatrick@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 8, 2026

@copilot apply changes based on the comments in this thread

Done. All 5 test files have been updated to use the makeAppContext(req, env, authContext, prisma?) pattern matching the new handler signatures:

  • admin-users.test.ts — replaced (req, env, authContext) calls; eliminated _internals.createPrismaClient stubs by passing prisma directly into context
  • admin-agents.test.ts — same refactor; 503 tests now omit prisma from context instead of using makeEnvNoHyperdrive(); removed stub/_internals usage entirely
  • admin-usage.test.ts — updated all handler invocations
  • admin-neon.test.ts — updated all 22 invocations across guard-rail and happy-path tests
  • auth-config.test.ts — updated all 13 invocations

Also fixed package.json: @vitest/ui and vitest versions changed from ^2.2.7 (non-existent) to ^2.1.9 (compatible with @cloudflare/vitest-pool-workers@^0.5.25).

All 380 handler tests pass (commit 9738dc19).

…workers entries

pnpm install --frozen-lockfile was failing in CI because package.json added
@cloudflare/vitest-pool-workers, @vitest/ui, and vitest to devDependencies
but pnpm-lock.yaml was never updated. This caused all CI jobs that run
`pnpm install --frozen-lockfile` to fail with "3 dependencies were added" error.

Agent-Logs-Url: https://github.com/jaypatrick/adblock-compiler/sessions/12bf17e7-9022-4747-adf5-6b5b67d09c65

Co-authored-by: jaypatrick <1800595+jaypatrick@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 8, 2026

@copilot Fix CI

Fixed in commit 19cae31. The CI was failing due to two issues:

  1. @vitest/ui@^2.2.7 in package.json (non-existent version) — already corrected to ^2.1.9 in the previous session
  2. pnpm-lock.yaml was never updated to include the three new root devDependencies (@cloudflare/vitest-pool-workers, @vitest/ui, vitest) — this caused pnpm install --frozen-lockfile to fail in all frontend/build CI jobs

The lockfile is now updated and pnpm install --frozen-lockfile passes locally.

@socket-security
Copy link
Copy Markdown

socket-security Bot commented Apr 8, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​cloudflare/​vitest-pool-workers@​0.14.29710078100100

View full report

@jaypatrick
Copy link
Copy Markdown
Owner

@copilot fix CI

…o fix frontend CI

- Upgrades @cloudflare/vitest-pool-workers from ^0.5.25 to ^0.14.2 (vitest@4.x compat)
- Changes root vitest from ^2.1.9 to ^4.1.0 to match frontend, eliminating dual-vitest
  version conflict that caused vite defaultClientConditions ESM export error
- Removes @vitest/ui from root devDependencies (no longer needed, was causing
  @vitest/ui@2.1.9 to be wired as peer of vitest@4.1.0)
- Updates vitest.worker.config.ts to new 0.14.x API (cloudflareTest plugin instead of
  defineWorkersConfig; defineConfig from vitest/config instead of defineWorkersProject)
- Regenerates pnpm-lock.yaml with minimal diff: adds @cloudflare/vitest-pool-workers@0.14.2
  + workerd@1.20260405.1 snapshots; removes old 0.5.x vitest@2.1.9 entries; no Angular
  hash changes; better-auth stays on vitest@4.1.0

Agent-Logs-Url: https://github.com/jaypatrick/adblock-compiler/sessions/af38f403-44e9-47fa-a6b7-21f2d6819ecb

Co-authored-by: jaypatrick <1800595+jaypatrick@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 8, 2026

@copilot fix CI

Fixed in commit 916be44.

Root cause: the previous lockfile introduced two conflicting vitest versions in the workspace (vitest@2.1.9 for @cloudflare/vitest-pool-workers@0.5.x and vitest@4.1.0 for the frontend). pnpm's auto-install-peers then wired @vitest/ui@2.1.9 as a peer of vitest@4.1.0, which broke vite's ESM defaultClientConditions export when the frontend tests ran.

Fix:

  • Upgraded @cloudflare/vitest-pool-workers from ^0.5.25^0.14.2 (the first version supporting vitest@^4.1.0)
  • Changed root vitest from ^2.1.9^4.1.0 to match the frontend, eliminating the dual-vitest conflict
  • Removed @vitest/ui from root devDependencies (no longer needed)
  • Updated vitest.worker.config.ts to the new cloudflareTest plugin API (the old defineWorkersConfig//config path was removed in 0.13.x)
  • Regenerated pnpm-lock.yaml with minimal diff — better-auth unchanged on vitest@4.1.0, Angular hashes unchanged

@jaypatrick jaypatrick marked this pull request as ready for review April 8, 2026 05:20
Copilot AI review requested due to automatic review settings April 8, 2026 05:20
@jaypatrick jaypatrick merged commit 1767d13 into main Apr 8, 2026
28 checks passed
@jaypatrick jaypatrick deleted the claude/add-hono-ecosystem-integrations branch April 8, 2026 05:20
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 16 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment thread vitest.worker.config.ts
* - Tests that need the Workers runtime environment
*
* ## When NOT to use
* - Pure TypeScript unit tests (use Deno test runner via `deno task test:src`)
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The header comment says to use deno task test:src for “pure TypeScript unit tests”, but the worker unit tests in this repo run via deno task test:worker (and deno task test runs both). Updating this avoids confusing readers about the intended runner for worker tests.

Suggested change
* - Pure TypeScript unit tests (use Deno test runner via `deno task test:src`)
* - Pure TypeScript unit tests (use Deno test runner via `deno task test:worker`)

Copilot uses AI. Check for mistakes.
Comment thread worker/test-helpers.ts
Comment on lines +132 to +157
export function makeAppContext(
request: Request,
env: Env,
authContext: IAuthContext,
prisma?: unknown,
): AppContext {
const vars: Record<string, unknown> = { authContext };
if (prisma !== undefined) {
vars.prisma = prisma;
}
return {
req: {
url: request.url,
raw: request,
json: () => request.json(),
text: () => request.text(),
header: (name: string) => request.headers.get(name) ?? undefined,
method: request.method,
path: new URL(request.url).pathname,
},
env,
get: (key: string) => vars[key],
set: (key: string, value: unknown) => {
vars[key] = value;
},
} as unknown as AppContext;
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makeAppContext() currently builds vars as Record<string, unknown> and exposes get: (key: string) => vars[key], then casts to AppContext. This weakens type-safety in tests (e.g., typos in variable keys or wrong value shapes won’t be caught). Consider typing vars as Partial<Variables> (from worker/routes/shared.ts) and making get/set key-based (<K extends keyof Variables>(key: K) => Variables[K]) so tests stay aligned with the real Hono context contract.

Copilot uses AI. Check for mistakes.
Comment on lines 15 to +18
import { assertEquals } from '@std/assert';
import { stub } from '@std/testing/mock';
import { handleAdminGetAgentSession, handleAdminListAgentAuditLog, handleAdminListAgentSessions, handleAdminTerminateAgentSession } from './admin-agents.ts';
import { type Env, type HyperdriveBinding, type IAuthContext, UserTier } from '../types.ts';
import { _internals } from '../lib/prisma.ts';
import { makeAppContext } from '../test-helpers.ts';
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file header still says “Prisma is stubbed at the createPrismaClient boundary”, but these tests now pass a Prisma mock via makeAppContext(..., prisma) and no longer stub createPrismaClient. Please update the header comment to reflect the new testing approach to avoid misleading future readers.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PR 2: Hono Ecosystem Integrations (Vitest + Queue + Durable Objects + Prisma Context + RPC Authenticated Client)

4 participants