Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support single fal_key env var #10

Merged
merged 2 commits into from
Sep 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/demo-app/pages/api/_fal/proxy.ts
Original file line number Diff line number Diff line change
@@ -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
24 changes: 14 additions & 10 deletions apps/demo-app/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>(DEFAULT_PROMPT);
// Result state
Expand Down Expand Up @@ -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 {
Expand All @@ -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));
}
},
});
Expand All @@ -91,8 +95,8 @@ export function Index() {
// @snippet:end
};
return (
<div className="min-h-screen dark:bg-gray-800 dark:text-gray-50 bg-gray-100 text-gray-800">
<main className="flex flex-col items-center justify-center w-full flex-1 px-20 py-10 space-y-8">
<div className="min-h-screen dark:bg-gray-900 bg-gray-100">
<main className="container dark:text-gray-50 text-gray-900 flex flex-col items-center justify-center w-full flex-1 py-10 space-y-8">
<h1 className="text-4xl font-bold mb-8">
Hello <code className="font-light text-pink-600">fal</code>
</h1>
Expand Down Expand Up @@ -134,7 +138,7 @@ export function Index() {
<p className="text-sm text-current/80">
{`Elapsed Time (seconds): ${(elapsedTime / 1000).toFixed(2)}`}
</p>
<pre className="text-sm bg-black/80 text-white/80 font-mono h-60 rounded whitespace-pre overflow-auto w-full">
<pre className="text-sm bg-black/70 text-white/80 font-mono h-60 rounded whitespace-pre overflow-auto w-full">
{result
? JSON.stringify(result, null, 2)
: '// result pending...'}
Expand All @@ -143,7 +147,7 @@ export function Index() {

<div className="space-y-2">
<h3 className="text-xl font-light">Logs</h3>
<pre className="text-sm bg-black/80 text-white/80 font-mono h-60 rounded whitespace-pre overflow-auto w-full">
<pre className="text-sm bg-black/70 text-white/80 font-mono h-60 rounded whitespace-pre overflow-auto w-full">
{logs.filter(Boolean).join('\n')}
</pre>
</div>
Expand Down
5 changes: 1 addition & 4 deletions libs/client/src/config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
33 changes: 11 additions & 22 deletions libs/client/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,13 @@
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<any>;

Check warning on line 11 in libs/client/src/config.ts

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
};

export type RequiredConfig = Required<Config>;
Expand All @@ -28,29 +23,22 @@
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}`;
};

/**
Expand Down Expand Up @@ -93,6 +81,7 @@
export function getConfig(): RequiredConfig {
if (!configuration) {
console.info('Using default configuration for the fal client');
return { ...DEFAULT_CONFIG } as RequiredConfig;
}
return configuration;
}
10 changes: 1 addition & 9 deletions libs/client/src/function.spec.ts
Original file line number Diff line number Diff line change
@@ -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();
Expand Down
21 changes: 16 additions & 5 deletions libs/client/src/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,28 @@ export async function run<Input, Output>(
id: string,
options: RunOptions<Input> = {}
): Promise<Output> {
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',
Expand Down
1 change: 0 additions & 1 deletion libs/client/src/index.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down
23 changes: 17 additions & 6 deletions libs/nextjs/src/handler.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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',
Expand Down
Loading