@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
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}`);
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: