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,550 lines (1,538 loc) 81.1 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}`); const currentOnlineClient = FileNetClientManager.getOnlineClientByConnectionID(socket.id); if (currentOnlineClient) { this.handleClientOffline({}, { connectionID: currentOnlineClient.id, ...currentOnlineClient }); } 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", 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("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 getTidalyticsBetween(start, end, source = "both", filter = {}) { return await tidalytics.getMetricsBetween(start, end, source, filter); } async getStoredTidalytics(filter = {}) { return await tidalytics.getStoredMetrics(filter); } async getTidalyticsFromDomains(domains = [], options = {}) { return await tidalytics.getMetricsFromDomains(domains, options); } 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(); } async getTiderPresenceVisualData(options = {}) { const { interval = "1d", limit = Infinity, filter = {}, rangeMode = "since", mode = "all", start = null, end = null } = options; const sources = ["storage", "memory"]; const fetch = _assertClassBrand(_FileMessager_brand, this, _getTidalyticsRangeFetcher).call(this, interval, rangeMode, start, end); const [storedOnline, storedOffline] = await Promise.all([fetch({ ...filter, tags: { "tider:online": "tider:online" } }, sources[0]), fetch({ ...filter, tags: { "tider:offline": "tider:offline" } }, sources[0])]); const [liveOnline] = await Promise.all([fetch({ ...filter, tags: { "tider:online": "tider:online" } }, sources[1])]); 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 { chartConfig = {}, interval = "1d", filter = {}, limit = Infinity, format = "raw", rangeMode = "since", start = null, end = null } = options; const fullChartConfig = { groupBy: "metadata.user", mode: "count", valueKey: "value", metadataFields: null, previewLimit: 3, ...chartConfig }; const sources = ["storage", "memory"]; const fetch = _assertClassBrand(_FileMessager_brand, this, _getTidalyticsRangeFetcher).call(this, interval, rangeMode, start, end); const [alreadyOnlineEvents, onlineJoins] = await Promise.all([fetch({ ...filter, tags: { "tider:already-online": "tider:already-online" } }, sources[0]), fetch({ ...filter, tags: { "tider:online": "tider:online" } }, sources[0])]); 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 { chartConfig = {}, interval = "1d", filter = {}, limit = Infinity, format = "raw", rangeMode = "since", start = null, end = null } = options; const fullChartConfig = { groupBy: "type", mode: "count", ...chartConfig }; const sources = ["storage", "memory"]; const fetch = _assertClassBrand(_FileMessager_brand, this, _getTidalyticsRangeFetcher).call(this, interval, rangeMode, start, end); const [banned, kicked, unbanned] = await Promise.all([fetch({ ...filter, tags: { "tider:banned": "tider:banned" } }, sources[0]), fetch({ ...filter, tags: { "tider:kicked": "tider:kicked" } }, sources[0]), fetch({ ...filter, tags: { "tider:unbanned": "tider:unbanned" } }, sources[0])]); 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 { chartConfig = {}, interval = "1d", filter = {}, limit = Infinity, format = "raw", rangeMode = "since", start = null, end = null } = options; const fullChartConfig = { groupBy: "type", mode: "count", ...chartConfig }; const sources = ["storage", "memory"]; const fetch = _assertClassBrand(_FileMessager_brand, this, _getTidalyticsRangeFetcher).call(this, interval, rangeMode, start, end); const [incomingTransfers, incoming, completed] = await Promise.all([fetch({ ...filter, tags: { "tide:incoming-transfer": "tide:incoming-transfer" } }, sources[0]), fetch({ ...filter, tags: { "tide:incoming-file": "tide:incoming-file" } }, sources[0]), fetch({ ...filter, tags: { "tide:completed-transfer": "tide:completed-transfer" } }, sources[0])]); 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 { chartConfig = {}, interval = "1d", filter = {}, limit = Infinity, format = "raw", rangeMode = "since", start = null, end = null } = 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 fetch = _assertClassBrand(_FileMessager_brand, this, _getTidalyticsRangeFetcher).call(this, interval, rangeMode, start, end); const [transferStates] = await Promise.all([fetch({ ...filter, tags: { "tide:transfer-state": "tide:transfer-state" } }, sources[0])]); 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 { chartConfig = {}, interval = "1d", filter = {}, limit = Infinity, format = "raw", rangeMode = "since", start = null, end = null } = options; const fullChartConfig = { groupBy: "type", mode: "count", ...chartConfig }; const sources = ["storage", "memory"]; const fetch = _assertClassBrand(_FileMessager_brand, this, _getTidalyticsRangeFetcher).call(this, interval, rangeMode, start, end); const [requested, received] = await Promise.all([fetch({ ...filter, tags: { "file-list:request": "file-list:request" } }, sources[0]), fetch({ ...filter, tags: { "file-list:received": "file-list:received" } }, sources[0])]); 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 { chartConfig = {}, interval = "1d", filter = {}, limit = Infinity, format = "raw", rangeMode = "since", start = null, end = null } = options; const fullChartConfig = { groupBy: "type", mode: "count", ...chartConfig }; const sources = ["storage", "memory"]; const fetch = _assertClassBrand(_FileMessager_brand, this, _getTidalyticsRangeFetcher).call(this, interval, rangeMode, start, end); const [accepted, denied] = await Promise.all([fetch({ ...filter, tags: { "tide-barrier:accepted": "tide-barrier:accepted" } }, sources[0]), fetch({ ...filter, tags: { "tide-barrier:denied": "tide-barrier:denied" } }, sources[0])]); 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 Tidalytics Event Management --- attachTiderPresenceVisualStream(options = {}) { const { visualDataConfigurations = {}, mode = "buffer", forward } = options; if (!forward) { return; } const handle = async () => { if (mode === "signal") { forward("rehydrate"); return; } setTimeout(() => { this.getTiderPresenceVisualData(visualDataConfigurations).then(forward); }, 25); }; 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; } setTimeout(() => { this.getAlreadyOnlineVisualData(visualDataConfigurations).then(forward); }, 25); }; 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; } setTimeout(() => { this.getTiderBanVisualData(visualDataConfigurations).then(forward); }, 25); }; 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; } setTimeout(() => { this.getTideFileTransferVisualData(visualDataConfigurations).then(forward); }, 25); }; 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; } setTimeout(() => { this.getTideTransferStateVisualData(visualDataConfigurations).then(forward); }, 25); }; 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; } setTimeout(() => { this.getFileListVisualData(visualDataConfigurations).then(forward); }, 25); }; 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; } setTimeout(() => { this.getTideBarrierVisualData(visualDataConfigurations).then(forward); }, 25); }; tidalytics.events.on("metric:created:tide-barrier:accepted", handle); tidalytics.events.on("metric:created:tide-barrier:denied", handle); } // --- File Messager Tide Actions Management --- async getAllTideActions(options = {}) { const { interval = null, limit = 1000, source = "both", filter = {}, rangeMode = "since", order = "desc", start = null, end = null } = options; const tagMap = { "tide:completed-transfer": "tide-completed-transfer", "tide:transfer-state": "tide-transfer-state", "tide:incoming-file": "tide-incoming-file", "tide:incoming-transfer": "tide-incoming-transfer", "tide-barrier:accepted": "tide-barrier-accepted", "tide-barrier:denied": "tide-barrier-denied", "file-list:received": "file-list-received", "file-list:request": "file-list-request", "tider:banned": "ban-client-activated", "tider:kicked": "kick-client-activated", "tider:unbanned": "unban-client-activated", "tider:already-online": "tider-already-online", "tider:online": "tider-online", "tider:offline": "tider-offline" }; const domains = Object.keys(tagMap); const rawEvents = await this.getTidalyticsFromDomains(domains, { interval, source, rangeMode, start, end, filter, order }); if (!Array.isArray(rawEvents)) { return []; } const actions = rawEvents.map(event => { const tag = Object.keys(event.tags || {})[0]; const actionID = tagMap[tag] || "unknown"; const metadata = event.metadata || {}; const base = { tideActionID: actionID, tideActionDate: event.timestamp || Date.now(), tideActionDetails: {} }; switch (tag) { case "tide:completed-transfer": base.tideActionDetails = { recipientID: metadata.recipientID, senderID: metadata.senderID, path: metadata.path, fileSize: metadata.fileSize, fileName: metadata.fileName, completedChunks: metadata.completedChunks, totalChunks: metadata.totalChunks, chunkSize: metadata.chunkSize }; break; case "tide:transfer-state": base.tideActionDetails = { transferStateID: metadata.transferStateID, recipientID: metadata.recipientID, senderID: metadata.senderID, path: metadata.path, startTime: metadata.startTime, currentTransferredSize: metadata.currentTransferredSize, speed: metadata.speed, transferProgress: metadata.transferProgress, transferSize: metadata.transferSize, transferETA: metadata.transferETA, fileName: metadata.fileName }; break; case "tide:incoming-file": base.tideActionDetails = { recipientID: metadata.recipientID, senderID: metadata.senderID, path: metadata.path, fileSize: metadata.fileSize, fileName: metadata.fileName, totalChunks: metadata.totalChunks, chunkSize: metadata.chunkSize }; break; case "tide:incoming-transfer": base.tideActionDetails = { recipientID: metadata.recipientID, senderID: metadata.senderID, fileSize: metadata.fileSize, fileName: metadata.fileName, path: metadata.path, startedOn: metadata.startedOn }; break; case "tide-barrier:accepted": case "tide-barrier:denied": base.tideActionDetails = { recipientID: metadata.recipientID, transferID: metadata.transferID, senderID: metadata.senderID, tideItemCount: metadata.tideItemCount, tideItemSize: metadata.tideItemSize, tideTransferContentType: metadata.tideTransferContentType, tideTransferType: metadata.tideTransferType, tideDestinationPath: metadata.tideDestinationPath, tideOriginPath: metadata.tideOriginPath }; break; case "file-list:received": base.tideActionDetails = { recipientID: metadata.recipientID, senderID: metadata.senderID, totalItems: metadata.totalItems, runningOn: metadata.runningOn }; break; case "file-list:request": base.tideActionDetails = { recipientID: metadata.recipientID, requesterID: metadata.requesterID, depth: metadata.depth }; break; case "tider:banned": case "tider:kicked": base.tideActionDetails = { clientID: metadata.clientID, options: metadata.options }; break; case "tider:unbanned": base.tideActionDetails = { clientID: metadata.clientID }; break; case "tider:already-online": case "tider:online": case "tider:offline": base.tideActionDetails = { currentTideChannel: metadata.currentTideChannel || "file-room", originUser: metadata.originUser, user: metadata.user, connectionID: metadata.connectionID, pClientID: metadata.pClientID, tideID: metadata.tideID, connectedOn: metadata.connectedOn }; break; } return base; }); return actions.slice(0, limit); } async getTideCompletedTransferActions(options = {}) { const { interval = "1d", limit = 50, source = "storage", filter = {}, rangeMode = "since", start = null, end = null } = options; const fetch = _assertClassBrand(_FileMessager_brand, this, _getTidalyticsRangeFetcher).call(this, interval, rangeMode, start, end); const rawMetrics = await fetch({ ...filter, tags: { "tide:completed-transfer": "tide:completed-transfer" } }, source); 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(options = {}) { const { interval = "1d", limit = Infinity, source = "storage", filter = {}, rangeMode = "since", start = null, end = null } = options; const fetch = _assertClassBrand(_FileMessager_brand, this, _getTidalyticsRangeFetcher).call(this, interval, rangeMode, start, end); const rawMetrics = await fetch({ ...filter, tags: { "tide:transfer-state": "tide:transfer-state" } }, source); 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(options = {}) { const { interval = "1d", limit = 50, source = "storage", filter = {}, rangeMode = "since", start = null, end = null } = options; const fetch = _assertClassBrand(_FileMessager_brand, this, _getTidalyticsRangeFetcher).call(this, interval, rangeMode, start, end); const rawMetrics = await fetch({ ...filter, tags: { "tide:incoming-file": "tide:incoming-file" } }, source); 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(options = {}) { const { interval = "1d", limit = 50, source = "storage", filter = {}, rangeMode = "since", start = null, end = null } = options; const fetch = _assertClassBrand(_FileMessager_brand, this, _getTidalyticsRangeFetcher).call(this, interval, rangeMode, start, end); const rawMetrics = await fetch({ ...filter, tags: { "tide:incoming-transfer": "tide:incoming-transfer" } }, source); 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(options = {}) { const { interval = "1d", limit = 50, source = "storage", filter = {}, rangeMode = "since", start = null, end = null } = options; const fetch = _assertClassBrand(_FileMessager_brand, this, _getTidalyticsRangeFetcher).call(this, interval, rangeMode, start, end); const rawMetrics = await fetch({ ...filter, tags: { "tide-barrier:accepted": "tide-barrier:accepted" } }, source); 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(options = {}) { const { interval = "1d", limit = 50, source = "storage", filter = {}, rangeMode = "since", start = null, end = null } = options; const fetch = _assertClassBrand(_FileMessager_brand, this, _getTidalyticsRangeFetcher).call(this, interval, rangeMode, start, end); const rawMetrics = await fetch({ ...filter, tags: { "tide-barrier:denied": "tide-barrier:denied" } }, source); 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(options = {}) { const { interval = "1d", limit = 50, source = "storage", filter = {}, rangeMode = "since", start = null, end = null } = options; const fetch = _assertClassBrand(_FileMessager_brand, this, _getTidalyticsRangeFetcher).call(this, interval, rangeMode, start, end); const rawMetrics = await fetch({ ...filter, tags: { "file-list:received": "file-list:received" } }, source); 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(options = {}) { const { interval = "1d", limit = 50, source = "storage", filter = {}, rangeMode = "since", start = null, end = null } = options; const fetch = _assertClassBrand(_FileMessager_brand, this, _getTidalyticsRangeFetcher).call(this, interval, rangeMode, start, end); const rawMetrics = await fetch({ ...filter, tags: { "file-list:request": "file-list:request" } }, source); 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(options = {}) { const { interval = "1d", limit = 50, source = "storage", filter = {}, rangeMode = "since", start = null, end = null } = options; const fetch = _assertClassBrand(_FileMessager_brand, this, _getTidalyticsRangeFetcher).call(this, interval, rangeMode, start, end); const rawMetrics = await fetch({ ...filter, tags: { "tider:banned": "tider:banned" } }, source); 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(options = {}) { const { interval = "1d", limit = 50, source = "storage", filter = {}, rangeMode = "since", start = null, end = null } = options; const fetch = _assertClassBrand(_FileMessager_brand, this, _getTidalyticsRangeFetcher).call(this, interval, rangeMode, start, end); const rawMetrics = await fetch({ ...filter, tags: { "tider:kicked": "tider:kicked" } }, source); 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(options = {}) { const { interval = "1d", limit = 50, source = "storage", filter = {}, rangeMode = "since", start = null, end = null } = options; const fetch = _assertClassBrand(_FileMessager_brand, this, _getTidalyticsRangeFetcher).call(this, interval, rangeMode, start, end); const rawMetrics = await fetch({ ...filter, tags: { "tider:unbanned": "tider:unbanned" } }, source); 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(options = {}) { const { interval = "1d", limit = 50, source = "storage", filter = {}, rangeMode = "since", start = null, end = null } = options; const fetch = _assertClassBrand(_FileMessager_brand, this, _getTidalyticsRangeFetcher).call(this, interval, rangeMode, start, end); const rawMetrics = await fetch({ ...filter, tags: { "tider:already-online": "tider:already-online" } }, source); 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(options = {}) { const { interval = "1d", limit = 50, source = "storage", filter = {}, rangeMode = "since", start = null, end = null } = options; const fetch = _assertClassBrand(_FileMessager_brand, this, _getTidalyticsRangeFetcher).call(this, interval, rangeMode, start, end); const rawMetrics = await fetch({ ...filter, tags: { "tider:online": "tider:online" } }, source); 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(options = {}) { const { interval = "1d", limit = 50, source = "storage", filter = {}, rangeMode = "since", start = null, end = null } = options; const fetch = _assertClassBrand(_FileMessager_brand, this, _getTidalyticsRangeFetcher).call(this, interval, rangeMode, start, end); const rawMetrics = await fetch({ ...filter, tags: { "tider:offline": "tider:offline" } }, source); 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; } getOnlineClients() { return FileNetClientManager.getOnlineClients(); } 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 * @param {Object} metadata - Any additional identifying data attached to the transfer */ sendFileChunkToClient(senderData, clientData, fileChunk, fileName, chunkIndex, chunkSize, totalChunks, filePath = process.cwd(), fileSize = 100, metadata = {}) { 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, transferMetadata: metadata } }); this.onTideAction({ tideActionID: "tide-incoming-file", tideActionDate: Date.now(), tideActionDetails: { recipientID: clientData.userID, senderID: senderData.userID, path: filePath, fileSize, fileName, totalChunks, chunkSize, transferMetadata: metadata } }); } this.fileNet.emitToTide(client.tideID, "transfer-progress", { senderName: senderData.userID, senderID: senderData.id, fileName, fileChunk, chunkIndex, chunkSize, path: filePath, fileSize, metadata }); _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: