Merge branch 'master' of https://github.com/thecodingmachine/workadventure into feature/back-players-proximity
This commit is contained in:
commit
bb2315e7ac
@ -21,9 +21,13 @@
|
||||
"homepage": "https://github.com/thecodingmachine/workadventure#readme",
|
||||
"dependencies": {
|
||||
"@types/express": "^4.17.4",
|
||||
"@types/http-status-codes": "^1.2.0",
|
||||
"@types/jsonwebtoken": "^8.3.8",
|
||||
"@types/socket.io": "^2.1.4",
|
||||
"body-parser": "^1.19.0",
|
||||
"express": "^4.17.1",
|
||||
"http-status-codes": "^1.4.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"socket.io": "^2.3.0",
|
||||
"ts-node-dev": "^1.0.0-pre.44",
|
||||
"typescript": "^3.8.3"
|
||||
|
@ -1,5 +1,6 @@
|
||||
// lib/app.ts
|
||||
import {IoSocketController} from "./Controller/IoSocketController";
|
||||
import {IoSocketController} from "./Controller/IoSocketController"; //TODO fix impot by "_Controller/..."
|
||||
import {AuthenticateController} from "./Controller/AuthenticateController"; //TODO fix impot by "_Controller/..."
|
||||
import express from "express";
|
||||
import {Application} from 'express';
|
||||
import bodyParser = require('body-parser');
|
||||
@ -9,14 +10,21 @@ class App {
|
||||
public app: Application;
|
||||
public server: http.Server;
|
||||
public ioSocketController: IoSocketController;
|
||||
public authenticateController: AuthenticateController;
|
||||
|
||||
constructor() {
|
||||
this.app = express();
|
||||
|
||||
//config server http
|
||||
this.config();
|
||||
this.server = http.createServer(this.app);
|
||||
|
||||
//create controllers
|
||||
this.ioSocketController = new IoSocketController(this.server);
|
||||
this.authenticateController = new AuthenticateController(this.app);
|
||||
}
|
||||
|
||||
// TODO add session user
|
||||
private config(): void {
|
||||
this.app.use(bodyParser.json());
|
||||
this.app.use(bodyParser.urlencoded({extended: false}));
|
||||
|
28
back/src/Controller/AuthenticateController.ts
Normal file
28
back/src/Controller/AuthenticateController.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import {Application, Request, Response} from "express";
|
||||
import Jwt, {JsonWebTokenError} from "jsonwebtoken";
|
||||
import {BAD_REQUEST, OK} from "http-status-codes";
|
||||
import {SECRET_KEY} from "../Enum/EnvironmentVariable";
|
||||
|
||||
export class AuthenticateController{
|
||||
App : Application;
|
||||
|
||||
constructor(App : Application) {
|
||||
this.App = App;
|
||||
this.login();
|
||||
}
|
||||
|
||||
//permit to login on application. Return token to connect on Websocket IO.
|
||||
login(){
|
||||
this.App.post("/login", (req: Request, res: Response) => {
|
||||
let param = req.body;
|
||||
if(!param.email){
|
||||
return res.status(BAD_REQUEST).send({
|
||||
message: "email parameter is empty"
|
||||
});
|
||||
}
|
||||
//TODO check user email for The Coding Machine game
|
||||
let token = Jwt.sign({email: param.email}, SECRET_KEY, {expiresIn: '24h'});
|
||||
return res.status(OK).send({token: token});
|
||||
});
|
||||
}
|
||||
}
|
@ -1,36 +1,80 @@
|
||||
import socketIO = require('socket.io');
|
||||
import {Socket} from "socket.io";
|
||||
import * as http from "http";
|
||||
import {MessageUserPosition} from "@Model/Websocket/MessageUserPosition";
|
||||
import {MessageUserPosition} from "../Model/Websocket/MessageUserPosition"; //TODO fix import by "_Model/.."
|
||||
import {ExSocketInterface} from "../Model/Websocket/ExSocketInterface"; //TODO fix import by "_Model/.."
|
||||
import Jwt, {JsonWebTokenError} from "jsonwebtoken";
|
||||
import {SECRET_KEY} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..."
|
||||
|
||||
export class IoSocketController{
|
||||
Io: socketIO.Server;
|
||||
constructor(server : http.Server) {
|
||||
this.Io = socketIO(server);
|
||||
|
||||
// Authentication with token. it will be decoded and stored in the socket.
|
||||
this.Io.use( (socket: Socket, next) => {
|
||||
if (!socket.handshake.query || !socket.handshake.query.token) {
|
||||
return next(new Error('Authentication error'));
|
||||
}
|
||||
Jwt.verify(socket.handshake.query.token, SECRET_KEY, (err: JsonWebTokenError, tokenDecoded: object) => {
|
||||
if (err) {
|
||||
return next(new Error('Authentication error'));
|
||||
}
|
||||
(socket as ExSocketInterface).token = tokenDecoded;
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
this.ioConnection();
|
||||
}
|
||||
|
||||
ioConnection() {
|
||||
this.Io.on('connection', (socket: Socket) => {
|
||||
//TODO check token access
|
||||
|
||||
/*join-rom event permit to join one room.
|
||||
message :
|
||||
userId : user identification
|
||||
roomId: room identification
|
||||
positionXUser: user x position map
|
||||
positionYUser: user y position on map
|
||||
position: position of user in map
|
||||
x: user x position on map
|
||||
y: user y position on map
|
||||
*/
|
||||
socket.on('join-room', (message : MessageUserPosition) => {
|
||||
socket.join(message.roomId);
|
||||
|
||||
socket.on('join-room', (message : string) => {
|
||||
let messageUserPosition = this.hydrateMessageReceive(message);
|
||||
if(messageUserPosition instanceof Error){
|
||||
return socket.emit("message-error", JSON.stringify({message: messageUserPosition.message}))
|
||||
}
|
||||
//join user in room
|
||||
socket.join(messageUserPosition.roomId);
|
||||
// sending to all clients in room except sender
|
||||
socket.to(message.roomId).emit('join-room', message.toString());
|
||||
this.saveUserPosition((socket as ExSocketInterface), messageUserPosition);
|
||||
socket.to(messageUserPosition.roomId).emit('join-room', messageUserPosition.toString());
|
||||
});
|
||||
|
||||
socket.on('user-position', (message : MessageUserPosition) => {
|
||||
socket.on('user-position', (message : string) => {
|
||||
let messageUserPosition = this.hydrateMessageReceive(message);
|
||||
if(messageUserPosition instanceof Error){
|
||||
return socket.emit("message-error", JSON.stringify({message: messageUserPosition.message}));
|
||||
}
|
||||
// sending to all clients in room except sender
|
||||
socket.to(message.roomId).emit('join-room', message.toString());
|
||||
this.saveUserPosition((socket as ExSocketInterface), messageUserPosition);
|
||||
socket.to(messageUserPosition.roomId).emit('join-room', messageUserPosition.toString());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//permit to save user position in socket
|
||||
saveUserPosition(socket : ExSocketInterface, message : MessageUserPosition){
|
||||
socket.position = message.position;
|
||||
}
|
||||
|
||||
//Hydrate and manage error
|
||||
hydrateMessageReceive(message : string) : MessageUserPosition | Error{
|
||||
try {
|
||||
return new MessageUserPosition(message);
|
||||
}catch (err) {
|
||||
//TODO log error
|
||||
return new Error(err);
|
||||
}
|
||||
}
|
||||
}
|
5
back/src/Enum/EnvironmentVariable.ts
Normal file
5
back/src/Enum/EnvironmentVariable.ts
Normal file
@ -0,0 +1,5 @@
|
||||
const SECRET_KEY = process.env.SECRET_KEY || "THECODINGMACHINE_SECRET_KEY";
|
||||
|
||||
export {
|
||||
SECRET_KEY
|
||||
}
|
7
back/src/Model/Websocket/ExSocketInterface.ts
Normal file
7
back/src/Model/Websocket/ExSocketInterface.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import {Socket} from "socket.io";
|
||||
import {PointInterface} from "./PointInterface";
|
||||
|
||||
export interface ExSocketInterface extends Socket {
|
||||
token: object;
|
||||
position: PointInterface;
|
||||
}
|
@ -4,6 +4,9 @@ export class Message {
|
||||
|
||||
constructor(message: string) {
|
||||
let data = JSON.parse(message);
|
||||
if(!data.userId || !data.roomId){
|
||||
throw Error("userId and roomId cannot be null");
|
||||
}
|
||||
this.userId = data.userId;
|
||||
this.roomId = data.roomId;
|
||||
}
|
||||
|
@ -1,14 +1,33 @@
|
||||
import {Message} from "./Message";
|
||||
import {PointInterface} from "./PointInterface";
|
||||
|
||||
export class Point implements PointInterface{
|
||||
x: number;
|
||||
y: number;
|
||||
|
||||
constructor(x : number, y : number) {
|
||||
if(!x || !y){
|
||||
throw Error("position x and y cannot be null");
|
||||
}
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
toJson(){
|
||||
return {
|
||||
x : this.x,
|
||||
y: this.y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class MessageUserPosition extends Message{
|
||||
positionXUser: string;
|
||||
positionYUser: string;
|
||||
position: PointInterface
|
||||
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
let data = JSON.parse(message);
|
||||
this.positionXUser = data.positionXUser;
|
||||
this.positionYUser = data.positionYUser;
|
||||
this.position = new Point(data.position.x, data.position.y);
|
||||
}
|
||||
|
||||
toString() {
|
||||
@ -16,8 +35,7 @@ export class MessageUserPosition extends Message{
|
||||
Object.assign(
|
||||
super.toJson(),
|
||||
{
|
||||
positionXUser: this.positionXUser,
|
||||
positionYUser: this.positionYUser
|
||||
position: this.position.toJson()
|
||||
})
|
||||
);
|
||||
}
|
||||
|
5
back/src/Model/Websocket/PointInterface.ts
Normal file
5
back/src/Model/Websocket/PointInterface.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface PointInterface {
|
||||
x: number;
|
||||
y: number;
|
||||
toJson() : object;
|
||||
}
|
@ -43,8 +43,8 @@
|
||||
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
"baseUrl": ".", /* Base directory to resolve non-absolute module names. */
|
||||
"paths": {
|
||||
"@Controller/*": ["src/Controller/*"],
|
||||
"@Model/*": ["src/Model/*"]
|
||||
"_Controller/*": ["src/Controller/*"],
|
||||
"_Model/*": ["src/Model/*"]
|
||||
}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
|
106
back/yarn.lock
106
back/yarn.lock
@ -66,11 +66,25 @@
|
||||
"@types/qs" "*"
|
||||
"@types/serve-static" "*"
|
||||
|
||||
"@types/http-status-codes@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/http-status-codes/-/http-status-codes-1.2.0.tgz#6e5244835aaf7164dd306f1d4d2dfdbb2159d909"
|
||||
integrity sha512-vjpjevMaxtrtdrrV/TQNIFT7mKL8nvIKG7G/LjMDZdVvqRxRg5SNfGkeuSaowVc0rbK8xDA2d/Etunyb5GyzzA==
|
||||
dependencies:
|
||||
http-status-codes "*"
|
||||
|
||||
"@types/json-schema@^7.0.3":
|
||||
version "7.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339"
|
||||
integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==
|
||||
|
||||
"@types/jsonwebtoken@^8.3.8":
|
||||
version "8.3.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.3.8.tgz#b27c9156dde2049ae03e56528a53ef5a8294aa82"
|
||||
integrity sha512-g2ke5+AR/RKYpQxd+HJ2yisLHGuOV0uourOcPtKlcT5Zqv4wFg9vKhFpXEztN4H/6Y6RSUKioz/2PTFPP30CTA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/mime@*":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d"
|
||||
@ -317,6 +331,11 @@ brace-expansion@^1.1.7:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
buffer-equal-constant-time@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
|
||||
integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=
|
||||
|
||||
buffer-from@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||
@ -546,6 +565,13 @@ dynamic-dedupe@^0.3.0:
|
||||
dependencies:
|
||||
xtend "^4.0.0"
|
||||
|
||||
ecdsa-sig-formatter@1.0.11:
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
|
||||
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
|
||||
dependencies:
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
ee-first@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||
@ -967,6 +993,11 @@ http-errors@~1.7.2:
|
||||
statuses ">= 1.5.0 < 2"
|
||||
toidentifier "1.0.0"
|
||||
|
||||
http-status-codes@*, http-status-codes@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-1.4.0.tgz#6e4c15d16ff3a9e2df03b89f3a55e1aae05fb477"
|
||||
integrity sha512-JrT3ua+WgH8zBD3HEJYbeEgnuQaAnUeRRko/YojPAJjGmIfGD3KPU/asLdsLwKjfxOmQe5nXMQ0pt/7MyapVbQ==
|
||||
|
||||
iconv-lite@0.4.24, iconv-lite@^0.4.24:
|
||||
version "0.4.24"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
||||
@ -1126,6 +1157,39 @@ json-stable-stringify-without-jsonify@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
|
||||
integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
|
||||
|
||||
jsonwebtoken@^8.5.1:
|
||||
version "8.5.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
|
||||
integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==
|
||||
dependencies:
|
||||
jws "^3.2.2"
|
||||
lodash.includes "^4.3.0"
|
||||
lodash.isboolean "^3.0.3"
|
||||
lodash.isinteger "^4.0.4"
|
||||
lodash.isnumber "^3.0.3"
|
||||
lodash.isplainobject "^4.0.6"
|
||||
lodash.isstring "^4.0.1"
|
||||
lodash.once "^4.0.0"
|
||||
ms "^2.1.1"
|
||||
semver "^5.6.0"
|
||||
|
||||
jwa@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a"
|
||||
integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==
|
||||
dependencies:
|
||||
buffer-equal-constant-time "1.0.1"
|
||||
ecdsa-sig-formatter "1.0.11"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
jws@^3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304"
|
||||
integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==
|
||||
dependencies:
|
||||
jwa "^1.4.1"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
levn@^0.3.0, levn@~0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
|
||||
@ -1145,6 +1209,41 @@ load-json-file@^1.0.0:
|
||||
pinkie-promise "^2.0.0"
|
||||
strip-bom "^2.0.0"
|
||||
|
||||
lodash.includes@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
|
||||
integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=
|
||||
|
||||
lodash.isboolean@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
|
||||
integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=
|
||||
|
||||
lodash.isinteger@^4.0.4:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
|
||||
integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=
|
||||
|
||||
lodash.isnumber@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
|
||||
integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=
|
||||
|
||||
lodash.isplainobject@^4.0.6:
|
||||
version "4.0.6"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
|
||||
integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=
|
||||
|
||||
lodash.isstring@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
|
||||
integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=
|
||||
|
||||
lodash.once@^4.0.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
|
||||
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
|
||||
|
||||
lodash@^4.17.14, lodash@^4.17.15:
|
||||
version "4.17.15"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
|
||||
@ -1568,12 +1667,17 @@ safe-buffer@5.1.2:
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
|
||||
safe-buffer@^5.0.1:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
|
||||
integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
|
||||
|
||||
"safer-buffer@>= 2.1.2 < 3":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
"semver@2 || 3 || 4 || 5", semver@^5.5.0:
|
||||
"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
|
@ -29,6 +29,7 @@ services:
|
||||
command: yarn dev
|
||||
environment:
|
||||
STARTUP_COMMAND_1: yarn install
|
||||
SECRET_KEY: yourSecretKey
|
||||
volumes:
|
||||
- ./back:/usr/src/app
|
||||
labels:
|
||||
|
Loading…
Reference in New Issue
Block a user