diff --git a/apps/demo-app/pages/api/_fal/proxy.ts b/apps/demo-app/pages/api/_fal/proxy.ts index 682f38b..9c01c5a 100644 --- a/apps/demo-app/pages/api/_fal/proxy.ts +++ b/apps/demo-app/pages/api/_fal/proxy.ts @@ -1,3 +1,3 @@ -// @snippet:start(client.proxy.nextjs) +// @snippet:start("client.proxy.nextjs") export { config, handler as default } from '@fal-ai/serverless-nextjs'; // @snippet:end diff --git a/apps/demo-app/pages/index.tsx b/apps/demo-app/pages/index.tsx index 7e61668..56482c0 100644 --- a/apps/demo-app/pages/index.tsx +++ b/apps/demo-app/pages/index.tsx @@ -33,10 +33,11 @@ function Error(props) { ); } -const DEFAULT_PROMPT = "a city landscape of a cyberpunk metropolis, raining, purple, pink and teal neon lights, highly detailed, uhd"; +const DEFAULT_PROMPT = + 'a city landscape of a cyberpunk metropolis, raining, purple, pink and teal neon lights, highly detailed, uhd'; export function Index() { - // @snippet:start(client.ui.state) + // @snippet:start("client.ui.state") // Input state const [prompt, setPrompt] = useState(DEFAULT_PROMPT); // Result state @@ -64,7 +65,7 @@ export function Index() { const handleOnClick = async (e) => { e.preventDefault(); reset(); - // @snippet:start(client.queue.subscribe) + // @snippet:start("client.queue.subscribe") setLoading(true); const start = Date.now(); try { @@ -74,10 +75,13 @@ export function Index() { model_name: 'stabilityai/stable-diffusion-xl-base-1.0', image_size: 'square_hd', }, - onQueueUpdate(status) { + onQueueUpdate(update) { setElapsedTime(Date.now() - start); - if (status.status === 'IN_PROGRESS') { - setLogs(status.logs.map((log) => log.message)); + if ( + update.status === 'IN_PROGRESS' || + update.status === 'COMPLETED' + ) { + setLogs(update.logs.map((log) => log.message)); } }, }); @@ -91,8 +95,8 @@ export function Index() { // @snippet:end }; return ( -
-
+
+

Hello fal

@@ -134,7 +138,7 @@ export function Index() {

{`Elapsed Time (seconds): ${(elapsedTime / 1000).toFixed(2)}`}

-
+            
               {result
                 ? JSON.stringify(result, null, 2)
                 : '// result pending...'}
@@ -143,7 +147,7 @@ export function Index() {
 
           

Logs

-
+            
               {logs.filter(Boolean).join('\n')}
             
diff --git a/libs/client/src/config.spec.ts b/libs/client/src/config.spec.ts index 06d2255..4f98ec0 100644 --- a/libs/client/src/config.spec.ts +++ b/libs/client/src/config.spec.ts @@ -4,10 +4,7 @@ describe('The config test suite', () => { it('should set the config variables accordingly', () => { const newConfig = { host: 'some-other-host', - credentials: { - keyId: 'key-id', - keySecret: 'key-secret', - }, + credentials: 'key-id:key-secret', }; config(newConfig); const currentConfig = getConfig(); diff --git a/libs/client/src/config.ts b/libs/client/src/config.ts index 73d0955..13b83ff 100644 --- a/libs/client/src/config.ts +++ b/libs/client/src/config.ts @@ -2,15 +2,10 @@ import type { RequestMiddleware } from './middleware'; import type { ResponseHandler } from './response'; import { defaultResponseHandler } from './response'; -export type Credentials = { - keyId: string; - keySecret: string; -}; - -export type CredentialsResolver = () => Credentials; +export type CredentialsResolver = () => string | undefined; export type Config = { - credentials?: Credentials | CredentialsResolver; + credentials?: undefined | string | CredentialsResolver; host?: string; requestMiddleware?: RequestMiddleware; responseHandler?: ResponseHandler; @@ -28,29 +23,22 @@ function hasEnvVariables(): boolean { return ( process && process.env && - typeof process.env.FAL_KEY_ID !== 'undefined' && - typeof process.env.FAL_KEY_SECRET !== 'undefined' + (typeof process.env.FAL_KEY !== 'undefined' || + (typeof process.env.FAL_KEY_ID !== 'undefined' && + typeof process.env.FAL_KEY_SECRET !== 'undefined')) ); } export const credentialsFromEnv: CredentialsResolver = () => { if (!hasEnvVariables()) { - return { - keyId: '', - keySecret: '', - }; + return undefined; } - if (typeof window !== 'undefined') { - console.warn( - "The fal credentials are exposed in the browser's environment. " + - "That's not recommended for production use cases." - ); + + if (typeof process.env.FAL_KEY !== 'undefined') { + return process.env.FAL_KEY; } - return { - keyId: process.env.FAL_KEY_ID || '', - keySecret: process.env.FAL_KEY_SECRET || '', - }; + return `${process.env.FAL_KEY_ID}:${process.env.FAL_KEY_SECRET}`; }; /** @@ -93,6 +81,7 @@ export function config(config: Config) { export function getConfig(): RequiredConfig { if (!configuration) { console.info('Using default configuration for the fal client'); + return { ...DEFAULT_CONFIG } as RequiredConfig; } return configuration; } diff --git a/libs/client/src/function.spec.ts b/libs/client/src/function.spec.ts index 5735c8d..66411fa 100644 --- a/libs/client/src/function.spec.ts +++ b/libs/client/src/function.spec.ts @@ -1,15 +1,7 @@ import { randomUUID } from 'crypto'; -import { config, getConfig } from './config'; +import { getConfig } from './config'; import { buildUrl } from './function'; -config({ - host: 'gateway.alpha.fal.ai', - credentials: { - keyId: 'a91ff3ca-71bc-4c8c-b400-859f6cbe804d', - keySecret: '0123456789abcdfeghijklmnopqrstuv', - }, -}); - describe('The function test suite', () => { it('should build the URL with a function UUIDv4', () => { const id = randomUUID(); diff --git a/libs/client/src/function.ts b/libs/client/src/function.ts index 93fc3b2..1bae678 100644 --- a/libs/client/src/function.ts +++ b/libs/client/src/function.ts @@ -70,17 +70,28 @@ export async function run( id: string, options: RunOptions = {} ): Promise { - const { credentials, requestMiddleware, responseHandler } = getConfig(); + const { + credentials: credentialsValue, + requestMiddleware, + responseHandler, + } = getConfig(); const method = (options.method ?? 'post').toLowerCase(); const userAgent = isBrowser() ? {} : { 'User-Agent': getUserAgent() }; - const { keyId, keySecret } = - typeof credentials === 'function' ? credentials() : credentials; + const credentials = + typeof credentialsValue === 'function' + ? credentialsValue() + : credentialsValue; const { url, headers } = await requestMiddleware({ url: buildUrl(id, options), }); - const authHeader = - keyId && keySecret ? { Authorization: `Key ${keyId}:${keySecret}` } : {}; + const authHeader = credentials ? { Authorization: `Key ${credentials}` } : {}; + if (typeof window !== 'undefined' && credentials) { + console.warn( + "The fal credentials are exposed in the browser's environment. " + + "That's not recommended for production use cases." + ); + } const requestHeaders = { ...authHeader, Accept: 'application/json', diff --git a/libs/client/src/index.ts b/libs/client/src/index.ts index 00a6eb5..b3df91b 100644 --- a/libs/client/src/index.ts +++ b/libs/client/src/index.ts @@ -1,5 +1,4 @@ export { config, getConfig } from './config'; -export type { Credentials } from './config'; export { queue, run } from './function'; export { withMiddleware } from './middleware'; export type { RequestMiddleware } from './middleware'; diff --git a/libs/nextjs/src/handler.ts b/libs/nextjs/src/handler.ts index 0333657..6de92c0 100644 --- a/libs/nextjs/src/handler.ts +++ b/libs/nextjs/src/handler.ts @@ -1,6 +1,7 @@ import type { NextApiHandler, NextApiRequest, PageConfig } from 'next'; import { TARGET_URL_HEADER } from './config'; +const FAL_KEY = process.env.FAL_KEY || process.env.NEXT_PUBLIC_FAL_KEY; const FAL_KEY_ID = process.env.FAL_KEY_ID || process.env.NEXT_PUBLIC_FAL_KEY_ID; const FAL_KEY_SECRET = process.env.FAL_KEY_SECRET || process.env.NEXT_PUBLIC_FAL_KEY_SECRET; @@ -28,10 +29,19 @@ function getHeader(request: NextApiRequest, key: string): string | undefined { function cleanUpHeaders(request: NextApiRequest) { delete request.headers['origin']; delete request.headers['referer']; - // delete request.headers['transfer-encoding']; delete request.headers[TARGET_URL_HEADER]; } +function getFalKey(): string | undefined { + if (FAL_KEY) { + return FAL_KEY; + } + if (FAL_KEY_ID && FAL_KEY_SECRET) { + return `${FAL_KEY_ID}:${FAL_KEY_SECRET}`; + } + return undefined; +} + /** * A Next request handler that proxies the request to the fal-serverless * endpoint. This is useful so client-side calls to the fal-serverless endpoint @@ -55,15 +65,16 @@ export const handler: NextApiHandler = async (request, response) => { cleanUpHeaders(request); - const authHeader = - FAL_KEY_ID && FAL_KEY_SECRET - ? { authorization: `Key ${FAL_KEY_ID}:${FAL_KEY_SECRET}` } - : {}; + const falKey = getFalKey(); + if (!falKey) { + response.status(401).send('Missing fal.ai credentials'); + return; + } const res = await fetch(targetUrl, { method: request.method, headers: { - ...authHeader, + authorization: `Key ${falKey}`, accept: 'application/json', 'content-type': 'application/json', 'x-fal-client-proxy': '@fal-ai/serverless-nextjs',