Skip to content

Commit

Permalink
impr: add selfhosting using docker only (fehmer) (#5170)
Browse files Browse the repository at this point in the history
* 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 <jack@monkeytype.com>
  • Loading branch information
fehmer and Miodec authored Mar 12, 2024
1 parent b31dba6 commit 9432602
Show file tree
Hide file tree
Showing 18 changed files with 582 additions and 5 deletions.
22 changes: 21 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@

node_modules
frontend/node_modules
backend/node_modules
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
3 changes: 2 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
backend/build
backend/build
docker
76 changes: 76 additions & 0 deletions .github/workflows/publish-docker-images.yml
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 2 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"esbenp.prettier-vscode",
"Orta.vscode-jest",
"vitest.explorer",
"ryanluker.vscode-coverage-gutters"
"ryanluker.vscode-coverage-gutters",
"huntertran.auto-markdown-toc"
]
}
192 changes: 192 additions & 0 deletions SELF_HOSTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# Monkeytype Self Hosting

<!-- TOC ignore:true -->

## Table of contents

<!-- TOC -->

- [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)

<!-- /TOC -->


## 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.
2 changes: 1 addition & 1 deletion backend/docker/compose.db-only.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ services:

volumes:
mongo-data:
redis-data:
redis-data:
16 changes: 16 additions & 0 deletions docker/backend-configuration.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"configuration": {
"results": {
"savingEnabled": true
},
"users": {
"signUp": false,
"profiles": {
"enabled": false
}
},
"dailyLeaderboards": {
"enabled": false
}
}
}
44 changes: 44 additions & 0 deletions docker/backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -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" ]
Loading

0 comments on commit 9432602

Please sign in to comment.