diff --git a/.dockerignore b/.dockerignore index 285200a8b1..f9fc785f92 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,4 @@ -server/node_modules/ -server/.env \ No newline at end of file +.git +.env +node_modules +.nx/cache diff --git a/packages/twenty-docker/Makefile b/packages/twenty-docker/Makefile index 6e3324e548..e4510047ba 100644 --- a/packages/twenty-docker/Makefile +++ b/packages/twenty-docker/Makefile @@ -29,6 +29,12 @@ prod-docs-build: prod-docs-run: @docker run -d -p 3000:3000 --name twenty-docs twenty-docs +prod-build: + @cd ../.. && docker build -f ./packages/twenty-docker/prod/twenty/Dockerfile --tag twenty . && cd - + +prod-run: + @docker run -d -p 3000:3000 --name twenty twenty + prod-front-build: @cd ../.. && docker build -f ./packages/twenty-docker/prod/twenty-front/Dockerfile --tag twenty-front . && cd - diff --git a/packages/twenty-docker/prod/twenty/Dockerfile b/packages/twenty-docker/prod/twenty/Dockerfile new file mode 100644 index 0000000000..526e578c2d --- /dev/null +++ b/packages/twenty-docker/prod/twenty/Dockerfile @@ -0,0 +1,77 @@ +# Base image for common dependencies +FROM node:18.17.1-alpine as common-deps + +WORKDIR /app + +# Copy only the necessary files for dependency resolution +COPY ./package.json ./yarn.lock ./.yarnrc.yml ./tsconfig.base.json ./nx.json /app/ +COPY ./.yarn/releases /app/.yarn/releases + +COPY ./packages/twenty-emails/package.json /app/packages/twenty-emails/ +COPY ./packages/twenty-server/package.json /app/packages/twenty-server/ +COPY ./packages/twenty-server/patches /app/packages/twenty-server/patches +COPY ./packages/twenty-ui/package.json /app/packages/twenty-ui/ +COPY ./packages/twenty-front/package.json /app/packages/twenty-front/ + +# Install all dependencies +RUN yarn && yarn cache clean && npx nx reset + + +# Build the back +FROM common-deps as twenty-server-build + +# Copy sourcecode after installing dependences to accelerate subsequents builds +COPY ./packages/twenty-emails /app/packages/twenty-emails +COPY ./packages/twenty-server /app/packages/twenty-server + +RUN npx nx run twenty-server:build && \ + mv /app/packages/twenty-server/dist /app/packages/twenty-server/build && \ + npx nx run twenty-server:build:packageJson && \ + mv /app/packages/twenty-server/dist/package.json /app/packages/twenty-server/package.json && \ + rm -rf /app/packages/twenty-server/dist && \ + mv /app/packages/twenty-server/build /app/packages/twenty-server/dist + +RUN yarn workspaces focus --production twenty-emails twenty-server + + +# Build the front +FROM common-deps as twenty-front-build + +ARG REACT_APP_SERVER_BASE_URL + +COPY ./packages/twenty-front /app/packages/twenty-front +COPY ./packages/twenty-ui /app/packages/twenty-ui +RUN yarn nx build twenty-front + + +# Final stage: Run the application +FROM node:18.17.1-alpine as twenty + +# Used to run healthcheck in docker +RUN apk add --no-cache curl jq + +COPY ./packages/twenty-docker/prod/twenty/entrypoint.sh /app/entrypoint.sh +RUN chmod +x /app/entrypoint.sh + +WORKDIR /app/packages/twenty-server + +ARG REACT_APP_SERVER_BASE_URL +ENV REACT_APP_SERVER_BASE_URL $REACT_APP_SERVER_BASE_URL + +# Copy built applications from previous stages +COPY --chown=1000 --from=twenty-server-build /app /app +COPY --chown=1000 --from=twenty-server-build /app/packages/twenty-server /app/packages/twenty-server +COPY --chown=1000 --from=twenty-front-build /app/packages/twenty-front/build /app/packages/twenty-server/dist/front + +# Set metadata and labels +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 and frontend, ensuring it deploys faster and runs the same way regardless of the deployment environment." + +RUN mkdir /app/.local-storage +RUN chown -R 1000 /app + +# Use non root user with uid 1000 +USER 1000 + +CMD ["node", "dist/src/main"] +ENTRYPOINT ["/app/entrypoint.sh"] diff --git a/packages/twenty-docker/prod/twenty/entrypoint.sh b/packages/twenty-docker/prod/twenty/entrypoint.sh new file mode 100755 index 0000000000..a6167afcae --- /dev/null +++ b/packages/twenty-docker/prod/twenty/entrypoint.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +# Check if the initialization has already been done and that we enabled automatic migration +if [ "${ENABLE_DB_MIGRATIONS}" = "true" ] && [ ! -f /app/${STORAGE_LOCAL_PATH}/db_initialized ]; then + echo "Running database setup and migrations..." + + # Run setup and migration scripts + npx ts-node ./scripts/setup-db.ts + yarn database:migrate:prod + + # Mark initialization as done + echo "Successfuly migrated DB!" + touch /app/${STORAGE_LOCAL_PATH}/db_initialized +fi + +# Continue with the original Docker command +exec "$@" diff --git a/packages/twenty-docs/docs/start/local-setup/troubleshooting.mdx b/packages/twenty-docs/docs/start/local-setup/troubleshooting.mdx index 72eccba95c..fba34447f5 100644 --- a/packages/twenty-docs/docs/start/local-setup/troubleshooting.mdx +++ b/packages/twenty-docs/docs/start/local-setup/troubleshooting.mdx @@ -41,3 +41,7 @@ This should work out of the box with the eslint extension installed. If this doe "source.fixAll.eslint": "explicit" } ``` + +## Docker container build + +To successfully build Docker images, ensure that your system has a minimum of 2GB of memory available. For users of Docker Desktop, please verify that you've allocated sufficient resources to Docker within the application's settings. diff --git a/packages/twenty-server/package.json b/packages/twenty-server/package.json index 2309ebe441..8063537f16 100644 --- a/packages/twenty-server/package.json +++ b/packages/twenty-server/package.json @@ -44,7 +44,8 @@ "graphql-middleware": "^6.1.35", "jwt-decode": "^4.0.0", "passport": "^0.7.0", - "psl": "^1.9.0" + "psl": "^1.9.0", + "tsconfig-paths": "^4.2.0" }, "devDependencies": { "@nestjs/cli": "10.3.0", diff --git a/packages/twenty-server/src/main.ts b/packages/twenty-server/src/main.ts index 8546fabe1d..25e6f3cff7 100644 --- a/packages/twenty-server/src/main.ts +++ b/packages/twenty-server/src/main.ts @@ -11,6 +11,7 @@ import '@sentry/tracing'; import { AppModule } from './app.module'; +import { generateFrontConfig } from './utils/generate-front-config'; import { settings } from './engine/constants/settings'; import { LoggerService } from './engine/integrations/logger/logger.service'; import { EnvironmentService } from './engine/integrations/environment/environment.service'; @@ -60,6 +61,9 @@ const bootstrap = async () => { }), ); + // Create the env-config.js of the front at runtime + generateFrontConfig(); + await app.listen(app.get(EnvironmentService).get('PORT')); }; diff --git a/packages/twenty-server/src/utils/generate-front-config.ts b/packages/twenty-server/src/utils/generate-front-config.ts new file mode 100644 index 0000000000..c4d15838a2 --- /dev/null +++ b/packages/twenty-server/src/utils/generate-front-config.ts @@ -0,0 +1,31 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +import { ConfigService } from '@nestjs/config'; +import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; + +const environmentService = new EnvironmentService(new ConfigService()); + +export function generateFrontConfig(): void { + const configObject = { + window: { + _env_: { + REACT_APP_SERVER_BASE_URL: environmentService.get('SERVER_URL'), + }, + }, + }; + + const configString = `window._env_ = ${JSON.stringify( + configObject.window._env_, + null, + 2, + )};`; + + const distPath = path.join(__dirname, '../..', 'front'); + + if (!fs.existsSync(distPath)) { + fs.mkdirSync(distPath, { recursive: true }); + } + + fs.writeFileSync(path.join(distPath, 'env-config.js'), configString, 'utf8'); +} diff --git a/yarn.lock b/yarn.lock index 0310f59927..605e0049b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -45763,6 +45763,7 @@ __metadata: passport: "npm:^0.7.0" psl: "npm:^1.9.0" rimraf: "npm:^5.0.5" + tsconfig-paths: "npm:^4.2.0" typescript: "npm:^5.3.3" languageName: unknown linkType: soft