uyem
Version:
WebRTC client-server SFU application
393 lines • 17.7 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createServer = exports.prisma = void 0;
/******************************************************************************************
* Repository: https://github.com/kolserdav/werift-sfu-react.git
* File name: main.ts
* Author: Sergey Kolmiller
* Email: <uyem.ru@gmail.com>
* License: MIT
* License text: See in LICENSE file
* Copyright: kolserdav, All rights reserved (c)
* Create Date: Wed Aug 24 2022 14:14:09 GMT+0700 (Krasnoyarsk Standard Time)
******************************************************************************************/
/* eslint-disable import/first */
/* eslint-disable no-case-declarations */
const uuid_1 = require("uuid");
const dotenv_1 = __importDefault(require("dotenv"));
dotenv_1.default.config();
const client_1 = require("@prisma/client");
const ws_1 = __importDefault(require("./core/ws"));
const rtc_1 = __importDefault(require("./core/rtc"));
const interfaces_1 = require("./types/interfaces");
const lib_1 = require("./utils/lib");
const constants_1 = require("./utils/constants");
const db_1 = __importDefault(require("./core/db"));
const chat_1 = __importDefault(require("./addons/chat"));
const recordVideo_1 = __importDefault(require("./addons/recordVideo"));
const settings_1 = __importDefault(require("./addons/settings"));
exports.prisma = new client_1.PrismaClient();
const database = new db_1.default({ prisma: exports.prisma });
const chat = new chat_1.default({ prisma: exports.prisma });
process.on('uncaughtException', (err) => {
(0, lib_1.log)('error', 'uncaughtException', err);
});
process.on('unhandledRejection', (err) => {
(0, lib_1.log)('error', 'unhandledRejection', err);
});
/**
* Create WebRTC SFU server
*/
function createServer({ port = constants_1.PORT, cors = constants_1.CORS, db = constants_1.DATABASE_URL, onRoomOpen, onRoomClose, onRoomConnect, onRoomDisconnect, checkTokenCb, cloudPath: _cloudPath, logLevel, }, cb) {
if (require.main !== module) {
if (!db && !constants_1.DATABASE_URL) {
(0, lib_1.log)('error', 'DATABASE_URL: not provided', { db, DATABASE_URL: constants_1.DATABASE_URL }, true);
return;
}
(0, lib_1.log)('info', 'Using DATABASE_URL:', (0, lib_1.cleanDbUrl)(db), true);
}
process.env.DATABASE_URL = db || constants_1.DATABASE_URL;
if (!_cloudPath) {
/*
log('warn', 'Cloud dir path "--cloud" is not set, video recording is not available', {
_cloudPath,
});
*/
}
(0, lib_1.setLogLevel)(logLevel);
const cloudPath = _cloudPath || constants_1.CLOUD_DIR_PATH;
const wss = new ws_1.default({ port, cloudPath, prisma: exports.prisma });
const rtc = new rtc_1.default({ ws: wss, prisma: exports.prisma });
const settings = new settings_1.default({ cloudPath, prisma: exports.prisma });
const recordVideo = new recordVideo_1.default({
settings,
rtc,
ws: wss,
cloudPath,
prisma: exports.prisma,
});
settings.checkTokenCb = checkTokenCb || settings.checkTokenCb;
chat.checkTokenCb = checkTokenCb || chat.checkTokenCb;
recordVideo.checkTokenCb = checkTokenCb || recordVideo.checkTokenCb;
wss.checkTokenCb = checkTokenCb || wss.checkTokenCb;
const getConnectionId = () => {
const connId = (0, uuid_1.v4)();
if (wss.sockets[connId]) {
return getConnectionId();
}
return connId;
};
wss.connection.on('connection', (ws, req) => {
const { origin } = req.headers;
const protocol = req.headers['sec-websocket-protocol'];
const notAllowed = cors.split(',').indexOf(origin || '') === -1;
const connId = getConnectionId();
if (cors && notAllowed) {
const message = 'Block CORS attempt';
(0, lib_1.log)('warn', message, { headers: req.headers });
ws.send(JSON.stringify({
type: interfaces_1.MessageType.SET_ERROR,
connId,
data: {
message,
type: 'warn',
},
}));
ws.close();
return;
}
ws.on('message', async (message) => {
let _data = '';
if (typeof message !== 'string') {
_data = message.toString('utf8');
}
const rawMessage = wss.parseMessage(_data);
if (!rawMessage) {
return;
}
const { type, id } = rawMessage;
switch (type) {
case interfaces_1.MessageType.GET_USER_ID:
const { isRoom, userName, locale } = wss.getMessage(interfaces_1.MessageType.GET_USER_ID, rawMessage).data;
await wss.setSocket({ id, ws, connId, isRoom: isRoom || false, userName, locale });
wss.sendMessage({
type: interfaces_1.MessageType.SET_USER_ID,
id,
data: {
name: userName,
},
connId,
});
break;
case interfaces_1.MessageType.GET_ROOM:
rtc.handleGetRoomMessage({
message: wss.getMessage(interfaces_1.MessageType.GET_ROOM, rawMessage),
port,
cors,
onRoomConnect,
onRoomOpen,
});
break;
case interfaces_1.MessageType.GET_CHAT_UNIT:
chat.setUnit({
roomId: wss.getMessage(interfaces_1.MessageType.GET_CHAT_UNIT, rawMessage).id,
userId: wss.getMessage(interfaces_1.MessageType.GET_CHAT_UNIT, rawMessage).data.userId,
ws,
locale: wss.getMessage(interfaces_1.MessageType.GET_CHAT_UNIT, rawMessage).data.locale,
connId,
});
break;
case interfaces_1.MessageType.GET_SETTINGS_UNIT:
settings.setUnit({
roomId: wss.getMessage(interfaces_1.MessageType.GET_SETTINGS_UNIT, rawMessage).id,
userId: wss.getMessage(interfaces_1.MessageType.GET_SETTINGS_UNIT, rawMessage).data.userId,
ws,
locale: wss.getMessage(interfaces_1.MessageType.GET_SETTINGS_UNIT, rawMessage).data.locale,
connId,
});
break;
case interfaces_1.MessageType.GET_CHAT_MESSAGES:
chat.getChatMessages(rawMessage);
break;
case interfaces_1.MessageType.GET_ROOM_GUESTS:
const _roomId = wss.getMessage(interfaces_1.MessageType.GET_ROOM_GUESTS, rawMessage).data.roomId;
wss.sendMessage({
type: interfaces_1.MessageType.SET_ROOM_GUESTS,
id,
data: {
roomUsers: rtc.rooms[_roomId],
muteds: rtc.muteds[_roomId],
adminMuteds: rtc.adminMuteds[_roomId],
asked: rtc.askeds[_roomId],
banneds: rtc.banneds[_roomId],
},
connId,
});
break;
case interfaces_1.MessageType.GET_CLOSE_PEER_CONNECTION:
rtc.closePeerConnectionHandler(rawMessage);
break;
case interfaces_1.MessageType.GET_ROOM_MESSAGE:
chat.handleRoomMessage(rawMessage);
break;
case interfaces_1.MessageType.GET_LOCALE:
ws.send(JSON.stringify({
type: interfaces_1.MessageType.SET_LOCALE,
data: {
locale: (0, lib_1.getLocale)(wss.getMessage(interfaces_1.MessageType.GET_LOCALE, rawMessage).data.locale)
.client,
},
}));
break;
case interfaces_1.MessageType.GET_EDIT_MESSAGE:
chat.handleEditMessage(rawMessage);
break;
case interfaces_1.MessageType.GET_CREATE_MESSAGE:
chat.handleCreateMessage(rawMessage);
break;
case interfaces_1.MessageType.GET_CREATE_QUOTE:
chat.handleCreateQuote(rawMessage);
break;
case interfaces_1.MessageType.GET_DELETE_MESSAGE:
chat.handleDeleteMessage(rawMessage);
break;
case interfaces_1.MessageType.GET_BLOCK_CHAT:
chat.getBlockChatHandler(rawMessage);
break;
case interfaces_1.MessageType.GET_TO_MUTE:
rtc.getToMuteHandler(rawMessage);
break;
case interfaces_1.MessageType.GET_TO_BAN:
rtc.handleGetToBan(rawMessage);
break;
case interfaces_1.MessageType.GET_TO_UNMUTE:
rtc.handleGetToUnMute(rawMessage);
break;
case interfaces_1.MessageType.GET_RECORD:
recordVideo.handleVideoRecord(rawMessage);
break;
case interfaces_1.MessageType.GET_VIDEO_DELETE:
settings.videoDeleteHandler(rawMessage);
break;
case interfaces_1.MessageType.GET_TO_UNBAN:
rtc.handleGetToUnBan(rawMessage);
break;
case interfaces_1.MessageType.GET_MUTE_FOR_ALL:
rtc.getMuteForAllHandler(rawMessage);
break;
case interfaces_1.MessageType.GET_MUTE:
rtc.getMuteHandler(rawMessage);
break;
case interfaces_1.MessageType.GET_VIDEO_FIND_MANY:
settings.videoFindManyHandler(rawMessage);
break;
case interfaces_1.MessageType.GET_ASK_FLOOR:
rtc.setAskedFloorHandler(rawMessage);
break;
case interfaces_1.MessageType.GET_VIDEO_TRACK:
rtc.getVideoTrackHandler(rawMessage);
break;
case interfaces_1.MessageType.GET_VIDEO_SETTINGS:
recordVideo.getVideoSettingsHandler(rawMessage);
break;
case interfaces_1.MessageType.GET_TO_ADMIN:
rtc.getToAdminHandler(rawMessage);
break;
case interfaces_1.MessageType.GET_VIDEO_FIND_FIRST:
settings.videoFindFirstHandler(rawMessage);
break;
case interfaces_1.MessageType.GET_VIDEO_UPDATE:
settings.videoUpdateHandler(rawMessage);
break;
default:
wss.sendMessage(rawMessage);
}
});
const getUserId = (_connId) => {
let userId = '';
const keys = Object.keys(wss.sockets);
keys.every((item) => {
const sock = item.split(rtc.delimiter);
if (sock[1] === _connId) {
// eslint-disable-next-line prefer-destructuring
userId = sock[0];
return false;
}
return true;
});
return userId;
};
// eslint-disable-next-line no-param-reassign
ws.on('close', async () => {
let skip = false;
if (protocol === 'chat') {
Object.keys(chat.users).every((item) => {
if (skip) {
return false;
}
Object.keys(chat.users[item]).every((_item) => {
if (chat.users[item][_item].connId === connId) {
chat.cleanUnit({ roomId: item, userId: _item });
skip = true;
return false;
}
return true;
});
return true;
});
}
else if (protocol === 'settings') {
Object.keys(settings.users).every((item) => {
if (skip) {
return false;
}
Object.keys(settings.users[item]).every((_item) => {
if (settings.users[item][_item].connId === connId) {
settings.cleanUnit({ roomId: item, userId: _item });
skip = true;
return false;
}
return true;
});
return true;
});
}
if (protocol !== 'room') {
return;
}
const userId = getUserId(connId);
if (userId) {
const socketId = wss.getSocketId(userId, connId);
if (wss.sockets[socketId]) {
(0, lib_1.log)('log', 'Delete socket', { userId, connId });
delete wss.sockets[socketId];
}
else {
(0, lib_1.log)('warn', 'No socket delete', { s: Object.keys(wss.sockets) });
}
database.changeUserOnline({ userId, online: false });
(0, lib_1.log)('info', 'User disconnected', userId);
const roomKeys = Object.keys(rtc.rooms);
roomKeys.every((item) => {
let index = -1;
rtc.rooms[item].every((_item, i) => {
if (_item.id.toString() === userId) {
index = i;
return false;
}
return true;
});
if (index !== -1) {
rtc.rooms[item].splice(index, 1);
if (onRoomDisconnect) {
onRoomDisconnect({ roomId: item, userId, roomUsers: rtc.rooms[item] });
}
if (rtc.onRoomDisconnect) {
rtc.onRoomDisconnect({ roomId: item, userId, roomUsers: rtc.rooms[item] });
}
// delete mute
const mute = rtc.muteds[item].indexOf(userId);
if (mute !== -1) {
rtc.muteds[item].splice(mute, 1);
}
// delete offVideo
const offVideo = rtc.offVideo[item].indexOf(userId);
if (offVideo !== -1) {
rtc.offVideo[item].splice(offVideo, 1);
}
// delete askeds
const askeds = rtc.askeds[item].indexOf(userId);
if (askeds !== -1) {
rtc.askeds[item].splice(askeds, 1);
}
rtc.sendCloseMessages({ roomId: item, userId });
rtc.cleanConnections(item, userId.toString());
if (rtc.rooms[item].length === 0) {
delete rtc.rooms[item];
delete rtc.streams[item];
delete rtc.banneds[item];
delete rtc.askeds[item];
delete rtc.muteForAll[item];
delete rtc.offVideo[item];
delete rtc.peerConnectionsServer[item];
database.changeRoomArchive({ roomId: item.toString(), archive: true });
delete rtc.muteds[item];
delete rtc.adminMuteds[item];
delete chat.users[item];
delete chat.blocked[item];
if (onRoomClose) {
onRoomClose({ roomId: item, roomLength: rtc.getRoomLenght(), port });
}
}
database.deleteGuest({ userId, roomId: item });
delete wss.users[userId];
return false;
}
return true;
});
}
});
});
wss.connection.on('listening', () => {
(0, lib_1.log)('info', 'Uyem server listen at port:', port, true);
if (cb) {
cb(wss.connection);
}
});
}
exports.createServer = createServer;
if (require.main === module) {
createServer({
port: constants_1.PORT,
cors: constants_1.CORS,
cloudPath: constants_1.CLOUD_DIR_PATH,
checkTokenCb: async () => ({
errorCode: 0,
unitId: '1',
}),
});
}
//# sourceMappingURL=main.js.map