Add AI-assisted world and character drafting flows
All checks were successful
CI / validate (push) Successful in 14m3s

This commit is contained in:
2026-08-17 09:25:37 +05:00
parent 830aa5dc1f
commit cdeea8653c
23 changed files with 811 additions and 92 deletions

View File

@@ -18,4 +18,34 @@ describe('stage-two Supabase client', () => {
body: '{}',
})).resolves.toBeUndefined()
})
it('supports new Supabase secret keys without using them as bearer tokens', async () => {
vi.stubGlobal('useRuntimeConfig', () => ({
supabaseUrl: 'https://example.supabase.co',
supabaseServiceRoleKey: 'sb_secret_example',
public: { supabaseAnonKey: 'sb_publishable_example' },
}))
const fetchMock = vi.fn(async (_url: URL, init?: RequestInit) => new Response('[]', { status: 200 }))
vi.stubGlobal('fetch', fetchMock)
await stageTwoDatabase('profiles?select=id&limit=1')
expect(fetchMock).toHaveBeenCalledOnce()
expect(fetchMock.mock.calls[0]?.[1]?.headers).toMatchObject({ apikey: 'sb_secret_example' })
expect(fetchMock.mock.calls[0]?.[1]?.headers).not.toHaveProperty('Authorization')
})
it('keeps legacy service-role JWT authorization support', async () => {
vi.stubGlobal('useRuntimeConfig', () => ({
supabaseUrl: 'https://example.supabase.co',
supabaseServiceRoleKey: 'header.payload.signature',
public: { supabaseAnonKey: 'anon-key' },
}))
const fetchMock = vi.fn(async (_url: URL, init?: RequestInit) => new Response('[]', { status: 200 }))
vi.stubGlobal('fetch', fetchMock)
await stageTwoDatabase('profiles?select=id&limit=1')
expect(fetchMock.mock.calls[0]?.[1]?.headers).toMatchObject({
Authorization: 'Bearer header.payload.signature',
})
})
})

View File

@@ -13,6 +13,15 @@ interface RequestOptions extends RequestInit {
prefer?: string
}
function serviceAuthenticationHeaders(serviceKey: string): Record<string, string> {
// Legacy service-role keys are JWTs and may be used as the PostgREST bearer.
// New Supabase `sb_secret_…` keys are gateway API keys and must not be sent
// as an Authorization token.
return serviceKey.split('.').length === 3
? { Authorization: `Bearer ${serviceKey}` }
: {}
}
export interface StageTwoUser {
id: string
email?: string
@@ -51,7 +60,7 @@ export async function stageTwoDatabase<T>(path: string, options: RequestOptions
...options,
headers: {
apikey: serviceKey,
Authorization: `Bearer ${serviceKey}`,
...serviceAuthenticationHeaders(serviceKey),
'Content-Type': 'application/json',
...(options.prefer ? { Prefer: options.prefer } : {}),
...options.headers,