Merge remote-tracking branch 'upstream/develop' into electron
This commit is contained in:
commit
4535a8ae96
@ -28,7 +28,7 @@ You can use [GitHub issue tracker](https://github.com/thecodingmachine/workadven
|
|||||||
- File bug reports
|
- File bug reports
|
||||||
- Ask for feature requests
|
- Ask for feature requests
|
||||||
|
|
||||||
If you have more general questions, a good place to ask is [our Discord server](https://discord.gg/YGtngdh9gt).
|
If you have more general questions, a good place to ask is [our Discord server](https://discord.gg/G6Xh9ZM9aR).
|
||||||
|
|
||||||
Finally, you can come and talk to the WorkAdventure core team... on WorkAdventure, of course! [Our offices are here](https://play.staging.workadventu.re/@/tcm/workadventure/wa-village).
|
Finally, you can come and talk to the WorkAdventure core team... on WorkAdventure, of course! [Our offices are here](https://play.staging.workadventu.re/@/tcm/workadventure/wa-village).
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ Please ask first before embarking on any significant pull request (e.g. implemen
|
|||||||
otherwise you risk spending a lot of time working on something that the project's developers might not want to merge
|
otherwise you risk spending a lot of time working on something that the project's developers might not want to merge
|
||||||
into the project.
|
into the project.
|
||||||
|
|
||||||
You can ask us on [Discord](https://discord.gg/YGtngdh9gt) or in the [GitHub issues](https://github.com/thecodingmachine/workadventure/issues).
|
You can ask us on [Discord](https://discord.gg/G6Xh9ZM9aR) or in the [GitHub issues](https://github.com/thecodingmachine/workadventure/issues).
|
||||||
|
|
||||||
### Linting your code
|
### Linting your code
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
![](https://github.com/thecodingmachine/workadventure/workflows/Continuous%20Integration/badge.svg) [![Discord](https://img.shields.io/discord/821338762134290432?label=Discord)](https://discord.gg/YGtngdh9gt)
|
![](https://github.com/thecodingmachine/workadventure/workflows/Continuous%20Integration/badge.svg) [![Discord](https://img.shields.io/discord/821338762134290432?label=Discord)](https://discord.gg/G6Xh9ZM9aR)
|
||||||
|
|
||||||
![WorkAdventure logo](README-LOGO.svg)
|
![WorkAdventure logo](README-LOGO.svg)
|
||||||
![WorkAdventure office image](README-MAP.png)
|
![WorkAdventure office image](README-MAP.png)
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
# protobuf build
|
# protobuf build
|
||||||
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder
|
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder
|
||||||
WORKDIR /usr/src
|
WORKDIR /usr/src
|
||||||
|
COPY messages/yarn.lock messages/package.json ./
|
||||||
|
RUN yarn install
|
||||||
COPY messages .
|
COPY messages .
|
||||||
RUN yarn install && yarn proto
|
RUN yarn proto
|
||||||
|
|
||||||
# typescript build
|
# typescript build
|
||||||
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder2
|
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder2
|
||||||
@ -18,9 +20,9 @@ RUN yarn run tsc
|
|||||||
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d
|
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d
|
||||||
WORKDIR /usr/src
|
WORKDIR /usr/src
|
||||||
COPY back/yarn.lock back/package.json ./
|
COPY back/yarn.lock back/package.json ./
|
||||||
COPY --from=builder2 /usr/src/dist /usr/src/dist
|
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
RUN yarn install --production
|
RUN yarn install --production
|
||||||
|
COPY --from=builder2 /usr/src/dist /usr/src/dist
|
||||||
|
|
||||||
USER node
|
USER node
|
||||||
CMD ["yarn", "run", "runprod"]
|
CMD ["yarn", "run", "runprod"]
|
||||||
|
@ -9,10 +9,7 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: ./
|
context: ./
|
||||||
dockerfile: front/Dockerfile
|
dockerfile: front/Dockerfile
|
||||||
environment:
|
command: /bin/sh -c "/templater.sh && envsubst < /usr/share/nginx/html/env-config.template.js > /usr/share/nginx/html/env-config.js && exec nginx -g 'daemon off;'"
|
||||||
STARTUP_COMMAND_1: 'envsubst < dist/env-config.template.js > dist/env-config.js'
|
|
||||||
STARTUP_COMMAND_2: ''
|
|
||||||
command: apache2-foreground
|
|
||||||
volumes: []
|
volumes: []
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
@ -29,9 +26,6 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: ./
|
context: ./
|
||||||
dockerfile: pusher/Dockerfile
|
dockerfile: pusher/Dockerfile
|
||||||
environment:
|
|
||||||
STARTUP_COMMAND_1: ''
|
|
||||||
STARTUP_COMMAND_2: ''
|
|
||||||
command: yarn run runprod
|
command: yarn run runprod
|
||||||
volumes: []
|
volumes: []
|
||||||
|
|
||||||
@ -40,8 +34,5 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: ./
|
context: ./
|
||||||
dockerfile: back/Dockerfile
|
dockerfile: back/Dockerfile
|
||||||
environment:
|
|
||||||
STARTUP_COMMAND_1: ''
|
|
||||||
STARTUP_COMMAND_2: ''
|
|
||||||
command: yarn run runprod
|
command: yarn run runprod
|
||||||
volumes: []
|
volumes: []
|
||||||
|
@ -76,6 +76,9 @@ services:
|
|||||||
OPID_CLIENT_ISSUER: $OPID_CLIENT_ISSUER
|
OPID_CLIENT_ISSUER: $OPID_CLIENT_ISSUER
|
||||||
OPID_CLIENT_REDIRECT_URL: $OPID_CLIENT_REDIRECT_URL
|
OPID_CLIENT_REDIRECT_URL: $OPID_CLIENT_REDIRECT_URL
|
||||||
OPID_PROFILE_SCREEN_PROVIDER: $OPID_PROFILE_SCREEN_PROVIDER
|
OPID_PROFILE_SCREEN_PROVIDER: $OPID_PROFILE_SCREEN_PROVIDER
|
||||||
|
OPID_SCOPE: $OPID_SCOPE
|
||||||
|
OPID_USERNAME_CLAIM: $OPID_USERNAME_CLAIM
|
||||||
|
OPID_LOCALE_CLAIM: $OPID_LOCALE_CLAIM
|
||||||
DISABLE_ANONYMOUS: $DISABLE_ANONYMOUS
|
DISABLE_ANONYMOUS: $DISABLE_ANONYMOUS
|
||||||
volumes:
|
volumes:
|
||||||
- ./pusher:/usr/src/app
|
- ./pusher:/usr/src/app
|
||||||
|
@ -85,6 +85,9 @@ services:
|
|||||||
OPID_CLIENT_ISSUER: $OPID_CLIENT_ISSUER
|
OPID_CLIENT_ISSUER: $OPID_CLIENT_ISSUER
|
||||||
OPID_CLIENT_REDIRECT_URL: $OPID_CLIENT_REDIRECT_URL
|
OPID_CLIENT_REDIRECT_URL: $OPID_CLIENT_REDIRECT_URL
|
||||||
OPID_PROFILE_SCREEN_PROVIDER: $OPID_PROFILE_SCREEN_PROVIDER
|
OPID_PROFILE_SCREEN_PROVIDER: $OPID_PROFILE_SCREEN_PROVIDER
|
||||||
|
OPID_SCOPE: $OPID_SCOPE
|
||||||
|
OPID_USERNAME_CLAIM: $OPID_USERNAME_CLAIM
|
||||||
|
OPID_LOCALE_CLAIM: $OPID_LOCALE_CLAIM
|
||||||
DISABLE_ANONYMOUS: $DISABLE_ANONYMOUS
|
DISABLE_ANONYMOUS: $DISABLE_ANONYMOUS
|
||||||
volumes:
|
volumes:
|
||||||
- ./pusher:/usr/src/app
|
- ./pusher:/usr/src/app
|
||||||
|
@ -13,6 +13,8 @@ In order to create Jitsi meet zones:
|
|||||||
* In layer properties, you MUST add a "`jitsiRoom`" property (of type "`string`"). The value of the property is the name of the room in Jitsi. Note: the name of the room will be "slugified" and prepended with the name of the instance of the map (so that different instances of the map have different rooms)
|
* In layer properties, you MUST add a "`jitsiRoom`" property (of type "`string`"). The value of the property is the name of the room in Jitsi. Note: the name of the room will be "slugified" and prepended with the name of the instance of the map (so that different instances of the map have different rooms)
|
||||||
* You may also use "jitsiWidth" property (of type "number" between 0 and 100) to control the width of the iframe containing the meeting room.
|
* You may also use "jitsiWidth" property (of type "number" between 0 and 100) to control the width of the iframe containing the meeting room.
|
||||||
|
|
||||||
|
You can have this layer (i.e. your meeting area) to be selectable as the precise location for your meeting using the [Google Calendar integration for Work Adventure](/integrations/google-calendar). To do so, you must set the `meetingRoomLabel` property. You can provide any name that you would like your meeting room to have (as a string).
|
||||||
|
|
||||||
## Triggering of the "Jitsi meet" action
|
## Triggering of the "Jitsi meet" action
|
||||||
|
|
||||||
By default, Jitsi meet will open when a user enters the zone defined on the map.
|
By default, Jitsi meet will open when a user enters the zone defined on the map.
|
||||||
|
@ -1,31 +1,28 @@
|
|||||||
|
# protobuf build
|
||||||
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder
|
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder
|
||||||
|
WORKDIR /usr/src
|
||||||
WORKDIR /usr/src/messages
|
COPY messages/yarn.lock messages/package.json ./
|
||||||
|
RUN yarn install
|
||||||
COPY messages .
|
COPY messages .
|
||||||
RUN yarn install && yarn ts-proto
|
RUN yarn ts-proto
|
||||||
|
|
||||||
WORKDIR /usr/src/front
|
# typescript build
|
||||||
|
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder2
|
||||||
|
WORKDIR /usr/src
|
||||||
|
COPY front/yarn.lock front/package.json ./
|
||||||
|
RUN yarn install
|
||||||
COPY front .
|
COPY front .
|
||||||
|
COPY --from=builder /usr/src/ts-proto-generated/protos src/Messages/ts-proto-generated
|
||||||
# move messages to front
|
|
||||||
RUN cp -r ../messages/ts-proto-generated/protos/* src/Messages/ts-proto-generated
|
|
||||||
RUN sed -i 's/import { Observable } from "rxjs";/import type { Observable } from "rxjs";/g' src/Messages/ts-proto-generated/messages.ts
|
RUN sed -i 's/import { Observable } from "rxjs";/import type { Observable } from "rxjs";/g' src/Messages/ts-proto-generated/messages.ts
|
||||||
RUN cp -r ../messages/JsonMessages/* src/Messages/JsonMessages
|
COPY --from=builder /usr/src/JsonMessages src/Messages/JsonMessages
|
||||||
|
RUN yarn run typesafe-i18n && yarn run build-iframe-api && yarn build
|
||||||
|
|
||||||
RUN yarn install && yarn run typesafe-i18n && yarn run build-iframe-api && yarn build
|
# final production image
|
||||||
|
FROM nginx:1.21.6-alpine
|
||||||
|
|
||||||
FROM thecodingmachine/nodejs:14-apache
|
COPY front/nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
COPY front/templater.sh /
|
||||||
|
COPY --from=builder2 /usr/src/dist /usr/share/nginx/html
|
||||||
|
|
||||||
COPY --from=builder --chown=docker:docker /usr/src/front/dist dist
|
EXPOSE 80
|
||||||
COPY front/templater.sh .
|
CMD ["/bin/sh", "-c", "/templater.sh && envsubst < /usr/share/nginx/html/env-config.template.js > /usr/share/nginx/html/env-config.js && exec nginx -g 'daemon off;'"]
|
||||||
|
|
||||||
USER root
|
|
||||||
RUN DEBIAN_FRONTEND=noninteractive apt-get update \
|
|
||||||
&& apt-get install -y \
|
|
||||||
gettext-base \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
USER docker
|
|
||||||
|
|
||||||
ENV STARTUP_COMMAND_0="./templater.sh"
|
|
||||||
ENV STARTUP_COMMAND_1="envsubst < dist/env-config.template.js > dist/env-config.js"
|
|
||||||
ENV APACHE_DOCUMENT_ROOT=dist/
|
|
||||||
|
51
front/nginx.conf
Normal file
51
front/nginx.conf
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
server_name localhost;
|
||||||
|
access_log off;
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
gzip_comp_level 6;
|
||||||
|
gzip_min_length 1000;
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_disable "msie6";
|
||||||
|
gzip_types
|
||||||
|
application/atom+xml
|
||||||
|
application/geo+json
|
||||||
|
application/javascript
|
||||||
|
application/x-javascript
|
||||||
|
application/json
|
||||||
|
application/ld+json
|
||||||
|
application/manifest+json
|
||||||
|
application/rdf+xml
|
||||||
|
application/rss+xml
|
||||||
|
application/xhtml+xml
|
||||||
|
application/xml
|
||||||
|
font/eot
|
||||||
|
font/otf
|
||||||
|
font/ttf
|
||||||
|
image/svg+xml
|
||||||
|
text/css
|
||||||
|
text/javascript
|
||||||
|
text/plain
|
||||||
|
text/xml;
|
||||||
|
|
||||||
|
# serve static assets (that have a cache busting hash) with an efficient cache policy
|
||||||
|
location /assets {
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public";
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
rewrite ^/register/ /index.html break;
|
||||||
|
rewrite ^/login /index.html break;
|
||||||
|
rewrite ^/jwt /index.html break;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ ^/[@_]/ {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
BIN
front/public/resources/logos/jitsi.png
Normal file
BIN
front/public/resources/logos/jitsi.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
@ -1,49 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
|
||||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
||||||
width="452.388px" height="452.388px" viewBox="0 0 452.388 452.388" style="enable-background:new 0 0 452.388 452.388;"
|
|
||||||
xml:space="preserve">
|
|
||||||
<g>
|
|
||||||
<g id="Layer_8_38_">
|
|
||||||
<path d="M441.677,43.643H10.687C4.785,43.643,0,48.427,0,54.329v297.425c0,5.898,4.785,10.676,10.687,10.676h162.069v25.631
|
|
||||||
c0,0.38,0.074,0.722,0.112,1.089h-23.257c-5.407,0-9.796,4.389-9.796,9.795c0,5.408,4.389,9.801,9.796,9.801h158.506
|
|
||||||
c5.406,0,9.795-4.389,9.795-9.801c0-5.406-4.389-9.795-9.795-9.795h-23.256c0.032-0.355,0.115-0.709,0.115-1.089V362.43H441.7
|
|
||||||
c5.898,0,10.688-4.782,10.688-10.676V54.329C452.37,48.427,447.589,43.643,441.677,43.643z M422.089,305.133
|
|
||||||
c0,5.903-4.784,10.687-10.683,10.687H40.96c-5.898,0-10.684-4.783-10.684-10.687V79.615c0-5.898,4.786-10.684,10.684-10.684
|
|
||||||
h370.446c5.898,0,10.683,4.785,10.683,10.684V305.133z M303.942,290.648H154.025c0-29.872,17.472-55.661,42.753-67.706
|
|
||||||
c-15.987-10.501-26.546-28.571-26.546-49.13c0-32.449,26.306-58.755,58.755-58.755c32.448,0,58.753,26.307,58.753,58.755
|
|
||||||
c0,20.553-10.562,38.629-26.545,49.13C286.475,234.987,303.942,260.781,303.942,290.648z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.6 KiB |
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
icon.src = isJitsi
|
icon.src = isJitsi
|
||||||
? "/resources/logos/meet.svg"
|
? "/resources/logos/jitsi.png"
|
||||||
: `${ICON_URL}/icon?url=${coWebsite.getUrl().hostname}&size=64..96..256&fallback_icon_color=14304c`;
|
: `${ICON_URL}/icon?url=${coWebsite.getUrl().hostname}&size=64..96..256&fallback_icon_color=14304c`;
|
||||||
icon.alt = coWebsite.getUrl().hostname;
|
icon.alt = coWebsite.getUrl().hostname;
|
||||||
icon.onload = () => {
|
icon.onload = () => {
|
||||||
@ -188,10 +188,16 @@
|
|||||||
/>
|
/>
|
||||||
</rect>
|
</rect>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
<!-- TODO use trigger message property -->
|
||||||
|
<div class="cowebsite-hover" class:hide={!isJitsi} style="width: max-content;">
|
||||||
|
<p>Open / Close Jitsi meeting!</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.cowebsite-thumbnail {
|
.cowebsite-thumbnail {
|
||||||
|
cursor: url("../../../style/images/cursor_pointer.png"), pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background-color: rgba(#000000, 0.6);
|
background-color: rgba(#000000, 0.6);
|
||||||
@ -236,6 +242,11 @@
|
|||||||
height: 40px;
|
height: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cowebsite-hover {
|
||||||
|
top: -4px;
|
||||||
|
left: 55px;
|
||||||
|
}
|
||||||
|
|
||||||
animation: shake 0.35s ease-in-out;
|
animation: shake 0.35s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,10 +326,33 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.jitsi {
|
&.jitsi {
|
||||||
filter: invert(100%);
|
|
||||||
-webkit-filter: invert(100%);
|
|
||||||
padding: 7px;
|
padding: 7px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.cowebsite-hover {
|
||||||
|
display: block;
|
||||||
|
width: max-content !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cowebsite-hover {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
|
top: -40px;
|
||||||
|
left: -4px;
|
||||||
|
width: 0 !important;
|
||||||
|
min-height: 20px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
overflow: hidden;
|
||||||
|
color: white;
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
p {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
left: 2%;
|
left: 2%;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
|
overflow: visible;
|
||||||
|
|
||||||
&.vertical {
|
&.vertical {
|
||||||
height: auto !important;
|
height: auto !important;
|
||||||
@ -31,8 +32,6 @@
|
|||||||
bottom: auto !important;
|
bottom: auto !important;
|
||||||
left: auto !important;
|
left: auto !important;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: auto;
|
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding-top: 4px;
|
padding-top: 4px;
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
on:dragstart|preventDefault={noDrag}
|
on:dragstart|preventDefault={noDrag}
|
||||||
on:click|preventDefault={showMenu}
|
on:click|preventDefault={showMenu}
|
||||||
/>
|
/>
|
||||||
|
{/if}
|
||||||
<img
|
<img
|
||||||
src={logoTalk}
|
src={logoTalk}
|
||||||
alt={$LL.menu.icon.open.chat()}
|
alt={$LL.menu.icon.open.chat()}
|
||||||
@ -69,7 +70,6 @@
|
|||||||
on:dragstart|preventDefault={noDrag}
|
on:dragstart|preventDefault={noDrag}
|
||||||
on:click|preventDefault={showChat}
|
on:click|preventDefault={showChat}
|
||||||
/>
|
/>
|
||||||
{/if}
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
@ -4,13 +4,15 @@
|
|||||||
import type { Streamable } from "../../Stores/StreamableCollectionStore";
|
import type { Streamable } from "../../Stores/StreamableCollectionStore";
|
||||||
|
|
||||||
import type { ScreenSharingPeer } from "../../WebRtc/ScreenSharingPeer";
|
import type { ScreenSharingPeer } from "../../WebRtc/ScreenSharingPeer";
|
||||||
import { getColorByString, srcObject } from "./utils";
|
import { getColorByString, srcObject, getTextColorByBackgroundColor } from "./utils";
|
||||||
|
|
||||||
export let clickable = false;
|
export let clickable = false;
|
||||||
|
|
||||||
export let peer: ScreenSharingPeer;
|
export let peer: ScreenSharingPeer;
|
||||||
let streamStore = peer.streamStore;
|
let streamStore = peer.streamStore;
|
||||||
let name = peer.userName;
|
let name = peer.userName;
|
||||||
|
let backGroundColor = getColorByString(peer.userName);
|
||||||
|
let textColor = getTextColorByBackgroundColor(backGroundColor);
|
||||||
let statusStore = peer.statusStore;
|
let statusStore = peer.statusStore;
|
||||||
|
|
||||||
let embedScreen: EmbedScreen;
|
let embedScreen: EmbedScreen;
|
||||||
@ -32,7 +34,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{#if $streamStore !== null}
|
{#if $streamStore !== null}
|
||||||
<i class="container">
|
<i class="container">
|
||||||
<span style="background-color: {getColorByString(name)};">{name}</span>
|
<span style="background-color: {backGroundColor}; color: {textColor};">{name}</span>
|
||||||
</i>
|
</i>
|
||||||
<!-- svelte-ignore a11y-media-has-caption -->
|
<!-- svelte-ignore a11y-media-has-caption -->
|
||||||
<video
|
<video
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
import reportImg from "./images/report.svg";
|
import reportImg from "./images/report.svg";
|
||||||
import blockSignImg from "./images/blockSign.svg";
|
import blockSignImg from "./images/blockSign.svg";
|
||||||
import { showReportScreenStore } from "../../Stores/ShowReportScreenStore";
|
import { showReportScreenStore } from "../../Stores/ShowReportScreenStore";
|
||||||
import { getColorByString, srcObject } from "./utils";
|
import { getColorByString, getTextColorByBackgroundColor, srcObject } from "./utils";
|
||||||
import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore";
|
import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore";
|
||||||
import type { EmbedScreen } from "../../Stores/EmbedScreensStore";
|
import type { EmbedScreen } from "../../Stores/EmbedScreensStore";
|
||||||
import type { Streamable } from "../../Stores/StreamableCollectionStore";
|
import type { Streamable } from "../../Stores/StreamableCollectionStore";
|
||||||
@ -20,6 +20,8 @@
|
|||||||
let streamStore = peer.streamStore;
|
let streamStore = peer.streamStore;
|
||||||
let volumeStore = peer.volumeStore;
|
let volumeStore = peer.volumeStore;
|
||||||
let name = peer.userName;
|
let name = peer.userName;
|
||||||
|
let backGroundColor = getColorByString(peer.userName);
|
||||||
|
let textColor = getTextColorByBackgroundColor(backGroundColor);
|
||||||
let statusStore = peer.statusStore;
|
let statusStore = peer.statusStore;
|
||||||
let constraintStore = peer.constraintsStore;
|
let constraintStore = peer.constraintsStore;
|
||||||
|
|
||||||
@ -65,7 +67,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<!-- {#if !$constraintStore || $constraintStore.video === false} -->
|
<!-- {#if !$constraintStore || $constraintStore.video === false} -->
|
||||||
<i class="container">
|
<i class="container">
|
||||||
<span style="background-color: {getColorByString(name)};">{name}</span>
|
<span style="background-color: {backGroundColor}; color: {textColor};">{name}</span>
|
||||||
</i>
|
</i>
|
||||||
<div class="woka-icon {($constraintStore && $constraintStore.video !== false) || minimized ? '' : 'no-video'}">
|
<div class="woka-icon {($constraintStore && $constraintStore.video !== false) || minimized ? '' : 'no-video'}">
|
||||||
<Woka userId={peer.userId} placeholderSrc={""} />
|
<Woka userId={peer.userId} placeholderSrc={""} />
|
||||||
|
@ -18,6 +18,24 @@ export function getColorByString(str: string): string | null {
|
|||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param color: string
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
export function getTextColorByBackgroundColor(color: string | null): string {
|
||||||
|
if (!color) {
|
||||||
|
return "white";
|
||||||
|
}
|
||||||
|
const rgb = color.slice(1);
|
||||||
|
const brightness = Math.round(
|
||||||
|
(parseInt(rgb[0] + rgb[1], 16) * 299 +
|
||||||
|
parseInt(rgb[2] + rgb[3], 16) * 587 +
|
||||||
|
parseInt(rgb[4] + rgb[5], 16) * 114) /
|
||||||
|
1000
|
||||||
|
);
|
||||||
|
return brightness > 125 ? "black" : "white";
|
||||||
|
}
|
||||||
|
|
||||||
export function srcObject(node: HTMLVideoElement, stream: MediaStream | null) {
|
export function srcObject(node: HTMLVideoElement, stream: MediaStream | null) {
|
||||||
node.srcObject = stream;
|
node.srcObject = stream;
|
||||||
return {
|
return {
|
||||||
|
@ -4,36 +4,33 @@
|
|||||||
import { ADMIN_URL } from "../../Enum/EnvironmentVariable";
|
import { ADMIN_URL } from "../../Enum/EnvironmentVariable";
|
||||||
import LL from "../../i18n/i18n-svelte";
|
import LL from "../../i18n/i18n-svelte";
|
||||||
|
|
||||||
const upgradeLink = ADMIN_URL + "/pricing";
|
|
||||||
const registerLink = ADMIN_URL + "/second-step-register";
|
const registerLink = ADMIN_URL + "/second-step-register";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main class="warningMain" transition:fly={{ y: -200, duration: 500 }}>
|
<main class="warningMain" transition:fly={{ y: -200, duration: 500 }}>
|
||||||
{#if $userIsAdminStore}
|
{#if $userIsAdminStore}
|
||||||
<h2>{$LL.warning.title()}</h2>
|
<h2>{$LL.warning.title()}</h2>
|
||||||
<p>
|
<p>{@html $LL.warning.content()}</p>
|
||||||
{$LL.warning.content({ upgradeLink })}
|
|
||||||
</p>
|
|
||||||
{:else if $limitMapStore}
|
{:else if $limitMapStore}
|
||||||
<p>
|
<p>
|
||||||
This map is available for 2 days. You can register your domain <a href={registerLink}>here</a>!
|
This map is available for 2 days. You can register your domain <a href={registerLink}>here</a>!
|
||||||
</p>
|
</p>
|
||||||
{:else}
|
{:else}
|
||||||
<h2>{$LL.warning.title()}</h2>
|
<h2>{$LL.warning.title()}</h2>
|
||||||
<p>{$LL.warning.limit()}</p>
|
<p>{@html $LL.warning.content()}</p>
|
||||||
{/if}
|
{/if}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
main.warningMain {
|
main.warningMain {
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
width: 80%;
|
width: 100%;
|
||||||
background-color: #f9e81e;
|
background-color: #f9e81e;
|
||||||
color: #14304c;
|
color: #14304c;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
||||||
top: 4%;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
@ -16,6 +16,10 @@ import { isRegisterData } from "../Messages/JsonMessages/RegisterData";
|
|||||||
import { isAdminApiData } from "../Messages/JsonMessages/AdminApiData";
|
import { isAdminApiData } from "../Messages/JsonMessages/AdminApiData";
|
||||||
import { limitMapStore } from "../Stores/GameStore";
|
import { limitMapStore } from "../Stores/GameStore";
|
||||||
import { showLimitRoomModalStore } from "../Stores/ModalStore";
|
import { showLimitRoomModalStore } from "../Stores/ModalStore";
|
||||||
|
import { gameManager } from "../Phaser/Game/GameManager";
|
||||||
|
import { locales } from "../i18n/i18n-util";
|
||||||
|
import type { Locales } from "../i18n/i18n-types";
|
||||||
|
import { setCurrentLocale } from "../i18n/locales";
|
||||||
|
|
||||||
class ConnectionManager {
|
class ConnectionManager {
|
||||||
private localUser!: LocalUser;
|
private localUser!: LocalUser;
|
||||||
@ -342,9 +346,12 @@ class ConnectionManager {
|
|||||||
throw new Error("No Auth code provided");
|
throw new Error("No Auth code provided");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const { authToken, userUuid, textures, email } = await Axios.get(`${PUSHER_URL}/login-callback`, {
|
const { authToken, userUuid, textures, email, username, locale } = await Axios.get(
|
||||||
|
`${PUSHER_URL}/login-callback`,
|
||||||
|
{
|
||||||
params: { code, nonce, token, playUri: this.currentRoom?.key },
|
params: { code, nonce, token, playUri: this.currentRoom?.key },
|
||||||
}).then((res) => {
|
}
|
||||||
|
).then((res) => {
|
||||||
return res.data;
|
return res.data;
|
||||||
});
|
});
|
||||||
localUserStore.setAuthToken(authToken);
|
localUserStore.setAuthToken(authToken);
|
||||||
@ -352,6 +359,27 @@ class ConnectionManager {
|
|||||||
localUserStore.saveUser(this.localUser);
|
localUserStore.saveUser(this.localUser);
|
||||||
this.authToken = authToken;
|
this.authToken = authToken;
|
||||||
|
|
||||||
|
if (username) {
|
||||||
|
gameManager.setPlayerName(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locale) {
|
||||||
|
try {
|
||||||
|
if (locales.indexOf(locale) == -1) {
|
||||||
|
locales.forEach((l) => {
|
||||||
|
if (l.startsWith(locale.split("-")[0])) {
|
||||||
|
setCurrentLocale(l);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setCurrentLocale(locale as Locales);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warn("Could not set locale", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//user connected, set connected store for menu at true
|
//user connected, set connected store for menu at true
|
||||||
userIsConnected.set(true);
|
userIsConnected.set(true);
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ export abstract class Character extends Container implements OutlineableInterfac
|
|||||||
fontFamily: '"Press Start 2P"',
|
fontFamily: '"Press Start 2P"',
|
||||||
fontSize: "8px",
|
fontSize: "8px",
|
||||||
strokeThickness: 2,
|
strokeThickness: 2,
|
||||||
stroke: "gray",
|
stroke: "#14304C",
|
||||||
metrics: {
|
metrics: {
|
||||||
ascent: 20,
|
ascent: 20,
|
||||||
descent: 10,
|
descent: 10,
|
||||||
|
@ -13,7 +13,7 @@ export class ActivatablesManager {
|
|||||||
|
|
||||||
private canSelectByDistance: boolean = true;
|
private canSelectByDistance: boolean = true;
|
||||||
|
|
||||||
private readonly outlineColor = 0xffff00;
|
private readonly outlineColor = 0xf9e81e;
|
||||||
private readonly directionalActivationPositionShift = 50;
|
private readonly directionalActivationPositionShift = 50;
|
||||||
|
|
||||||
constructor(currentPlayer: Player) {
|
constructor(currentPlayer: Player) {
|
||||||
|
@ -1220,6 +1220,8 @@ ${escapedMessage}
|
|||||||
openCoWebsite.closable ?? true
|
openCoWebsite.closable ?? true
|
||||||
);
|
);
|
||||||
|
|
||||||
|
coWebsiteManager.addCoWebsiteToStore(coWebsite, openCoWebsite.position);
|
||||||
|
|
||||||
if (openCoWebsite.lazy === undefined || !openCoWebsite.lazy) {
|
if (openCoWebsite.lazy === undefined || !openCoWebsite.lazy) {
|
||||||
await coWebsiteManager.loadCoWebsite(coWebsite);
|
await coWebsiteManager.loadCoWebsite(coWebsite);
|
||||||
}
|
}
|
||||||
@ -1596,6 +1598,8 @@ ${escapedMessage}
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((reason) => console.warn(reason));
|
.catch((reason) => console.warn(reason));
|
||||||
|
|
||||||
|
urlManager.clearHashParameter();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(`Cannot proceed with moveTo command:\n\t-> ${err}`);
|
console.warn(`Cannot proceed with moveTo command:\n\t-> ${err}`);
|
||||||
}
|
}
|
||||||
@ -1715,7 +1719,7 @@ ${escapedMessage}
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.CurrentPlayer.on(Phaser.Input.Events.POINTER_OVER, (pointer: Phaser.Input.Pointer) => {
|
this.CurrentPlayer.on(Phaser.Input.Events.POINTER_OVER, (pointer: Phaser.Input.Pointer) => {
|
||||||
this.CurrentPlayer.pointerOverOutline(0x00ffff);
|
this.CurrentPlayer.pointerOverOutline(0x365dff);
|
||||||
});
|
});
|
||||||
this.CurrentPlayer.on(Phaser.Input.Events.POINTER_OUT, (pointer: Phaser.Input.Pointer) => {
|
this.CurrentPlayer.on(Phaser.Input.Events.POINTER_OUT, (pointer: Phaser.Input.Pointer) => {
|
||||||
this.CurrentPlayer.pointerOutOutline();
|
this.CurrentPlayer.pointerOutOutline();
|
||||||
|
@ -56,7 +56,7 @@ export class ActionableItem implements ActivatableInterface {
|
|||||||
|
|
||||||
this.getOutlinePlugin()?.add(this.sprite, {
|
this.getOutlinePlugin()?.add(this.sprite, {
|
||||||
thickness: 2,
|
thickness: 2,
|
||||||
outlineColor: 0xffff00,
|
outlineColor: 0xf9e81e,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +58,10 @@ class UrlManager {
|
|||||||
return this.getHashParameters()[name];
|
return this.getHashParameters()[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public clearHashParameter(): void {
|
||||||
|
window.location.hash = "";
|
||||||
|
}
|
||||||
|
|
||||||
private getHashParameters(): Record<string, string> {
|
private getHashParameters(): Record<string, string> {
|
||||||
return window.location.hash
|
return window.location.hash
|
||||||
.substring(1)
|
.substring(1)
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import type { Translation } from "../i18n-types";
|
import type { Translation } from "../i18n-types";
|
||||||
|
import { ADMIN_URL } from "../../Enum/EnvironmentVariable";
|
||||||
|
|
||||||
|
const upgradeLink = ADMIN_URL + "/pricing";
|
||||||
|
|
||||||
const warning: NonNullable<Translation["warning"]> = {
|
const warning: NonNullable<Translation["warning"]> = {
|
||||||
title: "Warnung!",
|
title: "Warnung!",
|
||||||
content:
|
content: `Diese Welt erreicht bald die maximale Kapazität. Du kannst die Kapazität <a href="${upgradeLink}" target="_blank">hier</a> erhöhen`,
|
||||||
'Diese Welt erreicht bald die maximale Kapazität. Du kannst die Kapazität <a href={upgradeLink} target="_blank">hier</a> erhöhen',
|
|
||||||
limit: "Diese Welt erreicht bald die maximale Kapazität!",
|
limit: "Diese Welt erreicht bald die maximale Kapazität!",
|
||||||
accessDenied: {
|
accessDenied: {
|
||||||
camera: "Zugriff auf die Kamera verweigert. Hier klicken um deine Browser Berechtigungen zu prüfen.",
|
camera: "Zugriff auf die Kamera verweigert. Hier klicken um deine Browser Berechtigungen zu prüfen.",
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import type { BaseTranslation } from "../i18n-types";
|
import type { BaseTranslation } from "../i18n-types";
|
||||||
|
import { ADMIN_URL } from "../../Enum/EnvironmentVariable";
|
||||||
|
|
||||||
|
const upgradeLink = ADMIN_URL + "/pricing";
|
||||||
|
|
||||||
const warning: BaseTranslation = {
|
const warning: BaseTranslation = {
|
||||||
title: "Warning!",
|
title: "Warning!",
|
||||||
content:
|
content: `This world is close to its limit!. You can upgrade its capacity <a href="${upgradeLink}" target="_blank">here</a>`,
|
||||||
'This world is close to its limit!. You can upgrade its capacity <a href={upgradeLink} target="_blank">here</a>',
|
|
||||||
limit: "This world is close to its limit!",
|
limit: "This world is close to its limit!",
|
||||||
accessDenied: {
|
accessDenied: {
|
||||||
camera: "Camera access denied. Click here and check your browser permissions.",
|
camera: "Camera access denied. Click here and check your browser permissions.",
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import type { Translation } from "../i18n-types";
|
import type { Translation } from "../i18n-types";
|
||||||
|
import { ADMIN_URL } from "../../Enum/EnvironmentVariable";
|
||||||
|
|
||||||
|
const upgradeLink = ADMIN_URL + "/pricing";
|
||||||
|
|
||||||
const warning: NonNullable<Translation["warning"]> = {
|
const warning: NonNullable<Translation["warning"]> = {
|
||||||
title: "Attention!",
|
title: "Attention!",
|
||||||
content:
|
content: `Ce monde est proche de sa limite ! Vous pouvez améliorer sa capacité <a href="${upgradeLink}" target="_blank">içi</a>`,
|
||||||
'Ce monde est proche de sa limite ! Vous pouvez améliorer sa capacité <a href={upgradeLink} target="_blank">içi</a>',
|
|
||||||
limit: "Ce monde est proche de ses limites!",
|
limit: "Ce monde est proche de ses limites!",
|
||||||
accessDenied: {
|
accessDenied: {
|
||||||
camera: "Accès à la caméra refusé. Cliquez ici et vérifiez les autorisations de votre navigateur.",
|
camera: "Accès à la caméra refusé. Cliquez ici et vérifiez les autorisations de votre navigateur.",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env sh
|
||||||
set -x
|
set -x
|
||||||
set -o nounset errexit
|
set -o nounset errexit
|
||||||
index_file=dist/index.html
|
index_file=/usr/share/nginx/html/index.html
|
||||||
tmp_trackcodefile=/tmp/trackcode
|
tmp_trackcodefile=/tmp/trackcode
|
||||||
|
|
||||||
# To inject tracking code, you have two choices:
|
# To inject tracking code, you have two choices:
|
||||||
@ -10,12 +10,12 @@ tmp_trackcodefile=/tmp/trackcode
|
|||||||
# The ANALYTICS_CODE_PATH is the location of a file inside the container
|
# The ANALYTICS_CODE_PATH is the location of a file inside the container
|
||||||
ANALYTICS_CODE_PATH="${ANALYTICS_CODE_PATH:-dist/ga.html.tmpl}"
|
ANALYTICS_CODE_PATH="${ANALYTICS_CODE_PATH:-dist/ga.html.tmpl}"
|
||||||
|
|
||||||
if [[ "${INSERT_ANALYTICS:-NO}" == "NO" ]]; then
|
if [ "${INSERT_ANALYTICS:-NO}" = "NO" ]; then
|
||||||
echo "" > "${tmp_trackcodefile}"
|
echo "" > "${tmp_trackcodefile}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Automatically insert analytics if GA_TRACKING_ID is set
|
# Automatically insert analytics if GA_TRACKING_ID is set
|
||||||
if [[ "${GA_TRACKING_ID:-}" != "" || "${INSERT_ANALYTICS:-NO}" != "NO" ]]; then
|
if [ "${GA_TRACKING_ID:-}" != "" ] || [ "${INSERT_ANALYTICS:-NO}" != "NO" ]; then
|
||||||
echo "Templating code from ${ANALYTICS_CODE_PATH}"
|
echo "Templating code from ${ANALYTICS_CODE_PATH}"
|
||||||
sed "s#<!-- TRACKING NUMBER -->#${GA_TRACKING_ID:-}#g" "${ANALYTICS_CODE_PATH}" > "$tmp_trackcodefile"
|
sed "s#<!-- TRACKING NUMBER -->#${GA_TRACKING_ID:-}#g" "${ANALYTICS_CODE_PATH}" > "$tmp_trackcodefile"
|
||||||
fi
|
fi
|
||||||
|
@ -475,12 +475,7 @@ ansi-escapes@^4.3.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
type-fest "^0.21.3"
|
type-fest "^0.21.3"
|
||||||
|
|
||||||
ansi-regex@^5.0.0:
|
ansi-regex@^5.0.0, ansi-regex@^5.0.1:
|
||||||
version "5.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
|
|
||||||
integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
|
|
||||||
|
|
||||||
ansi-regex@^5.0.1:
|
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
|
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
|
||||||
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
|
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
# protobuf build
|
# protobuf build
|
||||||
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder
|
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder
|
||||||
WORKDIR /usr/src
|
WORKDIR /usr/src
|
||||||
|
COPY messages/yarn.lock messages/package.json ./
|
||||||
|
RUN yarn install
|
||||||
COPY messages .
|
COPY messages .
|
||||||
RUN yarn install && yarn proto
|
RUN yarn proto
|
||||||
|
|
||||||
# typescript build
|
# typescript build
|
||||||
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder2
|
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder2
|
||||||
@ -19,9 +21,9 @@ RUN yarn run tsc
|
|||||||
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d
|
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d
|
||||||
WORKDIR /usr/src
|
WORKDIR /usr/src
|
||||||
COPY pusher/yarn.lock pusher/package.json ./
|
COPY pusher/yarn.lock pusher/package.json ./
|
||||||
COPY --from=builder2 /usr/src/dist /usr/src/dist
|
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
RUN yarn install --production
|
RUN yarn install --production
|
||||||
|
COPY --from=builder2 /usr/src/dist /usr/src/dist
|
||||||
|
|
||||||
USER node
|
USER node
|
||||||
CMD ["yarn", "run", "runprod"]
|
CMD ["yarn", "run", "runprod"]
|
||||||
|
@ -84,11 +84,13 @@ export class AdminController extends BaseController {
|
|||||||
|
|
||||||
if (ADMIN_API_TOKEN === "") {
|
if (ADMIN_API_TOKEN === "") {
|
||||||
res.writeStatus("401 Unauthorized").end("No token configured!");
|
res.writeStatus("401 Unauthorized").end("No token configured!");
|
||||||
|
res.end();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (token !== ADMIN_API_TOKEN) {
|
if (token !== ADMIN_API_TOKEN) {
|
||||||
console.error("Admin access refused for token: " + token);
|
console.error("Admin access refused for token: " + token);
|
||||||
res.writeStatus("401 Unauthorized").end("Incorrect token");
|
res.writeStatus("401 Unauthorized").end("Incorrect token");
|
||||||
|
res.end();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +93,15 @@ export class AuthenticateController extends BaseController {
|
|||||||
res.writeStatus("200");
|
res.writeStatus("200");
|
||||||
this.addCorsHeaders(res);
|
this.addCorsHeaders(res);
|
||||||
res.writeHeader("Content-Type", "application/json");
|
res.writeHeader("Content-Type", "application/json");
|
||||||
return res.end(JSON.stringify({ ...resCheckTokenAuth, ...resUserData, authToken: token }));
|
return res.end(
|
||||||
|
JSON.stringify({
|
||||||
|
...resCheckTokenAuth,
|
||||||
|
...resUserData,
|
||||||
|
authToken: token,
|
||||||
|
username: authTokenData?.username,
|
||||||
|
locale: authTokenData?.locale,
|
||||||
|
})
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.info("User was not connected", err);
|
console.info("User was not connected", err);
|
||||||
}
|
}
|
||||||
@ -115,7 +123,12 @@ export class AuthenticateController extends BaseController {
|
|||||||
if (!email) {
|
if (!email) {
|
||||||
throw new Error("No email in the response");
|
throw new Error("No email in the response");
|
||||||
}
|
}
|
||||||
const authToken = jwtTokenManager.createAuthToken(email, userInfo?.access_token);
|
const authToken = jwtTokenManager.createAuthToken(
|
||||||
|
email,
|
||||||
|
userInfo?.access_token,
|
||||||
|
userInfo?.username,
|
||||||
|
userInfo?.locale
|
||||||
|
);
|
||||||
|
|
||||||
//Get user data from Admin Back Office
|
//Get user data from Admin Back Office
|
||||||
//This is very important to create User Local in LocalStorage in WorkAdventure
|
//This is very important to create User Local in LocalStorage in WorkAdventure
|
||||||
@ -124,7 +137,9 @@ export class AuthenticateController extends BaseController {
|
|||||||
res.writeStatus("200");
|
res.writeStatus("200");
|
||||||
this.addCorsHeaders(res);
|
this.addCorsHeaders(res);
|
||||||
res.writeHeader("Content-Type", "application/json");
|
res.writeHeader("Content-Type", "application/json");
|
||||||
return res.end(JSON.stringify({ ...data, authToken }));
|
return res.end(
|
||||||
|
JSON.stringify({ ...data, authToken, username: userInfo?.username, locale: userInfo?.locale })
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("openIDCallback => ERROR", e);
|
console.error("openIDCallback => ERROR", e);
|
||||||
return this.errorToResponse(e, res);
|
return this.errorToResponse(e, res);
|
||||||
|
@ -18,6 +18,9 @@ export const OPID_CLIENT_SECRET = process.env.OPID_CLIENT_SECRET || "";
|
|||||||
export const OPID_CLIENT_ISSUER = process.env.OPID_CLIENT_ISSUER || "";
|
export const OPID_CLIENT_ISSUER = process.env.OPID_CLIENT_ISSUER || "";
|
||||||
export const OPID_CLIENT_REDIRECT_URL = process.env.OPID_CLIENT_REDIRECT_URL || FRONT_URL + "/jwt";
|
export const OPID_CLIENT_REDIRECT_URL = process.env.OPID_CLIENT_REDIRECT_URL || FRONT_URL + "/jwt";
|
||||||
export const OPID_PROFILE_SCREEN_PROVIDER = process.env.OPID_PROFILE_SCREEN_PROVIDER || ADMIN_URL + "/profile";
|
export const OPID_PROFILE_SCREEN_PROVIDER = process.env.OPID_PROFILE_SCREEN_PROVIDER || ADMIN_URL + "/profile";
|
||||||
|
export const OPID_SCOPE = process.env.OPID_SCOPE || "openid email";
|
||||||
|
export const OPID_USERNAME_CLAIM = process.env.OPID_USERNAME_CLAIM || "username";
|
||||||
|
export const OPID_LOCALE_CLAIM = process.env.OPID_LOCALE_CLAIM || "locale";
|
||||||
export const DISABLE_ANONYMOUS: boolean = process.env.DISABLE_ANONYMOUS === "true";
|
export const DISABLE_ANONYMOUS: boolean = process.env.DISABLE_ANONYMOUS === "true";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -5,6 +5,8 @@ import { InvalidTokenError } from "../Controller/InvalidTokenError";
|
|||||||
export interface AuthTokenData {
|
export interface AuthTokenData {
|
||||||
identifier: string; //will be a email if logged in or an uuid if anonymous
|
identifier: string; //will be a email if logged in or an uuid if anonymous
|
||||||
accessToken?: string;
|
accessToken?: string;
|
||||||
|
username?: string;
|
||||||
|
locale?: string;
|
||||||
}
|
}
|
||||||
export interface AdminSocketTokenData {
|
export interface AdminSocketTokenData {
|
||||||
authorizedRoomIds: string[]; //the list of rooms the client is authorized to read from.
|
authorizedRoomIds: string[]; //the list of rooms the client is authorized to read from.
|
||||||
@ -16,8 +18,8 @@ class JWTTokenManager {
|
|||||||
return Jwt.verify(token, ADMIN_SOCKETS_TOKEN) as AdminSocketTokenData;
|
return Jwt.verify(token, ADMIN_SOCKETS_TOKEN) as AdminSocketTokenData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public createAuthToken(identifier: string, accessToken?: string) {
|
public createAuthToken(identifier: string, accessToken?: string, username?: string, locale?: string) {
|
||||||
return Jwt.sign({ identifier, accessToken }, SECRET_KEY, { expiresIn: "30d" });
|
return Jwt.sign({ identifier, accessToken, username, locale }, SECRET_KEY, { expiresIn: "30d" });
|
||||||
}
|
}
|
||||||
|
|
||||||
public verifyJWTToken(token: string, ignoreExpiration: boolean = false): AuthTokenData {
|
public verifyJWTToken(token: string, ignoreExpiration: boolean = false): AuthTokenData {
|
||||||
|
@ -4,6 +4,9 @@ import {
|
|||||||
OPID_CLIENT_SECRET,
|
OPID_CLIENT_SECRET,
|
||||||
OPID_CLIENT_ISSUER,
|
OPID_CLIENT_ISSUER,
|
||||||
OPID_CLIENT_REDIRECT_URL,
|
OPID_CLIENT_REDIRECT_URL,
|
||||||
|
OPID_USERNAME_CLAIM,
|
||||||
|
OPID_LOCALE_CLAIM,
|
||||||
|
OPID_SCOPE,
|
||||||
} from "../Enum/EnvironmentVariable";
|
} from "../Enum/EnvironmentVariable";
|
||||||
|
|
||||||
class OpenIDClient {
|
class OpenIDClient {
|
||||||
@ -25,8 +28,11 @@ class OpenIDClient {
|
|||||||
|
|
||||||
public authorizationUrl(state: string, nonce: string, playUri?: string, redirect?: string) {
|
public authorizationUrl(state: string, nonce: string, playUri?: string, redirect?: string) {
|
||||||
return this.initClient().then((client) => {
|
return this.initClient().then((client) => {
|
||||||
|
if (!OPID_SCOPE.includes("email") || !OPID_SCOPE.includes("openid")) {
|
||||||
|
throw new Error("Invalid scope, 'email' and 'openid' are required in OPID_SCOPE.");
|
||||||
|
}
|
||||||
return client.authorizationUrl({
|
return client.authorizationUrl({
|
||||||
scope: "openid email",
|
scope: OPID_SCOPE,
|
||||||
prompt: "login",
|
prompt: "login",
|
||||||
state: state,
|
state: state,
|
||||||
nonce: nonce,
|
nonce: nonce,
|
||||||
@ -36,7 +42,10 @@ class OpenIDClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public getUserInfo(code: string, nonce: string): Promise<{ email: string; sub: string; access_token: string }> {
|
public getUserInfo(
|
||||||
|
code: string,
|
||||||
|
nonce: string
|
||||||
|
): Promise<{ email: string; sub: string; access_token: string; username: string; locale: string }> {
|
||||||
return this.initClient().then((client) => {
|
return this.initClient().then((client) => {
|
||||||
return client.callback(OPID_CLIENT_REDIRECT_URL, { code }, { nonce }).then((tokenSet) => {
|
return client.callback(OPID_CLIENT_REDIRECT_URL, { code }, { nonce }).then((tokenSet) => {
|
||||||
return client.userinfo(tokenSet).then((res) => {
|
return client.userinfo(tokenSet).then((res) => {
|
||||||
@ -45,6 +54,8 @@ class OpenIDClient {
|
|||||||
email: res.email as string,
|
email: res.email as string,
|
||||||
sub: res.sub,
|
sub: res.sub,
|
||||||
access_token: tokenSet.access_token as string,
|
access_token: tokenSet.access_token as string,
|
||||||
|
username: res[OPID_USERNAME_CLAIM] as string,
|
||||||
|
locale: res[OPID_LOCALE_CLAIM] as string,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -11,9 +11,9 @@ RUN yarn run tsc
|
|||||||
FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76
|
FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76
|
||||||
WORKDIR /usr/src
|
WORKDIR /usr/src
|
||||||
COPY uploader/yarn.lock uploader/package.json ./
|
COPY uploader/yarn.lock uploader/package.json ./
|
||||||
COPY --from=builder2 /usr/src/dist /usr/src/dist
|
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
RUN yarn install --production
|
RUN yarn install --production
|
||||||
|
COPY --from=builder2 /usr/src/dist /usr/src/dist
|
||||||
|
|
||||||
USER node
|
USER node
|
||||||
CMD ["yarn", "run", "runprod"]
|
CMD ["yarn", "run", "runprod"]
|
||||||
|
Loading…
Reference in New Issue
Block a user