mirror of
https://github.com/pavlobu/deskreen.git
synced 2025-05-18 16:30:10 -07:00
234 lines
5.9 KiB
TypeScript
234 lines
5.9 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;
|
|
}
|
|
|
|
function isLocalhostSocket(socket: Io.Socket) {
|
|
return socket.request.connection.remoteAddress.includes(LOCALHOST_SOCKET_IP);
|
|
}
|
|
|
|
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) => {
|
|
if (!isLocalhostSocket(this.socket)) {
|
|
return;
|
|
}
|
|
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) => {
|
|
payload.fromSocketID = this.socket.id;
|
|
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: isLocalhostSocket(this.socket),
|
|
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();
|
|
}
|
|
});
|
|
}
|
|
}
|