fix: proxy error json response (#16)
* chore: remove deprecated package * fix: proxy error json response
This commit is contained in:
parent
dcd513c1e7
commit
e02118f43f
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"presets": [["@nx/js/babel", { "useBuiltIns": "usage" }]]
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": ["../../.eslintrc.json"],
|
|
||||||
"ignorePatterns": ["!**/*"],
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
|
||||||
"rules": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files": ["*.ts", "*.tsx"],
|
|
||||||
"rules": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files": ["*.js", "*.jsx"],
|
|
||||||
"rules": {}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
# @fal-ai/serverless-nextjs
|
|
||||||
|
|
||||||
This package is not longer maintained. Check out the [@fal-ai/serverless-proxy](../proxy/)package, which supports Next.js and other Express-based frameworks.
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
export default {
|
|
||||||
displayName: 'nextjs',
|
|
||||||
preset: '../../jest.preset.js',
|
|
||||||
globals: {},
|
|
||||||
testEnvironment: 'node',
|
|
||||||
transform: {
|
|
||||||
'^.+\\.[tj]sx?$': [
|
|
||||||
'ts-jest',
|
|
||||||
{
|
|
||||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
|
||||||
coverageDirectory: '../../coverage/libs/nextjs',
|
|
||||||
};
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@fal-ai/serverless-nextjs",
|
|
||||||
"description": "The fal-serverless Next.js integration",
|
|
||||||
"version": "0.2.4",
|
|
||||||
"license": "MIT",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/fal-ai/serverless-js.git",
|
|
||||||
"directory": "libs/nextjs"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"fal",
|
|
||||||
"serverless",
|
|
||||||
"client",
|
|
||||||
"next",
|
|
||||||
"nextjs",
|
|
||||||
"proxy"
|
|
||||||
],
|
|
||||||
"peerDependencies": {
|
|
||||||
"next": "^13.0.0",
|
|
||||||
"react": "^18.0.0",
|
|
||||||
"react-dom": "^18.0.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"react": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"react-dom": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "nextjs",
|
|
||||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
||||||
"sourceRoot": "libs/nextjs/src",
|
|
||||||
"projectType": "library",
|
|
||||||
"targets": {
|
|
||||||
"build": {
|
|
||||||
"executor": "@nx/js:tsc",
|
|
||||||
"outputs": ["{options.outputPath}"],
|
|
||||||
"options": {
|
|
||||||
"outputPath": "dist/libs/nextjs",
|
|
||||||
"tsConfig": "libs/nextjs/tsconfig.lib.json",
|
|
||||||
"packageJson": "libs/nextjs/package.json",
|
|
||||||
"main": "libs/nextjs/src/index.ts",
|
|
||||||
"assets": ["LICENSE", "CODE_OF_CONDUCT.md", "libs/nextjs/README.md"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lint": {
|
|
||||||
"executor": "@nx/linter:eslint",
|
|
||||||
"outputs": ["{options.outputFile}"],
|
|
||||||
"options": {
|
|
||||||
"lintFilePatterns": ["libs/nextjs/**/*.ts"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"test": {
|
|
||||||
"executor": "@nx/jest:jest",
|
|
||||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
|
||||||
"options": {
|
|
||||||
"jestConfig": "libs/nextjs/jest.config.ts",
|
|
||||||
"passWithNoTests": true
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"ci": {
|
|
||||||
"ci": true,
|
|
||||||
"codeCoverage": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
}
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
import type { RequestMiddleware } from '@fal-ai/serverless-client';
|
|
||||||
|
|
||||||
export type NextProxyConfig = {
|
|
||||||
targetUrl: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const defaultConfig: NextProxyConfig = {
|
|
||||||
targetUrl: '/api/_fal/proxy',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const TARGET_URL_HEADER = 'x-fal-target-url';
|
|
||||||
|
|
||||||
export function withNextProxy(
|
|
||||||
config: NextProxyConfig = defaultConfig
|
|
||||||
): RequestMiddleware {
|
|
||||||
// when running on the server, we don't need to proxy the request
|
|
||||||
if (typeof window === 'undefined') {
|
|
||||||
return (requestConfig) => Promise.resolve(requestConfig);
|
|
||||||
}
|
|
||||||
return (requestConfig) =>
|
|
||||||
Promise.resolve({
|
|
||||||
...requestConfig,
|
|
||||||
url: config.targetUrl,
|
|
||||||
headers: {
|
|
||||||
[TARGET_URL_HEADER]: requestConfig.url,
|
|
||||||
...(requestConfig.headers || {}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@ -1,113 +0,0 @@
|
|||||||
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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility to get a header value as `string` from a Headers object.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @param request the Next request object.
|
|
||||||
* @param key the header key.
|
|
||||||
* @returns the header value as `string` or `undefined` if the header is not set.
|
|
||||||
*/
|
|
||||||
function getHeader(request: NextApiRequest, key: string): string | undefined {
|
|
||||||
const headerValue = request.headers[key.toLowerCase()];
|
|
||||||
if (Array.isArray(headerValue)) {
|
|
||||||
return headerValue[0];
|
|
||||||
}
|
|
||||||
return headerValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clean up headers that should not be forwarded to the proxy.
|
|
||||||
* @param request the Next request object.
|
|
||||||
*/
|
|
||||||
function cleanUpHeaders(request: NextApiRequest) {
|
|
||||||
delete request.headers['origin'];
|
|
||||||
delete request.headers['referer'];
|
|
||||||
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
|
|
||||||
* can be made without CORS issues and the correct credentials can be added
|
|
||||||
* effortlessly.
|
|
||||||
*
|
|
||||||
* @param request the Next request object.
|
|
||||||
* @param response the Next response object.
|
|
||||||
* @returns Promise<any> the promise that will be resolved once the request is done.
|
|
||||||
*/
|
|
||||||
export const handler: NextApiHandler = async (request, response) => {
|
|
||||||
const targetUrl = getHeader(request, TARGET_URL_HEADER);
|
|
||||||
if (!targetUrl) {
|
|
||||||
response.status(400).send(`Missing the ${TARGET_URL_HEADER} header`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (targetUrl.indexOf('fal.ai') === -1) {
|
|
||||||
response.status(412).send(`Invalid ${TARGET_URL_HEADER} header`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanUpHeaders(request);
|
|
||||||
|
|
||||||
const falKey = getFalKey();
|
|
||||||
if (!falKey) {
|
|
||||||
response.status(401).send('Missing fal.ai credentials');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// pass over headers prefixed with x-fal-*
|
|
||||||
const headers: Record<string, string | string[] | undefined> = {};
|
|
||||||
Object.keys(request.headers).forEach((key) => {
|
|
||||||
if (key.toLowerCase().startsWith('x-fal-')) {
|
|
||||||
headers[key.toLowerCase()] = request.headers[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const res = await fetch(targetUrl, {
|
|
||||||
method: request.method,
|
|
||||||
headers: {
|
|
||||||
...headers,
|
|
||||||
authorization: getHeader(request, 'authorization') ?? `Key ${falKey}`,
|
|
||||||
accept: 'application/json',
|
|
||||||
'content-type': 'application/json',
|
|
||||||
'x-fal-client-proxy': '@fal-ai/serverless-nextjs',
|
|
||||||
},
|
|
||||||
body:
|
|
||||||
request.method?.toUpperCase() === 'GET'
|
|
||||||
? undefined
|
|
||||||
: JSON.stringify(request.body),
|
|
||||||
});
|
|
||||||
|
|
||||||
// copy headers from res to response
|
|
||||||
res.headers.forEach((value, key) => {
|
|
||||||
response.setHeader(key, value);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res.headers.get('content-type') === 'application/json') {
|
|
||||||
const data = await res.json();
|
|
||||||
response.status(res.status).json(data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const data = await res.text();
|
|
||||||
response.status(res.status).send(data);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const config: PageConfig = {
|
|
||||||
api: {},
|
|
||||||
};
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
export * from './config';
|
|
||||||
export * from './handler';
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"files": [],
|
|
||||||
"include": [],
|
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.lib.json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.spec.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "./tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"module": "commonjs",
|
|
||||||
"outDir": "../../dist/out-tsc",
|
|
||||||
"declaration": true,
|
|
||||||
"types": ["node"]
|
|
||||||
},
|
|
||||||
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"],
|
|
||||||
"include": ["src/**/*.ts"]
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "./tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "../../dist/out-tsc",
|
|
||||||
"module": "commonjs",
|
|
||||||
"types": ["jest", "node"]
|
|
||||||
},
|
|
||||||
"include": [
|
|
||||||
"jest.config.ts",
|
|
||||||
"src/**/*.test.ts",
|
|
||||||
"src/**/*.spec.ts",
|
|
||||||
"src/**/*.test.tsx",
|
|
||||||
"src/**/*.spec.tsx",
|
|
||||||
"src/**/*.test.js",
|
|
||||||
"src/**/*.spec.js",
|
|
||||||
"src/**/*.test.jsx",
|
|
||||||
"src/**/*.spec.jsx",
|
|
||||||
"src/**/*.d.ts"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@fal-ai/serverless-proxy",
|
"name": "@fal-ai/serverless-proxy",
|
||||||
"version": "0.3.3",
|
"version": "0.3.4",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
@ -19,11 +19,10 @@ export const handler: RequestHandler = async (request, response, next) => {
|
|||||||
method: request.method,
|
method: request.method,
|
||||||
respondWith: (status, data) =>
|
respondWith: (status, data) =>
|
||||||
typeof data === 'string'
|
typeof data === 'string'
|
||||||
? response.status(status).send(data)
|
? response.status(status).json({ details: data })
|
||||||
: response.status(status).json(data),
|
: response.status(status).json(data),
|
||||||
getHeaders: () => request.headers,
|
getHeaders: () => request.headers,
|
||||||
getHeader: (name) => request.headers[name],
|
getHeader: (name) => request.headers[name],
|
||||||
removeHeader: (name) => response.removeHeader(name),
|
|
||||||
sendHeader: (name, value) => response.setHeader(name, value),
|
sendHeader: (name, value) => response.setHeader(name, value),
|
||||||
getBody: () => JSON.stringify(request.body),
|
getBody: () => JSON.stringify(request.body),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -18,7 +18,6 @@ export interface ProxyBehavior {
|
|||||||
respondWith(status: number, data: string | any): void;
|
respondWith(status: number, data: string | any): void;
|
||||||
getHeaders(): Record<string, string | string[] | undefined>;
|
getHeaders(): Record<string, string | string[] | undefined>;
|
||||||
getHeader(name: string): string | string[] | undefined;
|
getHeader(name: string): string | string[] | undefined;
|
||||||
removeHeader(name: string): void;
|
|
||||||
sendHeader(name: string, value: string): void;
|
sendHeader(name: string, value: string): void;
|
||||||
getBody(): string | undefined;
|
getBody(): string | undefined;
|
||||||
}
|
}
|
||||||
@ -42,16 +41,6 @@ function singleHeaderValue(
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clean up headers that should not be forwarded to the proxy.
|
|
||||||
* @param behavior The proxy implementation.
|
|
||||||
*/
|
|
||||||
function cleanUpHeaders(behavior: ProxyBehavior) {
|
|
||||||
behavior.removeHeader('origin');
|
|
||||||
behavior.removeHeader('referer');
|
|
||||||
behavior.removeHeader(TARGET_URL_HEADER);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFalKey(): string | undefined {
|
function getFalKey(): string | undefined {
|
||||||
if (FAL_KEY) {
|
if (FAL_KEY) {
|
||||||
return FAL_KEY;
|
return FAL_KEY;
|
||||||
@ -82,8 +71,6 @@ export const handleRequest = async (behavior: ProxyBehavior) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanUpHeaders(behavior);
|
|
||||||
|
|
||||||
const falKey = getFalKey();
|
const falKey = getFalKey();
|
||||||
if (!falKey) {
|
if (!falKey) {
|
||||||
behavior.respondWith(401, 'Missing fal.ai credentials');
|
behavior.respondWith(401, 'Missing fal.ai credentials');
|
||||||
@ -118,7 +105,7 @@ export const handleRequest = async (behavior: ProxyBehavior) => {
|
|||||||
behavior.sendHeader(key, value);
|
behavior.sendHeader(key, value);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.headers.get('content-type') === 'application/json') {
|
if (res.headers.get('content-type').includes('application/json')) {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
behavior.respondWith(res.status, data);
|
behavior.respondWith(res.status, data);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -19,11 +19,10 @@ export const handler: NextApiHandler = async (request, response) => {
|
|||||||
method: request.method,
|
method: request.method,
|
||||||
respondWith: (status, data) =>
|
respondWith: (status, data) =>
|
||||||
typeof data === 'string'
|
typeof data === 'string'
|
||||||
? response.status(status).send(data)
|
? response.status(status).json({ details: data })
|
||||||
: response.status(status).json(data),
|
: response.status(status).json(data),
|
||||||
getHeaders: () => request.headers,
|
getHeaders: () => request.headers,
|
||||||
getHeader: (name) => request.headers[name],
|
getHeader: (name) => request.headers[name],
|
||||||
removeHeader: (name) => response.removeHeader(name),
|
|
||||||
sendHeader: (name, value) => response.setHeader(name, value),
|
sendHeader: (name, value) => response.setHeader(name, value),
|
||||||
getBody: () => JSON.stringify(request.body),
|
getBody: () => JSON.stringify(request.body),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -16,7 +16,6 @@
|
|||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@fal-ai/serverless-client": ["libs/client/src/index.ts"],
|
"@fal-ai/serverless-client": ["libs/client/src/index.ts"],
|
||||||
"@fal-ai/serverless-nextjs": ["libs/nextjs/src/index.ts"],
|
|
||||||
"@fal-ai/serverless-proxy": ["libs/proxy/src/index.ts"],
|
"@fal-ai/serverless-proxy": ["libs/proxy/src/index.ts"],
|
||||||
"@fal-ai/serverless-proxy/express": ["libs/proxy/src/express.ts"],
|
"@fal-ai/serverless-proxy/express": ["libs/proxy/src/express.ts"],
|
||||||
"@fal-ai/serverless-proxy/nextjs": ["libs/proxy/src/nextjs.ts"]
|
"@fal-ai/serverless-proxy/nextjs": ["libs/proxy/src/nextjs.ts"]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user