1
0
mirror of https://github.com/pavlobu/deskreen.git synced 2025-05-18 00:10:12 -07:00
deskreen/app/server/darkwireSocket.ts

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();
}
});
}
}