UNPKG

@sync-in/server

Version:

The secure, open-source platform for file storage, sharing, collaboration, and sync

188 lines (187 loc) 8.92 kB
/* * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com> * This file is part of Sync-in | The open source file sync and share solution * See the LICENSE file for licensing details */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "WebSocketUsers", { enumerable: true, get: function() { return WebSocketUsers; } }); const _common = require("@nestjs/common"); const _websockets = require("@nestjs/websockets"); const _socketio = require("socket.io"); const _jwtpayloadinterface = require("../../authentication/interfaces/jwt-payload.interface"); const _functions = require("../../common/functions"); const _websocketuserdecorator = require("../../infrastructure/websocket/decorators/web-socket-user.decorator"); const _utils = require("../../infrastructure/websocket/utils"); const _user = require("./constants/user"); const _websocket = require("./constants/websocket"); const _websocketinterface = require("./interfaces/websocket.interface"); const _usersmanagerservice = require("./services/users-manager.service"); function _ts_decorate(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } function _ts_metadata(k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); } function _ts_param(paramIndex, decorator) { return function(target, key) { decorator(target, key, paramIndex); }; } let WebSocketUsers = class WebSocketUsers { afterInit(server) { server.on(this.internalEventOnlineUsers, (cb)=>cb(this.filterUserRooms(this.server.sockets.adapter.rooms.keys()))); setTimeout(()=>this.initialized = true, this.waitTime); } beforeApplicationShutdown(_signal) { this.shuttingDown = true; } async handleConnection(socket) { if (this.shuttingDown) return; socket.join(`${_websocket.USER_ROOM_PREFIX}${socket.user.id}`); this.sendOnlineUser(socket.user, parseInt(socket.handshake.query.onlineStatus || '0')).catch((e)=>this.logger.error(`${this.handleConnection.name} - ${e}`)); this.sendAllOnlineUsers(socket.user.id).catch((e)=>this.logger.error(`${this.handleConnection.name} - ${e}`)); this.logger.log(`Connected: *${socket.user.login}* (${socket.user.id}) [${socket.id}] ${(0, _utils.getClientAddress)(socket)} ${socket.handshake.headers['user-agent']}`); } async handleDisconnect(socket) { if (this.shuttingDown) return; socket.leave(`${_websocket.USER_ROOM_PREFIX}${socket.user.id}`); this.sendOfflineUser(socket.user.id).catch((e)=>this.logger.error(`${this.handleDisconnect.name} - ${e}`)); this.logger.log(`Disconnected: *${socket.user.login}* (${socket.user.id}) [${socket.id}] ${(0, _utils.getClientAddress)(socket)} ${socket.handshake.headers['user-agent']}`); } setOnlineStatus(user, body) { if (body.store) { // store in db this.usersManager.setOnlineStatus(user, body.status); } this.sendOnlineStatus(user.id, body.status).catch((e)=>this.logger.error(`${this.setOnlineStatus.name} - ${e}`)); } sendMessageToUsers(userIds, eventName, body) { if (userIds.length) { this.server.to(userIds.map((uid)=>`${_websocket.USER_ROOM_PREFIX}${uid}`)).emit(eventName, body); } } async sendOnlineUser(user, status) { if (!this.initialized) { // all online users will be propagated in the next seconds return; } if (status === _user.USER_ONLINE_STATUS.OFFLINE) { // user's online status is offline, do not send update to others return; } const userIdsToNotify = await this.getOnlineUserIdsWhitelist(user.id); if (userIdsToNotify.length) { const onlineUser = { id: user.id, login: user.login, email: user.email, fullName: user.fullName, onlineStatus: status }; this.sendMessageToUsers(userIdsToNotify, _websocket.USERS_WS.EVENTS.ONLINE_USER, onlineUser); } } async sendOfflineUser(userId) { // avoid sending offline event on quick reconnects await (0, _functions.sleep)(this.waitTime); // check if user room is empty before send event if ((await this.getOnlineUserIds()).indexOf(userId) === -1) { this.sendOnlineStatus(userId, _user.USER_ONLINE_STATUS.OFFLINE).catch((e)=>this.logger.error(`${this.sendOfflineUser.name} - ${e}`)); } } async sendAllOnlineUsers(toUserId) { if (!this.initialized) { // wait for all reconnections before propagating all online users await (0, _functions.sleep)(this.waitTime); } const onlineUserIds = await this.getOnlineUserIdsWhitelist(toUserId); if (onlineUserIds.length) { const onlineUsers = await this.usersManager.getOnlineUsers(onlineUserIds); if (onlineUsers.length) { this.sendMessageToUsers([ toUserId ], _websocket.USERS_WS.EVENTS.ONLINE_USERS, onlineUsers); } } } async sendOnlineStatus(userId, status) { const userIdsToNotify = await this.getOnlineUserIdsWhitelist(userId, false); if (userIdsToNotify.length) { const onlineStatus = { userId: userId, status: status }; this.sendMessageToUsers(userIdsToNotify, _websocket.USERS_WS.EVENTS.ONLINE_STATUS, onlineStatus); } } async getOnlineUserIdsWhitelist(userId, excludeUser = true) { /* get online users visible for the user */ const currentOnlineUserIds = await this.getOnlineUserIds(); if (currentOnlineUserIds.length) { let usersWhitelist = await this.usersManager.usersWhitelist(userId); if (usersWhitelist) { if (excludeUser) { usersWhitelist = usersWhitelist.filter((uid)=>uid !== userId); } return usersWhitelist.filter((uid)=>currentOnlineUserIds.indexOf(uid) > -1); } } return []; } async getOnlineUserIds() { /* get all online users from all workers */ const localUserIds = this.filterUserRooms(this.server.sockets.adapter.rooms.keys()); const remoteUserIds = await this.server.serverSideEmitWithAck(this.internalEventOnlineUsers); return [ ...new Set([ ...localUserIds, ...remoteUserIds ].flat()) ]; } filterUserRooms(rooms) { return [ ...rooms ].filter((r)=>r.startsWith(_websocket.USER_ROOM_PREFIX)).map((ur)=>parseInt(ur.split(_websocket.USER_ROOM_PREFIX).pop())); } // to show local rooms : this.server.of('/').adapter.rooms constructor(usersManager){ this.usersManager = usersManager; this.internalEventOnlineUsers = 'listOnlineUsers'; this.logger = new _common.Logger(WebSocketUsers.name); this.initialized = false; this.shuttingDown = false; this.waitTime = 3000; //ms } }; _ts_decorate([ (0, _websockets.WebSocketServer)(), _ts_metadata("design:type", typeof _socketio.Server === "undefined" ? Object : _socketio.Server) ], WebSocketUsers.prototype, "server", void 0); _ts_decorate([ (0, _websockets.SubscribeMessage)(_websocket.USERS_WS.EVENTS.ONLINE_STATUS), _ts_param(0, (0, _websocketuserdecorator.GetWsUser)()), _ts_param(1, (0, _websockets.MessageBody)()), _ts_metadata("design:type", Function), _ts_metadata("design:paramtypes", [ typeof _jwtpayloadinterface.JwtIdentityPayload === "undefined" ? Object : _jwtpayloadinterface.JwtIdentityPayload, typeof _websocketinterface.EventChangeOnlineStatus === "undefined" ? Object : _websocketinterface.EventChangeOnlineStatus ]), _ts_metadata("design:returntype", void 0) ], WebSocketUsers.prototype, "setOnlineStatus", null); WebSocketUsers = _ts_decorate([ (0, _websockets.WebSocketGateway)(), _ts_metadata("design:type", Function), _ts_metadata("design:paramtypes", [ typeof _usersmanagerservice.UsersManager === "undefined" ? Object : _usersmanagerservice.UsersManager ]) ], WebSocketUsers); //# sourceMappingURL=users.gateway.js.map