mirror of
https://github.com/pavlobu/deskreen.git
synced 2025-05-18 16:30:10 -07:00
229 lines
6.0 KiB
TypeScript
229 lines
6.0 KiB
TypeScript
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
/*
|
|
* original JS code from darkwire.io
|
|
* translated and adapted to typescript for Deskreen app
|
|
* */
|
|
|
|
/* eslint-disable no-async-promise-executor */
|
|
import _ from 'lodash';
|
|
import Io from 'socket.io';
|
|
import socketsIPService from './socketsIPService';
|
|
import getStore from './store';
|
|
import socketIOServerStore from './store/socketIOServerStore';
|
|
|
|
const LOCALHOST_SOCKET_IP = '127.0.0.1';
|
|
|
|
interface SocketOPTS {
|
|
roomId: string;
|
|
socket: Io.Socket;
|
|
room: Room;
|
|
roomIdOriginal: string;
|
|
}
|
|
|
|
export default class Socket implements SocketOPTS {
|
|
roomId: string;
|
|
|
|
socket: Io.Socket;
|
|
|
|
room: Room;
|
|
|
|
roomIdOriginal: string;
|
|
|
|
constructor(opts: SocketOPTS) {
|
|
const { roomId, socket, room, roomIdOriginal } = opts;
|
|
|
|
this.roomId = roomId;
|
|
this.socket = socket;
|
|
this.roomIdOriginal = roomIdOriginal;
|
|
this.room = room;
|
|
if (room.isLocked) {
|
|
this.sendRoomLocked();
|
|
return;
|
|
}
|
|
|
|
this.init();
|
|
}
|
|
|
|
async init() {
|
|
await this.joinRoom();
|
|
this.handleSocket();
|
|
}
|
|
|
|
sendRoomLocked() {
|
|
this.socket.emit('ROOM_LOCKED');
|
|
}
|
|
|
|
async saveRoom(room: Room) {
|
|
const json = {
|
|
...room,
|
|
updatedAt: Date.now(),
|
|
};
|
|
return getStore().set('rooms', this.roomId, JSON.stringify(json));
|
|
}
|
|
|
|
async destroyRoom() {
|
|
return getStore().del('rooms', this.roomId);
|
|
}
|
|
|
|
fetchRoom() {
|
|
return new Promise(async (resolve) => {
|
|
const res = await getStore().get('rooms', this.roomId);
|
|
resolve(JSON.parse(res || '{}'));
|
|
});
|
|
}
|
|
|
|
// eslint-disable-next-line class-methods-use-this
|
|
joinRoom() {
|
|
return new Promise((resolve, reject) => {
|
|
this.socket.join(this.roomId, (err) => {
|
|
if (err) {
|
|
reject();
|
|
}
|
|
resolve(undefined);
|
|
});
|
|
});
|
|
}
|
|
|
|
handleSocket() {
|
|
this.socket.on('GET_MY_IP', (acknowledgeFunction) => {
|
|
acknowledgeFunction(socketsIPService.getSocketIPByID(this.socket.id));
|
|
});
|
|
|
|
this.socket.on('GET_IP_BY_SOCKET_ID', (socketID, acknowledgeFunction) => {
|
|
// TODO: for security only allow localhost to use this socket event! right now it may be emitted by client which may be not secure. The purpose of this event is for host to get the actual IP of connected client socket and compare them with what was sent by client in DEVICE_DETAILS.
|
|
acknowledgeFunction(socketsIPService.getSocketIPByID(socketID));
|
|
});
|
|
|
|
this.socket.on('IS_ROOM_LOCKED', async (acknowledgeFunction) => {
|
|
const room: Room = (await this.fetchRoom()) as Room;
|
|
acknowledgeFunction(room.isLocked);
|
|
});
|
|
|
|
this.socket.on('ENCRYPTED_MESSAGE', (payload) => {
|
|
this.socket.to(this.roomId).emit('ENCRYPTED_MESSAGE', payload);
|
|
});
|
|
|
|
this.socket.on('DISCONNECT_SOCKET_BY_DEVICE_IP', async (payload) => {
|
|
const room: Room = (await this.fetchRoom()) as Room;
|
|
const ownerUser = (room.users || []).find(
|
|
(u) => u.socketId === this.socket.id && u.isOwner
|
|
);
|
|
if (!ownerUser) return;
|
|
const socketIDToDisconnect = socketsIPService.getSocketIDByIP(payload.ip);
|
|
if (!socketIDToDisconnect) return;
|
|
|
|
this.handleDisconnect(
|
|
socketIOServerStore.getServer().sockets.connected[socketIDToDisconnect]
|
|
);
|
|
});
|
|
|
|
this.socket.on('USER_ENTER', async (payload) => {
|
|
let room: Room = (await this.fetchRoom()) as Room;
|
|
if (_.isEmpty(room)) {
|
|
room = {
|
|
id: this.roomId,
|
|
users: [],
|
|
isLocked: false,
|
|
createdAt: Date.now(),
|
|
};
|
|
} else {
|
|
const userFound = room.users.find(
|
|
(r) => r.publicKey === payload.publicKey
|
|
);
|
|
if (userFound) return;
|
|
}
|
|
|
|
const newRoom: Room = {
|
|
...room,
|
|
users: [
|
|
...(room.users || []),
|
|
{
|
|
socketId: this.socket.id,
|
|
publicKey: payload.publicKey,
|
|
isOwner: this.socket.request.connection.remoteAddress.includes(
|
|
LOCALHOST_SOCKET_IP
|
|
),
|
|
ip: payload.ip ? payload.ip : '', // TODO: remove as it is not used
|
|
},
|
|
],
|
|
};
|
|
await this.saveRoom(newRoom);
|
|
|
|
socketIOServerStore
|
|
.getServer()
|
|
.to(this.roomId)
|
|
.emit('USER_ENTER', {
|
|
...newRoom,
|
|
id: this.roomIdOriginal,
|
|
});
|
|
});
|
|
|
|
this.socket.on('TOGGLE_LOCK_ROOM', async () => {
|
|
// TODO: in here if there is somehow already more than ONE client connected, then we were spoofed! Need to add code to interrupt connection immediately.
|
|
const room: Room = (await this.fetchRoom()) as Room;
|
|
const user = (room.users || []).find(
|
|
(u) => u.socketId === this.socket.id && u.isOwner
|
|
);
|
|
|
|
if (!user) {
|
|
return;
|
|
}
|
|
|
|
await this.saveRoom({
|
|
...room,
|
|
isLocked: !room.isLocked,
|
|
});
|
|
});
|
|
|
|
this.socket.on('disconnect', () => {
|
|
this.handleDisconnect(this.socket);
|
|
});
|
|
|
|
this.socket.on('USER_DISCONNECT', () => {
|
|
this.handleDisconnect(this.socket);
|
|
});
|
|
}
|
|
|
|
async handleDisconnect(socket: Io.Socket) {
|
|
const room: Room = (await this.fetchRoom()) as Room;
|
|
const isOwnerUser = !!(room.users || []).find(
|
|
(u) => u.socketId === socket.id && u.isOwner
|
|
);
|
|
|
|
const newRoom = {
|
|
...room,
|
|
users: (room.users || [])
|
|
.filter((u) => u.socketId !== socket.id)
|
|
.map((u, index) => ({
|
|
...u,
|
|
isOwner: index === 0,
|
|
})),
|
|
};
|
|
|
|
if (isOwnerUser) {
|
|
this.disconnectAllUsers(newRoom);
|
|
await this.destroyRoom();
|
|
} else {
|
|
await this.saveRoom(newRoom);
|
|
}
|
|
|
|
socketIOServerStore
|
|
.getServer()
|
|
.to(this.roomId)
|
|
.emit('USER_EXIT', newRoom.users);
|
|
|
|
socket.disconnect(true);
|
|
}
|
|
|
|
// eslint-disable-next-line class-methods-use-this
|
|
disconnectAllUsers(room: Room) {
|
|
room.users.forEach((u) => {
|
|
if (socketIOServerStore.getServer().sockets.connected[u.socketId]) {
|
|
socketIOServerStore
|
|
.getServer()
|
|
.sockets.connected[u.socketId].disconnect();
|
|
}
|
|
});
|
|
}
|
|
}
|