diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml
index 3ba63cc2..b4171110 100644
--- a/docker/docker-compose.dev.yml
+++ b/docker/docker-compose.dev.yml
@@ -16,6 +16,11 @@ services:
- ../api/migrations:/app/migrations
#- ../api/node_modules:/app/node_modules
command: ["npm", "run", "start:debug"]
+
+ hexabot-frontend:
+ build:
+ context: ../
+ dockerfile: ./frontend/Dockerfile
mongo-express:
container_name: mongoUi
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index 80102c66..5549acf3 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -30,7 +30,6 @@ services:
condition: service_healthy
database-init:
condition: service_completed_successfully
-
healthcheck:
test: "wget --spider http://localhost:3000"
interval: 10s
@@ -40,12 +39,7 @@ services:
hexabot-frontend:
container_name: frontend
- build:
- context: ../
- dockerfile: ./frontend/Dockerfile
- args:
- - NEXT_PUBLIC_API_ORIGIN=${NEXT_PUBLIC_API_ORIGIN}
- - NEXT_PUBLIC_SSO_ENABLED=${NEXT_PUBLIC_SSO_ENABLED}
+ image: hexabot-ui:latest
env_file: .env
ports:
- ${APP_FRONTEND_PORT}:8080
diff --git a/frontend/Dockerfile b/frontend/Dockerfile
index cea1c11b..d8d812c6 100644
--- a/frontend/Dockerfile
+++ b/frontend/Dockerfile
@@ -21,14 +21,6 @@ RUN \
# Rebuild the source code only when needed
FROM base AS builder
-ARG NEXT_PUBLIC_API_ORIGIN
-ENV NEXT_PUBLIC_API_ORIGIN=${NEXT_PUBLIC_API_ORIGIN}
-ARG NEXT_PUBLIC_SSO_ENABLED
-ENV NEXT_PUBLIC_SSO_ENABLED=${NEXT_PUBLIC_SSO_ENABLED}
-
-ENV REACT_APP_WIDGET_API_URL=${NEXT_PUBLIC_API_ORIGIN}
-ENV REACT_APP_WIDGET_CHANNEL=test
-ENV REACT_APP_WIDGET_TOKEN=test
WORKDIR /app
@@ -57,10 +49,6 @@ ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime.
ENV NEXT_TELEMETRY_DISABLED 1
-# Set the environment variable API_ORIGIN
-ENV NEXT_PUBLIC_API_ORIGIN ${NEXT_PUBLIC_API_ORIGIN:-"http://localhost:3000"}
-ENV NEXT_PUBLIC_SSO_ENABLED ${NEXT_PUBLIC_SSO_ENABLED:-"false"}
-
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
diff --git a/frontend/next.config.mjs b/frontend/next.config.mjs
index 774cf83f..f1c7e508 100644
--- a/frontend/next.config.mjs
+++ b/frontend/next.config.mjs
@@ -4,6 +4,14 @@ import withTM from "next-transpile-modules";
const apiUrl = process.env.NEXT_PUBLIC_API_ORIGIN || "http://localhost:4000/";
const url = new URL(apiUrl);
const nextConfig = withTM(["hexabot-widget"])({
+ async rewrites() {
+ return [
+ {
+ source: "/config",
+ destination: "/api/config",
+ },
+ ];
+ },
webpack(config, _options) {
return config;
},
diff --git a/frontend/src/hooks/useConfig.tsx b/frontend/src/hooks/useConfig.tsx
new file mode 100644
index 00000000..f8a4fd94
--- /dev/null
+++ b/frontend/src/hooks/useConfig.tsx
@@ -0,0 +1,50 @@
+import { createContext, useContext, useEffect, useState } from "react";
+
+const ConfigContext = createContext(null);
+
+export interface IConfig {
+ NEXT_PUBLIC_API_ORIGIN: string;
+ NEXT_PUBLIC_SSO_ENABLED: boolean;
+ REACT_APP_WIDGET_API_URL: string;
+ REACT_APP_WIDGET_CHANNEL: string;
+ REACT_APP_WIDGET_TOKEN: string;
+}
+
+export const ConfigProvider = ({ children }) => {
+ const [config, setConfig] = useState(null);
+
+ useEffect(() => {
+ const fetchConfig = async () => {
+ try {
+ const res = await fetch("/config");
+ const data = await res.json();
+
+ setConfig(data);
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.error("Failed to fetch configuration:", error);
+ }
+ };
+
+ fetchConfig();
+ }, []);
+
+ if (!config) {
+ // You can return a loader here if you want
+ return null;
+ }
+
+ return (
+ {children}
+ );
+};
+
+export const useConfig = () => {
+ const context = useContext(ConfigContext);
+
+ if (!context) {
+ throw new Error("useConfig must be used within a ConfigProvider");
+ }
+
+ return context;
+};
diff --git a/frontend/src/pages/_app.tsx b/frontend/src/pages/_app.tsx
index ad0bbab1..b93e392d 100644
--- a/frontend/src/pages/_app.tsx
+++ b/frontend/src/pages/_app.tsx
@@ -20,6 +20,7 @@ import { ReactQueryDevtools } from "react-query/devtools";
import { SnackbarCloseButton } from "@/app-components/displays/Toast/CloseButton";
import { ApiClientProvider } from "@/hooks/useApiClient";
import { AuthProvider } from "@/hooks/useAuth";
+import { ConfigProvider } from "@/hooks/useConfig";
import { PermissionProvider } from "@/hooks/useHasPermission";
import { SettingsProvider } from "@/hooks/useSetting";
import { ToastProvider } from "@/hooks/useToast";
@@ -69,33 +70,35 @@ const App = ({ Component, pageProps }: TAppPropsWithLayout) => {
/>
-
- (
-
- )}
- >
-
-
-
-
-
-
-
-
- {getLayout()}
-
-
-
-
-
-
-
-
-
-
+
+
+ (
+
+ )}
+ >
+
+
+
+
+
+
+
+
+ {getLayout()}
+
+
+
+
+
+
+
+
+
+
+
>
);
diff --git a/frontend/src/pages/api/config.ts b/frontend/src/pages/api/config.ts
new file mode 100644
index 00000000..2af6d1ab
--- /dev/null
+++ b/frontend/src/pages/api/config.ts
@@ -0,0 +1,16 @@
+import type { NextApiRequest, NextApiResponse } from "next";
+
+type ResponseData = {
+ apiUrl: string;
+ ssoEnabled: boolean;
+};
+
+export default function handler(
+ req: NextApiRequest,
+ res: NextApiResponse,
+) {
+ res.status(200).json({
+ apiUrl: process.env.NEXT_PUBLIC_API_ORIGIN || "http://localhost:3000",
+ ssoEnabled: process.env.NEXT_PUBLIC_SSO_ENABLED === "true" || false,
+ });
+}