From 9432602727e5d938c76627541e59f373fcfafacf Mon Sep 17 00:00:00 2001 From: Christian Fehmer Date: Tue, 12 Mar 2024 12:02:13 +0100 Subject: [PATCH] impr: add selfhosting using docker only (fehmer) (#5170) * impr: add selfhosting using docker only * add recaptcha config and docs * add documentation on the backend-configuration.json file, remove ---redacted--- from example config --------- Co-authored-by: Jack --- .dockerignore | 22 ++- .eslintignore | 3 +- .github/workflows/publish-docker-images.yml | 76 ++++++++ .gitignore | 1 + .vscode/extensions.json | 3 +- SELF_HOSTING.md | 192 ++++++++++++++++++++ backend/docker/compose.db-only.yml | 2 +- docker/backend-configuration.json | 16 ++ docker/backend/Dockerfile | 44 +++++ docker/backend/applyConfig.sh | 22 +++ docker/backend/entry-point.sh | 3 + docker/docker-compose.yml | 95 ++++++++++ docker/example.env | 32 ++++ docker/frontend/Dockerfile | 30 +++ docker/frontend/firebase-config-live.ts | 13 ++ docker/frontend/updateConfig.sh | 16 ++ docker/serviceAccountKey-example.json | 13 ++ frontend/vite.config.js | 4 +- 18 files changed, 582 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/publish-docker-images.yml create mode 100644 SELF_HOSTING.md create mode 100644 docker/backend-configuration.json create mode 100644 docker/backend/Dockerfile create mode 100755 docker/backend/applyConfig.sh create mode 100644 docker/backend/entry-point.sh create mode 100644 docker/docker-compose.yml create mode 100644 docker/example.env create mode 100644 docker/frontend/Dockerfile create mode 100644 docker/frontend/firebase-config-live.ts create mode 100755 docker/frontend/updateConfig.sh create mode 100644 docker/serviceAccountKey-example.json diff --git a/.dockerignore b/.dockerignore index a15262703..28e2caa51 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,23 @@ + node_modules frontend/node_modules -backend/node_modules \ No newline at end of file +backend/node_modules + +# Firebase +.firebase/ +.firebaserc +serviceAccountKey*.json +frontend/src/ts/constants/firebase-config.ts +frontend/src/ts/constants/firebase-config-live.ts + +#frontend +frontend/.env + +#cloudflare +.cloudflareKey.txt +.cloudflareKey_copy.txt + +#backend +backend/src/credentials/*.json +backend/.env +backend/build diff --git a/.eslintignore b/.eslintignore index ac261a5bf..cc37f2484 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,2 @@ -backend/build \ No newline at end of file +backend/build +docker \ No newline at end of file diff --git a/.github/workflows/publish-docker-images.yml b/.github/workflows/publish-docker-images.yml new file mode 100644 index 000000000..4242133f4 --- /dev/null +++ b/.github/workflows/publish-docker-images.yml @@ -0,0 +1,76 @@ +name: Publish Docker image + +on: + release: + types: [published] + workflow_dispatch: + +jobs: + push_to_registry: + env: + BE_REPO: monkeytype/monkeytype-backend + FE_REPO: monkeytype/monkeytype-frontend + + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Log in to Docker Hub + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + + - name: Backend extract metadata (tags, labels) + id: bemeta + uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 + with: + images: ${{ env.BE_REPO }} + tags: | + type=semver,pattern={{version}} + + - name: Backend build and push Docker image + uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 + with: + context: . + file: ./docker/backend/Dockerfile + push: true + tags: ${{ env.BE_REPO }}:latest,${{ steps.bemeta.outputs.tags }} + labels: ${{ steps.bemeta.outputs.labels }} + + - name: Backend publish description + uses: peter-evans/dockerhub-description@e98e4d1628a5f3be2be7c231e50981aee98723ae + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + repository: ${{ env.BE_REPO }} + short-description: Backend server for monkeytype.com + readme-filepath: ./SELF_HOSTING.md + + - name: Frontend extract metadata (tags, labels) + id: femeta + uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 + with: + images: ${{ env.FE_REPO }} + tags: | + type=semver,pattern={{version}} + + - name: Frontend build and push Docker image + uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 + with: + context: . + file: ./docker/frontend/Dockerfile + push: true + tags: ${{ env.FE_REPO }}:latest,${{ steps.femeta.outputs.tags }} + labels: ${{ steps.femeta.outputs.labels }} + + - name: Frontend publish description + uses: peter-evans/dockerhub-description@e98e4d1628a5f3be2be7c231e50981aee98723ae + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + repository: ${{ env.FE_REPO }} + short-description: Frontend server for monkeytype.com + readme-filepath: ./SELF_HOSTING.md diff --git a/.gitignore b/.gitignore index 382b85366..970eeca9a 100644 --- a/.gitignore +++ b/.gitignore @@ -82,6 +82,7 @@ node_modules_bak/ .firebaserc .firebaserc_copy serviceAccountKey*.json +!docker/serviceAccountKey-example.json frontend/src/ts/constants/firebase-config.ts frontend/src/ts/constants/firebase-config-live.ts diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 6b3390aa7..4245cf47d 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,6 +3,7 @@ "esbenp.prettier-vscode", "Orta.vscode-jest", "vitest.explorer", - "ryanluker.vscode-coverage-gutters" + "ryanluker.vscode-coverage-gutters", + "huntertran.auto-markdown-toc" ] } diff --git a/SELF_HOSTING.md b/SELF_HOSTING.md new file mode 100644 index 000000000..2cb05a035 --- /dev/null +++ b/SELF_HOSTING.md @@ -0,0 +1,192 @@ +# Monkeytype Self Hosting + + + +## Table of contents + + + +- [Monkeytype Self Hosting](#monkeytype-self-hosting) + - [Table of contents](#table-of-contents) + - [Prerequisites](#prerequisites) + - [Quickstart](#quickstart) + - [Account System](#account-system) + - [Setup Firebase](#setup-firebase) + - [Update backend configuration](#update-backend-configuration) + - [Setup Recaptcha](#setup-recaptcha) + - [Enable daily leaderboards](#enable-daily-leaderboards) + - [Configuration files](#configuration-files) + - [env file](#env-file) + - [serviceAccountKey.json](#serviceaccountkeyjson) + - [backend-configuration.json](#backend-configurationjson) + + + + +## Prerequisites +- you need `docker` and `docker-compose-plugin` installed. Follow the [docker documentation](https://docs.docker.com/compose/install/) on how to do this. + +## Quickstart + +- create a new directory, e.g. `monkeytype` and open it. +- download the [docker-compose.yml](https://github.com/monkeytypegame/monkeytype/tree/master/docker/docker-compose.yml) +- create an `.env` file, you can copy the content from the [example.env](https://github.com/monkeytypegame/monkeytype/tree/master/docker/example.env). +- create an `serviceAccountKey.json` file. you can copy the content from the [serviceAccountKey-example.json](https://github.com/monkeytypegame/monkeytype/tree/master/docker/serviceAccountKey-example.json). +- download the [backend-configuration.json](https://github.com/monkeytypegame/monkeytype/tree/master/docker/backend-configuration.json) +- run `docker compose up -d` +- After the command exits successfully you can access [http://localhost:8080](http://localhost:8080) + + +## Account System + +User signup/login is disabled by default. To allow users to signup you'll need to setup a Firebase project. + +### Setup Firebase + +- create a [Firebase](https://firebase.google.com/) account +- create a [new Firebase project](https://console.firebase.google.com/u/0/). + - name "monkeytype" + - uncheck "enable google analytics" +- enable authentication + - open the [firebase console](https://console.firebase.google.com/) and open your project + - go to `Authentication > Sign-in method` + - enable `Email/Password` and save +- generate service account + - open the project settings by clicking the `⚙` icon on the sidebar and `Project settings` + - go to `Service accounts` + - click `Generate new private key`. This will download a `.json` file. + - store the `.json` file as `serviceAccountKey.json` + +- update the `.env` file + - open the [firebase console](https://console.firebase.google.com/) and open your project + - open the project settings by clicking the `⚙` icon on the sidebar and `Project settings` + - If there is no app in your project create a new web-app `` + - nickname `monkeytype` + - uncheck `set up firebase hosting` + - click `Register app` + - select your app and select `Config` for `SDK setup and configuration` + - it will display sth like this: + ``` + const firebaseConfig = { + apiKey: "AAAAAAAA", + authDomain: "monkeytype-00000.firebaseapp.com", + projectId: "monkeytype-00000", + storageBucket: "monkeytype-00000.appspot.com", + messagingSenderId: "90000000000", + appId: "1:90000000000:web:000000000000" + }; + ``` + - update the `.env` file with the values above: + ``` + FIREBASE_APIKEY="AAAAAAAA" + FIREBASE_AUTHDOMAIN="monkeytype-00000.firebaseapp.com" + FIREBASE_PROJECTID="monkeytype-00000" + FIREBASE_STORAGEBUCKET="monkeytype-00000.appspot.com" + FIREBASE_MESSAGINGSENDERID="90000000000" + FIREBASE_APPID="1:90000000000:web:000000000000" + ``` + +### Update backend configuration + +- update the `backend-configuration.json` file and add/modify + ```json + { + "users": { + "signUp": true, + "profiles": { + "enabled": true + } + } + } + ``` + +### Setup Recaptcha + +- [create](https://www.google.com/recaptcha/admin/create) a new recaptcha token + - label: monkeytype + - type: v2 + - domain: the domain of the frontend +- update the `.env` file with the site key from the previous step + ``` + RECAPTCHA_SITE_KEY="your site key" + RECAPTCHA_SECRET="your secret key" + ``` + + + +## Enable daily leaderboards + +To enable daily leaderboards update the `backend-configuration.json` file and add/modify +```json +{ + "dailyLeaderboards": { + "enabled": true, + "maxResults": 250, + "leaderboardExpirationTimeInDays": 1, + "validModeRules": [ + { + "language": "english", + "mode": "time", + "mode2": "15" + }, + { + "language": "english", + "mode": "time", + "mode2": "60" + } + ] + } +} +``` + +- language is one of the supported language +- mode can be `time` or `words` +- mode2 can be `15`,`30`,`60` or `120` if you picked `mode=time` or `10`,`25`,`50` or `100` if you picked `mode=words`. + +## Configuration files + +### env file + +All settings are described in the [example.env](https://github.com/monkeytypegame/monkeytype/tree/master/docker/example.env) file. + +### serviceAccountKey.json + +Contains your firebase config, only needed if you want to allow users to signup. + +### backend-configuration.json + +Configuration of the backend. + +If you don't want to update this file manually you can + +- open the backend url in your browser, e.g. `http://localhost:5005/configure/` +- adjust the settings and click `Save Changes` +- open the configuration in your browser, e.g. `http://localhost:5005/configuration` +- copy everything from `data` into the `backend-configuration.json` file. + +Example output from `http://localhost:5005/configuration`: +```json +{ + "message": "Configuration retrieved", + "data": + { + "maintenance": false, + "results": {}, + .... + } +} +``` + +Example content from `backend-configuration.json`: +``` +{ + "maintenance": false, + "results": {}, + .... +} +``` + +If you have the `curl` and `jq` installed you can also run `curl -wO- http://localhost:5005/configuration | jq ".data" > backend-configuration.json` to update the configuration file. + + +_Note:_ The configuration is applied on container startup only. You have to restart the container for your changes to become active. \ No newline at end of file diff --git a/backend/docker/compose.db-only.yml b/backend/docker/compose.db-only.yml index 13321af5b..350d48f3c 100644 --- a/backend/docker/compose.db-only.yml +++ b/backend/docker/compose.db-only.yml @@ -21,4 +21,4 @@ services: volumes: mongo-data: - redis-data: + redis-data: \ No newline at end of file diff --git a/docker/backend-configuration.json b/docker/backend-configuration.json new file mode 100644 index 000000000..7c7fa8f09 --- /dev/null +++ b/docker/backend-configuration.json @@ -0,0 +1,16 @@ +{ + "configuration": { + "results": { + "savingEnabled": true + }, + "users": { + "signUp": false, + "profiles": { + "enabled": false + } + }, + "dailyLeaderboards": { + "enabled": false + } + } +} diff --git a/docker/backend/Dockerfile b/docker/backend/Dockerfile new file mode 100644 index 000000000..907ec08cd --- /dev/null +++ b/docker/backend/Dockerfile @@ -0,0 +1,44 @@ +FROM node:hydrogen-alpine as builder +WORKDIR /app + +#copy +COPY .eslintignore .eslintignore +COPY .eslintrc.json .eslintrc.json +COPY package.json package.json +COPY package-lock.json package-lock.json +COPY shared-types shared-types +COPY backend backend + +#build +RUN npm ci +RUN cd backend && npm ci +RUN cd backend && npm run build + +# target +FROM node:hydrogen-alpine +RUN apk add wget +WORKDIR / +COPY backend/redis-scripts /redis-scripts + +WORKDIR /app +COPY backend/package.json package.json +COPY backend/package-lock.json package-lock.json +COPY docker/backend/entry-point.sh entry-point.sh +COPY docker/backend/applyConfig.sh applyConfig.sh +COPY --from=builder /app/backend/build . + +#install deps (no dev-dependencies) +RUN npm ci --omit=dev + + +## logs +RUN mkdir logs + +#run in env mode (no anticheat) +ENV MODE=dev + + +EXPOSE 5005 +USER node + +CMD [ "/bin/sh", "./entry-point.sh" ] \ No newline at end of file diff --git a/docker/backend/applyConfig.sh b/docker/backend/applyConfig.sh new file mode 100755 index 000000000..f3360b396 --- /dev/null +++ b/docker/backend/applyConfig.sh @@ -0,0 +1,22 @@ +#!/bin/sh +if [ -f backend-configuration.json ]; then + echo "waiting for backend..." + + timeout 30 sh -c 'until nc -z $0 $1; do sleep 1; done' localhost 5005 + + if [ $? -ne 0 ]; then + echo "failed to apply config" + exit 1 + fi + + echo "apply server config" + + wget -qO- --method=PATCH \ + --body-data="`cat backend-configuration.json`" \ + --header='Content-Type:application/json' \ + http://localhost:5005/configuration + + echo "server config applied" +else + echo "skip backend configuration" +fi diff --git a/docker/backend/entry-point.sh b/docker/backend/entry-point.sh new file mode 100644 index 000000000..9ae39c6cb --- /dev/null +++ b/docker/backend/entry-point.sh @@ -0,0 +1,3 @@ +#!bin/sh +./applyConfig.sh & +node server.js \ No newline at end of file diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 000000000..cd49094cd --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,95 @@ +version: "3.8" + +services: + monkeytype-frontend: + container_name: monkeytype-frontend + image: monkeytype/monkeytype-frontend:latest + ports: + - "${HTTP_PORT:-8080}:80" + restart: on-failure + depends_on: + monkeytype-backend: + condition: service_healthy + env_file: + - path: ./.env + required: true + environment: + - FIREBASE_APIKEY=${FIREBASE_APIKEY:-""} + - FIREBASE_AUTHDOMAIN=${FIREBASE_AUTHDOMAIN:-""} + - FIREBASE_PROJECTID=${FIREBASE_PROJECTID:-""} + - FIREBASE_STORAGEBUCKET=${FIREBASE_STORAGEBUCKET:-""} + - FIREBASE_MESSAGINGSENDERID=${FIREBASE_MESSAGINGSENDERID:-""} + - FIREBASE_APPID=${FIREBASE_APPID:-""} + - MONKEYTYPE_BACKENDURL=${MONKEYTYPE_BACKENDURL} + - RECAPTCHA_SITE_KEY=${RECAPTCHA_SITE_KEY:-""} + + monkeytype-backend: + container_name: monkeytype-backend + image: monkeytype/monkeytype-backend:latest + ports: + - "${BACKEND_PORT:-5005}:5005" + restart: on-failure + environment: + - DB_NAME=monkeytype + - DB_URI=mongodb://monkeytype-mongodb:27017 + - REDIS_URI=redis://monkeytype-redis:6379 + - FRONTEND_URL=${MONKEYTYPE_FRONTENDURL} + - RECAPTCHA_SECRET=${RECAPTCHA_SECRET:-""} + volumes: + - type: bind + source: ./serviceAccountKey.json + target: /src/credentials/serviceAccountKey.json + read_only: true + - type: bind + source: ./backend-configuration.json + target: /app/backend-configuration.json + read_only: true + depends_on: + monkeytype-redis: + condition: service_healthy + monkeytype-mongodb: + condition: service_healthy + healthcheck: + test: ["CMD", "nc", "-z", "-v", "localhost", "5005"] + interval: 5s + timeout: 10s + retries: 15 + start_period: 5s + + monkeytype-redis: + container_name: monkeytype-redis + image: redis:6.2.6 + #uncomment if you want to expose the redis server + #ports: + # - "${REDIS_PORT:-6379}:6379" + restart: on-failure + volumes: + - redis-data:/data + healthcheck: + test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ] + interval: 5s + timeout: 10s + retries: 15 + start_period: 10s + + monkeytype-mongodb: + container_name: monkeytype-mongodb + image: mongo:5.0.8 + restart: on-failure + volumes: + - mongo-data:/data/db + #uncomment if you want to expose the mongodb server + #ports: + # - "${MONGO_PORT:-27017}:27017" + healthcheck: + test: echo 'db.stats().ok' | mongo localhost:27017/test --quiet + interval: 5s + timeout: 10s + retries: 15 + start_period: 10s + +volumes: + mongo-data: + name: monkeytype_mongo_data + redis-data: + name: monkeytype_redis_data diff --git a/docker/example.env b/docker/example.env new file mode 100644 index 000000000..f6dfa14d1 --- /dev/null +++ b/docker/example.env @@ -0,0 +1,32 @@ +#url of the frontend, this must be accessible by your clients browser +MONKEYTYPE_FRONTENDURL="http://myserver:8080" + +#url of the backend server, this must be accessible by your clients browser +MONKEYTYPE_BACKENDURL="http://myserver:5005" + +# below config is only needed, if you need user accounts +# firebase config +FIREBASE_APIKEY="" +FIREBASE_AUTHDOMAIN="" +FIREBASE_PROJECTID="" +FIREBASE_STORAGEBUCKET="" +FIREBASE_MESSAGINGSENDERID="" +FIREBASE_APPID="" + +# google recapture +RECAPTCHA_SITE_KEY="" +RECAPTCHA_SECRET="" + +# use alternative ports + +# port of the frontend http server +# HTTP_PORT=8080 + +# port of the backend api server +# BACKEND_PORT=5005 + +# port of the redis server, not exposed by default +# REDIS_PORT=6379 + +# port of the mongodb server, not exposed by default +# MONGO_PORT:27017 \ No newline at end of file diff --git a/docker/frontend/Dockerfile b/docker/frontend/Dockerfile new file mode 100644 index 000000000..efa63e3ee --- /dev/null +++ b/docker/frontend/Dockerfile @@ -0,0 +1,30 @@ +FROM node:hydrogen-alpine as builder +WORKDIR /app + +#ENV +ENV BACKEND_URL="###MONKEYTYPE_BACKENDURL###" +ENV RECAPTCHA_SITE_KEY="###RECAPTCHA_SITE_KEY###" + +#COPY +COPY .eslintrc.json .eslintrc.json +COPY package.json package.json +COPY package-lock.json package-lock.json +COPY shared-types shared-types +COPY frontend frontend +COPY docker/frontend/firebase-config-live.ts frontend/src/ts/constants/firebase-config.ts +COPY docker/frontend/firebase-config-live.ts frontend/src/ts/constants/firebase-config-live.ts + +#BUILD +RUN npm ci +RUN cd frontend && npm ci +RUN cd frontend && npx vite build + + +# COPY to target +FROM nginx:mainline-alpine +COPY --from=builder /app/frontend/dist /usr/share/nginx/html +COPY docker/frontend/updateConfig.sh /docker-entrypoint.d/updateConfig.sh +RUN chmod +x /docker-entrypoint.d/updateConfig.sh + +# entry +#CMD ["./entryPoint.sh"] \ No newline at end of file diff --git a/docker/frontend/firebase-config-live.ts b/docker/frontend/firebase-config-live.ts new file mode 100644 index 000000000..0509f24da --- /dev/null +++ b/docker/frontend/firebase-config-live.ts @@ -0,0 +1,13 @@ +// To find your config, go to https://console.firebase.google.com/ and select your project +// Go to (top left) Settings > Project Settings > General +// scroll down to Your apps > Web Apps (if it doesnt exist, create one) > SDK setup and configuration > select npm +// your config should be visible there + +export const firebaseConfig = { + apiKey: "###FIREBASE_APIKEY###", + authDomain: "###FIREBASE_AUTHDOMAIN###", + projectId: "###FIREBASE_PROJECTID###", + storageBucket: "###FIREBASE_STORAGEBUCKET###", + messagingSenderId: "###FIREBASE_MESSAGINGSENDERID###", + appId: "###FIREBASE_APPID###", +}; diff --git a/docker/frontend/updateConfig.sh b/docker/frontend/updateConfig.sh new file mode 100755 index 000000000..726784be0 --- /dev/null +++ b/docker/frontend/updateConfig.sh @@ -0,0 +1,16 @@ +#!/bin/sh +cd /usr/share/nginx/html +echo "repace firebase config" +sed -i "s/###FIREBASE_APIKEY###/${FIREBASE_APIKEY}/g" js/firebase.*.js +sed -i "s/###FIREBASE_AUTHDOMAIN###/${FIREBASE_AUTHDOMAIN}/g" js/firebase.*.js +sed -i "s/###FIREBASE_PROJECTID###/${FIREBASE_PROJECTID}/g" js/firebase.*.js +sed -i "s/###FIREBASE_STORAGEBUCKET###/${FIREBASE_STORAGEBUCKET}/g" js/firebase.*.js +sed -i "s/###FIREBASE_MESSAGINGSENDERID###/${FIREBASE_MESSAGINGSENDERID}/g" js/firebase.*.js +sed -i "s/###FIREBASE_APPID###/${FIREBASE_APPID}/g" js/firebase.*.js + + +echo "use backend url ${MONKEYTYPE_BACKENDURL}" +sed -i "s/###MONKEYTYPE_BACKENDURL###/${MONKEYTYPE_BACKENDURL//\//\\/}/g" js/*.js + +echo "use recapture ${RECAPTCHA_SITE_KEY}" +sed -i "s/###RECAPTCHA_SITE_KEY###/${RECAPTCHA_SITE_KEY//\//\\/}/g" js/*.js diff --git a/docker/serviceAccountKey-example.json b/docker/serviceAccountKey-example.json new file mode 100644 index 000000000..7a10fd179 --- /dev/null +++ b/docker/serviceAccountKey-example.json @@ -0,0 +1,13 @@ +{ + "type": "service_account", + "project_id": "", + "private_key_id": "", + "private_key": "-----BEGIN PRIVATE KEY-----\n\n-----END PRIVATE KEY-----\n", + "client_email": "", + "client_id": "", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "", + "universe_domain": "googleapis.com" +} diff --git a/frontend/vite.config.js b/frontend/vite.config.js index 3a67c4893..83be0d426 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -192,7 +192,9 @@ const BUILD_CONFIG = { ]), ], define: { - BACKEND_URL: JSON.stringify("https://api.monkeytype.com"), + BACKEND_URL: JSON.stringify( + process.env.BACKEND_URL || "https://api.monkeytype.com" + ), IS_DEVELOPMENT: JSON.stringify(false), CLIENT_VERSION: JSON.stringify(buildClientVersion()), RECAPTCHA_SITE_KEY: JSON.stringify(process.env.RECAPTCHA_SITE_KEY),