@trap_stevo/filetide 0.0.13 → 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 +61 -51
- package/dist/cjs/FileMessagerClient.js +73 -9
- package/dist/cjs/FileTide.js +141 -9
- package/dist/cjs/HUDComponents/ConsoleTable.js +349 -0
- package/dist/cjs/HUDManagers/FileNetClientManager.js +33 -0
- package/dist/cjs/HUDManagers/FileNetConfigManager.js +32 -0
- package/dist/cjs/HUDManagers/FileTransferManager.js +104 -0
- package/dist/cjs/HUDManagers/FileUtilityManager.js +109 -0
- package/package.json +5 -2
- package/test.txt +0 -1
package/dist/cjs/FileMessager.js
CHANGED
|
@@ -5,13 +5,19 @@ const {
|
|
|
5
5
|
FileMessagerConfigManager
|
|
6
6
|
} = require("./HUDManagers/FileMessagerConfigManager");
|
|
7
7
|
const {
|
|
8
|
-
|
|
9
|
-
} = require("./HUDManagers/
|
|
8
|
+
FileNetClientManager
|
|
9
|
+
} = require("./HUDManagers/FileNetClientManager");
|
|
10
|
+
const {
|
|
11
|
+
FileTransferManager
|
|
12
|
+
} = require("./HUDManagers/FileTransferManager");
|
|
10
13
|
class FileMessager {
|
|
11
|
-
constructor(options = {}
|
|
14
|
+
constructor(options = {}, transportOptions = {
|
|
15
|
+
parallelChunks: 3,
|
|
16
|
+
maxRetries: 3
|
|
17
|
+
}) {
|
|
12
18
|
const serverOptions = FileMessagerConfigManager.getServerOptions(options);
|
|
19
|
+
this.transporter = new FileTransferManager(transportOptions);
|
|
13
20
|
this.fileNet = new IoTide(serverOptions.port, serverOptions, true, this.onConnect.bind(this), this.onDisconnect.bind(this));
|
|
14
|
-
this.messagerClients = new Map();
|
|
15
21
|
this.onlineClients = new Map();
|
|
16
22
|
return;
|
|
17
23
|
}
|
|
@@ -37,96 +43,100 @@ class FileMessager {
|
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
/**
|
|
40
|
-
* Send file to a specific client
|
|
46
|
+
* Send file chunk to a specific client
|
|
41
47
|
* @param {String} senderID - The ID of the sender (tide ID)
|
|
42
48
|
* @param {String} clientId - The ID of the client (socket ID)
|
|
43
|
-
* @param {Buffer}
|
|
49
|
+
* @param {Buffer} fileChunk - The file chunk to send
|
|
44
50
|
* @param {String} fileName - The name of the file
|
|
51
|
+
* @param {Number} chunkIndex - The index of the current chunk
|
|
45
52
|
*/
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
console.log(`Client ~ ${clientId} not authorized.`);
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
const senderConnectionID = this.messagerClients.get(senderID);
|
|
52
|
-
const connectionID = this.messagerClients.get(clientId);
|
|
53
|
-
const senderClient = this.onlineClients.get(connectionID);
|
|
54
|
-
const client = this.onlineClients.get(connectionID);
|
|
53
|
+
sendFileChunkToClient(senderID, clientId, fileChunk, fileName, chunkIndex, totalChunks, filePath = process.cwd()) {
|
|
54
|
+
const client = this.onlineClients.get(clientId);
|
|
55
55
|
if (!client) {
|
|
56
56
|
console.log(`Client ~ ${clientId} not found.`);
|
|
57
57
|
return;
|
|
58
58
|
}
|
|
59
|
-
|
|
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", {
|
|
60
68
|
fileName,
|
|
61
|
-
|
|
69
|
+
fileChunk,
|
|
70
|
+
chunkIndex,
|
|
62
71
|
path: filePath
|
|
63
72
|
});
|
|
64
|
-
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);
|
|
65
75
|
if (senderClient) {
|
|
66
76
|
senderClient.emit("transfer-status", {
|
|
67
|
-
status: `
|
|
77
|
+
status: `Chunk ${chunkIndex} of ${fileName} sent to client ${clientId}!`,
|
|
68
78
|
recipientID: clientId,
|
|
69
79
|
success: true
|
|
70
80
|
});
|
|
71
81
|
}
|
|
72
|
-
return;
|
|
73
82
|
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Handle file transfer requests between clients
|
|
77
|
-
* @param {Object} transferData - The data containing the sender, recipient, and file details
|
|
78
|
-
*/
|
|
79
83
|
handleClientToClientTransfer(transferData) {
|
|
80
84
|
const {
|
|
81
85
|
senderID,
|
|
82
86
|
recipientId,
|
|
83
87
|
fileName,
|
|
84
|
-
|
|
85
|
-
filePath
|
|
88
|
+
fileChunk,
|
|
89
|
+
filePath,
|
|
90
|
+
chunkIndex,
|
|
91
|
+
totalChunks
|
|
86
92
|
} = transferData;
|
|
87
|
-
|
|
88
|
-
|
|
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}`);
|
|
89
113
|
}
|
|
90
114
|
handleClientsOnline(clientID) {
|
|
91
|
-
if (!
|
|
115
|
+
if (!FileNetClientManager.getOnlineClient(clientID)) {
|
|
92
116
|
console.log(`Client ~ ${clientID} not authorized.`);
|
|
93
117
|
return;
|
|
94
118
|
}
|
|
95
|
-
const connectionID =
|
|
119
|
+
const connectionID = FileNetClientManager.getOnlineClient(clientID);
|
|
96
120
|
const client = this.onlineClients.get(connectionID);
|
|
97
121
|
if (!client) {
|
|
98
122
|
console.log(`Client ~ ${clientID} not found.`);
|
|
99
123
|
return;
|
|
100
124
|
}
|
|
101
|
-
client.
|
|
102
|
-
currentClients:
|
|
125
|
+
this.fileNet.emitToTide(client.tideID, "current-clients-online", {
|
|
126
|
+
currentClients: FileNetClientManager.getOnlineClients()
|
|
103
127
|
});
|
|
104
128
|
return;
|
|
105
129
|
}
|
|
106
|
-
handleClientOnline(clientID, connectionID) {
|
|
130
|
+
handleClientOnline(clientID, tideID, pClientID, connectionID) {
|
|
107
131
|
if (!this.onlineClients.has(connectionID)) {
|
|
108
132
|
return;
|
|
109
133
|
}
|
|
110
|
-
|
|
134
|
+
FileNetClientManager.addOnlineClient(clientID, pClientID, tideID, connectionID);
|
|
135
|
+
this.fileNet.emitToTide(tideID, "current-online-clients", Object.fromEntries(FileNetClientManager.getOnlineClients()));
|
|
111
136
|
return;
|
|
112
137
|
}
|
|
113
138
|
handleClientOffline(clientID) {
|
|
114
|
-
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
this.messagerClients.delete(clientID);
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
handleFileTransferStart(fileData, emitToChannel) {
|
|
121
|
-
console.log(`Starting file transfer: ${fileData.fileName}`);
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
handleFileTransferProgress(data, emitToChannel) {
|
|
125
|
-
console.log(`Progress: ${data.progress}`);
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
handleFileTransferComplete(data) {
|
|
129
|
-
console.log(`File transfer completed: ${data.fileName}`);
|
|
139
|
+
FileNetClientManager.clearOnlineClient(clientID);
|
|
130
140
|
return;
|
|
131
141
|
}
|
|
132
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,10 +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
|
|
7
|
-
const FileMessager = require("./FileMessager");
|
|
6
|
+
const chalk = require("chalk");
|
|
8
7
|
const path = require("path");
|
|
9
8
|
const fs = require("fs");
|
|
9
|
+
const {
|
|
10
|
+
FileNetConfigManager
|
|
11
|
+
} = require("./HUDManagers/FileNetConfigManager.js");
|
|
12
|
+
const {
|
|
13
|
+
FileUtilityManager
|
|
14
|
+
} = require("./HUDManagers/FileUtilityManager.js");
|
|
15
|
+
const ConsoleTable = require("./HUDComponents/ConsoleTable.js");
|
|
16
|
+
const FileMessagerClient = require("./FileMessagerClient");
|
|
17
|
+
const FileMessager = require("./FileMessager");
|
|
10
18
|
var _FileTide_brand = /*#__PURE__*/new WeakSet();
|
|
11
19
|
class FileTide {
|
|
12
20
|
constructor() {
|
|
@@ -57,6 +65,16 @@ class FileTide {
|
|
|
57
65
|
}
|
|
58
66
|
return;
|
|
59
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
|
+
|
|
60
78
|
/**
|
|
61
79
|
* Send a file directly to a device without it needing to create a client.
|
|
62
80
|
* @param {String} senderID - The ID of the client device that sent the file
|
|
@@ -77,8 +95,54 @@ class FileTide {
|
|
|
77
95
|
return;
|
|
78
96
|
}
|
|
79
97
|
console.log(`[FileTide] ~ Sending file to client ~ ${userID}...`);
|
|
80
|
-
|
|
81
|
-
|
|
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));
|
|
82
146
|
return;
|
|
83
147
|
}
|
|
84
148
|
console.log(`[FileTide] ~ No active client ~ ${userID} found.`);
|
|
@@ -139,22 +203,90 @@ class FileTide {
|
|
|
139
203
|
}
|
|
140
204
|
}
|
|
141
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
|
+
});
|
|
142
247
|
client.clientTide.onEvent(userID, "incoming-file", data => {
|
|
143
|
-
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
|
+
});
|
|
144
254
|
const saveDir = data.path;
|
|
145
255
|
if (!fs.existsSync(saveDir)) {
|
|
146
256
|
fs.mkdirSync(saveDir, {
|
|
147
257
|
recursive: true
|
|
148
258
|
});
|
|
149
|
-
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;
|
|
150
269
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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)}%`);
|
|
154
274
|
if (onIncomingFile) {
|
|
155
275
|
onIncomingFile(data);
|
|
156
276
|
}
|
|
157
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
|
+
});
|
|
158
290
|
return;
|
|
159
291
|
}
|
|
160
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;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const currentOnlineClients = new Map();
|
|
4
|
+
class FileNetClientManager {
|
|
5
|
+
static addOnlineClient(clientID, pClientID, tideID, id) {
|
|
6
|
+
if (currentOnlineClients.get(pClientID)) {
|
|
7
|
+
currentOnlineClients.delete(pClientID);
|
|
8
|
+
}
|
|
9
|
+
currentOnlineClients.set(clientID, {
|
|
10
|
+
pClientID: pClientID || "",
|
|
11
|
+
clientID,
|
|
12
|
+
tideID,
|
|
13
|
+
id
|
|
14
|
+
});
|
|
15
|
+
return currentOnlineClients;
|
|
16
|
+
}
|
|
17
|
+
static clearOnlineClient(clientID) {
|
|
18
|
+
if (!currentOnlineClients.get(clientID)) {
|
|
19
|
+
return currentOnlineClients;
|
|
20
|
+
}
|
|
21
|
+
currentOnlineClients.delete(clientID);
|
|
22
|
+
return currentOnlineClients;
|
|
23
|
+
}
|
|
24
|
+
static getOnlineClient(clientID) {
|
|
25
|
+
return currentOnlineClients.get(clientID);
|
|
26
|
+
}
|
|
27
|
+
static getOnlineClients() {
|
|
28
|
+
return currentOnlineClients;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
module.exports = {
|
|
32
|
+
FileNetClientManager
|
|
33
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const currentOnlineClients = new Map();
|
|
4
|
+
class FileNetConfigManager {
|
|
5
|
+
static addOnlineClient(clientID, pClientID, tideID) {
|
|
6
|
+
if (currentOnlineClients.get(pClientID)) {
|
|
7
|
+
currentOnlineClients.delete(pClientID);
|
|
8
|
+
}
|
|
9
|
+
currentOnlineClients.set(clientID, {
|
|
10
|
+
pClientID: pClientID || "",
|
|
11
|
+
clientID,
|
|
12
|
+
tideID
|
|
13
|
+
});
|
|
14
|
+
return currentOnlineClients;
|
|
15
|
+
}
|
|
16
|
+
static clearOnlineClient(clientID, tideID) {
|
|
17
|
+
if (!currentOnlineClients.get(clientID)) {
|
|
18
|
+
return currentOnlineClients;
|
|
19
|
+
}
|
|
20
|
+
currentOnlineClients.delete(clientID);
|
|
21
|
+
return currentOnlineClients;
|
|
22
|
+
}
|
|
23
|
+
static getOnlineClient(clientID) {
|
|
24
|
+
if (!currentOnlineClients.get(clientID)) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return currentOnlineClients.get(clientID);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
module.exports = {
|
|
31
|
+
FileNetConfigManager
|
|
32
|
+
};
|
|
@@ -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": {
|
package/test.txt
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
oiyutryuihoj
|