Merge remote-tracking branch 'upstream/develop' into electron

This commit is contained in:
Anton Bracke 2022-03-03 14:43:33 +01:00
commit 4535a8ae96
No known key found for this signature in database
GPG Key ID: B1222603899C6B25
36 changed files with 271 additions and 147 deletions

View File

@ -28,7 +28,7 @@ You can use [GitHub issue tracker](https://github.com/thecodingmachine/workadven
- File bug reports
- 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).
@ -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
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

View File

@ -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 office image](README-MAP.png)

View File

@ -1,8 +1,10 @@
# protobuf build
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder
WORKDIR /usr/src
COPY messages/yarn.lock messages/package.json ./
RUN yarn install
COPY messages .
RUN yarn install && yarn proto
RUN yarn proto
# typescript build
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
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
COPY --from=builder2 /usr/src/dist /usr/src/dist
USER node
CMD ["yarn", "run", "runprod"]

View File

@ -9,10 +9,7 @@ services:
build:
context: ./
dockerfile: front/Dockerfile
environment:
STARTUP_COMMAND_1: 'envsubst < dist/env-config.template.js > dist/env-config.js'
STARTUP_COMMAND_2: ''
command: apache2-foreground
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;'"
volumes: []
labels:
- "traefik.enable=true"
@ -29,9 +26,6 @@ services:
build:
context: ./
dockerfile: pusher/Dockerfile
environment:
STARTUP_COMMAND_1: ''
STARTUP_COMMAND_2: ''
command: yarn run runprod
volumes: []
@ -40,8 +34,5 @@ services:
build:
context: ./
dockerfile: back/Dockerfile
environment:
STARTUP_COMMAND_1: ''
STARTUP_COMMAND_2: ''
command: yarn run runprod
volumes: []

View File

@ -76,6 +76,9 @@ services:
OPID_CLIENT_ISSUER: $OPID_CLIENT_ISSUER
OPID_CLIENT_REDIRECT_URL: $OPID_CLIENT_REDIRECT_URL
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
volumes:
- ./pusher:/usr/src/app

View File

@ -85,6 +85,9 @@ services:
OPID_CLIENT_ISSUER: $OPID_CLIENT_ISSUER
OPID_CLIENT_REDIRECT_URL: $OPID_CLIENT_REDIRECT_URL
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
volumes:
- ./pusher:/usr/src/app

View File

@ -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)
* 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
By default, Jitsi meet will open when a user enters the zone defined on the map.

View File

@ -1,31 +1,28 @@
# protobuf build
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder
WORKDIR /usr/src/messages
WORKDIR /usr/src
COPY messages/yarn.lock messages/package.json ./
RUN yarn install
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 .
# move messages to front
RUN cp -r ../messages/ts-proto-generated/protos/* src/Messages/ts-proto-generated
COPY --from=builder /usr/src/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 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
COPY front/templater.sh .
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/
EXPOSE 80
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;'"]

51
front/nginx.conf Normal file
View 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;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -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

View File

@ -21,7 +21,7 @@
onMount(() => {
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.alt = coWebsite.getUrl().hostname;
icon.onload = () => {
@ -188,10 +188,16 @@
/>
</rect>
</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>
<style lang="scss">
.cowebsite-thumbnail {
cursor: url("../../../style/images/cursor_pointer.png"), pointer;
position: relative;
padding: 0;
background-color: rgba(#000000, 0.6);
@ -236,6 +242,11 @@
height: 40px;
}
.cowebsite-hover {
top: -4px;
left: 55px;
}
animation: shake 0.35s ease-in-out;
}
@ -315,10 +326,33 @@
}
&.jitsi {
filter: invert(100%);
-webkit-filter: invert(100%);
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>

View File

@ -24,6 +24,7 @@
left: 2%;
overflow-x: auto;
overflow-y: hidden;
overflow: visible;
&.vertical {
height: auto !important;
@ -31,8 +32,6 @@
bottom: auto !important;
left: auto !important;
position: relative;
overflow-x: hidden;
overflow-y: auto;
flex-direction: column;
align-items: center;
padding-top: 4px;

View File

@ -61,6 +61,7 @@
on:dragstart|preventDefault={noDrag}
on:click|preventDefault={showMenu}
/>
{/if}
<img
src={logoTalk}
alt={$LL.menu.icon.open.chat()}
@ -69,7 +70,6 @@
on:dragstart|preventDefault={noDrag}
on:click|preventDefault={showChat}
/>
{/if}
</main>
<style lang="scss">

View File

@ -4,13 +4,15 @@
import type { Streamable } from "../../Stores/StreamableCollectionStore";
import type { ScreenSharingPeer } from "../../WebRtc/ScreenSharingPeer";
import { getColorByString, srcObject } from "./utils";
import { getColorByString, srcObject, getTextColorByBackgroundColor } from "./utils";
export let clickable = false;
export let peer: ScreenSharingPeer;
let streamStore = peer.streamStore;
let name = peer.userName;
let backGroundColor = getColorByString(peer.userName);
let textColor = getTextColorByBackgroundColor(backGroundColor);
let statusStore = peer.statusStore;
let embedScreen: EmbedScreen;
@ -32,7 +34,7 @@
{/if}
{#if $streamStore !== null}
<i class="container">
<span style="background-color: {getColorByString(name)};">{name}</span>
<span style="background-color: {backGroundColor}; color: {textColor};">{name}</span>
</i>
<!-- svelte-ignore a11y-media-has-caption -->
<video

View File

@ -5,7 +5,7 @@
import reportImg from "./images/report.svg";
import blockSignImg from "./images/blockSign.svg";
import { showReportScreenStore } from "../../Stores/ShowReportScreenStore";
import { getColorByString, srcObject } from "./utils";
import { getColorByString, getTextColorByBackgroundColor, srcObject } from "./utils";
import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore";
import type { EmbedScreen } from "../../Stores/EmbedScreensStore";
import type { Streamable } from "../../Stores/StreamableCollectionStore";
@ -20,6 +20,8 @@
let streamStore = peer.streamStore;
let volumeStore = peer.volumeStore;
let name = peer.userName;
let backGroundColor = getColorByString(peer.userName);
let textColor = getTextColorByBackgroundColor(backGroundColor);
let statusStore = peer.statusStore;
let constraintStore = peer.constraintsStore;
@ -65,7 +67,7 @@
{/if}
<!-- {#if !$constraintStore || $constraintStore.video === false} -->
<i class="container">
<span style="background-color: {getColorByString(name)};">{name}</span>
<span style="background-color: {backGroundColor}; color: {textColor};">{name}</span>
</i>
<div class="woka-icon {($constraintStore && $constraintStore.video !== false) || minimized ? '' : 'no-video'}">
<Woka userId={peer.userId} placeholderSrc={""} />

View File

@ -18,6 +18,24 @@ export function getColorByString(str: string): string | null {
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) {
node.srcObject = stream;
return {

View File

@ -4,36 +4,33 @@
import { ADMIN_URL } from "../../Enum/EnvironmentVariable";
import LL from "../../i18n/i18n-svelte";
const upgradeLink = ADMIN_URL + "/pricing";
const registerLink = ADMIN_URL + "/second-step-register";
</script>
<main class="warningMain" transition:fly={{ y: -200, duration: 500 }}>
{#if $userIsAdminStore}
<h2>{$LL.warning.title()}</h2>
<p>
{$LL.warning.content({ upgradeLink })}
</p>
<p>{@html $LL.warning.content()}</p>
{:else if $limitMapStore}
<p>
This map is available for 2 days. You can register your domain <a href={registerLink}>here</a>!
</p>
{:else}
<h2>{$LL.warning.title()}</h2>
<p>{$LL.warning.limit()}</p>
<p>{@html $LL.warning.content()}</p>
{/if}
</main>
<style lang="scss">
main.warningMain {
pointer-events: auto;
width: 80%;
width: 100%;
background-color: #f9e81e;
color: #14304c;
text-align: center;
position: absolute;
top: 4%;
top: 0;
left: 0;
right: 0;
margin-left: auto;

View File

@ -16,6 +16,10 @@ import { isRegisterData } from "../Messages/JsonMessages/RegisterData";
import { isAdminApiData } from "../Messages/JsonMessages/AdminApiData";
import { limitMapStore } from "../Stores/GameStore";
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 {
private localUser!: LocalUser;
@ -342,9 +346,12 @@ class ConnectionManager {
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 },
}).then((res) => {
}
).then((res) => {
return res.data;
});
localUserStore.setAuthToken(authToken);
@ -352,6 +359,27 @@ class ConnectionManager {
localUserStore.saveUser(this.localUser);
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
userIsConnected.set(true);
}

View File

@ -97,7 +97,7 @@ export abstract class Character extends Container implements OutlineableInterfac
fontFamily: '"Press Start 2P"',
fontSize: "8px",
strokeThickness: 2,
stroke: "gray",
stroke: "#14304C",
metrics: {
ascent: 20,
descent: 10,

View File

@ -13,7 +13,7 @@ export class ActivatablesManager {
private canSelectByDistance: boolean = true;
private readonly outlineColor = 0xffff00;
private readonly outlineColor = 0xf9e81e;
private readonly directionalActivationPositionShift = 50;
constructor(currentPlayer: Player) {

View File

@ -1220,6 +1220,8 @@ ${escapedMessage}
openCoWebsite.closable ?? true
);
coWebsiteManager.addCoWebsiteToStore(coWebsite, openCoWebsite.position);
if (openCoWebsite.lazy === undefined || !openCoWebsite.lazy) {
await coWebsiteManager.loadCoWebsite(coWebsite);
}
@ -1596,6 +1598,8 @@ ${escapedMessage}
}
})
.catch((reason) => console.warn(reason));
urlManager.clearHashParameter();
} catch (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.pointerOverOutline(0x00ffff);
this.CurrentPlayer.pointerOverOutline(0x365dff);
});
this.CurrentPlayer.on(Phaser.Input.Events.POINTER_OUT, (pointer: Phaser.Input.Pointer) => {
this.CurrentPlayer.pointerOutOutline();

View File

@ -56,7 +56,7 @@ export class ActionableItem implements ActivatableInterface {
this.getOutlinePlugin()?.add(this.sprite, {
thickness: 2,
outlineColor: 0xffff00,
outlineColor: 0xf9e81e,
});
}

View File

@ -58,6 +58,10 @@ class UrlManager {
return this.getHashParameters()[name];
}
public clearHashParameter(): void {
window.location.hash = "";
}
private getHashParameters(): Record<string, string> {
return window.location.hash
.substring(1)

View File

@ -1,9 +1,11 @@
import type { Translation } from "../i18n-types";
import { ADMIN_URL } from "../../Enum/EnvironmentVariable";
const upgradeLink = ADMIN_URL + "/pricing";
const warning: NonNullable<Translation["warning"]> = {
title: "Warnung!",
content:
'Diese Welt erreicht bald die maximale Kapazität. Du kannst die Kapazität <a href={upgradeLink} target="_blank">hier</a> erhöhen',
content: `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!",
accessDenied: {
camera: "Zugriff auf die Kamera verweigert. Hier klicken um deine Browser Berechtigungen zu prüfen.",

View File

@ -1,9 +1,11 @@
import type { BaseTranslation } from "../i18n-types";
import { ADMIN_URL } from "../../Enum/EnvironmentVariable";
const upgradeLink = ADMIN_URL + "/pricing";
const warning: BaseTranslation = {
title: "Warning!",
content:
'This world is close to its limit!. You can upgrade its capacity <a href={upgradeLink} target="_blank">here</a>',
content: `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!",
accessDenied: {
camera: "Camera access denied. Click here and check your browser permissions.",

View File

@ -1,9 +1,11 @@
import type { Translation } from "../i18n-types";
import { ADMIN_URL } from "../../Enum/EnvironmentVariable";
const upgradeLink = ADMIN_URL + "/pricing";
const warning: NonNullable<Translation["warning"]> = {
title: "Attention!",
content:
'Ce monde est proche de sa limite ! Vous pouvez améliorer sa capacité <a href={upgradeLink} target="_blank">içi</a>',
content: `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!",
accessDenied: {
camera: "Accès à la caméra refusé. Cliquez ici et vérifiez les autorisations de votre navigateur.",

View File

@ -1,7 +1,7 @@
#!/usr/bin/env bash
#!/usr/bin/env sh
set -x
set -o nounset errexit
index_file=dist/index.html
index_file=/usr/share/nginx/html/index.html
tmp_trackcodefile=/tmp/trackcode
# 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
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}"
fi
# 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}"
sed "s#<!-- TRACKING NUMBER -->#${GA_TRACKING_ID:-}#g" "${ANALYTICS_CODE_PATH}" > "$tmp_trackcodefile"
fi

View File

@ -475,12 +475,7 @@ ansi-escapes@^4.3.0:
dependencies:
type-fest "^0.21.3"
ansi-regex@^5.0.0:
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:
ansi-regex@^5.0.0, ansi-regex@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==

View File

@ -1,8 +1,10 @@
# protobuf build
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder
WORKDIR /usr/src
COPY messages/yarn.lock messages/package.json ./
RUN yarn install
COPY messages .
RUN yarn install && yarn proto
RUN yarn proto
# typescript build
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
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
COPY --from=builder2 /usr/src/dist /usr/src/dist
USER node
CMD ["yarn", "run", "runprod"]

View File

@ -84,11 +84,13 @@ export class AdminController extends BaseController {
if (ADMIN_API_TOKEN === "") {
res.writeStatus("401 Unauthorized").end("No token configured!");
res.end();
return;
}
if (token !== ADMIN_API_TOKEN) {
console.error("Admin access refused for token: " + token);
res.writeStatus("401 Unauthorized").end("Incorrect token");
res.end();
return;
}

View File

@ -93,7 +93,15 @@ export class AuthenticateController extends BaseController {
res.writeStatus("200");
this.addCorsHeaders(res);
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) {
console.info("User was not connected", err);
}
@ -115,7 +123,12 @@ export class AuthenticateController extends BaseController {
if (!email) {
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
//This is very important to create User Local in LocalStorage in WorkAdventure
@ -124,7 +137,9 @@ export class AuthenticateController extends BaseController {
res.writeStatus("200");
this.addCorsHeaders(res);
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) {
console.error("openIDCallback => ERROR", e);
return this.errorToResponse(e, res);

View File

@ -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_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_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 {

View File

@ -5,6 +5,8 @@ import { InvalidTokenError } from "../Controller/InvalidTokenError";
export interface AuthTokenData {
identifier: string; //will be a email if logged in or an uuid if anonymous
accessToken?: string;
username?: string;
locale?: string;
}
export interface AdminSocketTokenData {
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;
}
public createAuthToken(identifier: string, accessToken?: string) {
return Jwt.sign({ identifier, accessToken }, SECRET_KEY, { expiresIn: "30d" });
public createAuthToken(identifier: string, accessToken?: string, username?: string, locale?: string) {
return Jwt.sign({ identifier, accessToken, username, locale }, SECRET_KEY, { expiresIn: "30d" });
}
public verifyJWTToken(token: string, ignoreExpiration: boolean = false): AuthTokenData {

View File

@ -4,6 +4,9 @@ import {
OPID_CLIENT_SECRET,
OPID_CLIENT_ISSUER,
OPID_CLIENT_REDIRECT_URL,
OPID_USERNAME_CLAIM,
OPID_LOCALE_CLAIM,
OPID_SCOPE,
} from "../Enum/EnvironmentVariable";
class OpenIDClient {
@ -25,8 +28,11 @@ class OpenIDClient {
public authorizationUrl(state: string, nonce: string, playUri?: string, redirect?: string) {
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({
scope: "openid email",
scope: OPID_SCOPE,
prompt: "login",
state: state,
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 client.callback(OPID_CLIENT_REDIRECT_URL, { code }, { nonce }).then((tokenSet) => {
return client.userinfo(tokenSet).then((res) => {
@ -45,6 +54,8 @@ class OpenIDClient {
email: res.email as string,
sub: res.sub,
access_token: tokenSet.access_token as string,
username: res[OPID_USERNAME_CLAIM] as string,
locale: res[OPID_LOCALE_CLAIM] as string,
};
});
});

View File

@ -11,9 +11,9 @@ RUN yarn run tsc
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
COPY --from=builder2 /usr/src/dist /usr/src/dist
USER node
CMD ["yarn", "run", "runprod"]