diff --git a/contrib/docker/.env.prod.template b/contrib/docker/.env.prod.template index c0c10181..5e9adc87 100644 --- a/contrib/docker/.env.prod.template +++ b/contrib/docker/.env.prod.template @@ -1,20 +1,118 @@ +# Security +# + +SECRET_KEY= +ADMIN_API_TOKEN= + +# +# Networking +# + # The base domain DOMAIN=workadventure.localhost -DEBUG_MODE=false +# Subdomains +# MUST match the DOMAIN variable above +FRONT_HOST=front.workadventure.localhost +PUSHER_HOST=pusher.workadventure.localhost +BACK_HOST=api.workadventure.localhost +MAPS_HOST=maps.workadventure.localhost +ICON_HOST=icon.workadventure.localhost + +# SAAS admin panel +ADMIN_API_URL= + +# +# Basic configuration +# + +# The directory to store data in +DATA_DIR=./wa + +# The URL used by default, in the form: "/_/global/map/url.json" +START_ROOM_URL=/_/global/maps.workadventu.re/Floor0/floor0.json + +# If you want to have a contact page in your menu, +# you MUST set CONTACT_URL to the URL of the page that you want +CONTACT_URL= + +MAX_PER_GROUP=4 +MAX_USERNAME_LENGTH=8 +DISABLE_ANONYMOUS=false + +# The version of the docker image to use +# MUST uncomment "image" keys in the docker-compose file for it to be effective +VERSION=master + +TZ=Europe/Paris + +# +# Jitsi +# + 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 +# 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= +# +# Turn/Stun +# + # URL of the TURN server (needed to "punch a hole" through some networks for P2P connections) TURN_SERVER= TURN_USER= TURN_PASSWORD= +# If your Turn server is configured to use the Turn REST API, you MUST put the shared auth secret here. +# If you are using Coturn, this is the value of the "static-auth-secret" parameter in your coturn config file. +# Keep empty if you are sharing hard coded / clear text credentials. +TURN_STATIC_AUTH_SECRET= +# URL of the STUN server +STUN_SERVER= -# The URL used by default, in the form: "/_/global/map/url.json" -START_ROOM_URL=/_/global/maps.workadventu.re/Floor0/floor0.json +# +# Certificate config +# # The email address used by Let's encrypt to send renewal warnings (compulsory) ACME_EMAIL= + +# +# Additional app configs +# Configuration for apps which are not workadventure itself +# + +# openID +OPID_CLIENT_ID= +OPID_CLIENT_SECRET= +OPID_CLIENT_ISSUER= +OPID_CLIENT_REDIRECT_URL= +OPID_LOGIN_SCREEN_PROVIDER=http://pusher.workadventure.localhost/login-screen +OPID_PROFILE_SCREEN_PROVIDER= + + +# +# Advanced configuration +# Generally does not need to be changed +# + +# Networking +HTTP_PORT=80 +HTTPS_PORT=443 + +# Workadventure settings +DISABLE_NOTIFICATIONS=false +SKIP_RENDER_OPTIMIZATIONS=false +STORE_VARIABLES_FOR_LOCAL_MAPS=true + +# Debugging options +DEBUG_MODE=false +LOG_LEVEL=WARN + +# Internal URLs +API_URL=back:50051 + +RESTART_POLICY=unless-stopped diff --git a/contrib/docker/docker-compose.prod.yaml b/contrib/docker/docker-compose.prod.yaml index 62be6749..80ed192b 100644 --- a/contrib/docker/docker-compose.prod.yaml +++ b/contrib/docker/docker-compose.prod.yaml @@ -1,114 +1,128 @@ -version: "3.3" +version: "3.5" services: reverse-proxy: - image: traefik:v2.3 + image: traefik:v2.6 command: - - --log.level=WARN - #- --api.insecure=true + - --log.level=${LOG_LEVEL} - --providers.docker - - --entryPoints.web.address=:80 + # Entry points + - --entryPoints.web.address=:${HTTP_PORT} - --entrypoints.web.http.redirections.entryPoint.to=websecure - --entrypoints.web.http.redirections.entryPoint.scheme=https - - --entryPoints.websecure.address=:443 + - --entryPoints.websecure.address=:${HTTPS_PORT} + # HTTP challenge - --certificatesresolvers.myresolver.acme.email=${ACME_EMAIL} - --certificatesresolvers.myresolver.acme.storage=/acme.json - # used during the challenge - --certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web + # Let's Encrypt's staging server + # uncomment during testing to avoid rate limiting + #- --certificatesresolvers.dnsresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory ports: - - "80:80" - - "443:443" - # The Web UI (enabled by --api.insecure=true) - #- "8080:8080" - depends_on: - - pusher - - front + - "${HTTP_PORT}:80" + - "${HTTPS_PORT}:443" volumes: - /var/run/docker.sock:/var/run/docker.sock - - ./acme.json:/acme.json - restart: unless-stopped + - ${DATA_DIR}/letsencrypt/acme.json:/acme.json + restart: ${RESTART_POLICY} front: build: context: ../.. dockerfile: front/Dockerfile - #image: thecodingmachine/workadventure-front:master + #image: thecodingmachine/workadventure-front:${VERSION} environment: - DEBUG_MODE: "$DEBUG_MODE" - JITSI_URL: $JITSI_URL - JITSI_PRIVATE_MODE: "$JITSI_PRIVATE_MODE" - PUSHER_URL: //pusher.${DOMAIN} - ICON_URL: //icon.${DOMAIN} - TURN_SERVER: "${TURN_SERVER}" - TURN_USER: "${TURN_USER}" - TURN_PASSWORD: "${TURN_PASSWORD}" - START_ROOM_URL: "${START_ROOM_URL}" + - DEBUG_MODE + - JITSI_URL + - JITSI_PRIVATE_MODE + - PUSHER_URL=//${PUSHER_HOST} + - ICON_URL=//${ICON_HOST} + - TURN_SERVER + - TURN_USER + - TURN_PASSWORD + - TURN_STATIC_AUTH_SECRET + - STUN_SERVER + - START_ROOM_URL + - SKIP_RENDER_OPTIMIZATIONS + - MAX_PER_GROUP + - MAX_USERNAME_LENGTH + - DISABLE_ANONYMOUS + - DISABLE_NOTIFICATIONS labels: - - "traefik.http.routers.front.rule=Host(`play.${DOMAIN}`)" - - "traefik.http.routers.front.entryPoints=web,traefik" + - "traefik.http.routers.front.rule=Host(`${FRONT_HOST}`)" + - "traefik.http.routers.front.entryPoints=web" - "traefik.http.services.front.loadbalancer.server.port=80" - - "traefik.http.routers.front-ssl.rule=Host(`play.${DOMAIN}`)" + - "traefik.http.routers.front-ssl.rule=Host(`${FRONT_HOST}`)" - "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=true" - "traefik.http.routers.front-ssl.tls.certresolver=myresolver" - restart: unless-stopped + restart: ${RESTART_POLICY} pusher: build: context: ../.. dockerfile: pusher/Dockerfile - #image: thecodingmachine/workadventure-pusher:master + #image: thecodingmachine/workadventure-pusher:${VERSION} 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 - FRONT_URL: https://play.${DOMAIN} + - SECRET_JITSI_KEY + - SECRET_KEY + - API_URL + - FRONT_URL=https://${FRONT_HOST} + - JITSI_URL + - JITSI_ISS + - DISABLE_ANONYMOUS labels: - - "traefik.http.routers.pusher.rule=Host(`pusher.${DOMAIN}`)" - - "traefik.http.routers.pusher.entryPoints=web,traefik" + - "traefik.http.routers.pusher.rule=Host(`${PUSHER_HOST}`)" + - "traefik.http.routers.pusher.entryPoints=web" - "traefik.http.services.pusher.loadbalancer.server.port=8080" - - "traefik.http.routers.pusher-ssl.rule=Host(`pusher.${DOMAIN}`)" + - "traefik.http.routers.pusher-ssl.rule=Host(${PUSHER_HOST}`)" - "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=true" - "traefik.http.routers.pusher-ssl.tls.certresolver=myresolver" - restart: unless-stopped + restart: ${RESTART_POLICY} back: build: context: ../.. dockerfile: back/Dockerfile - #image: thecodingmachine/workadventure-back:master + #image: thecodingmachine/workadventure-back:${VERSION} 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 + - SECRET_JITSI_KEY + - SECRET_KEY + - ADMIN_API_TOKEN + - ADMIN_API_URL + - TURN_SERVER + - TURN_USER + - TURN_PASSWORD + - TURN_STATIC_AUTH_SECRET + - STUN_SERVER + - JITSI_URL + - JITSI_ISS + - MAX_PER_GROUP + - STORE_VARIABLES_FOR_LOCAL_MAPS labels: - - "traefik.http.routers.back.rule=Host(`api.${DOMAIN}`)" + - "traefik.http.routers.back.rule=Host(`${BACK_HOST}`)" - "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.rule=Host(`${BACK_HOST}`)" - "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=true" - "traefik.http.routers.back-ssl.tls.certresolver=myresolver" - restart: unless-stopped + restart: ${RESTART_POLICY} icon: image: matthiasluedtke/iconserver:v3.13.0 labels: - - "traefik.http.routers.icon.rule=Host(`icon.${DOMAIN}`)" + - "traefik.http.routers.icon.rule=Host(`${ICON_HOST}`)" - "traefik.http.routers.icon.entryPoints=web,traefik" - "traefik.http.services.icon.loadbalancer.server.port=8080" - - "traefik.http.routers.icon-ssl.rule=Host(`icon.${DOMAIN}`)" + - "traefik.http.routers.icon-ssl.rule=Host(`${ICON_HOST}`)" - "traefik.http.routers.icon-ssl.entryPoints=websecure" - - "traefik.http.routers.icon-ssl.tls=true" - "traefik.http.routers.icon-ssl.service=icon" + - "traefik.http.routers.icon-ssl.tls=true" - "traefik.http.routers.icon-ssl.tls.certresolver=myresolver" diff --git a/docs/maps/entry-exit.md b/docs/maps/entry-exit.md index 2040beb3..25e530de 100644 --- a/docs/maps/entry-exit.md +++ b/docs/maps/entry-exit.md @@ -82,7 +82,11 @@ We are able to direct a Woka to the desired place immediately after spawn. To ma ``` .../my_map.json#moveTo=meeting-room&start ``` +*...or even like this!* +``` +.../my_map.json#start&moveTo=200,100 +``` -For this to work, moveTo must be equal to the layer name of interest. This layer should have at least one tile defined. In case of layer having many tiles, user will go to one of them, randomly selected. +For this to work, moveTo must be equal to the x and y position, layer name, or object name of interest. Layer should have at least one tile defined. In case of layer having many tiles, user will go to one of them, randomly selected. ![](images/moveTo-layer-example.png) \ No newline at end of file diff --git a/front/package.json b/front/package.json index 86308a89..39c6f255 100644 --- a/front/package.json +++ b/front/package.json @@ -71,7 +71,7 @@ "zod": "^3.11.6" }, "scripts": { - "start": "run-p templater serve svelte-check-watch typesafe-i18n", + "start": "run-p templater serve svelte-check-watch typesafe-i18n-watch", "templater": "cross-env ./templater.sh", "serve": "cross-env TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" webpack serve --open", "build": "cross-env TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" NODE_ENV=production webpack", @@ -84,7 +84,8 @@ "svelte-check": "svelte-check --fail-on-warnings --fail-on-hints --compiler-warnings \"a11y-no-onchange:ignore,a11y-autofocus:ignore,a11y-media-has-caption:ignore\"", "pretty": "yarn prettier --write 'src/**/*.{ts,svelte}'", "pretty-check": "yarn prettier --check 'src/**/*.{ts,svelte}'", - "typesafe-i18n": "typesafe-i18n --no-watch" + "typesafe-i18n": "typesafe-i18n --no-watch", + "typesafe-i18n-watch": "typesafe-i18n" }, "lint-staged": { "*.svelte": [ diff --git a/front/src/Components/EmbedScreens/CoWebsiteThumbnailSlot.svelte b/front/src/Components/EmbedScreens/CoWebsiteThumbnailSlot.svelte index 2043315b..b01dbf0a 100644 --- a/front/src/Components/EmbedScreens/CoWebsiteThumbnailSlot.svelte +++ b/front/src/Components/EmbedScreens/CoWebsiteThumbnailSlot.svelte @@ -2,9 +2,10 @@ import { onMount } from "svelte"; import { ICON_URL } from "../../Enum/EnvironmentVariable"; - import { coWebsitesNotAsleep, mainCoWebsite, jitsiCoWebsite } from "../../Stores/CoWebsiteStore"; + import { mainCoWebsite } from "../../Stores/CoWebsiteStore"; import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore"; import type { CoWebsite } from "../../WebRtc/CoWebsite/CoWesbite"; + import { JitsiCoWebsite } from "../../WebRtc/CoWebsite/JitsiCoWebsite"; import { iframeStates } from "../../WebRtc/CoWebsiteManager"; import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager"; @@ -15,10 +16,10 @@ let icon: HTMLImageElement; let iconLoaded = false; let state = coWebsite.getStateSubscriber(); - let isJitsi: boolean = false; + let isJitsi: boolean = coWebsite instanceof JitsiCoWebsite; + const mainState = coWebsiteManager.getMainStateSubscriber(); onMount(() => { - isJitsi = Boolean($jitsiCoWebsite && $jitsiCoWebsite.getId() === coWebsite.getId()); icon.src = isJitsi ? "/resources/logos/meet.svg" : `${ICON_URL}/icon?url=${coWebsite.getUrl().hostname}&size=64..96..256&fallback_icon_color=14304c`; @@ -33,20 +34,23 @@ coWebsiteManager.goToMain(coWebsite); } else if ($mainCoWebsite) { if ($mainCoWebsite.getId() === coWebsite.getId()) { - const coWebsites = $coWebsitesNotAsleep; - const newMain = $highlightedEmbedScreen ?? coWebsites.length > 1 ? coWebsites[1] : undefined; - if (newMain && newMain.getId() !== $mainCoWebsite.getId()) { - coWebsiteManager.goToMain(newMain); - } else if (coWebsiteManager.getMainState() === iframeStates.closed) { + if (coWebsiteManager.getMainState() === iframeStates.closed) { coWebsiteManager.displayMain(); + } else if ($highlightedEmbedScreen?.type === "cowebsite") { + coWebsiteManager.goToMain($highlightedEmbedScreen.embed); } else { coWebsiteManager.hideMain(); } } else { - highlightedEmbedScreen.toggleHighlight({ - type: "cowebsite", - embed: coWebsite, - }); + if (coWebsiteManager.getMainState() === iframeStates.closed) { + coWebsiteManager.goToMain(coWebsite); + coWebsiteManager.displayMain(); + } else { + highlightedEmbedScreen.toggleHighlight({ + type: "cowebsite", + embed: coWebsite, + }); + } } } @@ -64,7 +68,10 @@ let isHighlight: boolean = false; let isMain: boolean = false; $: { - isMain = $mainCoWebsite !== undefined && $mainCoWebsite.getId() === coWebsite.getId(); + isMain = + $mainState === iframeStates.opened && + $mainCoWebsite !== undefined && + $mainCoWebsite.getId() === coWebsite.getId(); isHighlight = $highlightedEmbedScreen !== null && $highlightedEmbedScreen.type === "cowebsite" && @@ -212,7 +219,8 @@ } &:not(.vertical) { - animation: bounce 0.35s ease 6 alternate; + transition: all 300ms; + transform: translateY(0px); } &.vertical { @@ -233,7 +241,7 @@ &.displayed { &:not(.vertical) { - animation: activeThumbnail 300ms ease-in 0s forwards; + transform: translateY(-15px); } } @@ -262,16 +270,6 @@ } } - @keyframes activeThumbnail { - 0% { - transform: translateY(0); - } - - 100% { - transform: translateY(-15px); - } - } - @keyframes bounce { from { transform: translateY(0); diff --git a/front/src/Components/Menu/GuestSubMenu.svelte b/front/src/Components/Menu/GuestSubMenu.svelte index a0cd94d7..fbbc48a4 100644 --- a/front/src/Components/Menu/GuestSubMenu.svelte +++ b/front/src/Components/Menu/GuestSubMenu.svelte @@ -1,5 +1,12 @@