fix: load config on runtime

This commit is contained in:
Mohamed Marrouchi 2024-09-17 17:12:20 +01:00 committed by Yassine Sallemi
parent d2555d9f94
commit aa05fe1704
7 changed files with 110 additions and 46 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
},

View File

@ -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 (
<ConfigContext.Provider value={config}>{children}</ConfigContext.Provider>
);
};
export const useConfig = () => {
const context = useContext(ConfigContext);
if (!context) {
throw new Error("useConfig must be used within a ConfigProvider");
}
return context;
};

View File

@ -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) => {
/>
</Head>
<main className={roboto.className}>
<ThemeProvider theme={theme}>
<ToastProvider
maxSnack={3}
anchorOrigin={{ vertical: "top", horizontal: "center" }}
action={(snackbarKey) => (
<SnackbarCloseButton snackbarKey={snackbarKey} />
)}
>
<StyledEngineProvider injectFirst>
<QueryClientProvider client={queryClient}>
<CssBaseline />
<ApiClientProvider>
<AuthProvider>
<PermissionProvider>
<SettingsProvider>
<SocketProvider>
{getLayout(<Component {...pageProps} />)}
</SocketProvider>
</SettingsProvider>
</PermissionProvider>
</AuthProvider>
</ApiClientProvider>
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
</StyledEngineProvider>
</ToastProvider>
</ThemeProvider>
<ConfigProvider>
<ThemeProvider theme={theme}>
<ToastProvider
maxSnack={3}
anchorOrigin={{ vertical: "top", horizontal: "center" }}
action={(snackbarKey) => (
<SnackbarCloseButton snackbarKey={snackbarKey} />
)}
>
<StyledEngineProvider injectFirst>
<QueryClientProvider client={queryClient}>
<CssBaseline />
<ApiClientProvider>
<AuthProvider>
<PermissionProvider>
<SettingsProvider>
<SocketProvider>
{getLayout(<Component {...pageProps} />)}
</SocketProvider>
</SettingsProvider>
</PermissionProvider>
</AuthProvider>
</ApiClientProvider>
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
</StyledEngineProvider>
</ToastProvider>
</ThemeProvider>
</ConfigProvider>
</main>
</>
);

View File

@ -0,0 +1,16 @@
import type { NextApiRequest, NextApiResponse } from "next";
type ResponseData = {
apiUrl: string;
ssoEnabled: boolean;
};
export default function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseData>,
) {
res.status(200).json({
apiUrl: process.env.NEXT_PUBLIC_API_ORIGIN || "http://localhost:3000",
ssoEnabled: process.env.NEXT_PUBLIC_SSO_ENABLED === "true" || false,
});
}