fix: document service hangs in API-key auth contexts (#85)

* fix: use caller's supabase client in ensureDocumentsBucket

The bucket check was creating its own cookie-dependent service client
via createServiceClient(), which calls `await cookies()`. This hangs
in API-key auth contexts (MCP server) where no cookie store exists.

Now uses the caller's supabase client instead — both uploadDocument()
and createNewVersion() already receive a service-role client. Removes
the unused createServiceClient import.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use cookieless service client in ensureDocumentsBucket

ensureDocumentsBucket() needs a service-role client for storage admin
operations (getBucket/createBucket). Previously it used createServiceClient()
which calls `await cookies()` — this hangs in API-key auth contexts
(e.g. MCP server) where no cookie store exists.

Now uses createServiceClientNoCookies() internally, which provides a
service-role client without cookie dependency. This is correct for all
callers: both web API routes (which pass user-level clients) and the
MCP server (which passes a cookieless service client) — bucket admin
always requires service-role regardless of the caller's auth context.

Also cleans up stale test mock that referenced the removed import.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-03-22 21:50:13 +01:00
committed by GitHub
parent 7571b85d1a
commit a088df436e
2 changed files with 10 additions and 7 deletions
@@ -43,9 +43,8 @@ function makeClient(storageOverrides: Record<string, unknown> = {}) {
}
}
// Keep createServiceClient mock since ensureDocumentsBucket still uses it
vi.mock('@/lib/supabase/server', () => ({
createServiceClient: vi.fn(async () => makeClient()),
vi.mock('@/lib/auth/api-keys', () => ({
createServiceClientNoCookies: vi.fn(() => makeClient()),
}))
import { uploadDocument, createNewVersion, verifyIntegrity, _resetBucketVerified } from '../document-service'
+8 -4
View File
@@ -1,5 +1,5 @@
import type { SupabaseClient } from '@supabase/supabase-js'
import { createServiceClient } from '@/lib/supabase/server'
import { createServiceClientNoCookies } from '@/lib/auth/api-keys'
import { eventBus } from '@/lib/events'
import type { DocumentAttachment, DocumentUploadSource } from '@/types'
@@ -21,15 +21,19 @@ export function _resetBucketVerified() {
/**
* Ensure the 'documents' storage bucket exists, creating it if missing.
* Runs once per process lifetime (same pattern as ensureInitialized).
*
* Uses a cookieless service-role client for bucket admin operations
* (getBucket/createBucket require service-role). This avoids the cookie
* dependency that hangs in API-key auth contexts (e.g. MCP server).
*/
async function ensureDocumentsBucket(): Promise<void> {
if (bucketVerified) return
const supabase = await createServiceClient()
const { data: bucket } = await supabase.storage.getBucket('documents')
const serviceClient = createServiceClientNoCookies()
const { data: bucket } = await serviceClient.storage.getBucket('documents')
if (!bucket) {
await supabase.storage.createBucket('documents', {
await serviceClient.storage.createBucket('documents', {
public: false,
fileSizeLimit: 52428800, // 50 MB
})