Merge branch 'develop' of github.com:thecodingmachine/workadventure into fix/deploy-cleanup
# Conflicts: # front/Dockerfile
This commit is contained in:
commit
686427f6fe
222 changed files with 1355 additions and 12017 deletions
|
@ -10,8 +10,8 @@
|
|||
"runprod": "node --max-old-space-size=4096 ./dist/server.js",
|
||||
"profile": "tsc && node --prof ./dist/server.js",
|
||||
"test": "ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json",
|
||||
"lint": "node_modules/.bin/eslint src/ . --ext .ts",
|
||||
"fix": "node_modules/.bin/eslint --fix src/ . --ext .ts"
|
||||
"lint": "DEBUG= node_modules/.bin/eslint src/ . --ext .ts",
|
||||
"fix": "DEBUG= node_modules/.bin/eslint --fix src/ . --ext .ts"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -52,7 +52,7 @@
|
|||
"multer": "^1.4.2",
|
||||
"prom-client": "^12.0.0",
|
||||
"query-string": "^6.13.3",
|
||||
"systeminformation": "^4.30.5",
|
||||
"systeminformation": "^4.31.1",
|
||||
"ts-node-dev": "^1.0.0-pre.44",
|
||||
"typescript": "^3.8.3",
|
||||
"uWebSockets.js": "uNetworking/uWebSockets.js#v18.5.0",
|
||||
|
|
|
@ -60,10 +60,7 @@ export class AuthenticateController extends BaseController {
|
|||
}));
|
||||
|
||||
} catch (e) {
|
||||
console.error("An error happened", e)
|
||||
res.writeStatus(e.status || "500 Internal Server Error");
|
||||
this.addCorsHeaders(res);
|
||||
res.end('An error happened');
|
||||
this.errorToResponse(e, res);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -8,4 +8,21 @@ export class BaseController {
|
|||
res.writeHeader('access-control-allow-methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
|
||||
res.writeHeader('access-control-allow-origin', '*');
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns any exception into a HTTP response (and logs the error)
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
protected errorToResponse(e: any, res: HttpResponse): void {
|
||||
console.error("An error happened", e);
|
||||
if (e.response) {
|
||||
res.writeStatus(e.response.status+" "+e.response.statusText);
|
||||
this.addCorsHeaders(res);
|
||||
res.end("An error occurred: "+e.response.status+" "+e.response.statusText);
|
||||
} else {
|
||||
res.writeStatus("500 Internal Server Error")
|
||||
this.addCorsHeaders(res);
|
||||
res.end("An error occurred");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,18 +91,11 @@ export class IoSocketController {
|
|||
|
||||
if(message.event === 'user-message') {
|
||||
const messageToEmit = (message.message as { message: string, type: string, userUuid: string });
|
||||
switch (message.message.type) {
|
||||
case 'ban': {
|
||||
socketManager.emitSendUserMessage(messageToEmit.userUuid, messageToEmit.message, roomId);
|
||||
break;
|
||||
}
|
||||
case 'banned': {
|
||||
socketManager.emitBan(messageToEmit.userUuid, messageToEmit.message, roomId);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
if(messageToEmit.type === 'banned'){
|
||||
socketManager.emitBan(messageToEmit.userUuid, messageToEmit.message, messageToEmit.type);
|
||||
}
|
||||
if(messageToEmit.type === 'ban') {
|
||||
socketManager.emitSendUserMessage(messageToEmit.userUuid, messageToEmit.message, messageToEmit.type);
|
||||
}
|
||||
}
|
||||
}catch (err) {
|
||||
|
@ -148,6 +141,7 @@ export class IoSocketController {
|
|||
const websocketKey = req.getHeader('sec-websocket-key');
|
||||
const websocketProtocol = req.getHeader('sec-websocket-protocol');
|
||||
const websocketExtensions = req.getHeader('sec-websocket-extensions');
|
||||
const IPAddress = req.getHeader('x-forwarded-for');
|
||||
|
||||
const roomId = query.roomId;
|
||||
if (typeof roomId !== 'string') {
|
||||
|
@ -176,7 +170,7 @@ export class IoSocketController {
|
|||
characterLayers = [ characterLayers ];
|
||||
}
|
||||
|
||||
const userUuid = await jwtTokenManager.getUserUuidFromToken(token);
|
||||
const userUuid = await jwtTokenManager.getUserUuidFromToken(token, IPAddress, roomId);
|
||||
|
||||
let memberTags: string[] = [];
|
||||
let memberTextures: CharacterTexture[] = [];
|
||||
|
@ -217,6 +211,7 @@ export class IoSocketController {
|
|||
url,
|
||||
token,
|
||||
userUuid,
|
||||
IPAddress,
|
||||
roomId,
|
||||
name,
|
||||
characterLayers: characterLayerObjs,
|
||||
|
@ -336,6 +331,7 @@ export class IoSocketController {
|
|||
client.userId = this.nextUserId;
|
||||
this.nextUserId++;
|
||||
client.userUuid = ws.userUuid;
|
||||
client.IPAddress = ws.IPAddress;
|
||||
client.token = ws.token;
|
||||
client.batchedMessages = new BatchMessage();
|
||||
client.batchTimeout = null;
|
||||
|
|
|
@ -59,10 +59,7 @@ export class MapController extends BaseController{
|
|||
this.addCorsHeaders(res);
|
||||
res.end(JSON.stringify(mapDetails));
|
||||
} catch (e) {
|
||||
console.error(e.message || e);
|
||||
res.writeStatus("500 Internal Server Error")
|
||||
this.addCorsHeaders(res);
|
||||
res.end("An error occurred");
|
||||
this.errorToResponse(e, res);
|
||||
}
|
||||
})();
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
const SECRET_KEY = process.env.SECRET_KEY || "THECODINGMACHINE_SECRET_KEY";
|
||||
const URL_ROOM_STARTED = "/Floor0/floor0.json";
|
||||
const MINIMUM_DISTANCE = process.env.MINIMUM_DISTANCE ? Number(process.env.MINIMUM_DISTANCE) : 64;
|
||||
const GROUP_RADIUS = process.env.GROUP_RADIUS ? Number(process.env.GROUP_RADIUS) : 48;
|
||||
const ALLOW_ARTILLERY = process.env.ALLOW_ARTILLERY ? process.env.ALLOW_ARTILLERY == 'true' : false;
|
||||
|
@ -16,7 +15,6 @@ export const SOCKET_IDLE_TIMER = parseInt(process.env.SOCKET_IDLE_TIMER as strin
|
|||
|
||||
export {
|
||||
SECRET_KEY,
|
||||
URL_ROOM_STARTED,
|
||||
MINIMUM_DISTANCE,
|
||||
API_URL,
|
||||
ADMIN_API_URL,
|
||||
|
|
|
@ -24,6 +24,7 @@ export interface ExSocketInterface extends WebSocket, Identificable {
|
|||
roomId: string;
|
||||
//userId: number; // A temporary (autoincremented) identifier for this user
|
||||
userUuid: string; // A unique identifier for this user
|
||||
IPAddress: string; // IP address
|
||||
name: string;
|
||||
characterLayers: CharacterLayer[];
|
||||
position: PointInterface;
|
||||
|
|
|
@ -14,6 +14,11 @@ export interface AdminApiData {
|
|||
textures: CharacterTexture[]
|
||||
}
|
||||
|
||||
export interface AdminBannedData {
|
||||
is_banned: boolean,
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface CharacterTexture {
|
||||
id: number,
|
||||
level: number,
|
||||
|
@ -110,6 +115,18 @@ class AdminApi {
|
|||
headers: {"Authorization": `${ADMIN_API_TOKEN}`}
|
||||
});
|
||||
}
|
||||
|
||||
async verifyBanUser(organizationMemberToken: string, ipAddress: string, organization: string, world: string): Promise<AdminBannedData> {
|
||||
if (!ADMIN_API_URL) {
|
||||
return Promise.reject('No admin backoffice set!');
|
||||
}
|
||||
//todo: this call can fail if the corresponding world is not activated or if the token is invalid. Handle that case.
|
||||
return Axios.get(ADMIN_API_URL + '/api/check-moderate-user/'+organization+'/'+world+'?ipAddress='+ipAddress+'&token='+organizationMemberToken,
|
||||
{headers: {"Authorization": `${ADMIN_API_TOKEN}`}}
|
||||
).then((data) => {
|
||||
return data.data;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const adminApi = new AdminApi();
|
||||
|
|
|
@ -2,7 +2,7 @@ import {ADMIN_API_URL, ALLOW_ARTILLERY, SECRET_KEY} from "../Enum/EnvironmentVar
|
|||
import {uuid} from "uuidv4";
|
||||
import Jwt from "jsonwebtoken";
|
||||
import {TokenInterface} from "../Controller/AuthenticateController";
|
||||
import {adminApi, AdminApiData} from "../Services/AdminApi";
|
||||
import {adminApi, AdminBannedData} from "../Services/AdminApi";
|
||||
|
||||
class JWTTokenManager {
|
||||
|
||||
|
@ -10,7 +10,7 @@ class JWTTokenManager {
|
|||
return Jwt.sign({userUuid: userUuid}, SECRET_KEY, {expiresIn: '200d'}); //todo: add a mechanic to refresh or recreate token
|
||||
}
|
||||
|
||||
public async getUserUuidFromToken(token: unknown): Promise<string> {
|
||||
public async getUserUuidFromToken(token: unknown, ipAddress?: string, room?: string): Promise<string> {
|
||||
|
||||
if (!token) {
|
||||
throw new Error('An authentication error happened, a user tried to connect without a token.');
|
||||
|
@ -50,14 +50,22 @@ class JWTTokenManager {
|
|||
|
||||
if (ADMIN_API_URL) {
|
||||
//verify user in admin
|
||||
adminApi.fetchCheckUserByToken(tokenInterface.userUuid).then(() => {
|
||||
resolve(tokenInterface.userUuid);
|
||||
}).catch((err) => {
|
||||
//anonymous user
|
||||
if(err.response && err.response.status && err.response.status === 404){
|
||||
let promise = new Promise((resolve) => resolve());
|
||||
if(ipAddress && room) {
|
||||
promise = this.verifyBanUser(tokenInterface.userUuid, ipAddress, room);
|
||||
}
|
||||
promise.then(() => {
|
||||
adminApi.fetchCheckUserByToken(tokenInterface.userUuid).then(() => {
|
||||
resolve(tokenInterface.userUuid);
|
||||
return;
|
||||
}
|
||||
}).catch((err) => {
|
||||
//anonymous user
|
||||
if (err.response && err.response.status && err.response.status === 404) {
|
||||
resolve(tokenInterface.userUuid);
|
||||
return;
|
||||
}
|
||||
reject(err);
|
||||
});
|
||||
}).catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
} else {
|
||||
|
@ -67,6 +75,27 @@ class JWTTokenManager {
|
|||
});
|
||||
}
|
||||
|
||||
private verifyBanUser(userUuid: string, ipAddress: string, room: string): Promise<AdminBannedData> {
|
||||
const parts = room.split('/');
|
||||
if (parts.length < 3 || parts[0] !== '@') {
|
||||
return Promise.resolve({
|
||||
is_banned: false,
|
||||
message: ''
|
||||
});
|
||||
}
|
||||
|
||||
const organization = parts[1];
|
||||
const world = parts[2];
|
||||
return adminApi.verifyBanUser(userUuid, ipAddress, organization, world).then((data: AdminBannedData) => {
|
||||
if (data && data.is_banned) {
|
||||
throw new Error('User was banned');
|
||||
}
|
||||
return data;
|
||||
}).catch((err) => {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
private isValidToken(token: object): token is TokenInterface {
|
||||
return !(typeof((token as TokenInterface).userUuid) !== 'string');
|
||||
}
|
||||
|
|
|
@ -2,11 +2,8 @@ import {PusherRoom} from "../Model/PusherRoom";
|
|||
import {CharacterLayer, ExSocketInterface} from "../Model/Websocket/ExSocketInterface";
|
||||
import {
|
||||
GroupDeleteMessage,
|
||||
GroupUpdateMessage,
|
||||
ItemEventMessage,
|
||||
ItemStateMessage,
|
||||
PlayGlobalMessage,
|
||||
PointMessage,
|
||||
PositionMessage,
|
||||
RoomJoinedMessage,
|
||||
ServerToClientMessage,
|
||||
|
@ -14,23 +11,19 @@ import {
|
|||
SilentMessage,
|
||||
SubMessage,
|
||||
ReportPlayerMessage,
|
||||
UserJoinedMessage,
|
||||
UserLeftMessage,
|
||||
UserMovedMessage,
|
||||
UserMovesMessage,
|
||||
ViewportMessage,
|
||||
WebRtcDisconnectMessage,
|
||||
WebRtcSignalToClientMessage,
|
||||
WebRtcSignalToServerMessage,
|
||||
WebRtcStartMessage,
|
||||
QueryJitsiJwtMessage,
|
||||
SendJitsiJwtMessage,
|
||||
SendUserMessage,
|
||||
JoinRoomMessage,
|
||||
CharacterLayerMessage,
|
||||
PusherToBackMessage,
|
||||
AdminPusherToBackMessage,
|
||||
ServerToAdminClientMessage, AdminMessage, BanMessage
|
||||
ServerToAdminClientMessage,
|
||||
SendUserMessage,
|
||||
BanUserMessage, UserJoinedRoomMessage, UserLeftRoomMessage
|
||||
} from "../Messages/generated/messages_pb";
|
||||
import {PointInterface} from "../Model/Websocket/PointInterface";
|
||||
import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils";
|
||||
|
@ -79,23 +72,33 @@ export class SocketManager implements ZoneEventListener {
|
|||
}
|
||||
|
||||
async handleAdminRoom(client: ExAdminSocketInterface, roomId: string): Promise<void> {
|
||||
console.log('Calling adminRoom')
|
||||
const apiClient = await apiClientRepository.getClient(roomId);
|
||||
const adminRoomStream = apiClient.adminRoom();
|
||||
client.adminConnection = adminRoomStream;
|
||||
|
||||
adminRoomStream.on('data', (message: ServerToAdminClientMessage) => {
|
||||
if (message.hasUseruuidjoinedroom()) {
|
||||
const userUuid = message.getUseruuidjoinedroom();
|
||||
|
||||
if (message.hasUserjoinedroom()) {
|
||||
const userJoinedRoomMessage = message.getUserjoinedroom() as UserJoinedRoomMessage;
|
||||
if (!client.disconnecting) {
|
||||
client.send('MemberJoin:'+userUuid+';'+roomId);
|
||||
client.send(JSON.stringify({
|
||||
type: 'MemberJoin',
|
||||
data: {
|
||||
uuid: userJoinedRoomMessage.getUuid(),
|
||||
name: userJoinedRoomMessage.getName(),
|
||||
ipAddress: userJoinedRoomMessage.getIpaddress(),
|
||||
roomId: roomId,
|
||||
}
|
||||
}));
|
||||
}
|
||||
} else if (message.hasUseruuidleftroom()) {
|
||||
const userUuid = message.getUseruuidleftroom();
|
||||
|
||||
} else if (message.hasUserleftroom()) {
|
||||
const userLeftRoomMessage = message.getUserleftroom() as UserLeftRoomMessage;
|
||||
if (!client.disconnecting) {
|
||||
client.send('MemberLeave:'+userUuid+';'+roomId);
|
||||
client.send(JSON.stringify({
|
||||
type: 'MemberLeave',
|
||||
data: {
|
||||
uuid: userLeftRoomMessage.getUuid()
|
||||
}
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
throw new Error('Unexpected admin message');
|
||||
|
@ -145,15 +148,16 @@ export class SocketManager implements ZoneEventListener {
|
|||
}
|
||||
|
||||
async handleJoinRoom(client: ExSocketInterface): Promise<void> {
|
||||
const position = client.position;
|
||||
const viewport = client.viewport;
|
||||
try {
|
||||
|
||||
const joinRoomMessage = new JoinRoomMessage();
|
||||
joinRoomMessage.setUseruuid(client.userUuid);
|
||||
joinRoomMessage.setIpaddress(client.IPAddress);
|
||||
joinRoomMessage.setRoomid(client.roomId);
|
||||
joinRoomMessage.setName(client.name);
|
||||
joinRoomMessage.setPositionmessage(ProtobufUtils.toPositionMessage(client.position));
|
||||
joinRoomMessage.setTagList(client.tags);
|
||||
for (const characterLayer of client.characterLayers) {
|
||||
const characterLayerMessage = new CharacterLayerMessage();
|
||||
characterLayerMessage.setName(characterLayer.name);
|
||||
|
@ -540,51 +544,54 @@ export class SocketManager implements ZoneEventListener {
|
|||
client.send(serverToClientMessage.serializeBinary().buffer, true);
|
||||
}
|
||||
|
||||
public async emitSendUserMessage(userUuid: string, message: string, roomId: string): Promise<void> {
|
||||
public emitSendUserMessage(userUuid: string, message: string, type: string): void {
|
||||
const client = this.searchClientByUuid(userUuid);
|
||||
if(!client){
|
||||
throw Error('client not found');
|
||||
}
|
||||
|
||||
const backConnection = await apiClientRepository.getClient(roomId);
|
||||
|
||||
const adminMessage = new AdminMessage();
|
||||
adminMessage.setRecipientuuid(userUuid);
|
||||
const adminMessage = new SendUserMessage();
|
||||
adminMessage.setMessage(message);
|
||||
adminMessage.setRoomid(roomId);
|
||||
adminMessage.setType(type);
|
||||
const pusherToBackMessage = new PusherToBackMessage();
|
||||
pusherToBackMessage.setSendusermessage(adminMessage);
|
||||
client.backConnection.write(pusherToBackMessage);
|
||||
|
||||
/*const backConnection = await apiClientRepository.getClient(client.roomId);
|
||||
const adminMessage = new AdminMessage();
|
||||
adminMessage.setMessage(message);
|
||||
adminMessage.setRoomid(client.roomId);
|
||||
adminMessage.setRecipientuuid(client.userUuid);
|
||||
backConnection.sendAdminMessage(adminMessage, (error) => {
|
||||
if (error !== null) {
|
||||
console.error('Error while sending admin message', error);
|
||||
}
|
||||
});
|
||||
/*
|
||||
const socket = this.searchClientByUuid(messageToSend.userUuid);
|
||||
if(!socket){
|
||||
throw 'socket was not found';
|
||||
}
|
||||
|
||||
const sendUserMessage = new SendUserMessage();
|
||||
sendUserMessage.setMessage(messageToSend.message);
|
||||
sendUserMessage.setType(messageToSend.type);
|
||||
|
||||
const serverToClientMessage = new ServerToClientMessage();
|
||||
serverToClientMessage.setSendusermessage(sendUserMessage);
|
||||
|
||||
if (!socket.disconnecting) {
|
||||
socket.send(serverToClientMessage.serializeBinary().buffer, true);
|
||||
}
|
||||
return socket;*/
|
||||
});*/
|
||||
}
|
||||
|
||||
public async emitBan(userUuid: string, message: string, roomId: string): Promise<void> {
|
||||
const backConnection = await apiClientRepository.getClient(roomId);
|
||||
public emitBan(userUuid: string, message: string, type: string): void {
|
||||
const client = this.searchClientByUuid(userUuid);
|
||||
if(!client){
|
||||
throw Error('client not found');
|
||||
}
|
||||
|
||||
const banMessage = new BanMessage();
|
||||
banMessage.setRecipientuuid(userUuid);
|
||||
banMessage.setRoomid(roomId);
|
||||
const banUserMessage = new BanUserMessage();
|
||||
banUserMessage.setMessage(message);
|
||||
banUserMessage.setType(type);
|
||||
const pusherToBackMessage = new PusherToBackMessage();
|
||||
pusherToBackMessage.setBanusermessage(banUserMessage);
|
||||
client.backConnection.write(pusherToBackMessage);
|
||||
|
||||
backConnection.ban(banMessage, (error) => {
|
||||
/*const backConnection = await apiClientRepository.getClient(client.roomId);
|
||||
const adminMessage = new AdminMessage();
|
||||
adminMessage.setMessage(message);
|
||||
adminMessage.setRoomid(client.roomId);
|
||||
adminMessage.setRecipientuuid(client.userUuid);
|
||||
backConnection.sendAdminMessage(adminMessage, (error) => {
|
||||
if (error !== null) {
|
||||
console.error('Error while sending ban message', error);
|
||||
console.error('Error while sending admin message', error);
|
||||
}
|
||||
});
|
||||
});*/
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2723,10 +2723,10 @@ supports-color@^7.1.0:
|
|||
dependencies:
|
||||
has-flag "^4.0.0"
|
||||
|
||||
systeminformation@^4.30.5:
|
||||
version "4.30.5"
|
||||
resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-4.30.5.tgz#2219c305c8be56a2cfa527a5519c45bc81d4916c"
|
||||
integrity sha512-aYWs8yttl8ePpr6VOQ/Ak8cznuc9L/NQODVhbOKhInX73ZMLvV2BS86Mzr7LLfmUteVFR36CTDNQgiJgRqq+SQ==
|
||||
systeminformation@^4.31.1:
|
||||
version "4.31.1"
|
||||
resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-4.31.1.tgz#2e02c26987494d4b6a4d2d83138724593bc98d50"
|
||||
integrity sha512-dVCDWNMN8ncMZo5vbMCA5dpAdMgzafK2ucuJy5LFmGtp1cG6farnPg8QNvoOSky9SkFoEX1Aw0XhcOFV6TnLYA==
|
||||
|
||||
table@^5.2.3:
|
||||
version "5.4.6"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue