diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..f579acb1 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +**/node_modules/** +**/Dockerfile diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index baa3a764..218d9ad1 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -159,5 +159,5 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - msg: Environment deployed at https://${{ env.GITHUB_REF_SLUG }}.workadventure.test.thecodingmachine.com + msg: Environment deployed at https://play.${{ env.GITHUB_REF_SLUG }}.workadventure.test.thecodingmachine.com check_for_duplicate_msg: true diff --git a/README.md b/README.md index faafed98..5945ac48 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ In Work Adventure, you can move around your office and talk to your colleagues ( triggered when you move next to a colleague). -## Getting started +## Setting up a development environment Install Docker. @@ -101,5 +101,7 @@ Vagrant destroy * `Vagrant halt`: stop your VM Vagrant. * `Vagrant destroy`: delete your VM Vagrant. -## Features developed -You have more details of features developed in back [README.md](./back/README.md). +## Setting up a production environment + +The way you set up your production environment will highly depend on your servers. +We provide a production ready `docker-compose` file that you can use as a good starting point in the [contrib/docker](https://github.com/thecodingmachine/workadventure/tree/master/contrib/docker) directory. diff --git a/back/Dockerfile b/back/Dockerfile index 5ec83a8f..e95145cd 100644 --- a/back/Dockerfile +++ b/back/Dockerfile @@ -1,16 +1,26 @@ -FROM thecodingmachine/workadventure-back-base:latest as builder -WORKDIR /var/www/messages -COPY --chown=docker:docker messages . +# protobuf build +FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76 as builder +WORKDIR /usr/src +COPY messages . RUN yarn install && yarn proto -FROM thecodingmachine/nodejs:12 - -COPY --chown=docker:docker back . -COPY --from=builder --chown=docker:docker /var/www/messages/generated /usr/src/app/src/Messages/generated +# typescript build +FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76 as builder2 +WORKDIR /usr/src +COPY back/yarn.lock back/package.json ./ RUN yarn install - +COPY back . +COPY --from=builder /usr/src/generated src/Messages/generated ENV NODE_ENV=production RUN yarn run tsc -CMD ["yarn", "run", "runprod"] +# final production image +FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76 +WORKDIR /usr/src +COPY back/yarn.lock back/package.json ./ +COPY --from=builder2 /usr/src/dist /usr/src/dist +ENV NODE_ENV=production +RUN yarn install --production +USER node +CMD ["yarn", "run", "runprod"] diff --git a/back/README.md b/back/README.md deleted file mode 100644 index 8a78f403..00000000 --- a/back/README.md +++ /dev/null @@ -1,61 +0,0 @@ -# Back Features - -## Login -To start your game, you must authenticate on the server back. -When you are authenticated, the back server return token and room starting. -``` -POST => /login -Params : - email: email of user. -``` - -## Join a room -When a user is connected, the user can join a room. -So you must send emit `join-room` with information user: -``` -Socket.io => 'join-room' - - userId: user id of gamer - roomId: room id when user enter in game - position: { - x: position x on map - y: position y on map - } -``` -All data users are stocked on socket client. - -## Send position user -When user move on the map, you can share new position on back with event `user-position`. -The information sent: -``` -Socket.io => 'user-position' - - userId: user id of gamer - roomId: room id when user enter in game - position: { - x: position x on map - y: position y on map - } -``` -All data users are updated on socket client. - -## Receive positions of all users -The application sends position of all users in each room in every few 10 milliseconds. -The data will pushed on event `user-position`: -``` -Socket.io => 'user-position' - - [ - { - userId: user id of gamer - roomId: room id when user enter in game - position: { - x: position x on map - y: position y on map - } - }, - ... - ] -``` - -[<<< back](../README.md) \ No newline at end of file diff --git a/back/src/Enum/EnvironmentVariable.ts b/back/src/Enum/EnvironmentVariable.ts index 2cbfbf2e..95a705fa 100644 --- a/back/src/Enum/EnvironmentVariable.ts +++ b/back/src/Enum/EnvironmentVariable.ts @@ -1,4 +1,3 @@ -const SECRET_KEY = process.env.SECRET_KEY || "THECODINGMACHINE_SECRET_KEY"; const MINIMUM_DISTANCE = process.env.MINIMUM_DISTANCE ? Number(process.env.MINIMUM_DISTANCE) : 64; const GROUP_RADIUS = process.env.GROUP_RADIUS ? Number(process.env.GROUP_RADIUS) : 48; const ALLOW_ARTILLERY = process.env.ALLOW_ARTILLERY ? process.env.ALLOW_ARTILLERY == 'true' : false; @@ -14,7 +13,6 @@ const GRPC_PORT = parseInt(process.env.GRPC_PORT || '50051') || 50051; export const SOCKET_IDLE_TIMER = parseInt(process.env.SOCKET_IDLE_TIMER as string) || 30; // maximum time (in second) without activity before a socket is closed export { - SECRET_KEY, MINIMUM_DISTANCE, ADMIN_API_URL, ADMIN_API_TOKEN, diff --git a/contrib/docker/.env.prod.template b/contrib/docker/.env.prod.template new file mode 100644 index 00000000..c0c10181 --- /dev/null +++ b/contrib/docker/.env.prod.template @@ -0,0 +1,20 @@ +# The base domain +DOMAIN=workadventure.localhost + +DEBUG_MODE=false +JITSI_URL=meet.jit.si +# If your Jitsi environment has authentication set up, you MUST set JITSI_PRIVATE_MODE to "true" and you MUST pass a SECRET_JITSI_KEY to generate the JWT secret +JITSI_PRIVATE_MODE=false +JITSI_ISS= +SECRET_JITSI_KEY= + +# URL of the TURN server (needed to "punch a hole" through some networks for P2P connections) +TURN_SERVER= +TURN_USER= +TURN_PASSWORD= + +# The URL used by default, in the form: "/_/global/map/url.json" +START_ROOM_URL=/_/global/maps.workadventu.re/Floor0/floor0.json + +# The email address used by Let's encrypt to send renewal warnings (compulsory) +ACME_EMAIL= diff --git a/contrib/docker/docker-compose.prod.yaml b/contrib/docker/docker-compose.prod.yaml new file mode 100644 index 00000000..22860748 --- /dev/null +++ b/contrib/docker/docker-compose.prod.yaml @@ -0,0 +1,100 @@ +version: "3.3" +services: + reverse-proxy: + image: traefik:v2.3 + command: + - --log.level=WARN + #- --api.insecure=true + - --providers.docker + - --entryPoints.web.address=:80 + - --entrypoints.web.http.redirections.entryPoint.to=websecure + - --entrypoints.web.http.redirections.entryPoint.scheme=https + - --entryPoints.websecure.address=:443 + - --certificatesresolvers.myresolver.acme.email=d.negrier@thecodingmachine.com + - --certificatesresolvers.myresolver.acme.storage=/acme.json + # used during the challenge + - --certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web + ports: + - "80:80" + - "443:443" + # The Web UI (enabled by --api.insecure=true) + #- "8080:8080" + depends_on: + - pusher + - front + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ./acme.json:/acme.json + restart: unless-stopped + + + front: + build: + context: ../.. + dockerfile: front/Dockerfile + #image: thecodingmachine/workadventure-front:master + environment: + DEBUG_MODE: "$DEBUG_MODE" + JITSI_URL: $JITSI_URL + JITSI_PRIVATE_MODE: "$JITSI_PRIVATE_MODE" + API_URL: pusher.${DOMAIN} + TURN_SERVER: "${TURN_SERVER}" + TURN_USER: "${TURN_USER}" + TURN_PASSWORD: "${TURN_PASSWORD}" + START_ROOM_URL: "${START_ROOM_URL}" + labels: + - "traefik.http.routers.front.rule=Host(`play.${DOMAIN}`)" + - "traefik.http.routers.front.entryPoints=web,traefik" + - "traefik.http.services.front.loadbalancer.server.port=80" + - "traefik.http.routers.front-ssl.rule=Host(`play.${DOMAIN}`)" + - "traefik.http.routers.front-ssl.entryPoints=websecure" + - "traefik.http.routers.front-ssl.tls=true" + - "traefik.http.routers.front-ssl.service=front" + - "traefik.http.routers.front-ssl.tls.certresolver=myresolver" + restart: unless-stopped + + pusher: + build: + context: ../.. + dockerfile: pusher/Dockerfile + #image: thecodingmachine/workadventure-pusher:master + command: yarn run runprod + environment: + SECRET_JITSI_KEY: "$SECRET_JITSI_KEY" + SECRET_KEY: yourSecretKey + API_URL: back:50051 + JITSI_URL: $JITSI_URL + JITSI_ISS: $JITSI_ISS + labels: + - "traefik.http.routers.pusher.rule=Host(`pusher.${DOMAIN}`)" + - "traefik.http.routers.pusher.entryPoints=web,traefik" + - "traefik.http.services.pusher.loadbalancer.server.port=8080" + - "traefik.http.routers.pusher-ssl.rule=Host(`pusher.${DOMAIN}`)" + - "traefik.http.routers.pusher-ssl.entryPoints=websecure" + - "traefik.http.routers.pusher-ssl.tls=true" + - "traefik.http.routers.pusher-ssl.service=pusher" + - "traefik.http.routers.pusher-ssl.tls.certresolver=myresolver" + restart: unless-stopped + + back: + build: + context: ../.. + dockerfile: back/Dockerfile + #image: thecodingmachine/workadventure-back:master + command: yarn run runprod + environment: + SECRET_JITSI_KEY: "$SECRET_JITSI_KEY" + ADMIN_API_TOKEN: "$ADMIN_API_TOKEN" + ADMIN_API_URL: "$ADMIN_API_URL" + JITSI_URL: $JITSI_URL + JITSI_ISS: $JITSI_ISS + labels: + - "traefik.http.routers.back.rule=Host(`api.${DOMAIN}`)" + - "traefik.http.routers.back.entryPoints=web" + - "traefik.http.services.back.loadbalancer.server.port=8080" + - "traefik.http.routers.back-ssl.rule=Host(`api.${DOMAIN}`)" + - "traefik.http.routers.back-ssl.entryPoints=websecure" + - "traefik.http.routers.back-ssl.tls=true" + - "traefik.http.routers.back-ssl.service=back" + - "traefik.http.routers.back-ssl.tls.certresolver=myresolver" + restart: unless-stopped diff --git a/front/dist/index.html.tmpl b/front/dist/index.tmpl.html similarity index 100% rename from front/dist/index.html.tmpl rename to front/dist/index.tmpl.html diff --git a/front/templater.sh b/front/templater.sh index e63617c5..1851cdc5 100755 --- a/front/templater.sh +++ b/front/templater.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -x set -o nounset errexit -template_file_index=dist/index.html.tmpl +template_file_index=dist/index.tmpl.html generated_file_index=dist/index.html tmp_trackcodefile=/tmp/trackcode diff --git a/front/webpack.config.js b/front/webpack.config.js index 69d507f2..170466ef 100644 --- a/front/webpack.config.js +++ b/front/webpack.config.js @@ -39,7 +39,16 @@ module.exports = { plugins: [ new HtmlWebpackPlugin( { - template: './dist/index.html' + template: './dist/index.tmpl.html', + minify: { + collapseWhitespace: true, + keepClosingSlash: true, + removeComments: false, + removeRedundantAttributes: true, + removeScriptTypeAttributes: true, + removeStyleLinkTypeAttributes: true, + useShortDoctype: true + } } ), new webpack.ProvidePlugin({ diff --git a/maps/.dockerignore b/maps/.dockerignore new file mode 100644 index 00000000..64d1025d --- /dev/null +++ b/maps/.dockerignore @@ -0,0 +1,4 @@ +/node_modules/ +/dist/bundle.js +/yarn-error.log +/Dockerfile diff --git a/messages/.dockerignore b/messages/.dockerignore new file mode 100644 index 00000000..64d1025d --- /dev/null +++ b/messages/.dockerignore @@ -0,0 +1,4 @@ +/node_modules/ +/dist/bundle.js +/yarn-error.log +/Dockerfile diff --git a/pusher/Dockerfile b/pusher/Dockerfile index 42de3883..4aec9748 100644 --- a/pusher/Dockerfile +++ b/pusher/Dockerfile @@ -1,15 +1,26 @@ -FROM thecodingmachine/workadventure-back-base:latest as builder -WORKDIR /var/www/messages -COPY --chown=docker:docker messages . +# protobuf build +FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76 as builder +WORKDIR /usr/src +COPY messages . RUN yarn install && yarn proto -FROM thecodingmachine/nodejs:12 - -COPY --chown=docker:docker pusher . -COPY --from=builder --chown=docker:docker /var/www/messages/generated /usr/src/app/src/Messages/generated +# typescript build +FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76 as builder2 +WORKDIR /usr/src +COPY pusher/yarn.lock pusher/package.json ./ RUN yarn install - +COPY pusher . +COPY --from=builder /usr/src/generated src/Messages/generated ENV NODE_ENV=production RUN yarn run tsc +# final production image +FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76 +WORKDIR /usr/src +COPY pusher/yarn.lock pusher/package.json ./ +COPY --from=builder2 /usr/src/dist /usr/src/dist +ENV NODE_ENV=production +RUN yarn install --production + +USER node CMD ["yarn", "run", "runprod"] diff --git a/pusher/Dockerfile.prod b/pusher/Dockerfile.prod new file mode 100644 index 00000000..772532f0 --- /dev/null +++ b/pusher/Dockerfile.prod @@ -0,0 +1,17 @@ +FROM node:12.19.0-slim + +RUN mkdir -p /home/node/app && chown -R node:node /home/node/app +WORKDIR /home/node/app + +USER node +ENV NODE_ENV=production +ENV DEBUG=* + +COPY --chown=node:node package.json yarn.lock ./ + +RUN yarn install --prod --frozen-lockfile + +COPY --chown=node:node ./dist/ ./dist/ + +EXPOSE 8080 +CMD ["yarn", "run", "runprod"] diff --git a/uploader/Dockerfile b/uploader/Dockerfile index 3c471f6c..e4366308 100644 --- a/uploader/Dockerfile +++ b/uploader/Dockerfile @@ -1,9 +1,19 @@ -FROM thecodingmachine/nodejs:12 - -COPY --chown=docker:docker uploader . +# typescript build +FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76 as builder2 +WORKDIR /usr/src +COPY uploader/yarn.lock uploader/package.json ./ RUN yarn install - +COPY uploader . ENV NODE_ENV=production RUN yarn run tsc +# final production image +FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76 +WORKDIR /usr/src +COPY uploader/yarn.lock uploader/package.json ./ +COPY --from=builder2 /usr/src/dist /usr/src/dist +ENV NODE_ENV=production +RUN yarn install --production + +USER node CMD ["yarn", "run", "runprod"] diff --git a/website/.dockerignore b/website/.dockerignore new file mode 100644 index 00000000..576c21a2 --- /dev/null +++ b/website/.dockerignore @@ -0,0 +1,5 @@ +/dist/ +/node_modules/ +/dist/bundle.js +/yarn-error.log +/Dockerfile