@sync-in/server
Version:
The secure, open-source platform for file storage, sharing, collaboration, and sync
188 lines (187 loc) • 8.92 kB
JavaScript
/*
* 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