diff --git a/front/package.json b/front/package.json index 21cd338be..8bd4ec959 100644 --- a/front/package.json +++ b/front/package.json @@ -66,7 +66,8 @@ }, "scripts": { "start": "PORT=3001 craco start --max-warnings=0", - "build": "craco build", + "build:inject-runtime-env": "./scripts/inject-runtime-env.sh", + "build": "craco build && yarn build:inject-runtime-env", "test": "craco test", "coverage": "craco test --coverage .", "lint": "eslint src --max-warnings=0", diff --git a/front/public/env-config.js b/front/public/env-config.js new file mode 100644 index 000000000..20254cd25 --- /dev/null +++ b/front/public/env-config.js @@ -0,0 +1,3 @@ +window._env_ = { + // This file should stay empty. It will be overwritten by the build process. +} diff --git a/front/public/index.html b/front/public/index.html index 9578c1eca..f2d2be21b 100644 --- a/front/public/index.html +++ b/front/public/index.html @@ -36,6 +36,7 @@ /> Twenty + diff --git a/front/scripts/inject-runtime-env.sh b/front/scripts/inject-runtime-env.sh new file mode 100755 index 000000000..d4281004b --- /dev/null +++ b/front/scripts/inject-runtime-env.sh @@ -0,0 +1,11 @@ +echo "Generating env-config.js file from runtime environment variables..." + +BASE_FILENAME="build/env-config.js" +mkdir -p build +rm -rf "./$BASE_FILENAME" + +{ + echo "window._env_ = {" + echo " REACT_APP_SERVER_BASE_URL: \"$REACT_APP_SERVER_BASE_URL\"," + echo "}" +} > "./$BASE_FILENAME" \ No newline at end of file diff --git a/front/src/config/index.ts b/front/src/config/index.ts new file mode 100644 index 000000000..48d0908f2 --- /dev/null +++ b/front/src/config/index.ts @@ -0,0 +1,10 @@ +declare global { + interface Window { + _env_?: Record; + } +} + +export const REACT_APP_SERVER_BASE_URL = + window._env_?.REACT_APP_SERVER_BASE_URL || + process.env.REACT_APP_SERVER_BASE_URL || + 'http://localhost:3000'; diff --git a/front/src/modules/apollo/hooks/useApolloFactory.ts b/front/src/modules/apollo/hooks/useApolloFactory.ts index 9f996cf60..e2d66eb0e 100644 --- a/front/src/modules/apollo/hooks/useApolloFactory.ts +++ b/front/src/modules/apollo/hooks/useApolloFactory.ts @@ -6,6 +6,7 @@ import { useRecoilState } from 'recoil'; import { tokenPairState } from '@/auth/states/tokenPairState'; import { isDebugModeState } from '@/client-config/states/isDebugModeState'; import { AppPath } from '@/types/AppPath'; +import { REACT_APP_SERVER_BASE_URL } from '~/config'; import { ActivityTarget } from '~/generated/graphql'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; import { useUpdateEffect } from '~/hooks/useUpdateEffect'; @@ -23,7 +24,7 @@ export const useApolloFactory = () => { const apolloClient = useMemo(() => { apolloRef.current = new ApolloFactory({ - uri: `${process.env.REACT_APP_SERVER_BASE_URL}/graphql`, + uri: `${REACT_APP_SERVER_BASE_URL}/graphql`, cache: new InMemoryCache({ typePolicies: { Activity: { diff --git a/front/src/modules/auth/hooks/useAuth.ts b/front/src/modules/auth/hooks/useAuth.ts index c41282fe7..071bdd0c2 100644 --- a/front/src/modules/auth/hooks/useAuth.ts +++ b/front/src/modules/auth/hooks/useAuth.ts @@ -6,6 +6,7 @@ import { useRecoilState, } from 'recoil'; +import { REACT_APP_SERVER_BASE_URL } from '~/config'; import { useChallengeMutation, useCheckUserExistsLazyQuery, @@ -136,8 +137,7 @@ export const useAuth = () => { const handleGoogleLogin = useCallback((workspaceInviteHash?: string) => { const authServerUrl = - process.env.REACT_APP_SERVER_AUTH_URL ?? - process.env.REACT_APP_SERVER_BASE_URL + '/auth'; + REACT_APP_SERVER_BASE_URL ?? REACT_APP_SERVER_BASE_URL + '/auth'; window.location.href = `${authServerUrl}/google/${ workspaceInviteHash ? '?inviteHash=' + workspaceInviteHash : '' diff --git a/front/src/modules/metadata/components/ApolloMetadataClientProvider.tsx b/front/src/modules/metadata/components/ApolloMetadataClientProvider.tsx index 67371da99..b3eb9db9b 100644 --- a/front/src/modules/metadata/components/ApolloMetadataClientProvider.tsx +++ b/front/src/modules/metadata/components/ApolloMetadataClientProvider.tsx @@ -4,6 +4,7 @@ import { ApolloClient, InMemoryCache } from '@apollo/client'; import { useRecoilState } from 'recoil'; import { tokenPairState } from '@/auth/states/tokenPairState'; +import { REACT_APP_SERVER_BASE_URL } from '~/config'; import { ApolloMetadataClientContext } from '../context/ApolloClientMetadataContext'; @@ -17,7 +18,7 @@ export const ApolloMetadataClientProvider = ({ const apolloMetadataClient = useMemo(() => { if (tokenPair?.accessToken.token) { return new ApolloClient({ - uri: `${process.env.REACT_APP_SERVER_BASE_URL}/metadata`, + uri: `${REACT_APP_SERVER_BASE_URL}/metadata`, cache: new InMemoryCache(), headers: { Authorization: `Bearer ${tokenPair.accessToken.token}`, diff --git a/front/src/modules/users/utils/getProfilePictureAbsoluteURI.ts b/front/src/modules/users/utils/getProfilePictureAbsoluteURI.ts index 77350d114..ccd502d18 100644 --- a/front/src/modules/users/utils/getProfilePictureAbsoluteURI.ts +++ b/front/src/modules/users/utils/getProfilePictureAbsoluteURI.ts @@ -1,3 +1,5 @@ +import { REACT_APP_SERVER_BASE_URL } from '~/config'; + export const getImageAbsoluteURIOrBase64 = (imageUrl?: string | null) => { if (!imageUrl) { return null; @@ -12,8 +14,7 @@ export const getImageAbsoluteURIOrBase64 = (imageUrl?: string | null) => { } const serverFilesUrl = - process.env.REACT_APP_SERVER_FILES_URL ?? - process.env.REACT_APP_SERVER_BASE_URL + '/files'; + REACT_APP_SERVER_BASE_URL ?? REACT_APP_SERVER_BASE_URL + '/files'; return `${serverFilesUrl}/${imageUrl}`; }; diff --git a/infra/prod/front/Dockerfile b/infra/prod/front/Dockerfile index 88c156bc6..2b6c3e798 100644 --- a/infra/prod/front/Dockerfile +++ b/infra/prod/front/Dockerfile @@ -1,6 +1,5 @@ FROM node:18.16.0-alpine as build - ARG REACT_APP_SERVER_BASE_URL ARG REACT_APP_SERVER_AUTH_URL ARG REACT_APP_SERVER_FILES_URL @@ -18,10 +17,13 @@ COPY ./infra/prod/front/serve.json ./build FROM node:18.16.0-alpine as front WORKDIR /app/front + COPY --from=build /app/front/build ./build +COPY ./front/scripts/inject-runtime-env.sh /app/front/scripts/inject-runtime-env.sh RUN yarn global add serve + LABEL org.opencontainers.image.source=https://github.com/twentyhq/twenty LABEL org.opencontainers.image.description="This image provides a consistent and reproducible environment for the frontend." -CMD ["serve", "build"] +CMD ["/bin/sh", "-c", "/app/front/scripts/inject-runtime-env.sh && serve build"] diff --git a/infra/prod/server/Dockerfile b/infra/prod/server/Dockerfile index 2e795682a..f7fba86aa 100644 --- a/infra/prod/server/Dockerfile +++ b/infra/prod/server/Dockerfile @@ -1,4 +1,4 @@ -FROM node:18.16.0-alpine as server +FROM node:18.16.0-alpine as build WORKDIR /app/server COPY ./server/package.json ./ @@ -11,6 +11,12 @@ RUN npx prisma generate RUN yarn build +FROM node:18.16.0-alpine as server + +COPY --from=build /app/server/dist ./dist + +WORKDIR /app/server + LABEL org.opencontainers.image.source=https://github.com/twentyhq/twenty LABEL org.opencontainers.image.description="This image provides a consistent and reproducible environment for the backend, ensuring it deploys faster and runs the same way regardless of the deployment environment." diff --git a/render.yaml b/render.yaml index 4411c52df..e028d983b 100644 --- a/render.yaml +++ b/render.yaml @@ -14,7 +14,7 @@ services: name: server env: docker dockerfilePath: ./infra/prod/server/Dockerfile - dockerCommand: "sh -c yarn prisma:migrate && yarn database:setup && node dist/src/main" + dockerCommand: "sh -c ./scripts/compute-database-url.sh && yarn prisma:migrate && yarn database:setup && node dist/src/main" autoDeploy: false envVars: - key: FRONT_BASE_URL @@ -38,8 +38,6 @@ services: name: twenty_postgres type: pserv property: port - - key: PG_DATABASE_URL - value: postgres://twenty:twenty@twenty-postgres:5432/default?connection_limit=1 disk: name: twenty-disk mountPath: /.local-storage diff --git a/server/scripts/compute-database-url.sh b/server/scripts/compute-database-url.sh new file mode 100755 index 000000000..dad7d8521 --- /dev/null +++ b/server/scripts/compute-database-url.sh @@ -0,0 +1,2 @@ +#!/bin/sh +export PG_DATABASE_URL=postgres://twenty:twenty@$PG_DATABASE_HOST:5432/default \ No newline at end of file diff --git a/server/scripts/run-integration.sh b/server/scripts/run-integration.sh index fc30b598b..3673ab813 100755 --- a/server/scripts/run-integration.sh +++ b/server/scripts/run-integration.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# src/run-integration.sh +# scripts/run-integration.sh DIR="$(cd "$(dirname "$0")" && pwd)" source $DIR/set-env-test.sh diff --git a/server/scripts/set-env-test.sh b/server/scripts/set-env-test.sh index 8eb4eee9d..98e7ac7c9 100755 --- a/server/scripts/set-env-test.sh +++ b/server/scripts/set-env-test.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# scripts/setenv.sh +# scripts/set-env-test.sh # Get script's directory SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"