Skip to content

Commit

Permalink
Merge pull request #2129 from flanksource/direct-api-call-option
Browse files Browse the repository at this point in the history
fix: make direct api call opt-in for the saas
  • Loading branch information
mainawycliffe authored Jul 25, 2024
2 parents 5ccec8f + d1f9d08 commit faf394d
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 18 deletions.
24 changes: 22 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"monaco-editor": "0.48.0",
"monaco-themes": "0.4.4",
"monaco-yaml": "5.1.1",
"next-http-proxy-middleware": "^1.2.6",
"object-hash": "^3.0.0",
"prism-react-renderer": "^1.3.3",
"prop-types": "^15.8.1",
Expand Down
82 changes: 82 additions & 0 deletions pages/api/[...paths].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { clerkClient, getAuth } from "@clerk/nextjs/server";
import { NextApiRequest, NextApiResponse } from "next";
import httpProxyMiddleware from "next-http-proxy-middleware";

const API_URL = process.env.BACKEND_URL;
const isCanary = process.env.NEXT_PUBLIC_APP_DEPLOYMENT === "CANARY_CHECKER";
const env = process.env.ENV;
const isClerkAuth = process.env.NEXT_PUBLIC_AUTH_IS_CLERK === "true";

const canaryPrefix = isCanary ? "" : "/canary";

export const config = {
api: {
bodyParser: false
}
};

async function getTargetURL(req: NextApiRequest) {
if (isClerkAuth) {
const user = getAuth(req);
const org = await clerkClient.organizations.getOrganization({
organizationId: user.sessionClaims?.org_id!
});
const backendURL = org?.publicMetadata?.backend_url;
// for now, lets fallback to the old way of doing things, if the backend_url
// is not set in the org metadata
const target = backendURL;
return target;
}
return API_URL;
}

const clerkBackendPathRewrites = [
{
patternStr: "^/api",
replaceStr: "/"
}
];

const kratosBackendPathRewrites = ["localhost", "netlify"].includes(env!)
? [
{
patternStr: "^/api",
replaceStr: "/api"
}
]
: [
{
patternStr: "^/api/canary",
replaceStr: `${canaryPrefix}`
},
{
patternStr: "^/api/.ory",
replaceStr: "/kratos/"
},
{
patternStr: "^/api",
replaceStr: "/"
}
];

export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
console.log("Proxying request to backend");
const target = await getTargetURL(req);

if (!target) {
return res.status(500).json({ error: "Missing target" });
}

console.log(`Proxying to ${target}`);

return httpProxyMiddleware(req, res, {
target: target!,
xfwd: true,
pathRewrite: [
...(isClerkAuth ? clerkBackendPathRewrites : kratosBackendPathRewrites)
]
});
}
23 changes: 9 additions & 14 deletions src/api/axios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ import { toastError } from "../components/Toast/toast";

const isClerkAuthSystem = !!process.env.NEXT_PUBLIC_AUTH_IS_CLERK === true;

// The base URL for the API is either / or /api depending on the auth system,
// for clerk the base URL is /, for the rest it is /api
const API_BASE = isClerkAuthSystem ? "" : "/api";
const API_BASE = "/api";

export const apiBase = axios.create({
baseURL: `${API_BASE}`,
Expand Down Expand Up @@ -147,17 +145,14 @@ for (const client of [
Rback,
Snapshot
]) {
// only attach the interceptor if the system is not clerk
if (!isClerkAuthSystem) {
client.interceptors.response.use(
(response) => response,
(error) => {
redirectToLoginPageOnSessionExpiry(error);
toastError(error.response.data.message);
return Promise.reject(error);
}
);
}
client.interceptors.response.use(
(response) => response,
(error) => {
redirectToLoginPageOnSessionExpiry(error);
toastError(error.response.data.message);
return Promise.reject(error);
}
);
}

export function redirectToLoginPageOnSessionExpiry(error: AxiosError) {
Expand Down
31 changes: 29 additions & 2 deletions src/hooks/useClerkAttachAuthInterceptorsToAxios.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ const allAxiosInstances = [
Snapshot
];

function parseBoolean(value: unknown): boolean {
if (typeof value === "string") {
return value.toLowerCase() === "true";
}
if (typeof value === "boolean") {
return value;
}
if (typeof value === "undefined" || value === null) {
return false;
}
throw new Error("Invalid boolean value");
}

export default function useClerkAttachAuthInterceptorsToAxios() {
const { getToken } = useAuth();
const { organization } = useOrganization();
Expand All @@ -32,7 +45,19 @@ export default function useClerkAttachAuthInterceptorsToAxios() {
// organization set
const backendUrl = organization?.publicMetadata.backend_url as string;

// we need to know if th organization supports direct access to the backend or
// not so we can set the base URL correctly
const direct = parseBoolean(organization?.publicMetadata.direct);

console.log({ direct });

useEffect(() => {
// if the organization does not support direct access to the backend, we
// should not attach the interceptors
if (!direct) {
return;
}

const interceptorsRequestCleanups: number[] = [];
const interceptorsResponseCleanups: number[] = [];

Expand All @@ -51,7 +76,9 @@ export default function useClerkAttachAuthInterceptorsToAxios() {
config.headers.Authorization = `Bearer ${token}`;
}
// set the base URL to the organization's backend URL, not
const currentBaseUrl = config.baseURL;
// the API base URL
// For direct access, the base path does not include /api
const currentBaseUrl = config.baseURL?.replaceAll("/api", "");
config.baseURL = new URL(
currentBaseUrl ?? "/", //current base url is probably something like /api/auth or /api/db
backendUrl
Expand Down Expand Up @@ -89,5 +116,5 @@ export default function useClerkAttachAuthInterceptorsToAxios() {
});
});
};
}, [backendUrl, getToken]);
}, [backendUrl, direct, getToken]);
}

0 comments on commit faf394d

Please sign in to comment.