@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
JavaScript
"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