UNPKG

@trap_stevo/filetide

Version:

Revolutionizing real-time file transfer with seamless, instant communication across any device. Deliver files instantly, regardless of platform, and experience unparalleled speed and control in managing transfers. Elevate your file-sharing capabilities wi

1,590 lines (1,578 loc) 72.2 kB
"use strict"; function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); } function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); } function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); } const { OmniMap } = require("@trap_stevo/legendarybuilderpronodejs-utilities/HUDStructs"); const IoTide = require("@trap_stevo/iotide"); const { FileMessagerConfigManager } = require("./HUDManagers/FileMessagerConfigManager"); const { FileNetUtilityManager } = require("./HUDManagers/FileNetUtilityManager"); const { FileNetClientManager } = require("./HUDManagers/FileNetClientManager"); const { FileTransferManager } = require("./HUDManagers/FileTransferManager"); const { aggregateMultiAverageChartData, aggregateChartData } = require("./HUDManagers/TidalyticsUtilityManager"); const TideSentinelManager = require("./HUDManagers/TideSentinelManager"); const HUDTargetQueue = require("./HUDManagers/HUDTargetQueueManager"); const { tidalytics } = require("./HUDManagers/TidalyticsManager"); var _FileMessager_brand = /*#__PURE__*/new WeakSet(); class FileMessager { constructor(options = { pingTimeout: 180000, pingInterval: 60000 }, transportOptions = { parallelChunks: 3, maxRetries: 3 }, onTideAction = null, debugMode = false, tideSentinelOptions = {}) { _classPrivateMethodInitSpec(this, _FileMessager_brand); const serverOptions = FileMessagerConfigManager.getServerOptions(options); this.transporter = new FileTransferManager(transportOptions); this.transferQueue = new HUDTargetQueue(); this.currentTransferBarrierTransfers = new Map(); this.clientTransferBarriers = new Map(); this.currentTidingTransfers = new Map(); this.tideSentinel = new TideSentinelManager(tideSentinelOptions); this.tideSentinel.registerEntryAddedHook((key, entryData) => { console.log(key, entryData); }); this.fileNet = new IoTide(serverOptions.port, serverOptions, true, this.onConnect.bind(this), this.onDisconnect.bind(this)); this.onlineClients = new OmniMap(); this.onTideAction = onTideAction ?? (() => {}); this.debugMode = debugMode; this.tideSentinelOptions = tideSentinelOptions; this.transporterOptions = transportOptions; this.options = serverOptions; return; } onConnect(socket) { console.log(`Client connected ~ ${socket.id}`); this.onlineClients.set(socket.id, socket); return; } onDisconnect(connectionRes, socket) { console.log(`Client disconnected ~ ${socket.id}`); this.onlineClients.delete(socket.id); return; } close() { this.fileNet.close(); return; } start() { this.fileNet.on("get-client-transfer-barrier-response", this.handleClientTransferBarrierResponse.bind(this)); this.fileNet.on("get-client-transfer-state", this.handleGetClientTransferState.bind(this)); this.fileNet.on("get-client", this.handleGetClient.bind(this)); this.fileNet.on("acknowledge-transfer-tide", this.handleAcknowledgeTransferTide.bind(this)); this.fileNet.on("notify-transfer-barrier", this.handleNotifyTransferBarrier.bind(this)); this.fileNet.on("send-current-transfer-state", this.handleSendCurrentTransferState.bind(this)); this.fileNet.on("sent-file-list-to-client", this.handleSentFileListToClient.bind(this)); this.fileNet.on("send-file-list-to-client", this.handleSendFileListToClient.bind(this)); this.fileNet.on("list-client-files", this.handleListClientFiles.bind(this)); this.fileNet.on("transfer-start", this.handleFileTransferStart.bind(this)); this.fileNet.on("transfer-progress", this.handleFileTransferProgress.bind(this)); this.fileNet.on("transfer-complete", this.handleFileTransferComplete.bind(this)); this.fileNet.on("request-transfer-from-client", this.handleRequestTransferFromClient.bind(this)); this.fileNet.on("client-to-client-transfer", this.handleClientToClientTransfer.bind(this)); this.fileNet.on("check-client-banned", this.handleCheckClientBanned.bind(this)); this.fileNet.on("check-client-online", this.handleCheckClientOnline.bind(this)); this.fileNet.on("client-offline", this.handleClientOffline.bind(this)); this.fileNet.on("clients-online", this.handleClientsOnline.bind(this)); this.fileNet.on("client-online", this.handleClientOnline.bind(this)); this.fileNet.on("client-tiding", this.handleClientTiding.bind(this)); return; } // --- File Messager Tidalytics Management --- async getTidalyticsSince(interval = "1d", source = "storage", filter = {}) { return await tidalytics.getMetricsSince(interval, source, filter); } async getStoredTidalytics(filter = {}) { return await tidalytics.getStoredMetrics(filter); } async getTidalyticsInDomain(domain, options = {}) { return await tidalytics.getMetricsInDomain(domain, options); } getTidalytics(filter = {}) { return tidalytics.getMetrics(filter); } getTidalyticByID(id) { return tidalytics.getMetricByID(id); } searchTidalyticByTagValue(key, match) { return tidalytics.searchByTagValue(key, match); } getTidalyticAggregate(name) { return tidalytics.getAggregate(name); } getTidalyticAggregateByType(type) { return tidalytics.getAggregateByType(type); } groupTidalyticByTag(tagKey) { return tidalytics.groupByTag(tagKey); } getTidalyticTimeSeries(name, options = {}) { return tidalytics.getTimeSeries(name, options); } predictTidalytic(name, config = {}) { return tidalytics.predictMetric(name, config); } async clearTidalytics(domain, options = {}) { return await tidalytics.clearMetricsInDomain(domain, options); } clearAllTidalytics() { return tidalytics.clearAllMetrics(); } attachTiderPresenceVisualStream(options = {}) { const { visualDataConfigurations = {}, mode = "buffer", forward } = options; if (!forward) { return; } const handle = async () => { if (mode === "signal") { forward("rehydrate"); return; } const result = await this.getTiderPresenceVisualData(visualDataConfigurations); forward(result); }; tidalytics.events.on("metric:created:tider:offline", handle); tidalytics.events.on("metric:created:tider:online", handle); } attachAlreadyOnlineVisualStream(options = {}) { const { visualDataConfigurations = {}, mode = "buffer", forward } = options; if (!forward) { return; } const handle = async () => { if (mode === "signal") { forward("rehydrate"); return; } const result = await this.getAlreadyOnlineVisualData(visualDataConfigurations); forward(result); }; tidalytics.events.on("metric:created:tider:already-online", handle); } attachTiderBanVisualStream(options = {}) { const { visualDataConfigurations = {}, mode = "buffer", forward } = options; if (!forward) { return; } const handle = async () => { if (mode === "signal") { forward("rehydrate"); return; } const result = await this.getTiderBanVisualData(visualDataConfigurations); forward(result); }; tidalytics.events.on("metric:created:tider:unbanned", handle); tidalytics.events.on("metric:created:tider:banned", handle); tidalytics.events.on("metric:created:tider:kicked", handle); } attachTideFileTransferVisualStream(options = {}) { const { visualDataConfigurations = {}, mode = "buffer", forward } = options; if (!forward) { return; } const handle = async () => { if (mode === "signal") { forward("rehydrate"); return; } const result = await this.getTideFileTransferVisualData(visualDataConfigurations); forward(result); }; tidalytics.events.on("metric:created:tide:completed-transfer", handle); tidalytics.events.on("metric:created:tide:incoming-transfer", handle); tidalytics.events.on("metric:created:tide:incoming-file", handle); } attachTideTransferStateVisualStream(options = {}) { const { visualDataConfigurations = {}, mode = "buffer", forward } = options; if (!forward) { return; } const handle = async () => { if (mode === "signal") { forward("rehydrate"); return; } const result = await this.getTideTransferStateVisualData(visualDataConfigurations); forward(result); }; tidalytics.events.on("metric:created:tide:transfer-state", handle); } attachFileListVisualStream(options = {}) { const { visualDataConfigurations = {}, mode = "buffer", forward } = options; if (!forward) { return; } const handle = async () => { if (mode === "signal") { forward("rehydrate"); return; } const result = await this.getFileListVisualData(visualDataConfigurations); forward(result); }; tidalytics.events.on("metric:created:file-list:received", handle); tidalytics.events.on("metric:created:file-list:request", handle); } attachTideBarrierVisualStream(options = {}) { const { visualDataConfigurations = {}, mode = "buffer", forward } = options; if (!forward) { return; } const handle = async () => { if (mode === "signal") { forward("rehydrate"); return; } const result = await this.getTideBarrierVisualData(visualDataConfigurations); forward(result); }; tidalytics.events.on("metric:created:tide-barrier:accepted", handle); tidalytics.events.on("metric:created:tide-barrier:denied", handle); } async getTiderPresenceVisualData(options = {}) { const { interval = "1d", limit = Infinity, filter = {}, mode = "all" } = options; const sources = ["storage", "memory"]; const [storedOnline, storedOffline] = await Promise.all([this.getTidalyticsSince(interval, sources[0], { ...filter, tags: { "tider:online": "tider:online" } }), this.getTidalyticsSince(interval, sources[0], { ...filter, tags: { "tider:offline": "tider:offline" } })]); const [liveOnline] = await Promise.all([this.getTidalyticsSince(interval, sources[1], { ...filter, tags: { "tider:online": "tider:online" } })]); const onlineMetrics = [...storedOnline, ...liveOnline]; const offlineMetrics = storedOffline; const sessionsByKey = {}; for (const event of onlineMetrics) { const user = event.metadata?.user; const connectionID = event.metadata?.connectionID; const connectedOn = event.metadata?.connectedOn || event.timestamp; const key = `${user}_${connectionID}`; sessionsByKey[key] = { sessionDuration: null, disconnectedOn: null, connectionID, connectedOn, user }; } for (const event of offlineMetrics) { const user = event.metadata?.user; const connectionID = event.metadata?.connectionID; const disconnectedOn = event.timestamp; const key = `${user}_${connectionID}`; if (sessionsByKey[key]) { sessionsByKey[key].sessionDuration = disconnectedOn - sessionsByKey[key].connectedOn; sessionsByKey[key].disconnectedOn = disconnectedOn; } } let allSessions = Object.values(sessionsByKey); if (mode === "online") { allSessions = allSessions.filter(session => session.disconnectedOn === null); } else if (mode === "offline") { allSessions = allSessions.filter(session => session.disconnectedOn !== null); } return limit === Infinity ? allSessions : allSessions.slice(-limit).reverse(); } async getAlreadyOnlineVisualData(options = {}) { const { interval = "1d", filter = {}, limit = Infinity, format = "raw", chartConfig = {} } = options; const fullChartConfig = { groupBy: "metadata.user", mode: "count", valueKey: "value", metadataFields: null, previewLimit: 3, ...chartConfig }; const sources = ["storage", "memory"]; const [alreadyOnlineEvents, onlineJoins] = await Promise.all([this.getTidalyticsSince(interval, sources[0], { ...filter, tags: { "tider:already-online": "tider:already-online" } }), this.getTidalyticsSince(interval, sources[0], { ...filter, tags: { "tider:online": "tider:online" } })]); const entries = alreadyOnlineEvents.map(e => ({ ...e, type: "already-online" })).sort((a, b) => b.timestamp - a.timestamp); const joinCountsByUser = {}; for (const evt of onlineJoins) { const user = evt.metadata?.user; if (!user) { continue; } joinCountsByUser[user] = (joinCountsByUser[user] || 0) + 1; } if (format === "chart") { const result = fullChartConfig.mode === "average" ? aggregateMultiAverageChartData(entries, { ...fullChartConfig, groupBy: [].concat(fullChartConfig.groupBy) }) : aggregateChartData(entries, { ...fullChartConfig, typeKey: "type" }); return result.map(entry => { const userKey = entry.label; const totalJoins = joinCountsByUser[userKey] || 0; return { ...entry, totalJoins, collisionRate: totalJoins > 0 ? +(entry.count / totalJoins).toFixed(3) : 0 }; }); } return limit === Infinity ? entries : entries.slice(0, limit); } async getTiderBanVisualData(options = {}) { const { interval = "1d", filter = {}, limit = Infinity, format = "raw", chartConfig = {} } = options; const fullChartConfig = { groupBy: "type", mode: "count", ...chartConfig }; const sources = ["storage", "memory"]; const [banned, kicked, unbanned] = await Promise.all([this.getTidalyticsSince(interval, sources[0], { ...filter, tags: { "tider:banned": "tider:banned" } }), this.getTidalyticsSince(interval, sources[0], { ...filter, tags: { "tider:kicked": "tider:kicked" } }), this.getTidalyticsSince(interval, sources[0], { ...filter, tags: { "tider:unbanned": "tider:unbanned" } })]); const entries = [...banned.map(e => ({ ...e, type: "banned" })), ...kicked.map(e => ({ ...e, type: "kicked" })), ...unbanned.map(e => ({ ...e, type: "unbanned" }))].sort((a, b) => b.timestamp - a.timestamp); if (format === "chart") { return fullChartConfig.mode === "average" ? aggregateMultiAverageChartData(entries, { ...fullChartConfig, groupBy: [].concat(fullChartConfig.groupBy) }) : aggregateChartData(entries, { ...fullChartConfig, typeKey: "type" }); } return limit === Infinity ? entries : entries.slice(0, limit); } async getTideFileTransferVisualData(options = {}) { const { interval = "1d", filter = {}, limit = Infinity, format = "raw", chartConfig = {} } = options; const fullChartConfig = { groupBy: "type", mode: "count", ...chartConfig }; const sources = ["storage", "memory"]; const [incomingTransfers, incoming, completed] = await Promise.all([this.getTidalyticsSince(interval, sources[0], { ...filter, tags: { "tide:incoming-transfer": "tide:incoming-transfer" } }), this.getTidalyticsSince(interval, sources[0], { ...filter, tags: { "tide:incoming-file": "tide:incoming-file" } }), this.getTidalyticsSince(interval, sources[0], { ...filter, tags: { "tide:completed-transfer": "tide:completed-transfer" } })]); const entries = [...incomingTransfers.map(e => ({ ...e, type: "incoming-transfer" })), ...incoming.map(e => ({ ...e, type: "incoming" })), ...completed.map(e => ({ ...e, type: "completed" }))].sort((a, b) => b.timestamp - a.timestamp); if (format === "chart") { return fullChartConfig.mode === "average" ? aggregateMultiAverageChartData(entries, { ...fullChartConfig, groupBy: [].concat(fullChartConfig.groupBy) }) : aggregateChartData(entries, { ...fullChartConfig, typeKey: "type" }); } return limit === Infinity ? entries : entries.slice(0, limit); } async getTideTransferStateVisualData(options = {}) { const { interval = "1d", filter = {}, limit = Infinity, format = "raw", chartConfig = {} } = options; const fullChartConfig = { groupBy: "metadata.transferStateID", mode: "average", valueKey: "metadata.speed", metadataFields: ["senderID", "recipientID", "transferProgress", "speed", "transferETA", "fileName"], previewLimit: 3, ...chartConfig }; const sources = ["storage", "memory"]; const [transferStates] = await Promise.all([this.getTidalyticsSince(interval, sources[0], { ...filter, tags: { "tide:transfer-state": "tide:transfer-state" } })]); const entries = transferStates.map(e => ({ ...e, type: "transfer-state" })).sort((a, b) => b.timestamp - a.timestamp); if (format === "chart") { return fullChartConfig.mode === "average" ? aggregateMultiAverageChartData(entries, { ...fullChartConfig, groupBy: [].concat(fullChartConfig.groupBy) }) : aggregateChartData(entries, { ...fullChartConfig, typeKey: "type" }); } return limit === Infinity ? entries : entries.slice(0, limit); } async getFileListActivityVisualData(options = {}) { const { interval = "1d", filter = {}, limit = Infinity, format = "raw", chartConfig = {} } = options; const fullChartConfig = { groupBy: "type", mode: "count", ...chartConfig }; const sources = ["storage", "memory"]; const [requested, received] = await Promise.all([this.getTidalyticsSince(interval, sources[0], { ...filter, tags: { "file-list:request": "file-list:request" } }), this.getTidalyticsSince(interval, sources[0], { ...filter, tags: { "file-list:received": "file-list:received" } })]); const entries = [...requested.map(e => ({ ...e, type: "requested" })), ...received.map(e => ({ ...e, type: "received" }))].sort((a, b) => b.timestamp - a.timestamp); if (format === "chart") { return fullChartConfig.mode === "average" ? aggregateMultiAverageChartData(entries, { ...fullChartConfig, groupBy: [].concat(fullChartConfig.groupBy) }) : aggregateChartData(entries, { ...fullChartConfig, typeKey: "type" }); } return limit === Infinity ? entries : entries.slice(0, limit); } async getTideBarrierVisualData(options = {}) { const { interval = "1d", filter = {}, limit = Infinity, format = "raw", chartConfig = {} } = options; const fullChartConfig = { groupBy: "type", mode: "count", ...chartConfig }; const sources = ["storage", "memory"]; const [accepted, denied] = await Promise.all([this.getTidalyticsSince(interval, sources[0], { ...filter, tags: { "tide-barrier:accepted": "tide-barrier:accepted" } }), this.getTidalyticsSince(interval, sources[0], { ...filter, tags: { "tide-barrier:denied": "tide-barrier:denied" } })]); const entries = [...accepted.map(e => ({ ...e, type: "accepted" })), ...denied.map(e => ({ ...e, type: "denied" }))].sort((a, b) => b.timestamp - a.timestamp); if (format === "chart") { return fullChartConfig.mode === "average" ? aggregateMultiAverageChartData(entries, { ...fullChartConfig, groupBy: [].concat(fullChartConfig.groupBy) }) : aggregateChartData(entries, { ...fullChartConfig, typeKey: "type" }); } return limit === Infinity ? entries : entries.slice(0, limit); } // --- File Messager Tide Actions Management --- async getTideCompletedTransferActions({ interval = "1d", limit = 50, source = "storage", filter = {} } = {}) { const rawMetrics = await this.getTidalyticsSince(interval, source, { ...filter, tags: { "tide:completed-transfer": "tide:completed-transfer" } }); if (!Array.isArray(rawMetrics)) { return []; } const limitedMetrics = rawMetrics.slice(-limit).reverse(); return limitedMetrics.map(event => ({ tideActionID: "tide-completed-transfer", tideActionDate: event.timestamp || Date.now(), tideActionDetails: { recipientID: event.metadata?.recipientID, senderID: event.metadata?.senderID, path: event.metadata?.path, fileSize: event.metadata?.fileSize, fileName: event.metadata?.fileName, completedChunks: event.metadata?.completedChunks, totalChunks: event.metadata?.totalChunks, chunkSize: event.metadata?.chunkSize } })); } async getTideTransferStateActions({ interval = "1d", limit = Infinity, source = "storage", filter = {} } = {}) { const rawMetrics = await this.getTidalyticsSince(interval, source, { ...filter, tags: { "tide:transfer-state": "tide:transfer-state" } }); if (!Array.isArray(rawMetrics)) { return []; } const limitedMetrics = rawMetrics.slice(-limit).reverse(); return limitedMetrics.map(event => ({ tideActionID: "tide-transfer-state", tideActionDate: event.timestamp || Date.now(), tideActionDetails: { transferStateID: event.metadata?.transferStateID, recipientID: event.metadata?.recipientID, senderID: event.metadata?.senderID, path: event.metadata?.path, startTime: event.metadata?.startTime, currentTransferredSize: event.metadata?.currentTransferredSize, speed: event.metadata?.speed, transferProgress: event.metadata?.transferProgress, transferSize: event.metadata?.transferSize, transferETA: event.metadata?.transferETA, fileName: event.metadata?.fileName } })); } async getTideIncomingFileActions({ interval = "1d", limit = 50, source = "storage", filter = {} } = {}) { const rawMetrics = await this.getTidalyticsSince(interval, source, { ...filter, tags: { "tide:incoming-file": "tide:incoming-file" } }); if (!Array.isArray(rawMetrics)) { return []; } const limitedMetrics = rawMetrics.slice(-limit).reverse(); return limitedMetrics.map(event => ({ tideActionID: "tide-incoming-file", tideActionDate: event.timestamp || Date.now(), tideActionDetails: { recipientID: event.metadata?.recipientID, senderID: event.metadata?.senderID, path: event.metadata?.path, fileSize: event.metadata?.fileSize, fileName: event.metadata?.fileName, totalChunks: event.metadata?.totalChunks, chunkSize: event.metadata?.chunkSize } })); } async getTideIncomingTransferActions({ interval = "1d", limit = 50, source = "storage", filter = {} } = {}) { const rawMetrics = await this.getTidalyticsSince(interval, source, { ...filter, tags: { "tide:incoming-transfer": "tide:incoming-transfer" } }); if (!Array.isArray(rawMetrics)) { return []; } const limitedMetrics = rawMetrics.slice(-limit).reverse(); return limitedMetrics.map(event => ({ tideActionID: "tide-incoming-transfer", tideActionDate: event.timestamp || Date.now(), tideActionDetails: { recipientID: event.metadata?.recipientID, senderID: event.metadata?.senderID, fileSize: event.metadata?.fileSize, fileName: event.metadata?.fileName, path: event.metadata?.path, startedOn: event.metadata?.startedOn } })); } async getTideBarrierAcceptedActions({ interval = "1d", limit = 50, source = "storage", filter = {} } = {}) { const rawMetrics = await this.getTidalyticsSince(interval, source, { ...filter, tags: { "tide-barrier:accepted": "tide-barrier:accepted" } }); if (!Array.isArray(rawMetrics)) { return []; } const limitedMetrics = rawMetrics.slice(-limit).reverse(); return limitedMetrics.map(event => ({ tideActionID: "tide-barrier-accepted", tideActionDate: event.timestamp || Date.now(), tideActionDetails: { recipientID: event.metadata?.recipientID, transferID: event.metadata?.transferID, senderID: event.metadata?.senderID, tideItemCount: event.metadata?.tideItemCount, tideItemSize: event.metadata?.tideItemSize, tideTransferContentType: event.metadata?.tideTransferContentType, tideTransferType: event.metadata?.tideTransferType, tideDestinationPath: event.metadata?.tideDestinationPath, tideOriginPath: event.metadata?.tideOriginPath } })); } async getTideBarrierDeniedActions({ interval = "1d", limit = 50, source = "storage", filter = {} } = {}) { const rawMetrics = await this.getTidalyticsSince(interval, source, { ...filter, tags: { "tide-barrier:denied": "tide-barrier:denied" } }); if (!Array.isArray(rawMetrics)) { return []; } const limitedMetrics = rawMetrics.slice(-limit).reverse(); return limitedMetrics.map(event => ({ tideActionID: "tide-barrier-denied", tideActionDate: event.timestamp || Date.now(), tideActionDetails: { recipientID: event.metadata?.recipientID, transferID: event.metadata?.transferID, senderID: event.metadata?.senderID, tideItemCount: event.metadata?.tideItemCount, tideItemSize: event.metadata?.tideItemSize, tideTransferContentType: event.metadata?.tideTransferContentType, tideTransferType: event.metadata?.tideTransferType, tideDestinationPath: event.metadata?.tideDestinationPath, tideOriginPath: event.metadata?.tideOriginPath } })); } async getFileListReceivedActions({ interval = "1d", limit = 50, source = "storage", filter = {} } = {}) { const rawMetrics = await this.getTidalyticsSince(interval, source, { ...filter, tags: { "file-list:received": "file-list:received" } }); if (!Array.isArray(rawMetrics)) { return []; } const limitedMetrics = rawMetrics.slice(-limit).reverse(); return limitedMetrics.map(event => ({ tideActionID: "file-list-received", tideActionDate: event.timestamp || Date.now(), tideActionDetails: { recipientID: event.metadata?.recipientID, senderID: event.metadata?.senderID, totalItems: event.metadata?.totalItems, runningOn: event.metadata?.runningOn } })); } async getFileListRequestActions({ interval = "1d", limit = 50, source = "storage", filter = {} } = {}) { const rawMetrics = await this.getTidalyticsSince(interval, source, { ...filter, tags: { "file-list:request": "file-list:request" } }); if (!Array.isArray(rawMetrics)) { return []; } const limitedMetrics = rawMetrics.slice(-limit).reverse(); return limitedMetrics.map(event => ({ tideActionID: "file-list-request", tideActionDate: event.timestamp || Date.now(), tideActionDetails: { recipientID: event.metadata?.recipientID, requesterID: event.metadata?.requesterID, depth: event.metadata?.depth } })); } async getTiderBannedActions({ interval = "1d", limit = 50, source = "storage", filter = {} } = {}) { const rawMetrics = await this.getTidalyticsSince(interval, source, { ...filter, tags: { "tider:banned": "tider:banned" } }); if (!Array.isArray(rawMetrics)) { return []; } const limitedMetrics = rawMetrics.slice(-limit).reverse(); return limitedMetrics.map(event => ({ tideActionID: "ban-client-activated", tideActionDate: event.timestamp || Date.now(), tideActionDetails: { clientID: event.metadata?.clientID, options: event.metadata?.options } })); } async getTiderKickedActions({ interval = "1d", limit = 50, source = "storage", filter = {} } = {}) { const rawMetrics = await this.getTidalyticsSince(interval, source, { ...filter, tags: { "tider:kicked": "tider:kicked" } }); if (!Array.isArray(rawMetrics)) { return []; } const limitedMetrics = rawMetrics.slice(-limit).reverse(); return limitedMetrics.map(event => ({ tideActionID: "kick-client-activated", tideActionDate: event.timestamp || Date.now(), tideActionDetails: { clientID: event.metadata?.clientID, options: event.metadata?.options } })); } async getTiderUnbannedActions({ interval = "1d", limit = 50, source = "storage", filter = {} } = {}) { const rawMetrics = await this.getTidalyticsSince(interval, source, { ...filter, tags: { "tider:unbanned": "tider:unbanned" } }); if (!Array.isArray(rawMetrics)) { return []; } const limitedMetrics = rawMetrics.slice(-limit).reverse(); return limitedMetrics.map(event => ({ tideActionID: "unban-client-activated", tideActionDate: event.timestamp || Date.now(), tideActionDetails: { clientID: event.metadata?.clientID } })); } async getTiderAlreadyOnlineActions({ interval = "1d", limit = 50, source = "storage", filter = {} } = {}) { const rawMetrics = await this.getTidalyticsSince(interval, source, { ...filter, tags: { "tider:already-online": "tider:already-online" } }); if (!Array.isArray(rawMetrics)) { return []; } const limitedMetrics = rawMetrics.slice(-limit).reverse(); return limitedMetrics.map(event => ({ tideActionID: "tider-already-online", tideActionDate: event.timestamp || Date.now(), tideActionDetails: { currentTideChannel: event.metadata?.currentTideChannel || "file-room", originUser: event.metadata?.originUser, user: event.metadata?.user, connectionID: event.metadata?.connectionID, pClientID: event.metadata?.pClientID, tideID: event.metadata?.tideID, connectedOn: event.metadata?.connectedOn } })); } async getTiderOnlineActions({ interval = "1d", limit = 50, source = "storage", filter = {} } = {}) { const rawMetrics = await this.getTidalyticsSince(interval, source, { ...filter, tags: { "tider:online": "tider:online" } }); if (!rawMetrics || !Array.isArray(rawMetrics)) { return []; } const limitedMetrics = rawMetrics.slice(-limit).reverse(); return limitedMetrics.map(event => ({ tideActionID: "tider-online", tideActionDate: event.timestamp || Date.now(), tideActionDetails: { currentTideChannel: event.metadata?.currentTideChannel || "file-room", connectedOn: event.metadata?.connectedOn, originUser: event.metadata?.originUser, user: event.metadata?.user, connectionID: event.metadata?.connectionID, pClientID: event.metadata?.pClientID, tideID: event.metadata?.tideID } })); } async getTiderOfflineActions({ interval = "1d", limit = 50, source = "storage", filter = {} } = {}) { const rawMetrics = await this.getTidalyticsSince(interval, source, { ...filter, tags: { "tider:offline": "tider:offline" } }); if (!rawMetrics || !Array.isArray(rawMetrics)) { return []; } const limitedMetrics = rawMetrics.slice(-limit).reverse(); return limitedMetrics.map(event => ({ tideActionID: "tider-offline", tideActionDate: event.timestamp || Date.now(), tideActionDetails: { currentTideChannel: event.metadata?.currentTideChannel || "file-room", connectedOn: event.metadata?.connectedOn, originUser: event.metadata?.originUser, user: event.metadata?.user, connectionID: event.metadata?.connectionID, pClientID: event.metadata?.pClientID, tideID: event.metadata?.tideID } })); } // --- File Messager Tide Sentinel Management --- banClient(clientID, options = {}) { const result = this.tideSentinel.banClient(clientID, options); tidalytics.track("tider:banned", { value: 1, tags: { "tider:banned": "tider:banned", "tider": "tider", clientID }, metadata: { clientID, options } }); this.onTideAction({ tideActionID: "ban-client-activated", tideActionDate: Date.now(), tideActionDetails: { clientID, options } }); return result; } kickClient(clientID, options = { category: "kick", duration: "30s" }) { const { category = "kick", duration = "30s", ...optionsConfigurations } = options; const result = this.tideSentinel.banClient(clientID, { category, duration, ...optionsConfigurations }); tidalytics.track("tider:kicked", { value: 1, tags: { "tider:kicked": "tider:kicked", "tider": "tider", clientID }, metadata: { clientID, options } }); this.onTideAction({ tideActionID: "kick-client-activated", tideActionDate: Date.now(), tideActionDetails: { clientID, options } }); return result; } unbanClient(clientID) { const result = this.tideSentinel.unbanClient(clientID); tidalytics.track("tider:unbanned", { value: 1, tags: { "tider:unbanned": "tider:unbanned", "tider": "tider", clientID }, metadata: { clientID } }); this.onTideAction({ tideActionID: "unban-client-activated", tideActionDate: Date.now(), tideActionDetails: { clientID } }); return result; } clientBanned(clientID) { const result = this.tideSentinel.clientBanned(clientID); return result; } getBanEntry(clientID) { const result = this.tideSentinel.getBanEntry(clientID); return result; } filterBannedClients(filter = {}) { const result = this.tideSentinel.filterBannedClients(filter); return result; } registerTideSentinelExpireHook(list, callback) { const result = this.tideSentinel.registerExpireHook(list, callback); return result; } addToTideList(list, key, data) { const result = this.tideSentinel.addToList(list, key, data); return result; } removeFromTideList(list, key) { const result = this.tideSentinel.removeFromList(list, key); return result; } inTideList(list, key) { const result = this.tideSentinel.inList(list, key); return result; } getEntry(list, key) { const result = this.tideSentinel.getEntry(list, key); return result; } filterTideList(list, filter = {}) { const result = this.tideSentinel.filterList(list, filter); return result; } findSimilarKeys(keyword, options = {}) { const result = this.tideSentinel.findSimilarKeys(keyword, options); return result; } // --- File Messager Client Management --- getClientFromID(clientID) { const client = this.onlineClients.get(clientID); if (!client) { console.log(`Client ~ ${clientID} not found.`); return; } return client; } getClient(clientID) { const client = FileNetClientManager.getOnlineClient(clientID); if (!client) { console.log(`Client ~ ${clientID} not found.`); return; } return client; } requestFromClient(requesterID, senderID, filePath = process.cwd(), destinationPath) { const senderConnectionID = FileNetClientManager.getOnlineClient(senderID).id; const client = this.getClientFromID(senderConnectionID); if (!client) { return; } this.fileNet.emitToTide(client.tideID, "transfer-requested", { senderID, requesterID, path: filePath, destinationPath, type: "requestedTransfer" }); } /** * Send file chunk to a specific client * @param {Object} senderData - Holds the ID (tide ID), and client name (userID) of the sender * @param {Object} clientData - The ID (socket ID), and client name (userID) of the client * @param {Buffer} fileChunk - The file chunk to send * @param {String} fileName - The name of the file * @param {Number} chunkIndex - The index of the current chunk */ sendFileChunkToClient(senderData, clientData, fileChunk, fileName, chunkIndex, chunkSize, totalChunks, filePath = process.cwd(), fileSize = 100) { const senderClient = this.getClientFromID(senderData.id); const client = this.getClientFromID(clientData.id); if (!client) { return; } if (chunkIndex === 0) { this.fileNet.emitToTide(client.tideID, "incoming-file", { senderID: senderData.userID, clientID: clientData.userID, fileName, totalChunks, chunkIndex, path: filePath, fileSize }); tidalytics.track("tide:incoming-file", { value: 1, tags: { "tide:incoming-file": "tide:incoming-file", "tide": "tide", recipientID: clientData.userID, senderID: senderData.userID, path: filePath, fileSize, fileName }, metadata: { recipientID: clientData.userID, senderID: senderData.userID, path: filePath, fileSize, fileName, totalChunks, chunkSize } }); this.onTideAction({ tideActionID: "tide-incoming-file", tideActionDate: Date.now(), tideActionDetails: { recipientID: clientData.userID, senderID: senderData.userID, path: filePath, fileSize, fileName, totalChunks, chunkSize } }); } this.fileNet.emitToTide(client.tideID, "transfer-progress", { senderName: senderData.userID, senderID: senderData.id, fileName, fileChunk, chunkIndex, chunkSize, path: filePath, fileSize }); _assertClassBrand(_FileMessager_brand, this, _log).call(this, `[FileTide ~ File Messager] ~ Sent chunk ${chunkIndex} of ${fileName} to client ~ ${clientData.userID}!`); if (senderClient) { this.fileNet.emitToTide(senderClient.tideID, "transfer-status", { status: `Chunk ${chunkIndex} of ${fileName} sent to client ${clientData.userID}!`, recipientID: clientData.userID, senderName: senderData.userID, senderID: senderData.id, success: true, fileName, chunkIndex, totalChunks, chunkSize, path: filePath, fileSize }); } } saveClientTransferStates(data) { const tidingTransfers = this.currentTidingTransfers.get(data.clientID); if (tidingTransfers && tidingTransfers.size > 0) { for (let recipientID of tidingTransfers.keys()) { const recipientConnectionID = FileNetClientManager.getOnlineClient(data.recipientID ?? recipientID).id; const client = this.getClientFromID(recipientConnectionID); if (client) { this.fileNet.emitToTide(client.tideID, "save-current-transfer-states", { clientID: recipientID }); } } this.currentTidingTransfers.delete(data.clientID); } } // --- File Messager Event Management --- handleRequestTransferFromClient(senderTide, transferData) { const { requesterID, senderID, filePath, destinationPath } = transferData; this.requestFromClient(requesterID, senderID, filePath, destinationPath); } handleSentFileListToClient(senderTide, listData) { const { totalItemsSent, recipientID, senderID, runningOn } = listData; try { const recipientConnectionID = FileNetClientManager.getOnlineClient(recipientID).id; const senderConnectionID = FileNetClientManager.getOnlineClient(senderID).id; const senderClient = this.getClientFromID(senderConnectionID); const client = this.getClientFromID(recipientConnectionID); if (!client || !senderClient) { return; } this.fileNet.emitToTide(client.tideID, "client-file-list-received", { senderID, totalItemsSent, runningOn }); tidalytics.track("file-list:received", { value: 1, tags: { "file-list:received": "file-list:received", "file-list": "file-list", recipientID, senderID, totalItems: totalItemsSent, runningOn }, metadata: { recipientID, senderID, totalItems: totalItemsSent, runningOn } }); this.onTideAction({ tideActionID: "file-list-received", tideActionDate: Date.now(), tideActionDetails: { recipientID, senderID, totalItems: totalItemsSent, runningOn } }); } catch (error) { console.error(`[FileTide ~ Net Scanner] ~ `, error); return; } } handleSendFileListToClient(senderTide, listData) { const { listedFiles, totalItems, recipientID, senderID, runningOn } = listData; try { const recipientConnectionID = FileNetClientManager.getOnlineClient(recipientID).id; const senderConnectionID = FileNetClientManager.getOnlineClient(senderID).id; const senderClient = this.getClientFromID(senderConnectionID); const client = this.getClientFromID(recipientConnectionID); if (!client || !senderClient) { return; } this.fileNet.emitToTide(client.tideID, "client-file-list", { senderID, totalItems, listedFiles, runningOn }); } catch (error) { console.error(`[FileTide ~ Net Scanner] ~ `, error); return; } } handleListClientFiles(senderTide, clientData) { const { requesterID, recipientID, depth = 1 } = clientData; try { const recipientConnectionID = FileNetClientManager.getOnlineClient(recipientID).id; const senderConnectionID = FileNetClientManager.getOnlineClient(requesterID).id; const senderClient = this.getClientFromID(senderConnectionID); const client = this.getClientFromID(recipientConnectionID); if (!client || !senderClient) { return; } this.fileNet.emitToTide(client.tideID, "list-files", { senderID: requesterID, recipientID, depth }); tidalytics.track("file-list:request", { value: 1, tags: { "file-list:request": "file-list:request", "file-list": "file-list", recipientID, requesterID, depth }, metadata: { recipientID, requesterID, depth } }); this.onTideAction({ tideActionID: "file-list-request", tideActionDate: Date.now(), tideActionDetails: { recipientID, requesterID, depth } }); } catch (error) { console.error(`[FileTide ~ Net Scanner] ~ `, error); return; } } handleClientToClientTransfer(senderTide, transferData) { try { const { chunkBatch } = transferData; if (chunkBatch && chunkBatch.length > 0) { for (const chunkData of chunkBatch) { const { senderID, recipientId, fileName, fileChunk, fileSize, filePath, chunkIndex, chunkSize, completedChunks, totalChunks } = chunkData; _assertClassBrand(_FileMessager_brand, this, _log).call(this, `[FileTide ~ File Messager] ~ Transferring file ~ ${fileName} | chunk ${chunkIndex + 1} >> ${completedChunks} of ${totalChunks} to client (${recipientId})`); const recipientClient = FileNetClientManager.getOnlineClient(recipientId); const senderClient = FileNetClientManager.getOnlineClient(senderID); this.sendFileChunkToClient({ userID: senderID, id: senderClient.id }, { userID: recipientId, id: recipientClient.id }, fileChunk, fileName, chunkIndex, chunkSize, totalChunks, filePath, fileSize); if (completedChunks === totalChunks) { this.fileNet.emitToTide(recipientClient.tideID, "transfer-complete", { senderID, recipientId, fileName, filePath }); this.fileNet.emitToTide(senderClient.tideID, "transfer-complete", { senderID, recipientId, fileName, filePath }); tidalytics.track("tide:completed-transfer", { value: 1, tags: { "tide:completed-transfer": "tide:completed-transfer", "tide": "tide", recipientID: recipientId, senderID, path: filePath, fileSize, fileName }, metadata: { recipientID: recipientId, senderID, path: filePath, fileSize, fileName, completedChunks, totalChunks, chunkSize } }); this.onTideAction({ tideActionID: "tide-completed-transfer", tideActionDate: Date.now(), tideActionDetails: { recipientID: recipientId, senderID, path: filePath, fileSize, fileName, completedChunks, totalChunks, chunkSize } }); console.log(`[FileTide ~ File Messager] ~ File transfer completed: ${fileName} from ${senderID} to ${recipientId}`); const tidingTransfers = this.currentTidingTransfers.get(senderID); if (tidingTransfers) { tidingTransfers.delete(senderID); } } } return; } const { senderID, recipientId, fileName, fileChunk, fileSize, filePath, chunkIndex, chunkSize, completedChunks, totalChunks } = transferData; _assertClassBrand(_FileMessager_brand, this, _log).call(this, `[FileTide ~ File Messager] ~ Transferring file ~ ${fileName} | chunk ${chunkIndex + 1} >> ${completedChunks} of ${totalChunks} to client (${recipientId})`); const recipientClient = FileNetClientManager.getOnlineClient(recipientId); const senderClient = FileNetClientManager.getOnlineClient(senderID); this.sendFileChunkToClient({ userID: senderID, id: senderClient.id }, { userID: recipientId, id: recipientClient.id }, fileChunk, fileName, chunkIndex, chunkSize, totalChunks, filePath, fileSize); if (chunkIndex + 1 === totalChunks) { this.fileNet.emitToTide(recipientClient.tideID, "transfer-complete", { senderID, recipientId, fileName, filePath }); this.fileNet.emitToTide(senderClient.tideID, "transfer-complete", { senderID, recipientId, fileName, filePath }); tidalytics.track("tide:completed-transfer", { value: 1, tags: { "tide:completed-transfer": "tide:completed-transfer", "tide": "tide", recipientID: recipientId, senderID, path: filePath, fileSize, fileName }, metadata: { recipientID: recipientId, senderID, path: filePath, fileSize, fileName, completedChunks, totalChunks, chunkSize } }); this.onTideAction({ tideActionID: "tide-completed-transfer", tideActionDate: Date.now(), tideActionDetails: { recipientID: recipientId, senderID, path: filePath, fileSize, fileName, completedChunks, totalChunks, chunkSize } }); console.log(`[FileTide ~ File Messager] ~ File transfer completed: ${fileName} from ${senderID} to ${recipientId}`); const tidingTransfers = this.currentTidin