@trap_stevo/filetide 0.0.14 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/FileMessager.js +55 -49
- package/dist/cjs/FileMessagerClient.js +73 -9
- package/dist/cjs/FileTide.js +138 -9
- package/dist/cjs/HUDComponents/ConsoleTable.js +349 -0
- package/dist/cjs/HUDManagers/FileNetClientManager.js +3 -2
- package/dist/cjs/HUDManagers/FileTransferManager.js +104 -0
- package/dist/cjs/HUDManagers/FileUtilityManager.js +109 -0
- package/package.json +5 -2
package/dist/cjs/FileMessager.js
CHANGED
|
@@ -8,11 +8,15 @@ const {
|
|
|
8
8
|
FileNetClientManager
|
|
9
9
|
} = require("./HUDManagers/FileNetClientManager");
|
|
10
10
|
const {
|
|
11
|
-
|
|
12
|
-
} = require("./HUDManagers/
|
|
11
|
+
FileTransferManager
|
|
12
|
+
} = require("./HUDManagers/FileTransferManager");
|
|
13
13
|
class FileMessager {
|
|
14
|
-
constructor(options = {}
|
|
14
|
+
constructor(options = {}, transportOptions = {
|
|
15
|
+
parallelChunks: 3,
|
|
16
|
+
maxRetries: 3
|
|
17
|
+
}) {
|
|
15
18
|
const serverOptions = FileMessagerConfigManager.getServerOptions(options);
|
|
19
|
+
this.transporter = new FileTransferManager(transportOptions);
|
|
16
20
|
this.fileNet = new IoTide(serverOptions.port, serverOptions, true, this.onConnect.bind(this), this.onDisconnect.bind(this));
|
|
17
21
|
this.onlineClients = new Map();
|
|
18
22
|
return;
|
|
@@ -39,55 +43,73 @@ class FileMessager {
|
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
/**
|
|
42
|
-
* Send file to a specific client
|
|
46
|
+
* Send file chunk to a specific client
|
|
43
47
|
* @param {String} senderID - The ID of the sender (tide ID)
|
|
44
48
|
* @param {String} clientId - The ID of the client (socket ID)
|
|
45
|
-
* @param {Buffer}
|
|
49
|
+
* @param {Buffer} fileChunk - The file chunk to send
|
|
46
50
|
* @param {String} fileName - The name of the file
|
|
51
|
+
* @param {Number} chunkIndex - The index of the current chunk
|
|
47
52
|
*/
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
console.log(`Client ~ ${clientId} not authorized.`);
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
const senderConnectionID = FileNetClientManager.getOnlineClient(senderID);
|
|
54
|
-
const connectionID = FileNetClientManager.getOnlineClient(clientId);
|
|
55
|
-
const senderClient = this.onlineClients.get(connectionID);
|
|
56
|
-
const client = this.onlineClients.get(connectionID);
|
|
53
|
+
sendFileChunkToClient(senderID, clientId, fileChunk, fileName, chunkIndex, totalChunks, filePath = process.cwd()) {
|
|
54
|
+
const client = this.onlineClients.get(clientId);
|
|
57
55
|
if (!client) {
|
|
58
56
|
console.log(`Client ~ ${clientId} not found.`);
|
|
59
57
|
return;
|
|
60
58
|
}
|
|
61
|
-
|
|
59
|
+
if (chunkIndex === 0) {
|
|
60
|
+
client.emit("incoming-file", {
|
|
61
|
+
fileName,
|
|
62
|
+
totalChunks,
|
|
63
|
+
chunkIndex,
|
|
64
|
+
path: filePath
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
client.emit("transfer-progress", {
|
|
62
68
|
fileName,
|
|
63
|
-
|
|
69
|
+
fileChunk,
|
|
70
|
+
chunkIndex,
|
|
64
71
|
path: filePath
|
|
65
72
|
});
|
|
66
|
-
console.log(`[FileTide ~ File Messager] ~
|
|
73
|
+
console.log(`[FileTide ~ File Messager] ~ Sent chunk ${chunkIndex} of ${fileName} to client ~ ${clientId}!`);
|
|
74
|
+
const senderClient = this.onlineClients.get(senderID);
|
|
67
75
|
if (senderClient) {
|
|
68
76
|
senderClient.emit("transfer-status", {
|
|
69
|
-
status: `
|
|
77
|
+
status: `Chunk ${chunkIndex} of ${fileName} sent to client ${clientId}!`,
|
|
70
78
|
recipientID: clientId,
|
|
71
79
|
success: true
|
|
72
80
|
});
|
|
73
81
|
}
|
|
74
|
-
return;
|
|
75
82
|
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Handle file transfer requests between clients
|
|
79
|
-
* @param {Object} transferData - The data containing the sender, recipient, and file details
|
|
80
|
-
*/
|
|
81
83
|
handleClientToClientTransfer(transferData) {
|
|
82
84
|
const {
|
|
83
85
|
senderID,
|
|
84
86
|
recipientId,
|
|
85
87
|
fileName,
|
|
86
|
-
|
|
87
|
-
filePath
|
|
88
|
+
fileChunk,
|
|
89
|
+
filePath,
|
|
90
|
+
chunkIndex,
|
|
91
|
+
totalChunks
|
|
88
92
|
} = transferData;
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
console.log(`Transferring file ~ ${fileName} | chunk ${chunkIndex + 1} of ${totalChunks} to client (${recipientId})`);
|
|
94
|
+
this.sendFileChunkToClient(FileNetClientManager.getOnlineClient(senderID).id, FileNetClientManager.getOnlineClient(recipientId).id, fileChunk, fileName, chunkIndex, totalChunks, filePath);
|
|
95
|
+
if (chunkIndex + 1 === totalChunks) {
|
|
96
|
+
this.fileNet.emit("transfer-complete", {
|
|
97
|
+
senderID,
|
|
98
|
+
recipientId,
|
|
99
|
+
fileName,
|
|
100
|
+
filePath
|
|
101
|
+
});
|
|
102
|
+
console.log(`[FileTide ~ File Messager] ~ File transfer completed: ${fileName} from ${senderID} to ${recipientId}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
handleFileTransferStart(fileData, emitToChannel) {
|
|
106
|
+
console.log(`Starting file transfer: ${fileData.fileName}`);
|
|
107
|
+
}
|
|
108
|
+
handleFileTransferProgress(data, emitToChannel) {
|
|
109
|
+
console.log(`Progress: ${data.progress}`);
|
|
110
|
+
}
|
|
111
|
+
handleFileTransferComplete(data) {
|
|
112
|
+
console.log(`File transfer completed: ${data.fileName}`);
|
|
91
113
|
}
|
|
92
114
|
handleClientsOnline(clientID) {
|
|
93
115
|
if (!FileNetClientManager.getOnlineClient(clientID)) {
|
|
@@ -100,37 +122,21 @@ class FileMessager {
|
|
|
100
122
|
console.log(`Client ~ ${clientID} not found.`);
|
|
101
123
|
return;
|
|
102
124
|
}
|
|
103
|
-
client.
|
|
104
|
-
currentClients:
|
|
125
|
+
this.fileNet.emitToTide(client.tideID, "current-clients-online", {
|
|
126
|
+
currentClients: FileNetClientManager.getOnlineClients()
|
|
105
127
|
});
|
|
106
128
|
return;
|
|
107
129
|
}
|
|
108
|
-
handleClientOnline(clientID, connectionID) {
|
|
130
|
+
handleClientOnline(clientID, tideID, pClientID, connectionID) {
|
|
109
131
|
if (!this.onlineClients.has(connectionID)) {
|
|
110
132
|
return;
|
|
111
133
|
}
|
|
112
|
-
FileNetClientManager.addOnlineClient(clientID,
|
|
113
|
-
|
|
134
|
+
FileNetClientManager.addOnlineClient(clientID, pClientID, tideID, connectionID);
|
|
135
|
+
this.fileNet.emitToTide(tideID, "current-online-clients", Object.fromEntries(FileNetClientManager.getOnlineClients()));
|
|
114
136
|
return;
|
|
115
137
|
}
|
|
116
138
|
handleClientOffline(clientID) {
|
|
117
|
-
if (!FileNetClientManager.getOnlineClient(clientID)) {
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
139
|
FileNetClientManager.clearOnlineClient(clientID);
|
|
121
|
-
console.log(this.onlineClients, FileNetClientManager.getOnlineClients());
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
handleFileTransferStart(fileData, emitToChannel) {
|
|
125
|
-
console.log(`Starting file transfer: ${fileData.fileName}`);
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
handleFileTransferProgress(data, emitToChannel) {
|
|
129
|
-
console.log(`Progress: ${data.progress}`);
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
handleFileTransferComplete(data) {
|
|
133
|
-
console.log(`File transfer completed: ${data.fileName}`);
|
|
134
140
|
return;
|
|
135
141
|
}
|
|
136
142
|
}
|
|
@@ -6,12 +6,20 @@ const {
|
|
|
6
6
|
const {
|
|
7
7
|
FileMessagerConfigManager
|
|
8
8
|
} = require("./HUDManagers/FileMessagerConfigManager");
|
|
9
|
+
const {
|
|
10
|
+
FileTransferManager
|
|
11
|
+
} = require("./HUDManagers/FileTransferManager");
|
|
9
12
|
const {
|
|
10
13
|
FileUtilityManager
|
|
11
14
|
} = require("./HUDManagers/FileUtilityManager");
|
|
15
|
+
const path = require("path");
|
|
12
16
|
class FileMessagerClient {
|
|
13
|
-
constructor(options = {}
|
|
17
|
+
constructor(options = {}, transportOptions = {
|
|
18
|
+
parallelChunks: 3,
|
|
19
|
+
maxRetries: 3
|
|
20
|
+
}) {
|
|
14
21
|
this.clientOptions = FileMessagerConfigManager.getClientOptions(options);
|
|
22
|
+
this.transporter = new FileTransferManager(transportOptions);
|
|
15
23
|
this.clientTide = new HUDIoTide();
|
|
16
24
|
this.clients = new Map();
|
|
17
25
|
}
|
|
@@ -34,15 +42,71 @@ class FileMessagerClient {
|
|
|
34
42
|
});
|
|
35
43
|
});
|
|
36
44
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Sends all files in a directory in parallel using the sendFile method.
|
|
48
|
+
* @param {string} clientID - The ID of the client sending the files.
|
|
49
|
+
* @param {string} recipientId - The ID of the recipient.
|
|
50
|
+
* @param {Array} filesData - Array of file data objects from the directory.
|
|
51
|
+
* @param {string} baseDirectory - The base directory path that contains the files.
|
|
52
|
+
* @param {string} destinationPath - The path to which the files should be sent.
|
|
53
|
+
*/
|
|
54
|
+
sendDirectoryFiles(clientID, recipientId, filesData, baseDirectory, destinationPath) {
|
|
55
|
+
const baseDirectoryName = path.basename(baseDirectory);
|
|
56
|
+
const adjustedDestinationPath = path.join(destinationPath, baseDirectoryName);
|
|
57
|
+
const sendFilePromises = filesData.map(fileInfo => {
|
|
58
|
+
const relativeFilePath = path.dirname(path.relative(baseDirectory, fileInfo.filePath));
|
|
59
|
+
const fileDestinationPath = path.join(adjustedDestinationPath, relativeFilePath);
|
|
60
|
+
return this.sendFile(clientID, recipientId, fileInfo.fileName, fileInfo.fileData, fileDestinationPath);
|
|
44
61
|
});
|
|
45
|
-
|
|
62
|
+
Promise.all(sendFilePromises).then(() => console.log("All files in directory transferred successfully!")).catch(error => console.error("Did not transfer files: ", error));
|
|
63
|
+
}
|
|
64
|
+
sendFile(clientID, recipientId, fileName, file, filePath = process.cwd()) {
|
|
65
|
+
console.log(fileName, ": ", file);
|
|
66
|
+
if (!file) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
this.transporter.sendFile(file, {
|
|
70
|
+
onSendChunk: (transferId, chunkIndex, chunkData, totalChunks) => {
|
|
71
|
+
return new Promise((resolve, reject) => {
|
|
72
|
+
this.clientTide.emitEvent(clientID, "client-to-client-transfer", {
|
|
73
|
+
senderID: clientID,
|
|
74
|
+
recipientId,
|
|
75
|
+
fileName,
|
|
76
|
+
fileChunk: chunkData,
|
|
77
|
+
totalChunks,
|
|
78
|
+
chunkIndex,
|
|
79
|
+
filePath
|
|
80
|
+
});
|
|
81
|
+
console.log(`Chunk ${chunkIndex} sent successfully!`);
|
|
82
|
+
resolve();
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
onComplete: () => {
|
|
86
|
+
return new Promise((resolve, reject) => {
|
|
87
|
+
console.log({
|
|
88
|
+
senderID: clientID,
|
|
89
|
+
recipientId,
|
|
90
|
+
fileName,
|
|
91
|
+
filePath
|
|
92
|
+
});
|
|
93
|
+
this.clientTide.emitEvent(clientID, "transfer-complete", {
|
|
94
|
+
senderID: clientID,
|
|
95
|
+
recipientId,
|
|
96
|
+
fileName,
|
|
97
|
+
filePath
|
|
98
|
+
});
|
|
99
|
+
resolve();
|
|
100
|
+
});
|
|
101
|
+
},
|
|
102
|
+
onProgress: progress => {
|
|
103
|
+
console.log(`[FileTide ~ File Messager] ~ Progress: ${progress.toFixed(2)}%`);
|
|
104
|
+
},
|
|
105
|
+
fileDetails: {
|
|
106
|
+
name: fileName,
|
|
107
|
+
path: filePath
|
|
108
|
+
}
|
|
109
|
+
}).then(() => console.log('File transfer complete!')).catch(error => console.error('File transfer failed:', error));
|
|
46
110
|
}
|
|
47
111
|
onFileChunkReceived(userID) {
|
|
48
112
|
this.clientTide.onEvent(userID, "transfer-progress", data => {
|
package/dist/cjs/FileTide.js
CHANGED
|
@@ -3,13 +3,18 @@
|
|
|
3
3
|
function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); }
|
|
4
4
|
function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); }
|
|
5
5
|
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"); }
|
|
6
|
+
const chalk = require("chalk");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const fs = require("fs");
|
|
6
9
|
const {
|
|
7
10
|
FileNetConfigManager
|
|
8
11
|
} = require("./HUDManagers/FileNetConfigManager.js");
|
|
12
|
+
const {
|
|
13
|
+
FileUtilityManager
|
|
14
|
+
} = require("./HUDManagers/FileUtilityManager.js");
|
|
15
|
+
const ConsoleTable = require("./HUDComponents/ConsoleTable.js");
|
|
9
16
|
const FileMessagerClient = require("./FileMessagerClient");
|
|
10
17
|
const FileMessager = require("./FileMessager");
|
|
11
|
-
const path = require("path");
|
|
12
|
-
const fs = require("fs");
|
|
13
18
|
var _FileTide_brand = /*#__PURE__*/new WeakSet();
|
|
14
19
|
class FileTide {
|
|
15
20
|
constructor() {
|
|
@@ -60,6 +65,16 @@ class FileTide {
|
|
|
60
65
|
}
|
|
61
66
|
return;
|
|
62
67
|
}
|
|
68
|
+
getDirectoryData(directoryPath) {
|
|
69
|
+
return FileUtilityManager.getDirectoryData(directoryPath);
|
|
70
|
+
}
|
|
71
|
+
getFileData(filePath) {
|
|
72
|
+
return FileUtilityManager.getFileData(filePath);
|
|
73
|
+
}
|
|
74
|
+
getPathData(inputPath) {
|
|
75
|
+
return FileUtilityManager.getPathData(inputPath);
|
|
76
|
+
}
|
|
77
|
+
|
|
63
78
|
/**
|
|
64
79
|
* Send a file directly to a device without it needing to create a client.
|
|
65
80
|
* @param {String} senderID - The ID of the client device that sent the file
|
|
@@ -80,8 +95,54 @@ class FileTide {
|
|
|
80
95
|
return;
|
|
81
96
|
}
|
|
82
97
|
console.log(`[FileTide] ~ Sending file to client ~ ${userID}...`);
|
|
83
|
-
|
|
84
|
-
|
|
98
|
+
console.log({
|
|
99
|
+
recipientId: userID,
|
|
100
|
+
senderID,
|
|
101
|
+
fileName,
|
|
102
|
+
filePath,
|
|
103
|
+
fileData
|
|
104
|
+
});
|
|
105
|
+
this.fileNet.transporter.sendFile(fileData, {
|
|
106
|
+
onSendChunk: (transferId, chunkIndex, chunkData, totalChunks) => {
|
|
107
|
+
return new Promise((resolve, reject) => {
|
|
108
|
+
this.clientTide.emitEvent(senderID, "client-to-client-transfer", {
|
|
109
|
+
recipientId: userID,
|
|
110
|
+
senderID,
|
|
111
|
+
fileName,
|
|
112
|
+
fileChunk: chunkData,
|
|
113
|
+
totalChunks,
|
|
114
|
+
chunkIndex,
|
|
115
|
+
filePath
|
|
116
|
+
}, ack => {
|
|
117
|
+
if (ack.success) {
|
|
118
|
+
console.log(`Chunk ${chunkIndex} sent successfully!`);
|
|
119
|
+
resolve();
|
|
120
|
+
} else {
|
|
121
|
+
console.error(`Did not send chunk ${chunkIndex}`);
|
|
122
|
+
reject(new Error('Chunk failed.'));
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
onComplete: transferId => {
|
|
128
|
+
return new Promise((resolve, reject) => {
|
|
129
|
+
this.clientTide.emitEvent(senderID, "transfer-complete", {
|
|
130
|
+
recipientId: userID,
|
|
131
|
+
senderID,
|
|
132
|
+
fileName,
|
|
133
|
+
filePath
|
|
134
|
+
});
|
|
135
|
+
resolve();
|
|
136
|
+
});
|
|
137
|
+
},
|
|
138
|
+
onProgress: progress => {
|
|
139
|
+
console.log(`[FileTide ~ File Messager] ~ Progress: ${progress.toFixed(2)}%`);
|
|
140
|
+
},
|
|
141
|
+
fileDetails: {
|
|
142
|
+
name: fileName,
|
|
143
|
+
path: filePath
|
|
144
|
+
}
|
|
145
|
+
}).then(() => console.log("File transfer complete!")).catch(error => console.error("File transfer incomplete: ", error));
|
|
85
146
|
return;
|
|
86
147
|
}
|
|
87
148
|
console.log(`[FileTide] ~ No active client ~ ${userID} found.`);
|
|
@@ -142,22 +203,90 @@ class FileTide {
|
|
|
142
203
|
}
|
|
143
204
|
}
|
|
144
205
|
function _setupFileEventListeners(userID, client, onIncomingFile) {
|
|
206
|
+
const cliTable = new ConsoleTable({
|
|
207
|
+
padding: 2,
|
|
208
|
+
headerAlign: "center",
|
|
209
|
+
cellAlign: "left",
|
|
210
|
+
borderStyle: "bold",
|
|
211
|
+
columnOrder: ["clientID", "id", "tideID"],
|
|
212
|
+
columnNames: {
|
|
213
|
+
clientID: "Client Name",
|
|
214
|
+
id: "Identifier",
|
|
215
|
+
tideID: "Tide Identifier"
|
|
216
|
+
},
|
|
217
|
+
title: "✨ Online ✨",
|
|
218
|
+
tableNumber: 1,
|
|
219
|
+
gradient: {
|
|
220
|
+
title: ["#00FFFF", "#1E90FF"],
|
|
221
|
+
header: ["#FFD700", "#FF8C00"],
|
|
222
|
+
border: ["#2e2f30", "#788e9e"],
|
|
223
|
+
cell: ["#00BFFF", "#1E90FF"]
|
|
224
|
+
},
|
|
225
|
+
cellColor: (content, column) => {
|
|
226
|
+
if (column === "clientID") {
|
|
227
|
+
return chalk.blue(content);
|
|
228
|
+
}
|
|
229
|
+
return content;
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
let activeTransfers = new Map();
|
|
233
|
+
client.clientTide.onEvent(userID, "current-online-clients", data => {
|
|
234
|
+
const currentClients = {};
|
|
235
|
+
Object.keys(data).forEach(key => {
|
|
236
|
+
if (key !== userID) {
|
|
237
|
+
currentClients[key] = data[key];
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
if (Object.keys(currentClients).length <= 0) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
cliTable.setTitle(`✨ Online >${Object.keys(currentClients).length}< ✨`, 1);
|
|
244
|
+
cliTable.setData(currentClients);
|
|
245
|
+
console.log(`\n[FileTide ~ FileNet]\n\n`, cliTable.render(), "\n");
|
|
246
|
+
});
|
|
145
247
|
client.clientTide.onEvent(userID, "incoming-file", data => {
|
|
146
|
-
console.log(`[${userID}]
|
|
248
|
+
console.log(`[${userID}] Preparing to receive file : ${data.fileName}`);
|
|
249
|
+
activeTransfers.set(data.fileName, {
|
|
250
|
+
receivedChunks: [],
|
|
251
|
+
totalChunks: data.totalChunks,
|
|
252
|
+
fileInfo: data
|
|
253
|
+
});
|
|
147
254
|
const saveDir = data.path;
|
|
148
255
|
if (!fs.existsSync(saveDir)) {
|
|
149
256
|
fs.mkdirSync(saveDir, {
|
|
150
257
|
recursive: true
|
|
151
258
|
});
|
|
152
|
-
console.log(`[${userID}] Created directory: ${saveDir}`);
|
|
259
|
+
console.log(`[${userID}] Created directory : ${saveDir}`);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
client.clientTide.onEvent(userID, "transfer-progress", data => {
|
|
263
|
+
if (!data || data.fileChunk === undefined || data.fileChunk === null) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
const transferState = activeTransfers.get(data.fileName);
|
|
267
|
+
if (!transferState) {
|
|
268
|
+
return;
|
|
153
269
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
270
|
+
console.log(`[${userID}] Receiving chunk ${data.chunkIndex + 1}/${transferState.totalChunks} for file : ${data.fileName}`);
|
|
271
|
+
transferState.receivedChunks[data.chunkIndex] = Buffer.from(data.fileChunk);
|
|
272
|
+
const progress = (data.chunkIndex + 1) / transferState.totalChunks * 100;
|
|
273
|
+
console.log(`[${userID}] Progress : ${progress.toFixed(2)}%`);
|
|
157
274
|
if (onIncomingFile) {
|
|
158
275
|
onIncomingFile(data);
|
|
159
276
|
}
|
|
160
277
|
});
|
|
278
|
+
client.clientTide.onEvent(userID, "transfer-complete", data => {
|
|
279
|
+
console.log(`[${userID}] File transfer complete : ${data.fileName}`);
|
|
280
|
+
const transferState = activeTransfers.get(data.fileName);
|
|
281
|
+
if (!transferState) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
const savePath = path.join(data.filePath, data.fileName);
|
|
285
|
+
const fullFile = Buffer.concat(transferState.receivedChunks);
|
|
286
|
+
fs.writeFileSync(savePath, fullFile);
|
|
287
|
+
console.log(`[${userID}] File saved at : ${savePath}`);
|
|
288
|
+
activeTransfers.delete(data.fileName);
|
|
289
|
+
});
|
|
161
290
|
return;
|
|
162
291
|
}
|
|
163
292
|
;
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const chalk = require("chalk");
|
|
4
|
+
class ConsoleTable {
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
this.options = {
|
|
7
|
+
padding: 1,
|
|
8
|
+
headerAlign: "center",
|
|
9
|
+
cellAlign: "left",
|
|
10
|
+
borderStyle: "solid",
|
|
11
|
+
headerColor: null,
|
|
12
|
+
borderColor: null,
|
|
13
|
+
cellColor: (content, columnName, rowData) => content,
|
|
14
|
+
columnOrder: null,
|
|
15
|
+
columnNames: {},
|
|
16
|
+
title: "",
|
|
17
|
+
tableNumber: null,
|
|
18
|
+
gradient: {
|
|
19
|
+
title: null,
|
|
20
|
+
header: null,
|
|
21
|
+
border: null,
|
|
22
|
+
cell: null
|
|
23
|
+
},
|
|
24
|
+
...options
|
|
25
|
+
};
|
|
26
|
+
this.borderChars = this.getBorderChars(this.options.borderStyle);
|
|
27
|
+
}
|
|
28
|
+
getBorderChars(style) {
|
|
29
|
+
const styles = {
|
|
30
|
+
solid: {
|
|
31
|
+
topLeft: "┌",
|
|
32
|
+
topRight: "┐",
|
|
33
|
+
bottomLeft: "└",
|
|
34
|
+
bottomRight: "┘",
|
|
35
|
+
horizontal: "─",
|
|
36
|
+
vertical: "│",
|
|
37
|
+
topJoin: "┬",
|
|
38
|
+
bottomJoin: "┴",
|
|
39
|
+
leftJoin: "├",
|
|
40
|
+
rightJoin: "┤",
|
|
41
|
+
center: "┼"
|
|
42
|
+
},
|
|
43
|
+
double: {
|
|
44
|
+
topLeft: "╔",
|
|
45
|
+
topRight: "╗",
|
|
46
|
+
bottomLeft: "╚",
|
|
47
|
+
bottomRight: "╝",
|
|
48
|
+
horizontal: "═",
|
|
49
|
+
vertical: "║",
|
|
50
|
+
topJoin: "╦",
|
|
51
|
+
bottomJoin: "╩",
|
|
52
|
+
leftJoin: "╠",
|
|
53
|
+
rightJoin: "╣",
|
|
54
|
+
center: "╬"
|
|
55
|
+
},
|
|
56
|
+
round: {
|
|
57
|
+
topLeft: "╭",
|
|
58
|
+
topRight: "╮",
|
|
59
|
+
bottomLeft: "╰",
|
|
60
|
+
bottomRight: "╯",
|
|
61
|
+
horizontal: "─",
|
|
62
|
+
vertical: "│",
|
|
63
|
+
topJoin: "┬",
|
|
64
|
+
bottomJoin: "┴",
|
|
65
|
+
leftJoin: "├",
|
|
66
|
+
rightJoin: "┤",
|
|
67
|
+
center: "┼"
|
|
68
|
+
},
|
|
69
|
+
bold: {
|
|
70
|
+
topLeft: "┏",
|
|
71
|
+
topRight: "┓",
|
|
72
|
+
bottomLeft: "┗",
|
|
73
|
+
bottomRight: "┛",
|
|
74
|
+
horizontal: "━",
|
|
75
|
+
vertical: "┃",
|
|
76
|
+
topJoin: "┳",
|
|
77
|
+
bottomJoin: "┻",
|
|
78
|
+
leftJoin: "┣",
|
|
79
|
+
rightJoin: "┫",
|
|
80
|
+
center: "╋"
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
return styles[style] || styles["solid"];
|
|
84
|
+
}
|
|
85
|
+
setData(data) {
|
|
86
|
+
if (Array.isArray(data)) {
|
|
87
|
+
this.data = data;
|
|
88
|
+
} else if (typeof data === "object") {
|
|
89
|
+
this.data = Object.values(data);
|
|
90
|
+
} else {
|
|
91
|
+
throw new Error("Array or an object only.");
|
|
92
|
+
}
|
|
93
|
+
this.extractColumns();
|
|
94
|
+
this.calculateColumnWidths();
|
|
95
|
+
}
|
|
96
|
+
extractColumns() {
|
|
97
|
+
if (this.options.columnOrder && Array.isArray(this.options.columnOrder)) {
|
|
98
|
+
this.columns = this.options.columnOrder;
|
|
99
|
+
} else {
|
|
100
|
+
const columnsSet = new Set();
|
|
101
|
+
this.data.forEach(item => {
|
|
102
|
+
Object.keys(item).forEach(key => columnsSet.add(key));
|
|
103
|
+
});
|
|
104
|
+
this.columns = Array.from(columnsSet);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
calculateColumnWidths() {
|
|
108
|
+
const {
|
|
109
|
+
padding
|
|
110
|
+
} = this.options;
|
|
111
|
+
this.columnWidths = {};
|
|
112
|
+
this.totalWidth = 0;
|
|
113
|
+
this.columns.forEach(column => {
|
|
114
|
+
const headerName = this.options.columnNames[column] || column;
|
|
115
|
+
let maxWidth = String(headerName).length;
|
|
116
|
+
this.data.forEach(item => {
|
|
117
|
+
const cellContent = item[column] !== undefined ? String(item[column]) : "";
|
|
118
|
+
maxWidth = Math.max(maxWidth, cellContent.length);
|
|
119
|
+
});
|
|
120
|
+
const columnWidth = maxWidth + padding * 2;
|
|
121
|
+
this.columnWidths[column] = columnWidth;
|
|
122
|
+
this.totalWidth += columnWidth;
|
|
123
|
+
});
|
|
124
|
+
this.totalWidth += this.columns.length + 1;
|
|
125
|
+
}
|
|
126
|
+
alignText(text, width, align) {
|
|
127
|
+
const {
|
|
128
|
+
padding
|
|
129
|
+
} = this.options;
|
|
130
|
+
const textLength = text.length;
|
|
131
|
+
let totalPadding = width - textLength;
|
|
132
|
+
let leftPadding = " ".repeat(padding);
|
|
133
|
+
let rightPadding = " ".repeat(padding);
|
|
134
|
+
if (totalPadding < padding * 2) {
|
|
135
|
+
leftPadding = "";
|
|
136
|
+
rightPadding = "";
|
|
137
|
+
} else {
|
|
138
|
+
totalPadding -= padding * 2;
|
|
139
|
+
if (align === "left") {
|
|
140
|
+
rightPadding += " ".repeat(totalPadding);
|
|
141
|
+
} else if (align === "right") {
|
|
142
|
+
leftPadding += " ".repeat(totalPadding);
|
|
143
|
+
} else {
|
|
144
|
+
const half = Math.floor(totalPadding / 2);
|
|
145
|
+
leftPadding += " ".repeat(half);
|
|
146
|
+
rightPadding += " ".repeat(totalPadding - half);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return leftPadding + text + rightPadding;
|
|
150
|
+
}
|
|
151
|
+
generateGradientText(text, startColor, endColor) {
|
|
152
|
+
const length = text.length;
|
|
153
|
+
const startRGB = this.hexToRgb(startColor);
|
|
154
|
+
const endRGB = this.hexToRgb(endColor);
|
|
155
|
+
let gradientText = "";
|
|
156
|
+
for (let i = 0; i < length; i++) {
|
|
157
|
+
const ratio = i / (length - 1 || 1);
|
|
158
|
+
const color = {
|
|
159
|
+
r: Math.round(startRGB.r + ratio * (endRGB.r - startRGB.r)),
|
|
160
|
+
g: Math.round(startRGB.g + ratio * (endRGB.g - startRGB.g)),
|
|
161
|
+
b: Math.round(startRGB.b + ratio * (endRGB.b - startRGB.b))
|
|
162
|
+
};
|
|
163
|
+
gradientText += chalk.rgb(color.r, color.g, color.b)(text[i]);
|
|
164
|
+
}
|
|
165
|
+
return gradientText;
|
|
166
|
+
}
|
|
167
|
+
hexToRgb(hex) {
|
|
168
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
169
|
+
return result ? {
|
|
170
|
+
r: parseInt(result[1], 16),
|
|
171
|
+
g: parseInt(result[2], 16),
|
|
172
|
+
b: parseInt(result[3], 16)
|
|
173
|
+
} : null;
|
|
174
|
+
}
|
|
175
|
+
generateBorder(type) {
|
|
176
|
+
const {
|
|
177
|
+
columns,
|
|
178
|
+
columnWidths
|
|
179
|
+
} = this;
|
|
180
|
+
let {
|
|
181
|
+
horizontal,
|
|
182
|
+
vertical,
|
|
183
|
+
topLeft,
|
|
184
|
+
topRight,
|
|
185
|
+
bottomLeft,
|
|
186
|
+
bottomRight,
|
|
187
|
+
topJoin,
|
|
188
|
+
bottomJoin,
|
|
189
|
+
leftJoin,
|
|
190
|
+
rightJoin,
|
|
191
|
+
center
|
|
192
|
+
} = this.borderChars;
|
|
193
|
+
const {
|
|
194
|
+
borderColor
|
|
195
|
+
} = this.options;
|
|
196
|
+
const borderGradient = this.options.gradient.border;
|
|
197
|
+
if (borderGradient && borderGradient.length === 2) {
|
|
198
|
+
const chars = [topLeft, topRight, bottomLeft, bottomRight, horizontal, vertical, topJoin, bottomJoin, leftJoin, rightJoin, center];
|
|
199
|
+
const coloredChars = {};
|
|
200
|
+
chars.forEach((char, index) => {
|
|
201
|
+
coloredChars[char] = this.generateGradientText(char, borderGradient[0], borderGradient[1]);
|
|
202
|
+
});
|
|
203
|
+
bottomRight = coloredChars[bottomRight];
|
|
204
|
+
bottomLeft = coloredChars[bottomLeft];
|
|
205
|
+
topRight = coloredChars[topRight];
|
|
206
|
+
topLeft = coloredChars[topLeft];
|
|
207
|
+
horizontal = coloredChars[horizontal];
|
|
208
|
+
vertical = coloredChars[vertical];
|
|
209
|
+
bottomJoin = coloredChars[bottomJoin];
|
|
210
|
+
rightJoin = coloredChars[rightJoin];
|
|
211
|
+
leftJoin = coloredChars[leftJoin];
|
|
212
|
+
topJoin = coloredChars[topJoin];
|
|
213
|
+
center = coloredChars[center];
|
|
214
|
+
} else if (borderColor) {
|
|
215
|
+
bottomRight = borderColor(bottomRight);
|
|
216
|
+
bottomLeft = borderColor(bottomLeft);
|
|
217
|
+
topRight = borderColor(topRight);
|
|
218
|
+
topLeft = borderColor(topLeft);
|
|
219
|
+
horizontal = borderColor(horizontal);
|
|
220
|
+
vertical = borderColor(vertical);
|
|
221
|
+
bottomJoin = borderColor(bottomJoin);
|
|
222
|
+
rightJoin = borderColor(rightJoin);
|
|
223
|
+
leftJoin = borderColor(leftJoin);
|
|
224
|
+
topJoin = borderColor(topJoin);
|
|
225
|
+
center = borderColor(center);
|
|
226
|
+
}
|
|
227
|
+
let border = "";
|
|
228
|
+
columns.forEach((column, index) => {
|
|
229
|
+
const first = index === 0;
|
|
230
|
+
const last = index === columns.length - 1;
|
|
231
|
+
const width = columnWidths[column];
|
|
232
|
+
const line = horizontal.repeat(width);
|
|
233
|
+
if (type === "top") {
|
|
234
|
+
border += first ? topLeft : topJoin;
|
|
235
|
+
} else if (type === "bottom") {
|
|
236
|
+
border += first ? bottomLeft : bottomJoin;
|
|
237
|
+
} else {
|
|
238
|
+
border += first ? leftJoin : center;
|
|
239
|
+
}
|
|
240
|
+
border += line;
|
|
241
|
+
if (last) {
|
|
242
|
+
if (type === "top") {
|
|
243
|
+
border += topRight;
|
|
244
|
+
} else if (type === "bottom") {
|
|
245
|
+
border += bottomRight;
|
|
246
|
+
} else {
|
|
247
|
+
border += rightJoin;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
return border;
|
|
252
|
+
}
|
|
253
|
+
generateRow(rowData, headerRow = false) {
|
|
254
|
+
const {
|
|
255
|
+
columns,
|
|
256
|
+
columnWidths
|
|
257
|
+
} = this;
|
|
258
|
+
let {
|
|
259
|
+
vertical
|
|
260
|
+
} = this.borderChars;
|
|
261
|
+
const {
|
|
262
|
+
headerAlign,
|
|
263
|
+
cellAlign,
|
|
264
|
+
headerColor,
|
|
265
|
+
cellColor
|
|
266
|
+
} = this.options;
|
|
267
|
+
const align = headerRow ? headerAlign : cellAlign;
|
|
268
|
+
const headerGradient = this.options.gradient.header;
|
|
269
|
+
const cellGradient = this.options.gradient.cell;
|
|
270
|
+
const borderGradient = this.options.gradient.border;
|
|
271
|
+
if (borderGradient && borderGradient.length === 2) {
|
|
272
|
+
vertical = this.generateGradientText(vertical, borderGradient[0], borderGradient[1]);
|
|
273
|
+
} else if (this.options.borderColor) {
|
|
274
|
+
vertical = this.options.borderColor(vertical);
|
|
275
|
+
}
|
|
276
|
+
let row = "";
|
|
277
|
+
columns.forEach((column, index) => {
|
|
278
|
+
const content = rowData[column] !== undefined ? String(rowData[column]) : "";
|
|
279
|
+
const width = columnWidths[column];
|
|
280
|
+
const alignedText = this.alignText(content, width, align);
|
|
281
|
+
let cellContent = alignedText;
|
|
282
|
+
const first = index === 0;
|
|
283
|
+
if (headerRow) {
|
|
284
|
+
if (headerGradient && headerGradient.length === 2) {
|
|
285
|
+
cellContent = this.generateGradientText(cellContent, headerGradient[0], headerGradient[1]);
|
|
286
|
+
} else if (headerColor) {
|
|
287
|
+
cellContent = headerColor(cellContent);
|
|
288
|
+
}
|
|
289
|
+
} else {
|
|
290
|
+
if (cellGradient && cellGradient.length === 2) {
|
|
291
|
+
cellContent = this.generateGradientText(cellContent, cellGradient[0], cellGradient[1]);
|
|
292
|
+
} else if (cellColor) {
|
|
293
|
+
cellContent = cellColor(cellContent, column, rowData);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (first) {
|
|
297
|
+
row += vertical;
|
|
298
|
+
}
|
|
299
|
+
row += cellContent + vertical;
|
|
300
|
+
});
|
|
301
|
+
return row;
|
|
302
|
+
}
|
|
303
|
+
setTitle(title, tableNumber = null) {
|
|
304
|
+
this.options.title = title;
|
|
305
|
+
if (tableNumber !== null) {
|
|
306
|
+
this.options.tableNumber = tableNumber;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
renderTitle() {
|
|
310
|
+
let titleText = this.options.title;
|
|
311
|
+
if (this.options.tableNumber !== null) {
|
|
312
|
+
titleText = titleText;
|
|
313
|
+
}
|
|
314
|
+
const totalWidth = this.totalWidth - 2;
|
|
315
|
+
const titleLength = titleText.length;
|
|
316
|
+
let leftPadding = Math.floor((totalWidth - titleLength) / 2);
|
|
317
|
+
if (leftPadding < 0) leftPadding = 0;
|
|
318
|
+
let titleLine = " ".repeat(leftPadding) + titleText;
|
|
319
|
+
const titleGradient = this.options.gradient.title;
|
|
320
|
+
if (titleGradient && titleGradient.length === 2) {
|
|
321
|
+
titleLine = this.generateGradientText(titleLine.trim(), titleGradient[0], titleGradient[1]);
|
|
322
|
+
}
|
|
323
|
+
return titleLine;
|
|
324
|
+
}
|
|
325
|
+
render() {
|
|
326
|
+
const {
|
|
327
|
+
data
|
|
328
|
+
} = this;
|
|
329
|
+
if (!data || data.length === 0) return "";
|
|
330
|
+
const lines = [];
|
|
331
|
+
if (this.options.title) {
|
|
332
|
+
const title = this.renderTitle();
|
|
333
|
+
lines.push(title);
|
|
334
|
+
}
|
|
335
|
+
lines.push(this.generateBorder("top"));
|
|
336
|
+
const headerData = {};
|
|
337
|
+
this.columns.forEach(column => {
|
|
338
|
+
headerData[column] = this.options.columnNames[column] || column;
|
|
339
|
+
});
|
|
340
|
+
lines.push(this.generateRow(headerData, true));
|
|
341
|
+
lines.push(this.generateBorder("middle"));
|
|
342
|
+
data.forEach(rowData => {
|
|
343
|
+
lines.push(this.generateRow(rowData));
|
|
344
|
+
});
|
|
345
|
+
lines.push(this.generateBorder("bottom"));
|
|
346
|
+
return lines.join("\n");
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
module.exports = ConsoleTable;
|
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
const currentOnlineClients = new Map();
|
|
4
4
|
class FileNetClientManager {
|
|
5
|
-
static addOnlineClient(clientID, pClientID, tideID) {
|
|
5
|
+
static addOnlineClient(clientID, pClientID, tideID, id) {
|
|
6
6
|
if (currentOnlineClients.get(pClientID)) {
|
|
7
7
|
currentOnlineClients.delete(pClientID);
|
|
8
8
|
}
|
|
9
9
|
currentOnlineClients.set(clientID, {
|
|
10
10
|
pClientID: pClientID || "",
|
|
11
11
|
clientID,
|
|
12
|
-
tideID
|
|
12
|
+
tideID,
|
|
13
|
+
id
|
|
13
14
|
});
|
|
14
15
|
return currentOnlineClients;
|
|
15
16
|
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); }
|
|
4
|
+
function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); }
|
|
5
|
+
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"); }
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
var _FileTransferManager_brand = /*#__PURE__*/new WeakSet();
|
|
8
|
+
class FileTransferManager {
|
|
9
|
+
constructor({
|
|
10
|
+
chunkSize = 512 * 1024,
|
|
11
|
+
parallelChunks = 5,
|
|
12
|
+
maxRetries = 3
|
|
13
|
+
} = {}) {
|
|
14
|
+
_classPrivateMethodInitSpec(this, _FileTransferManager_brand);
|
|
15
|
+
this.parallelChunks = parallelChunks;
|
|
16
|
+
this.activeTransfers = new Map();
|
|
17
|
+
this.maxRetries = maxRetries;
|
|
18
|
+
this.chunkSize = chunkSize;
|
|
19
|
+
}
|
|
20
|
+
sendFile(file, {
|
|
21
|
+
fileDetails,
|
|
22
|
+
onSendChunk,
|
|
23
|
+
onComplete,
|
|
24
|
+
onProgress,
|
|
25
|
+
onStart
|
|
26
|
+
}) {
|
|
27
|
+
const transferId = _assertClassBrand(_FileTransferManager_brand, this, _generateTransferId).call(this, fileDetails);
|
|
28
|
+
return _assertClassBrand(_FileTransferManager_brand, this, _startTransfer).call(this, file, transferId, fileDetails, onSendChunk, onComplete, onProgress, onStart);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function _startTransfer(file, transferId, fileDetails, onSendChunk, onComplete, onProgress, onStart) {
|
|
32
|
+
const totalChunks = Math.ceil(file.length / this.chunkSize);
|
|
33
|
+
this.activeTransfers.set(transferId, {
|
|
34
|
+
totalChunks,
|
|
35
|
+
completedChunks: 0,
|
|
36
|
+
activeChunks: 0,
|
|
37
|
+
failedChunks: new Map(),
|
|
38
|
+
currentChunk: 0
|
|
39
|
+
});
|
|
40
|
+
const updateProgress = () => {
|
|
41
|
+
const transferState = this.activeTransfers.get(transferId);
|
|
42
|
+
if (onProgress) {
|
|
43
|
+
onProgress(transferState.completedChunks / transferState.totalChunks * 100);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
const sendChunk = chunkIndex => {
|
|
48
|
+
const transferState = this.activeTransfers.get(transferId);
|
|
49
|
+
if (chunkIndex >= transferState.totalChunks) return;
|
|
50
|
+
const start = chunkIndex * this.chunkSize;
|
|
51
|
+
const end = Math.min(start + this.chunkSize, file.length);
|
|
52
|
+
const chunk = file.slice(start, end);
|
|
53
|
+
if (!fs.existsSync(fileDetails.path)) {
|
|
54
|
+
fs.mkdirSync(fileDetails.path, {
|
|
55
|
+
recursive: true
|
|
56
|
+
});
|
|
57
|
+
console.log(`[FileTide ~ Transporter] Created directory: ${fileDetails.path}`);
|
|
58
|
+
}
|
|
59
|
+
if (onStart && chunkIndex === 0) {
|
|
60
|
+
onStart({
|
|
61
|
+
name: fileDetails.name,
|
|
62
|
+
path: fileDetails.path,
|
|
63
|
+
totalChunks: transferState.totalChunks
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
transferState.activeChunks++;
|
|
67
|
+
onSendChunk(transferId, chunkIndex, chunk, transferState.totalChunks).then(() => {
|
|
68
|
+
transferState.completedChunks++;
|
|
69
|
+
transferState.activeChunks--;
|
|
70
|
+
updateProgress();
|
|
71
|
+
if (transferState.activeChunks < this.parallelChunks && transferState.currentChunk < transferState.totalChunks) {
|
|
72
|
+
sendChunk(transferState.currentChunk++);
|
|
73
|
+
}
|
|
74
|
+
if (transferState.completedChunks === transferState.totalChunks) {
|
|
75
|
+
onComplete(transferId).then(() => {
|
|
76
|
+
this.activeTransfers.delete(transferId);
|
|
77
|
+
resolve();
|
|
78
|
+
}).catch(reject);
|
|
79
|
+
}
|
|
80
|
+
}).catch(error => {
|
|
81
|
+
console.error(`Did not send chunk ${chunkIndex}: ${error.message}`);
|
|
82
|
+
transferState.activeChunks--;
|
|
83
|
+
if (transferState.failedChunks.get(chunkIndex) >= this.maxRetries) {
|
|
84
|
+
this.activeTransfers.delete(transferId);
|
|
85
|
+
reject(new Error(`Did not send chunk ${chunkIndex} after ${this.maxRetries} retries.`));
|
|
86
|
+
} else {
|
|
87
|
+
const retryCount = (transferState.failedChunks.get(chunkIndex) || 0) + 1;
|
|
88
|
+
transferState.failedChunks.set(chunkIndex, retryCount);
|
|
89
|
+
sendChunk(chunkIndex);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
const transferState = this.activeTransfers.get(transferId);
|
|
94
|
+
for (let i = 0; i < Math.min(this.parallelChunks, transferState.totalChunks); i++) {
|
|
95
|
+
sendChunk(transferState.currentChunk++);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function _generateTransferId(file) {
|
|
100
|
+
return `${file.name}-${Date.now()}`;
|
|
101
|
+
}
|
|
102
|
+
module.exports = {
|
|
103
|
+
FileTransferManager
|
|
104
|
+
};
|
|
@@ -4,6 +4,115 @@ const path = require('path');
|
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const filesPath = path.join(__dirname, '../files');
|
|
6
6
|
class FileUtilityManager {
|
|
7
|
+
/**
|
|
8
|
+
* Recursively calculates the total size of files in a directory.
|
|
9
|
+
* @param {string} dirPath - The directory path.
|
|
10
|
+
* @returns {number} - The total size in bytes of the directory's contents.
|
|
11
|
+
*/
|
|
12
|
+
calculateDirectorySize(dirPath) {
|
|
13
|
+
let totalSize = 0;
|
|
14
|
+
const files = fs.readdirSync(dirPath);
|
|
15
|
+
files.forEach(file => {
|
|
16
|
+
const filePath = path.join(dirPath, file);
|
|
17
|
+
const stats = fs.statSync(filePath);
|
|
18
|
+
if (stats.isDirectory()) {
|
|
19
|
+
totalSize += this.calculateDirectorySize(filePath);
|
|
20
|
+
} else {
|
|
21
|
+
totalSize += stats.size;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
return totalSize;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Normalizes the input path to handle both single and double slashes.
|
|
29
|
+
* @param {string} inputPath - The path to normalize.
|
|
30
|
+
* @returns {string} - A normalized path with proper slashes.
|
|
31
|
+
*/
|
|
32
|
+
static normalizePath(inputPath) {
|
|
33
|
+
return path.normalize(inputPath);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Gets all files' data from a given directory.
|
|
38
|
+
* @param {string} dirPath - The path to the directory.
|
|
39
|
+
* @returns {Array} - An array of objects containing file data, name, and path.
|
|
40
|
+
*/
|
|
41
|
+
static getDirectoryData(inputDirPath) {
|
|
42
|
+
const dirPath = this.normalizePath(inputDirPath);
|
|
43
|
+
if (!fs.existsSync(dirPath)) {
|
|
44
|
+
console.log(`Directory at path ${dirPath} not found.`);
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
const filesData = [];
|
|
48
|
+
function readDirectory(currentPath) {
|
|
49
|
+
const fileList = fs.readdirSync(currentPath);
|
|
50
|
+
fileList.forEach(file => {
|
|
51
|
+
const filePath = path.join(currentPath, file);
|
|
52
|
+
const stats = fs.statSync(filePath);
|
|
53
|
+
if (stats.isFile()) {
|
|
54
|
+
const fileData = FileUtilityManager.getFileData(filePath);
|
|
55
|
+
if (fileData) {
|
|
56
|
+
filesData.push(fileData);
|
|
57
|
+
}
|
|
58
|
+
} else if (stats.isDirectory()) {
|
|
59
|
+
readDirectory(filePath);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
readDirectory(dirPath);
|
|
64
|
+
return filesData;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Gets file data from a given file path.
|
|
69
|
+
* @param {string} inputFilePath - The full path to the file.
|
|
70
|
+
* @returns {Object|null} - An object containing file data, name, and path, or null if the file doesn't exist.
|
|
71
|
+
*/
|
|
72
|
+
static getFileData(inputFilePath) {
|
|
73
|
+
const filePath = this.normalizePath(inputFilePath);
|
|
74
|
+
if (!fs.existsSync(filePath)) {
|
|
75
|
+
console.log(`File at path ${filePath} not found.`);
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const fileData = fs.readFileSync(filePath, {
|
|
79
|
+
encoding: null
|
|
80
|
+
});
|
|
81
|
+
const fileName = path.basename(filePath);
|
|
82
|
+
return {
|
|
83
|
+
fileName,
|
|
84
|
+
fileData,
|
|
85
|
+
filePath
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Gets data from a file or directory based on the provided path.
|
|
91
|
+
* @param {string} inputPath - The path to a file or directory.
|
|
92
|
+
* @returns {Object|Array|null} - File data if it's a file, an array of file data if it's a directory, or null if the path doesn't exist.
|
|
93
|
+
*/
|
|
94
|
+
static getPathData(inputPath) {
|
|
95
|
+
const currentInputPath = this.normalizePath(inputPath);
|
|
96
|
+
if (!fs.existsSync(currentInputPath)) {
|
|
97
|
+
console.log(`Path at ${currentInputPath} not found.`);
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
const stats = fs.statSync(currentInputPath);
|
|
101
|
+
if (stats.isFile()) {
|
|
102
|
+
return {
|
|
103
|
+
content: this.getFileData(currentInputPath),
|
|
104
|
+
type: "file"
|
|
105
|
+
};
|
|
106
|
+
} else if (stats.isDirectory()) {
|
|
107
|
+
return {
|
|
108
|
+
content: this.getDirectoryData(currentInputPath),
|
|
109
|
+
type: "directory"
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
console.log(`Path at ${currentInputPath} neither a file nor a directory.`);
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
7
116
|
/**
|
|
8
117
|
* Save a chunk of a file for a specific user to the specified path with a custom file name.
|
|
9
118
|
* @param {String} userID - The ID of the user for whom the file is being saved.
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trap_stevo/filetide",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"description": "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 with a tool designed for precision, efficiency, and effortless connectivity.",
|
|
5
5
|
"main": "dist/cjs/FileTide.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "babel src -d dist/cjs --env-name cjs",
|
|
8
|
+
"start-messager": "node demos/FileTideClientDemo.js",
|
|
9
|
+
"start-net": "node demos/FileTideNetDemo.js",
|
|
8
10
|
"start": "node dist/cjs/FileTide.js"
|
|
9
11
|
},
|
|
10
12
|
"keywords": [
|
|
@@ -28,7 +30,8 @@
|
|
|
28
30
|
"license": "ISC",
|
|
29
31
|
"dependencies": {
|
|
30
32
|
"@trap_stevo/iotide": "^0.0.36",
|
|
31
|
-
"@trap_stevo/iotide-client": "^0.0.
|
|
33
|
+
"@trap_stevo/iotide-client": "^0.0.15",
|
|
34
|
+
"chalk": "^4.1.2",
|
|
32
35
|
"readline": "^1.3.0"
|
|
33
36
|
},
|
|
34
37
|
"devDependencies": {
|