Skip to content

Commit 9432602

Browse files
fehmerMiodec
andauthored
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 <[email protected]>
1 parent b31dba6 commit 9432602

18 files changed

+582
-5
lines changed

.dockerignore

+21-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
12
node_modules
23
frontend/node_modules
3-
backend/node_modules
4+
backend/node_modules
5+
6+
# Firebase
7+
.firebase/
8+
.firebaserc
9+
serviceAccountKey*.json
10+
frontend/src/ts/constants/firebase-config.ts
11+
frontend/src/ts/constants/firebase-config-live.ts
12+
13+
#frontend
14+
frontend/.env
15+
16+
#cloudflare
17+
.cloudflareKey.txt
18+
.cloudflareKey_copy.txt
19+
20+
#backend
21+
backend/src/credentials/*.json
22+
backend/.env
23+
backend/build

.eslintignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
backend/build
1+
backend/build
2+
docker
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
name: Publish Docker image
2+
3+
on:
4+
release:
5+
types: [published]
6+
workflow_dispatch:
7+
8+
jobs:
9+
push_to_registry:
10+
env:
11+
BE_REPO: monkeytype/monkeytype-backend
12+
FE_REPO: monkeytype/monkeytype-frontend
13+
14+
name: Push Docker image to Docker Hub
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Check out the repo
18+
uses: actions/checkout@v4
19+
20+
- name: Log in to Docker Hub
21+
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d
22+
with:
23+
username: ${{ secrets.DOCKER_USERNAME }}
24+
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
25+
26+
- name: Backend extract metadata (tags, labels)
27+
id: bemeta
28+
uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81
29+
with:
30+
images: ${{ env.BE_REPO }}
31+
tags: |
32+
type=semver,pattern={{version}}
33+
34+
- name: Backend build and push Docker image
35+
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671
36+
with:
37+
context: .
38+
file: ./docker/backend/Dockerfile
39+
push: true
40+
tags: ${{ env.BE_REPO }}:latest,${{ steps.bemeta.outputs.tags }}
41+
labels: ${{ steps.bemeta.outputs.labels }}
42+
43+
- name: Backend publish description
44+
uses: peter-evans/dockerhub-description@e98e4d1628a5f3be2be7c231e50981aee98723ae
45+
with:
46+
username: ${{ secrets.DOCKER_USERNAME }}
47+
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
48+
repository: ${{ env.BE_REPO }}
49+
short-description: Backend server for monkeytype.com
50+
readme-filepath: ./SELF_HOSTING.md
51+
52+
- name: Frontend extract metadata (tags, labels)
53+
id: femeta
54+
uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81
55+
with:
56+
images: ${{ env.FE_REPO }}
57+
tags: |
58+
type=semver,pattern={{version}}
59+
60+
- name: Frontend build and push Docker image
61+
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671
62+
with:
63+
context: .
64+
file: ./docker/frontend/Dockerfile
65+
push: true
66+
tags: ${{ env.FE_REPO }}:latest,${{ steps.femeta.outputs.tags }}
67+
labels: ${{ steps.femeta.outputs.labels }}
68+
69+
- name: Frontend publish description
70+
uses: peter-evans/dockerhub-description@e98e4d1628a5f3be2be7c231e50981aee98723ae
71+
with:
72+
username: ${{ secrets.DOCKER_USERNAME }}
73+
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
74+
repository: ${{ env.FE_REPO }}
75+
short-description: Frontend server for monkeytype.com
76+
readme-filepath: ./SELF_HOSTING.md

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ node_modules_bak/
8282
.firebaserc
8383
.firebaserc_copy
8484
serviceAccountKey*.json
85+
!docker/serviceAccountKey-example.json
8586
frontend/src/ts/constants/firebase-config.ts
8687
frontend/src/ts/constants/firebase-config-live.ts
8788

.vscode/extensions.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"esbenp.prettier-vscode",
44
"Orta.vscode-jest",
55
"vitest.explorer",
6-
"ryanluker.vscode-coverage-gutters"
6+
"ryanluker.vscode-coverage-gutters",
7+
"huntertran.auto-markdown-toc"
78
]
89
}

SELF_HOSTING.md

+192
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
# Monkeytype Self Hosting
2+
3+
<!-- TOC ignore:true -->
4+
5+
## Table of contents
6+
7+
<!-- TOC -->
8+
9+
- [Monkeytype Self Hosting](#monkeytype-self-hosting)
10+
- [Table of contents](#table-of-contents)
11+
- [Prerequisites](#prerequisites)
12+
- [Quickstart](#quickstart)
13+
- [Account System](#account-system)
14+
- [Setup Firebase](#setup-firebase)
15+
- [Update backend configuration](#update-backend-configuration)
16+
- [Setup Recaptcha](#setup-recaptcha)
17+
- [Enable daily leaderboards](#enable-daily-leaderboards)
18+
- [Configuration files](#configuration-files)
19+
- [env file](#env-file)
20+
- [serviceAccountKey.json](#serviceaccountkeyjson)
21+
- [backend-configuration.json](#backend-configurationjson)
22+
23+
<!-- /TOC -->
24+
25+
26+
## Prerequisites
27+
- you need `docker` and `docker-compose-plugin` installed. Follow the [docker documentation](https://docs.docker.com/compose/install/) on how to do this.
28+
29+
## Quickstart
30+
31+
- create a new directory, e.g. `monkeytype` and open it.
32+
- download the [docker-compose.yml](https://github.com/monkeytypegame/monkeytype/tree/master/docker/docker-compose.yml)
33+
- create an `.env` file, you can copy the content from the [example.env](https://github.com/monkeytypegame/monkeytype/tree/master/docker/example.env).
34+
- 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).
35+
- download the [backend-configuration.json](https://github.com/monkeytypegame/monkeytype/tree/master/docker/backend-configuration.json)
36+
- run `docker compose up -d`
37+
- After the command exits successfully you can access [http://localhost:8080](http://localhost:8080)
38+
39+
40+
## Account System
41+
42+
User signup/login is disabled by default. To allow users to signup you'll need to setup a Firebase project.
43+
44+
### Setup Firebase
45+
46+
- create a [Firebase](https://firebase.google.com/) account
47+
- create a [new Firebase project](https://console.firebase.google.com/u/0/).
48+
- name "monkeytype"
49+
- uncheck "enable google analytics"
50+
- enable authentication
51+
- open the [firebase console](https://console.firebase.google.com/) and open your project
52+
- go to `Authentication > Sign-in method`
53+
- enable `Email/Password` and save
54+
- generate service account
55+
- open the project settings by clicking the `` icon on the sidebar and `Project settings`
56+
- go to `Service accounts`
57+
- click `Generate new private key`. This will download a `.json` file.
58+
- store the `.json` file as `serviceAccountKey.json`
59+
60+
- update the `.env` file
61+
- open the [firebase console](https://console.firebase.google.com/) and open your project
62+
- open the project settings by clicking the `` icon on the sidebar and `Project settings`
63+
- If there is no app in your project create a new web-app `</>`
64+
- nickname `monkeytype`
65+
- uncheck `set up firebase hosting`
66+
- click `Register app`
67+
- select your app and select `Config` for `SDK setup and configuration`
68+
- it will display sth like this:
69+
```
70+
const firebaseConfig = {
71+
apiKey: "AAAAAAAA",
72+
authDomain: "monkeytype-00000.firebaseapp.com",
73+
projectId: "monkeytype-00000",
74+
storageBucket: "monkeytype-00000.appspot.com",
75+
messagingSenderId: "90000000000",
76+
appId: "1:90000000000:web:000000000000"
77+
};
78+
```
79+
- update the `.env` file with the values above:
80+
```
81+
FIREBASE_APIKEY="AAAAAAAA"
82+
FIREBASE_AUTHDOMAIN="monkeytype-00000.firebaseapp.com"
83+
FIREBASE_PROJECTID="monkeytype-00000"
84+
FIREBASE_STORAGEBUCKET="monkeytype-00000.appspot.com"
85+
FIREBASE_MESSAGINGSENDERID="90000000000"
86+
FIREBASE_APPID="1:90000000000:web:000000000000"
87+
```
88+
89+
### Update backend configuration
90+
91+
- update the `backend-configuration.json` file and add/modify
92+
```json
93+
{
94+
"users": {
95+
"signUp": true,
96+
"profiles": {
97+
"enabled": true
98+
}
99+
}
100+
}
101+
```
102+
103+
### Setup Recaptcha
104+
105+
- [create](https://www.google.com/recaptcha/admin/create) a new recaptcha token
106+
- label: monkeytype
107+
- type: v2
108+
- domain: the domain of the frontend
109+
- update the `.env` file with the site key from the previous step
110+
```
111+
RECAPTCHA_SITE_KEY="your site key"
112+
RECAPTCHA_SECRET="your secret key"
113+
```
114+
115+
116+
117+
## Enable daily leaderboards
118+
119+
To enable daily leaderboards update the `backend-configuration.json` file and add/modify
120+
```json
121+
{
122+
"dailyLeaderboards": {
123+
"enabled": true,
124+
"maxResults": 250,
125+
"leaderboardExpirationTimeInDays": 1,
126+
"validModeRules": [
127+
{
128+
"language": "english",
129+
"mode": "time",
130+
"mode2": "15"
131+
},
132+
{
133+
"language": "english",
134+
"mode": "time",
135+
"mode2": "60"
136+
}
137+
]
138+
}
139+
}
140+
```
141+
142+
- language is one of the supported language
143+
- mode can be `time` or `words`
144+
- mode2 can be `15`,`30`,`60` or `120` if you picked `mode=time` or `10`,`25`,`50` or `100` if you picked `mode=words`.
145+
146+
## Configuration files
147+
148+
### env file
149+
150+
All settings are described in the [example.env](https://github.com/monkeytypegame/monkeytype/tree/master/docker/example.env) file.
151+
152+
### serviceAccountKey.json
153+
154+
Contains your firebase config, only needed if you want to allow users to signup.
155+
156+
### backend-configuration.json
157+
158+
Configuration of the backend.
159+
160+
If you don't want to update this file manually you can
161+
162+
- open the backend url in your browser, e.g. `http://localhost:5005/configure/`
163+
- adjust the settings and click `Save Changes`
164+
- open the configuration in your browser, e.g. `http://localhost:5005/configuration`
165+
- copy everything from `data` into the `backend-configuration.json` file.
166+
167+
Example output from `http://localhost:5005/configuration`:
168+
```json
169+
{
170+
"message": "Configuration retrieved",
171+
"data":
172+
{
173+
"maintenance": false,
174+
"results": {},
175+
....
176+
}
177+
}
178+
```
179+
180+
Example content from `backend-configuration.json`:
181+
```
182+
{
183+
"maintenance": false,
184+
"results": {},
185+
....
186+
}
187+
```
188+
189+
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.
190+
191+
192+
_Note:_ The configuration is applied on container startup only. You have to restart the container for your changes to become active.

backend/docker/compose.db-only.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@ services:
2121

2222
volumes:
2323
mongo-data:
24-
redis-data:
24+
redis-data:

docker/backend-configuration.json

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"configuration": {
3+
"results": {
4+
"savingEnabled": true
5+
},
6+
"users": {
7+
"signUp": false,
8+
"profiles": {
9+
"enabled": false
10+
}
11+
},
12+
"dailyLeaderboards": {
13+
"enabled": false
14+
}
15+
}
16+
}

docker/backend/Dockerfile

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
FROM node:hydrogen-alpine as builder
2+
WORKDIR /app
3+
4+
#copy
5+
COPY .eslintignore .eslintignore
6+
COPY .eslintrc.json .eslintrc.json
7+
COPY package.json package.json
8+
COPY package-lock.json package-lock.json
9+
COPY shared-types shared-types
10+
COPY backend backend
11+
12+
#build
13+
RUN npm ci
14+
RUN cd backend && npm ci
15+
RUN cd backend && npm run build
16+
17+
# target
18+
FROM node:hydrogen-alpine
19+
RUN apk add wget
20+
WORKDIR /
21+
COPY backend/redis-scripts /redis-scripts
22+
23+
WORKDIR /app
24+
COPY backend/package.json package.json
25+
COPY backend/package-lock.json package-lock.json
26+
COPY docker/backend/entry-point.sh entry-point.sh
27+
COPY docker/backend/applyConfig.sh applyConfig.sh
28+
COPY --from=builder /app/backend/build .
29+
30+
#install deps (no dev-dependencies)
31+
RUN npm ci --omit=dev
32+
33+
34+
## logs
35+
RUN mkdir logs
36+
37+
#run in env mode (no anticheat)
38+
ENV MODE=dev
39+
40+
41+
EXPOSE 5005
42+
USER node
43+
44+
CMD [ "/bin/sh", "./entry-point.sh" ]

0 commit comments

Comments
 (0)