Skip to content

Commit

Permalink
feat: wip
Browse files Browse the repository at this point in the history
  • Loading branch information
shahargl committed Nov 24, 2024
1 parent 9f1ef24 commit 2430075
Show file tree
Hide file tree
Showing 10 changed files with 300 additions and 91 deletions.
40 changes: 13 additions & 27 deletions keep-ui/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { AuthError } from "next-auth";
import { AuthenticationError, AuthErrorCodes } from "@/errors";
import type { JWT } from "@auth/core/jwt";
// https://github.com/nextauthjs/next-auth/issues/11028
import { initProxyFetch } from "./proxyFetch";

export class BackendRefusedError extends AuthError {
static type = "BackendRefusedError";
Expand Down Expand Up @@ -43,23 +42,21 @@ const proxyUrl =
process.env.http_proxy ||
process.env.https_proxy;

// Helper function to dynamically import proxyFetch only when needed
async function getProxyFetch() {
if (typeof window === "undefined" && !process.env.NEXT_RUNTIME) {
// Only import in Node.js environment, not Edge
try {
const { initProxyFetch } = await import("./proxyFetch");
return initProxyFetch();
} catch (e) {
console.warn("Failed to load proxy fetch:", e);
return null;
}
}
return null;
import { ProxyAgent, fetch as undici } from "undici";
function proxy(...args: Parameters<typeof fetch>): ReturnType<typeof fetch> {
const dispatcher = new ProxyAgent(proxyUrl!);
// @ts-expect-error `undici` has a `duplex` option
return undici(args[0], { ...args[1], dispatcher });
}

// Create Azure AD provider configuration
const createAzureADProvider = () => {
// check if proxy is enabled
if (!proxyUrl) {
console.log("Proxy is not enabled");
} else {
console.log("Proxy is enabled - ", proxyUrl);
}
const baseConfig = {
clientId: process.env.KEEP_AZUREAD_CLIENT_ID!,
clientSecret: process.env.KEEP_AZUREAD_CLIENT_SECRET!,
Expand All @@ -72,24 +69,13 @@ const createAzureADProvider = () => {
.KEEP_AZUREAD_CLIENT_ID!}/default openid profile email`,
},
},
[customFetch]: proxyUrl ? proxy : undefined,
client: {
token_endpoint_auth_method: "client_secret_post",
},
};

if (!proxyUrl) {
console.log("Using built-in fetch for Azure AD provider");
return MicrosoftEntraID(baseConfig);
}

console.log("Using proxy fetch for Azure AD provider");
return MicrosoftEntraID({
...baseConfig,
async [customFetch](...args) {
const proxyFetch = await getProxyFetch();
return proxyFetch ? proxyFetch(...args) : fetch(...args);
},
});
return MicrosoftEntraID(baseConfig);
};

async function refreshAccessToken(token: any) {
Expand Down
41 changes: 24 additions & 17 deletions keep-ui/middleware.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { auth } from "@/auth";
import { NextResponse } from "next/server";
import { NextResponse, type NextRequest } from "next/server";
// https://github.com/nextauthjs/next-auth/issues/11028#issuecomment-2391497962
import { getToken } from "next-auth/jwt";
import { getApiURL } from "@/utils/apiUrl";

// Use auth as a wrapper for middleware logic
export default auth(async (req) => {
const { pathname, searchParams } = req.nextUrl;
export async function middleware(request: NextRequest) {
const { pathname, searchParams } = request.nextUrl;

// Keep it on header so it can be used in server components
const requestHeaders = new Headers(req.headers);
requestHeaders.set("x-url", req.url);
const requestHeaders = new Headers(request.headers);
requestHeaders.set("x-url", request.url);

// Get the token using next-auth/jwt instead of auth wrapper
const token = await getToken({
req: request,
secret: process.env.NEXTAUTH_SECRET,
});

// Handle legacy /backend/ redirects
if (pathname.startsWith("/backend/")) {
Expand All @@ -25,36 +31,37 @@ export default auth(async (req) => {
if (pathname.startsWith("/api/")) {
return NextResponse.next();
}

// If not authenticated and not on signin page, redirect to signin
if (!req.auth && !pathname.startsWith("/signin")) {
if (!token && !pathname.startsWith("/signin")) {
console.log("Redirecting to signin page because user is not authenticated");
return NextResponse.redirect(new URL("/signin", req.url));
return NextResponse.redirect(new URL("/signin", request.url));
}

// else if authenticated and on signin page, redirect to dashboard
if (req.auth && pathname.startsWith("/signin")) {
// If authenticated and on signin page, redirect to dashboard
if (token && pathname.startsWith("/signin")) {
console.log(
"Redirecting to incidents because user try to get /signin but already authenticated"
);
return NextResponse.redirect(new URL("/incidents", req.url));
return NextResponse.redirect(new URL("/incidents", request.url));
}

// Role-based routing (NOC users)
if (req.auth?.user?.role === "noc" && !pathname.startsWith("/alerts")) {
return NextResponse.redirect(new URL("/alerts/feed", req.url));
if (token?.user?.role === "noc" && !pathname.startsWith("/alerts")) {
return NextResponse.redirect(new URL("/alerts/feed", request.url));
}

// Allow all other authenticated requests
console.log("Allowing request to pass through");
console.log("Request URL: ", req.url);
// console.log("Request headers: ", requestHeaders)
console.log("Request URL: ", request.url);

return NextResponse.next({
request: {
// Apply new request headers
headers: requestHeaders,
},
});
});
}

// Update the matcher to handle static files and public routes
export const config = {
Expand Down
23 changes: 23 additions & 0 deletions keep-ui/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,29 @@ const { withSentryConfig } = require("@sentry/nextjs");
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: false,
webpack: (
config,
{ buildId, dev, isServer, defaultLoaders, nextRuntime, webpack }
) => {
// Only apply proxy configuration for Node.js server runtime
if (isServer && nextRuntime === "nodejs") {
// Add environment variables for proxy at build time
config.plugins.push(
new webpack.DefinePlugin({
"process.env.IS_NODEJS_RUNTIME": JSON.stringify(true),
})
);
} else {
// For edge runtime and client
config.plugins.push(
new webpack.DefinePlugin({
"process.env.IS_NODEJS_RUNTIME": JSON.stringify(false),
})
);
}

return config;
},
transpilePackages: ["next-auth"],
images: {
remotePatterns: [
Expand Down
89 changes: 64 additions & 25 deletions keep-ui/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 keep-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"lint": "next lint"
},
"dependencies": {
"@auth/core": "^0.37.4",
"@boiseitguru/cookie-cutter": "^0.2.3",
"@copilotkit/react-core": "^1.3.15",
"@copilotkit/react-ui": "^1.3.15",
Expand Down
24 changes: 24 additions & 0 deletions keep-ui/proxyFetch.node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// proxyFetch.node.ts
import { ProxyAgent, fetch as undici } from "undici";
import type { ProxyFetchFn } from "./proxyFetch";

export const createProxyFetch = async (): Promise<ProxyFetchFn | undefined> => {
const proxyUrl =
process.env.HTTP_PROXY ||
process.env.HTTPS_PROXY ||
process.env.http_proxy ||
process.env.https_proxy;

if (!proxyUrl) {
return undefined;
}

const dispatcher = new ProxyAgent(proxyUrl);

return function proxy(
...args: Parameters<typeof fetch>
): ReturnType<typeof fetch> {
// @ts-expect-error `undici` has a `duplex` option
return undici(args[0], { ...args[1], dispatcher });
};
};
23 changes: 7 additions & 16 deletions keep-ui/proxyFetch.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
// proxyFetch.ts
let proxyFetch: typeof fetch | undefined;

export async function initProxyFetch() {
const proxyUrl =
process.env.HTTP_PROXY ||
process.env.HTTPS_PROXY ||
process.env.http_proxy ||
process.env.https_proxy;
// We only export the type from this file
export type ProxyFetchFn = (
...args: Parameters<typeof fetch>
) => ReturnType<typeof fetch>;

if (proxyUrl && typeof window === "undefined") {
const { ProxyAgent, fetch: undici } = await import("undici");
const dispatcher = new ProxyAgent(proxyUrl);
return (...args: Parameters<typeof fetch>): ReturnType<typeof fetch> => {
// @ts-expect-error `undici` has a `duplex` option
return undici(args[0], { ...args[1], dispatcher });
};
}
// This function will be imported dynamically only in Node.js environment
export const createProxyFetch = async (): Promise<ProxyFetchFn | undefined> => {
return undefined;
}
};
1 change: 1 addition & 0 deletions keep-ui/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"compilerOptions": {
"sourceMap": true,
"target": "es6",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
Expand Down
Loading

0 comments on commit 2430075

Please sign in to comment.