@trap_stevo/filetide 0.0.31 → 0.0.33
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
CHANGED
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
const {
|
|
11
11
|
FileTransferManager
|
|
12
12
|
} = require("./HUDManagers/FileTransferManager");
|
|
13
|
+
const HUDTargetQueue = require("./HUDManagers/HUDTargetQueueManager");
|
|
13
14
|
class FileMessager {
|
|
14
15
|
constructor(options = {
|
|
15
16
|
pingTimeout: 3600000,
|
|
@@ -20,6 +21,9 @@ class FileMessager {
|
|
|
20
21
|
}) {
|
|
21
22
|
const serverOptions = FileMessagerConfigManager.getServerOptions(options);
|
|
22
23
|
this.transporter = new FileTransferManager(transportOptions);
|
|
24
|
+
this.transferQueue = new HUDTargetQueue();
|
|
25
|
+
this.currentTransferBarrierTransfers = new Map();
|
|
26
|
+
this.clientTransferBarriers = new Map();
|
|
23
27
|
this.fileNet = new IoTide(serverOptions.port, serverOptions, true, this.onConnect.bind(this), this.onDisconnect.bind(this));
|
|
24
28
|
this.onlineClients = new Map();
|
|
25
29
|
return;
|
|
@@ -35,6 +39,8 @@ class FileMessager {
|
|
|
35
39
|
return;
|
|
36
40
|
}
|
|
37
41
|
start() {
|
|
42
|
+
this.fileNet.on("get-client-transfer-barrier-response", this.handleClientTransferBarrierResponse.bind(this));
|
|
43
|
+
this.fileNet.on("notify-transfer-barrier", this.handleNotifyTransferBarrier.bind(this));
|
|
38
44
|
this.fileNet.on("transfer-start", this.handleFileTransferStart.bind(this));
|
|
39
45
|
this.fileNet.on("transfer-progress", this.handleFileTransferProgress.bind(this));
|
|
40
46
|
this.fileNet.on("transfer-complete", this.handleFileTransferComplete.bind(this));
|
|
@@ -63,7 +69,8 @@ class FileMessager {
|
|
|
63
69
|
senderID,
|
|
64
70
|
requesterID,
|
|
65
71
|
path: filePath,
|
|
66
|
-
destinationPath
|
|
72
|
+
destinationPath,
|
|
73
|
+
type: "requestedTransfer"
|
|
67
74
|
});
|
|
68
75
|
}
|
|
69
76
|
|
|
@@ -147,9 +154,48 @@ class FileMessager {
|
|
|
147
154
|
console.log(`[FileTide ~ File Messager] ~ File transfer completed: ${fileName} from ${senderID} to ${recipientId}`);
|
|
148
155
|
}
|
|
149
156
|
}
|
|
157
|
+
handleClientTransferBarrierResponse(data) {
|
|
158
|
+
console.log(`Checking ${data.recipientID}'s transfer barrier for transfer ~ ${data.transferID}...`);
|
|
159
|
+
const recipientConnectionID = FileNetClientManager.getOnlineClient(data.recipientID).id;
|
|
160
|
+
const senderConnectionID = FileNetClientManager.getOnlineClient(data.clientID).id;
|
|
161
|
+
const senderClient = this.getClientFromID(senderConnectionID);
|
|
162
|
+
const client = this.getClientFromID(recipientConnectionID);
|
|
163
|
+
if (!client || !senderClient) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
this.fileNet.emitToTide(client.tideID, "queue-incoming-transfer", {
|
|
167
|
+
...data,
|
|
168
|
+
transferType: "send"
|
|
169
|
+
});
|
|
170
|
+
this.transferQueue.waitForData(data.transferID, () => this.currentTransferBarrierTransfers.get(data.transferID), [true, false], 100, 50000).then(accepted => {
|
|
171
|
+
console.log(`Transfer ${accepted ? "accepted!" : "denied."}`);
|
|
172
|
+
this.fileNet.emitToTide(senderClient.tideID, "transfer-barrier-response", accepted);
|
|
173
|
+
this.currentTransferBarrierTransfers.delete(data.transferID);
|
|
174
|
+
if (accepted) {
|
|
175
|
+
this.fileNet.emitToTide(client.tideID, "accepted-transfer", {
|
|
176
|
+
accepted,
|
|
177
|
+
...data
|
|
178
|
+
});
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
this.fileNet.emitToTide(client.tideID, "denied-transfer", {
|
|
182
|
+
accepted,
|
|
183
|
+
...data
|
|
184
|
+
});
|
|
185
|
+
}).catch(error => {
|
|
186
|
+
console.error(error.message);
|
|
187
|
+
});
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
handleNotifyTransferBarrier(data, emitToChannel) {
|
|
191
|
+
const accepted = data.accepted;
|
|
192
|
+
const transferID = data.data.transferID;
|
|
193
|
+
this.currentTransferBarrierTransfers.set(transferID, accepted);
|
|
194
|
+
}
|
|
150
195
|
handleFileTransferStart(data, emitToChannel) {
|
|
151
196
|
console.log(`Alerting client of incoming transfer from ${data.senderID}...`);
|
|
152
|
-
const
|
|
197
|
+
const recipientConnectionID = FileNetClientManager.getOnlineClient(data.recipientID).id;
|
|
198
|
+
const client = this.getClientFromID(recipientConnectionID);
|
|
153
199
|
if (!client) {
|
|
154
200
|
return;
|
|
155
201
|
}
|
|
@@ -175,11 +221,15 @@ class FileMessager {
|
|
|
175
221
|
this.fileNet.emitToTide(connectionDetails.tideID, "current-online-clients", Object.fromEntries(FileNetClientManager.getOnlineClients()));
|
|
176
222
|
return;
|
|
177
223
|
}
|
|
178
|
-
handleClientOnline(clientID, tideID, pClientID, connectionID) {
|
|
224
|
+
handleClientOnline(clientID, tideID, pClientID, connectionID, activeTransferBarrier) {
|
|
179
225
|
if (!this.onlineClients.has(connectionID)) {
|
|
180
226
|
return;
|
|
181
227
|
}
|
|
182
228
|
FileNetClientManager.addOnlineClient(clientID, pClientID, tideID, connectionID);
|
|
229
|
+
this.clientTransferBarriers.set(tideID, {
|
|
230
|
+
clientID,
|
|
231
|
+
activeTransferBarrier
|
|
232
|
+
});
|
|
183
233
|
this.fileNet.emitToTide(tideID, "current-online-clients", Object.fromEntries(FileNetClientManager.getOnlineClients()));
|
|
184
234
|
return;
|
|
185
235
|
}
|
|
@@ -30,6 +30,7 @@ class FileMessagerClient {
|
|
|
30
30
|
this.progressBarManager = new ConsoleProgressBarManager([], {
|
|
31
31
|
completionMessageColor: chalk.blue.bold
|
|
32
32
|
});
|
|
33
|
+
this.activeTransferTypes = new Map();
|
|
33
34
|
this.clientTide = new HUDIoTide();
|
|
34
35
|
this.clients = new Map();
|
|
35
36
|
}
|
|
@@ -52,7 +53,11 @@ class FileMessagerClient {
|
|
|
52
53
|
});
|
|
53
54
|
});
|
|
54
55
|
}
|
|
55
|
-
requestTransfer(clientID, senderID, filePath, destinationPath) {
|
|
56
|
+
async requestTransfer(clientID, senderID, filePath, destinationPath) {
|
|
57
|
+
const transferAllowed = await this.allowedTransfer(clientID, senderID, destinationPath, "N/A", "requestSend");
|
|
58
|
+
if (!transferAllowed) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
56
61
|
this.clientTide.emitEvent(clientID, "request-transfer-from-client", {
|
|
57
62
|
requesterID: clientID,
|
|
58
63
|
senderID,
|
|
@@ -61,6 +66,34 @@ class FileMessagerClient {
|
|
|
61
66
|
});
|
|
62
67
|
return;
|
|
63
68
|
}
|
|
69
|
+
async allowedTransfer(clientID, recipientID, filePath, fileSize, transferType = "send") {
|
|
70
|
+
try {
|
|
71
|
+
const currentDate = Date.now();
|
|
72
|
+
const transferID = `${clientID}_${filePath}_${currentDate}`;
|
|
73
|
+
const transferAccepted = await this.clientTide.emitEventWithResponse(clientID, "get-client-transfer-barrier-response", "transfer-barrier-response", {
|
|
74
|
+
clientID,
|
|
75
|
+
recipientID,
|
|
76
|
+
transferID,
|
|
77
|
+
path: filePath,
|
|
78
|
+
date: currentDate,
|
|
79
|
+
transferType
|
|
80
|
+
}, 30000);
|
|
81
|
+
if (!transferAccepted) {
|
|
82
|
+
FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ${recipientID} denied your transfer.`, errorMessageColors);
|
|
83
|
+
return false;
|
|
84
|
+
} else if (transferAccepted) {
|
|
85
|
+
FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ${recipientID} accepted your transfer!`, significantMessageColors);
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
} catch (error) {
|
|
89
|
+
if (error.message.startsWith("Timeout: No response")) {
|
|
90
|
+
FileNetUtilityManager.outputGradient("[FileTide ~ File Messager] Transfer approval timed out.", errorMessageColors);
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] Transfer approval refused.\n${error}`, errorMessageColors);
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
64
97
|
|
|
65
98
|
/**
|
|
66
99
|
* Sends all files in a directory in parallel using the sendFile method.
|
|
@@ -70,21 +103,33 @@ class FileMessagerClient {
|
|
|
70
103
|
* @param {string} baseDirectory - The base directory path that contains the files.
|
|
71
104
|
* @param {string} destinationPath - The path to which the files should be sent.
|
|
72
105
|
*/
|
|
73
|
-
sendDirectoryFiles(clientID, recipientId, filesData, baseDirectory, destinationPath) {
|
|
106
|
+
async sendDirectoryFiles(clientID, recipientId, filesData, baseDirectory, destinationPath) {
|
|
74
107
|
try {
|
|
75
108
|
const baseDirectoryName = path.basename(baseDirectory);
|
|
76
109
|
const adjustedDestinationPath = FileUtilityManager.normalizePath(path.join(destinationPath, baseDirectoryName));
|
|
77
|
-
|
|
110
|
+
let totalSize = 0;
|
|
111
|
+
filesData.forEach(fileInfo => {
|
|
112
|
+
totalSize += fileInfo.size;
|
|
113
|
+
});
|
|
114
|
+
const incomingType = this.activeTransferTypes.get(destinationPath);
|
|
115
|
+
const transferAllowed = incomingType && incomingType === "requestedTransfer" ? true : await this.allowedTransfer(clientID, recipientId, adjustedDestinationPath, totalSize, "send");
|
|
116
|
+
if (incomingType) {
|
|
117
|
+
this.activeTransferTypes.delete(destinationPath);
|
|
118
|
+
}
|
|
119
|
+
if (!transferAllowed) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const sendFilePromises = filesData.map(async (fileInfo, index) => {
|
|
78
123
|
const relativeFilePath = path.dirname(path.relative(baseDirectory, fileInfo.filePath));
|
|
79
124
|
const fileDestinationPath = FileUtilityManager.normalizePath(path.join(adjustedDestinationPath, relativeFilePath));
|
|
80
|
-
return this.
|
|
125
|
+
return await this.sendDirectoryFile(clientID, recipientId, fileInfo.fileName, fileInfo.fileData, fileDestinationPath, fileInfo.size, adjustedDestinationPath, index);
|
|
81
126
|
});
|
|
82
127
|
Promise.all(sendFilePromises).then(() => console.log("\nAll files in directory transferred successfully!")).catch(error => console.error("Did not transfer directory: ", error));
|
|
83
128
|
} catch (error) {
|
|
84
129
|
FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Refused directory transfer: ${error}`, errorMessageColors);
|
|
85
130
|
}
|
|
86
131
|
}
|
|
87
|
-
|
|
132
|
+
sendDirectoryFile(clientID, recipientId, fileName, file, filePath = process.cwd(), fileSize, directoryPath = null, index = 0) {
|
|
88
133
|
if (!file) {
|
|
89
134
|
return;
|
|
90
135
|
}
|
|
@@ -146,6 +191,76 @@ class FileMessagerClient {
|
|
|
146
191
|
}
|
|
147
192
|
}).then(() => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ File ~ ${fileName} transfer complete!`, significantMessageColors)).catch(error => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Refused file transfer: ${error}`, errorMessageColors));
|
|
148
193
|
}
|
|
194
|
+
async sendFile(clientID, recipientId, fileName, file, filePath = process.cwd(), fileSize) {
|
|
195
|
+
if (!file) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const destinationPath = FileUtilityManager.normalizePath(path.join(filePath, fileName));
|
|
199
|
+
FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Requesting to transfer ~ ${destinationPath} to ${recipientId}!`, significantMessageColors);
|
|
200
|
+
const incomingType = this.activeTransferTypes.get(filePath);
|
|
201
|
+
const transferAllowed = incomingType && incomingType === "requestedTransfer" ? true : await this.allowedTransfer(clientID, recipientId, destinationPath, fileSize, "send");
|
|
202
|
+
if (incomingType) {
|
|
203
|
+
this.activeTransferTypes.delete(filePath);
|
|
204
|
+
}
|
|
205
|
+
if (!transferAllowed) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
this.progressBarManager.completionMessage = `Successfully sent to ${recipientId}!`;
|
|
209
|
+
this.progressBarManager.displayPage("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta);
|
|
210
|
+
this.progressBarManager.listenForInput("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta, chalk.yellow);
|
|
211
|
+
this.clientTide.emitEvent(clientID, "transfer-start", {
|
|
212
|
+
fileName,
|
|
213
|
+
senderID: clientID,
|
|
214
|
+
recipientID: recipientId,
|
|
215
|
+
path: filePath
|
|
216
|
+
});
|
|
217
|
+
this.transporter.sendFile(file, {
|
|
218
|
+
onSendChunk: (transferId, chunkIndex, chunkData, totalChunks) => {
|
|
219
|
+
if (chunkIndex === 0) {
|
|
220
|
+
this.progressBarManager.addTask({
|
|
221
|
+
name: fileName,
|
|
222
|
+
size: fileSize
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
return new Promise((resolve, reject) => {
|
|
226
|
+
this.clientTide.emitEvent(clientID, "client-to-client-transfer", {
|
|
227
|
+
senderID: clientID,
|
|
228
|
+
recipientId,
|
|
229
|
+
fileName,
|
|
230
|
+
fileChunk: chunkData,
|
|
231
|
+
totalChunks,
|
|
232
|
+
chunkIndex,
|
|
233
|
+
filePath,
|
|
234
|
+
fileSize
|
|
235
|
+
});
|
|
236
|
+
resolve();
|
|
237
|
+
});
|
|
238
|
+
},
|
|
239
|
+
onComplete: () => {
|
|
240
|
+
return new Promise((resolve, reject) => {
|
|
241
|
+
this.clientTide.emitEvent(clientID, "transfer-complete", {
|
|
242
|
+
senderID: clientID,
|
|
243
|
+
recipientId,
|
|
244
|
+
fileName,
|
|
245
|
+
filePath
|
|
246
|
+
});
|
|
247
|
+
resolve();
|
|
248
|
+
});
|
|
249
|
+
},
|
|
250
|
+
onProgress: progress => {
|
|
251
|
+
this.progressBarManager.updateTaskProgress(fileName, 512 * 1024);
|
|
252
|
+
},
|
|
253
|
+
originDetails: {
|
|
254
|
+
recipientID: recipientId,
|
|
255
|
+
senderID: clientID
|
|
256
|
+
},
|
|
257
|
+
fileDetails: {
|
|
258
|
+
name: fileName,
|
|
259
|
+
path: filePath,
|
|
260
|
+
size: fileSize
|
|
261
|
+
}
|
|
262
|
+
}).then(() => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ File ~ ${fileName} transfer complete!`, significantMessageColors)).catch(error => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Refused file transfer: ${error}`, errorMessageColors));
|
|
263
|
+
}
|
|
149
264
|
}
|
|
150
265
|
;
|
|
151
266
|
module.exports = FileMessagerClient;
|
package/dist/cjs/FileTide.js
CHANGED
|
@@ -34,6 +34,7 @@ class FileTide {
|
|
|
34
34
|
completionMessageColor: chalk.blue.bold
|
|
35
35
|
});
|
|
36
36
|
this.clientShorePolicies = new Map();
|
|
37
|
+
this.transferBarrier = false;
|
|
37
38
|
this.clients = new Map();
|
|
38
39
|
this.currentUserID = "";
|
|
39
40
|
return;
|
|
@@ -65,34 +66,47 @@ class FileTide {
|
|
|
65
66
|
* @param {String} roomName - The name of the room to join
|
|
66
67
|
* @param {String} userID - The ID of the client user
|
|
67
68
|
* @param {Function} onLaunch - Callback for when a file messager launches
|
|
69
|
+
* @param {Function} onIncomingTransfer - Callback for incoming transfers
|
|
70
|
+
* @param {Function} onTransferBarrier - Callback for when an incoming transfer status updates
|
|
68
71
|
*/
|
|
69
|
-
launchMessager(clientOptions = {}, connectionOptions = {}, roomName, userID, onLaunch, onIncomingFile) {
|
|
72
|
+
launchMessager(clientOptions = {}, connectionOptions = {}, roomName, userID, onLaunch, onIncomingFile, onIncomingTransfer, onTransferBarrier, enableTransferBarrier = true) {
|
|
70
73
|
if (this.clients.has(userID)) {
|
|
71
74
|
this.stopMessager(userID);
|
|
72
75
|
}
|
|
73
76
|
const newClient = new FileMessagerClient(clientOptions);
|
|
74
77
|
newClient.joinRoom(userID, roomName, connectionOptions);
|
|
78
|
+
this.transferBarrier = enableTransferBarrier;
|
|
75
79
|
this.clientShorePolicies.set(userID, [FileUtilityManager.getTidePath(">CurrentUser"), FileUtilityManager.getTidePath(">Downloads"), FileUtilityManager.getTidePath(">Documents")]);
|
|
76
80
|
this.clients.set(userID, newClient);
|
|
77
81
|
FileTide.outputGradient(`[FileTide] ~ Messager launched successfully for client ~ ${userID}!`, significantMessageColors);
|
|
78
|
-
_assertClassBrand(_FileTide_brand, this, _setupFileEventListeners).call(this, userID, newClient, onIncomingFile);
|
|
82
|
+
_assertClassBrand(_FileTide_brand, this, _setupFileEventListeners).call(this, userID, newClient, onIncomingFile, onIncomingTransfer, onTransferBarrier);
|
|
79
83
|
if (onLaunch) {
|
|
80
84
|
onLaunch(newClient);
|
|
81
85
|
}
|
|
82
86
|
return;
|
|
83
87
|
}
|
|
84
|
-
getDirectoryData(directoryPath) {
|
|
85
|
-
return FileUtilityManager.getDirectoryData(directoryPath);
|
|
88
|
+
async getDirectoryData(directoryPath) {
|
|
89
|
+
return await FileUtilityManager.getDirectoryData(directoryPath);
|
|
86
90
|
}
|
|
87
91
|
getFileData(filePath) {
|
|
88
92
|
return FileUtilityManager.getFileData(filePath);
|
|
89
93
|
}
|
|
90
|
-
getPathData(inputPath) {
|
|
91
|
-
return FileUtilityManager.getPathData(inputPath);
|
|
94
|
+
async getPathData(inputPath, filterContent = []) {
|
|
95
|
+
return await FileUtilityManager.getPathData(inputPath, filterContent);
|
|
92
96
|
}
|
|
93
97
|
getTidePath(inputPath) {
|
|
94
98
|
return FileUtilityManager.getTidePath(inputPath);
|
|
95
99
|
}
|
|
100
|
+
filterFilesAndDirectories(dirPath, filterContent = [], callback) {
|
|
101
|
+
FileUtilityManager.filterFilesAndDirectories(dirPath, filterContent, (files, error) => {
|
|
102
|
+
if (callback) {
|
|
103
|
+
callback(files, error);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
async listFiles(directories) {
|
|
108
|
+
return await FileUtilityManager.listFiles(directories);
|
|
109
|
+
}
|
|
96
110
|
clearShorePolicies(clientID, shores = []) {
|
|
97
111
|
let clientShorePolicies = this.clientShorePolicies.get(clientID);
|
|
98
112
|
if (!shores || shores.length <= 0 || !clientShorePolicies || !clientID) {
|
|
@@ -122,15 +136,16 @@ class FileTide {
|
|
|
122
136
|
* @param {String} recipientID - The ID of the client device to send the data to
|
|
123
137
|
* @param {Buffer} fileData - The path of the data to send
|
|
124
138
|
* @param {String} destinationPath - The destination path on the device
|
|
139
|
+
* @param {Array} filterDirectoryContent - List of paths to filter
|
|
125
140
|
*/
|
|
126
|
-
sendToDevice(recipientID, filePath, destinationPath) {
|
|
127
|
-
const fileDetails = this.getPathData(filePath);
|
|
141
|
+
async sendToDevice(recipientID, filePath, destinationPath, filterDirectoryContent = []) {
|
|
128
142
|
const clientID = this.currentUserID;
|
|
129
143
|
const clientMessager = this.clients.get(clientID);
|
|
130
144
|
if (!clientMessager) {
|
|
131
145
|
FileTide.outputGradient(`[FileTide] Client ~ ${clientID} not found.`, ["#F94144", "#F3722C"]);
|
|
132
146
|
return;
|
|
133
147
|
}
|
|
148
|
+
const fileDetails = await this.getPathData(filePath, filterDirectoryContent);
|
|
134
149
|
if (!fileDetails) {
|
|
135
150
|
FileTide.outputGradient(`[${clientID}] File at path ${filePath} not found.`, ["#F94144", "#F3722C"]);
|
|
136
151
|
return;
|
|
@@ -203,7 +218,7 @@ class FileTide {
|
|
|
203
218
|
}
|
|
204
219
|
}
|
|
205
220
|
_FileTide = FileTide;
|
|
206
|
-
function _setupFileEventListeners(userID, client, onIncomingFile) {
|
|
221
|
+
function _setupFileEventListeners(userID, client, onIncomingFile, onIncomingTransfer, onTransferBarrier) {
|
|
207
222
|
const cliTable = new ConsoleTable({
|
|
208
223
|
padding: 2,
|
|
209
224
|
headerAlign: "center",
|
|
@@ -245,9 +260,26 @@ function _setupFileEventListeners(userID, client, onIncomingFile) {
|
|
|
245
260
|
cliTable.setData(currentClients);
|
|
246
261
|
console.log(`\n[FileTide ~ FileNet]\n\n`, cliTable.render(), "\n");
|
|
247
262
|
});
|
|
248
|
-
client.clientTide.onEvent(userID, "incoming-transfer", data => {
|
|
263
|
+
client.clientTide.onEvent(userID, "queue-incoming-transfer", async data => {
|
|
264
|
+
if (this.transferBarrier && onIncomingTransfer) {
|
|
265
|
+
onIncomingTransfer(data, status => {
|
|
266
|
+
client.clientTide.emitEvent(userID, "notify-transfer-barrier", {
|
|
267
|
+
accepted: status,
|
|
268
|
+
data
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
return;
|
|
272
|
+
} else if (!this.transferBarrier) {
|
|
273
|
+
client.clientTide.emitEvent(userID, "notify-transfer-barrier", {
|
|
274
|
+
accepted: true,
|
|
275
|
+
data
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
return;
|
|
279
|
+
});
|
|
280
|
+
client.clientTide.onEvent(userID, "incoming-transfer", async data => {
|
|
249
281
|
_FileTide.outputGradient(`[${userID}] Preparing to receive transfer : ${FileUtilityManager.getTidePath(data.path)}`, significantMessageColors);
|
|
250
|
-
this.progressBarManager.completionMessage = `Successfully received transfer from ${
|
|
282
|
+
this.progressBarManager.completionMessage = `Successfully received transfer from ${data.senderID}!`;
|
|
251
283
|
this.progressBarManager.displayPage("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta);
|
|
252
284
|
this.progressBarManager.listenForInput("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta, chalk.yellow);
|
|
253
285
|
});
|
|
@@ -260,11 +292,14 @@ function _setupFileEventListeners(userID, client, onIncomingFile) {
|
|
|
260
292
|
fileInfo: data
|
|
261
293
|
});
|
|
262
294
|
const saveDir = FileUtilityManager.getTidePath(data.path);
|
|
263
|
-
|
|
295
|
+
const verifiedSender = data.senderID && data.senderID !== userID;
|
|
296
|
+
if (verifiedSender) {
|
|
264
297
|
this.progressBarManager.addTask({
|
|
265
298
|
name: data.fileName,
|
|
266
299
|
size: data.fileSize
|
|
267
300
|
});
|
|
301
|
+
}
|
|
302
|
+
if (verifiedSender && !fs.existsSync(saveDir)) {
|
|
268
303
|
fs.mkdirSync(saveDir, {
|
|
269
304
|
recursive: true
|
|
270
305
|
});
|
|
@@ -274,6 +309,18 @@ function _setupFileEventListeners(userID, client, onIncomingFile) {
|
|
|
274
309
|
_FileTide.outputGradient(`[${userID}] Halted file recieval preparation : ${data.fileName}\n${error}`, errorMessageColors);
|
|
275
310
|
}
|
|
276
311
|
});
|
|
312
|
+
client.clientTide.onEvent(userID, "accepted-transfer", async data => {
|
|
313
|
+
_FileTide.outputGradient(`[${userID}] Accepted ${data.clientID}'s transfer!`, significantMessageColors);
|
|
314
|
+
if (onTransferBarrier) {
|
|
315
|
+
onTransferBarrier(data);
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
client.clientTide.onEvent(userID, "denied-transfer", async data => {
|
|
319
|
+
_FileTide.outputGradient(`[${userID}] Denied ${data.clientID}'s transfer.`, errorMessageColors);
|
|
320
|
+
if (onTransferBarrier) {
|
|
321
|
+
onTransferBarrier(data);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
277
324
|
client.clientTide.onEvent(userID, "transfer-progress", data => {
|
|
278
325
|
if (!data || data.fileChunk === undefined || data.fileChunk === null) {
|
|
279
326
|
return;
|
|
@@ -290,13 +337,13 @@ function _setupFileEventListeners(userID, client, onIncomingFile) {
|
|
|
290
337
|
}
|
|
291
338
|
});
|
|
292
339
|
client.clientTide.onEvent(userID, "transfer-complete", data => {
|
|
340
|
+
const savePath = FileUtilityManager.getTidePath(path.join(data.filePath, data.fileName));
|
|
293
341
|
try {
|
|
294
342
|
_FileTide.outputGradient(`[${userID}] File transfer complete : ${data.fileName}`, significantMessageColors);
|
|
295
343
|
const transferState = activeTransfers.get(data.fileName);
|
|
296
344
|
if (!transferState) {
|
|
297
345
|
return;
|
|
298
346
|
}
|
|
299
|
-
const savePath = FileUtilityManager.getTidePath(path.join(data.filePath, data.fileName));
|
|
300
347
|
const fullFile = Buffer.concat(transferState.receivedChunks);
|
|
301
348
|
fs.writeFileSync(savePath, fullFile);
|
|
302
349
|
_FileTide.outputGradient(`[${userID}] File saved at : ${savePath}`, significantMessageColors);
|
|
@@ -306,9 +353,11 @@ function _setupFileEventListeners(userID, client, onIncomingFile) {
|
|
|
306
353
|
activeTransfers.delete(data.fileName);
|
|
307
354
|
}
|
|
308
355
|
});
|
|
309
|
-
client.clientTide.onEvent(userID, "transfer-requested", data => {
|
|
356
|
+
client.clientTide.onEvent(userID, "transfer-requested", async data => {
|
|
310
357
|
_FileTide.outputGradient(`[${userID}] Transfer requested from: ${data.requesterID}`, significantMessageColors);
|
|
311
|
-
this.
|
|
358
|
+
this.clients.get(userID).activeTransferTypes.set(data.destinationPath, data.type);
|
|
359
|
+
await this.sendToDevice(data.requesterID, data.path, data.destinationPath);
|
|
360
|
+
return;
|
|
312
361
|
});
|
|
313
362
|
this.currentUserID = userID;
|
|
314
363
|
return;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
const path = require("path");
|
|
4
|
+
const fsAsync = require("fs").promises;
|
|
4
5
|
const fs = require("fs");
|
|
5
6
|
const os = require("os");
|
|
6
7
|
const filesPath = path.join(__dirname, "../files");
|
|
@@ -27,46 +28,154 @@ class FileUtilityManager {
|
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* Function to filter files and directories.
|
|
30
|
-
* @param {string} dirPath - The directory path to scan
|
|
31
|
-
* @param {Array} filterContent - Array of paths (files, directories, extensions) to avoid (e.g., ['docs', 'example.txt', '.js'])
|
|
32
|
-
* @
|
|
31
|
+
* @param {string} dirPath - The directory path to scan.
|
|
32
|
+
* @param {Array} filterContent - Array of paths (files, directories, extensions) to avoid (e.g., ['docs', 'example.txt', '.js']).
|
|
33
|
+
* @returns {Promise<Array>} - A promise that resolves to an array of objects containing details about filtered files and directories.
|
|
33
34
|
*/
|
|
34
|
-
static filterFilesAndDirectories(dirPath, filterContent = []
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
35
|
+
static async filterFilesAndDirectories(dirPath, filterContent = []) {
|
|
36
|
+
try {
|
|
37
|
+
const currentDirPath = this.getTidePath(dirPath);
|
|
38
|
+
const items = await fs.promises.readdir(currentDirPath, {
|
|
39
|
+
withFileTypes: true
|
|
40
|
+
});
|
|
41
41
|
const results = [];
|
|
42
|
-
|
|
43
|
-
const itemPath = path.join(
|
|
42
|
+
for (const item of items) {
|
|
43
|
+
const itemPath = path.join(currentDirPath, item.name);
|
|
44
44
|
const filtered = filterContent.some(filterEntry => {
|
|
45
|
-
const resolvedFilterEntry = path.resolve(filterEntry);
|
|
45
|
+
const resolvedFilterEntry = path.resolve(currentDirPath, filterEntry);
|
|
46
46
|
const fileExtension = filterEntry.startsWith(".");
|
|
47
47
|
return itemPath === resolvedFilterEntry || item.name === filterEntry || fileExtension && path.extname(item.name) === filterEntry;
|
|
48
48
|
});
|
|
49
49
|
if (!filtered) {
|
|
50
|
-
fs.stat(itemPath
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
size: stats.size,
|
|
58
|
-
name: item.name,
|
|
59
|
-
path: itemPath
|
|
60
|
-
});
|
|
61
|
-
if (results.length === items.length) {
|
|
62
|
-
callback(null, results);
|
|
63
|
-
}
|
|
50
|
+
const stats = await fs.promises.stat(itemPath);
|
|
51
|
+
results.push({
|
|
52
|
+
type: item.isDirectory() ? "directory" : "file",
|
|
53
|
+
dateCreated: stats.birthtime,
|
|
54
|
+
size: stats.size,
|
|
55
|
+
name: item.name,
|
|
56
|
+
path: itemPath
|
|
64
57
|
});
|
|
65
|
-
} else if (results.length === items.length) {
|
|
66
|
-
callback(null, results);
|
|
67
58
|
}
|
|
68
|
-
}
|
|
59
|
+
}
|
|
60
|
+
return results;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.error("Did not read directory: ", error);
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Function to list all paths across the system using wildcard '*'.
|
|
69
|
+
* @returns {Promise<Array>} - A promise that resolves to an array of file and directory metadata across the system.
|
|
70
|
+
*/
|
|
71
|
+
static async listAllPathsInOS() {
|
|
72
|
+
const rootPaths = ["/", "C:/"];
|
|
73
|
+
let allResults = [];
|
|
74
|
+
const rootPromises = rootPaths.map(async rootPath => {
|
|
75
|
+
if (await this.exists(rootPath)) {
|
|
76
|
+
const subResult = await this.listPaths(rootPath);
|
|
77
|
+
allResults.push(...subResult.results);
|
|
78
|
+
}
|
|
69
79
|
});
|
|
80
|
+
await Promise.all(rootPromises);
|
|
81
|
+
return allResults;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Function to list all paths and their metadata recursively.
|
|
86
|
+
* @param {string} dir - The directory path to scan.
|
|
87
|
+
* @returns {Promise<object>} - A promise that resolves to an object containing a list of files/directories with metadata.
|
|
88
|
+
*/
|
|
89
|
+
static async listPaths(dir) {
|
|
90
|
+
let results = [];
|
|
91
|
+
const list = await fsAsync.readdir(dir, {
|
|
92
|
+
withFileTypes: true
|
|
93
|
+
});
|
|
94
|
+
const stats = await fsAsync.stat(dir);
|
|
95
|
+
const dirMetadata = {
|
|
96
|
+
totalItems: 0,
|
|
97
|
+
totalSize: 0,
|
|
98
|
+
createdTime: stats.birthtime,
|
|
99
|
+
lastModified: stats.mtime,
|
|
100
|
+
type: "directory",
|
|
101
|
+
path: dir
|
|
102
|
+
};
|
|
103
|
+
results.push(dirMetadata);
|
|
104
|
+
const promises = list.map(async file => {
|
|
105
|
+
const filePath = this.getTidePath(path.join(dir, file.name));
|
|
106
|
+
if (file.isDirectory()) {
|
|
107
|
+
const subDirResult = await this.listPaths(filePath);
|
|
108
|
+
dirMetadata.totalItems += subDirResult.totalItems;
|
|
109
|
+
dirMetadata.totalSize += subDirResult.totalSize;
|
|
110
|
+
results.push(...subDirResult.results);
|
|
111
|
+
} else {
|
|
112
|
+
const fileStats = await fsAsync.stat(filePath);
|
|
113
|
+
const fileMetadata = {
|
|
114
|
+
createdTime: fileStats.birthtime,
|
|
115
|
+
lastModified: fileStats.mtime,
|
|
116
|
+
size: fileStats.size,
|
|
117
|
+
path: filePath,
|
|
118
|
+
type: "file"
|
|
119
|
+
};
|
|
120
|
+
dirMetadata.totalSize += fileStats.size;
|
|
121
|
+
dirMetadata.totalItems += 1;
|
|
122
|
+
results.push(fileMetadata);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
await Promise.all(promises);
|
|
126
|
+
return {
|
|
127
|
+
totalItems: dirMetadata.totalItems,
|
|
128
|
+
totalSize: dirMetadata.totalSize,
|
|
129
|
+
results
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Allows for listing files in a specific directory or all files in the system '*'.
|
|
135
|
+
* @param {Array} directories - Array of directories to scan or '*' for entire system.
|
|
136
|
+
* @returns {Promise<Array>} - A promise that resolves to an array of file and directory metadata from specified directories or the entire system.
|
|
137
|
+
*/
|
|
138
|
+
static async listFiles(directories) {
|
|
139
|
+
let fileList = [];
|
|
140
|
+
if (directories.includes("*")) {
|
|
141
|
+
fileList = await this.listAllPathsInOS();
|
|
142
|
+
} else {
|
|
143
|
+
const dirPromises = directories.map(async dir => {
|
|
144
|
+
const currentDir = this.getTidePath(dir);
|
|
145
|
+
if (await this.exists(currentDir)) {
|
|
146
|
+
const subResult = await this.listPaths(currentDir);
|
|
147
|
+
fileList.push(...subResult.results);
|
|
148
|
+
} else {
|
|
149
|
+
console.warn(`Directory does not exist: ${currentDir}`);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
await Promise.all(dirPromises);
|
|
153
|
+
}
|
|
154
|
+
return fileList;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Checks the path's type.
|
|
159
|
+
* @param {string} inputPath - The path to check.
|
|
160
|
+
* @returns {Promise<boolean>} - Returns a promise that resolves to true if a directory, false otherwise.
|
|
161
|
+
*/
|
|
162
|
+
static async directoryPath(inputPath) {
|
|
163
|
+
const stats = await fsAsync.stat(inputPath);
|
|
164
|
+
return stats.isDirectory();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Helper function to check if a path exists.
|
|
169
|
+
* @param {string} inputPath - The path to check.
|
|
170
|
+
* @returns {Promise<boolean>} - Returns a promise that resolves to true if the path exists, false otherwise.
|
|
171
|
+
*/
|
|
172
|
+
static async exists(inputPath) {
|
|
173
|
+
try {
|
|
174
|
+
await fsAsync.access(inputPath);
|
|
175
|
+
return true;
|
|
176
|
+
} catch (e) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
70
179
|
}
|
|
71
180
|
|
|
72
181
|
/**
|
|
@@ -153,31 +262,31 @@ class FileUtilityManager {
|
|
|
153
262
|
/**
|
|
154
263
|
* Gets all files' data from a given directory.
|
|
155
264
|
* @param {string} dirPath - The path to the directory.
|
|
265
|
+
* @param {Array} filterContent - The paths to filter from the directory.
|
|
156
266
|
* @returns {Array} - An array of objects containing file data, name, and path.
|
|
157
267
|
*/
|
|
158
|
-
static getDirectoryData(inputDirPath) {
|
|
268
|
+
static async getDirectoryData(inputDirPath, filterContent = []) {
|
|
159
269
|
const dirPath = this.getTidePath(inputDirPath);
|
|
160
270
|
if (!fs.existsSync(dirPath)) {
|
|
161
271
|
console.log(`Directory at path ${dirPath} not found.`);
|
|
162
272
|
return [];
|
|
163
273
|
}
|
|
164
274
|
const filesData = [];
|
|
165
|
-
function readDirectory(currentPath) {
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
const stats = fs.statSync(filePath);
|
|
275
|
+
async function readDirectory(currentPath) {
|
|
276
|
+
const files = await FileUtilityManager.filterFilesAndDirectories(currentPath, filterContent);
|
|
277
|
+
for (const file of files) {
|
|
278
|
+
const stats = await fs.promises.stat(file.path);
|
|
170
279
|
if (stats.isFile()) {
|
|
171
|
-
const fileData = FileUtilityManager.getFileData(
|
|
280
|
+
const fileData = FileUtilityManager.getFileData(file.path);
|
|
172
281
|
if (fileData) {
|
|
173
282
|
filesData.push(fileData);
|
|
174
283
|
}
|
|
175
284
|
} else if (stats.isDirectory()) {
|
|
176
|
-
readDirectory(
|
|
285
|
+
await readDirectory(file.path);
|
|
177
286
|
}
|
|
178
|
-
}
|
|
287
|
+
}
|
|
179
288
|
}
|
|
180
|
-
readDirectory(dirPath);
|
|
289
|
+
await readDirectory(dirPath);
|
|
181
290
|
return filesData;
|
|
182
291
|
}
|
|
183
292
|
|
|
@@ -207,9 +316,10 @@ class FileUtilityManager {
|
|
|
207
316
|
/**
|
|
208
317
|
* Gets data from a file or directory based on the provided path.
|
|
209
318
|
* @param {string} inputPath - The path to a file or directory.
|
|
319
|
+
* @param {Array} filterContent - The paths to filter from the directory if getting a directory.
|
|
210
320
|
* @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.
|
|
211
321
|
*/
|
|
212
|
-
static getPathData(inputPath) {
|
|
322
|
+
static async getPathData(inputPath, filterContent = []) {
|
|
213
323
|
const currentInputPath = this.getTidePath(inputPath);
|
|
214
324
|
if (!fs.existsSync(currentInputPath)) {
|
|
215
325
|
console.log(`Path at ${currentInputPath} not found.`);
|
|
@@ -223,7 +333,7 @@ class FileUtilityManager {
|
|
|
223
333
|
};
|
|
224
334
|
} else if (stats.isDirectory()) {
|
|
225
335
|
return {
|
|
226
|
-
content: this.getDirectoryData(currentInputPath),
|
|
336
|
+
content: await this.getDirectoryData(currentInputPath, filterContent),
|
|
227
337
|
type: "directory"
|
|
228
338
|
};
|
|
229
339
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
class HUDTargetQueue {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.queues = new Map();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Adds a function to the queue for a specific ID and target value.
|
|
10
|
+
* @param {string} id - A unique identifier for the value being watched.
|
|
11
|
+
* @param {function} fn - The function to execute when the target value is reached.
|
|
12
|
+
*/
|
|
13
|
+
enqueue(id, fn) {
|
|
14
|
+
if (!this.queues.has(id)) {
|
|
15
|
+
this.queues.set(id, []);
|
|
16
|
+
}
|
|
17
|
+
this.queues.get(id).push(fn);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Waits for a value to change to the target value and executes only the functions
|
|
22
|
+
* in the queue associated with the specific ID.
|
|
23
|
+
* @param {string} id - A unique identifier for the value being watched.
|
|
24
|
+
* @param {function} getValue - A function that returns the current value.
|
|
25
|
+
* @param {any|array} targetValues - The value(s) to wait for.
|
|
26
|
+
* @param {number} interval - The interval (in milliseconds) to check the value.
|
|
27
|
+
* @param {number} [timeout] - Optional. The maximum time (in milliseconds) to wait before rejecting.
|
|
28
|
+
* @returns {Promise} - Resolves when the value matches the target value, executes queued functions.
|
|
29
|
+
*/
|
|
30
|
+
waitForData(id, getValue, targetValues, interval = 100, timeout) {
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
let elapsedTime = 0;
|
|
33
|
+
const targets = Array.isArray(targetValues) ? targetValues : [targetValues];
|
|
34
|
+
const checkData = async () => {
|
|
35
|
+
const currentValue = await getValue();
|
|
36
|
+
if (typeof currentValue === "object" && targets.some(target => typeof target === "object")) {
|
|
37
|
+
if (targets.some(target => JSON.stringify(currentValue) === JSON.stringify(target))) {
|
|
38
|
+
this.executeQueue(id);
|
|
39
|
+
resolve(currentValue);
|
|
40
|
+
}
|
|
41
|
+
} else if (targets.includes(currentValue)) {
|
|
42
|
+
this.executeQueue(id);
|
|
43
|
+
resolve(currentValue);
|
|
44
|
+
} else if (timeout && elapsedTime >= timeout) {
|
|
45
|
+
reject(new Error(`Timeout: Value did not reach target within ${timeout}ms`));
|
|
46
|
+
} else {
|
|
47
|
+
elapsedTime += interval;
|
|
48
|
+
setTimeout(checkData, interval);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
checkData();
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Executes all functions in the queue associated with the specific ID.
|
|
57
|
+
* @param {string} id - A unique identifier for the value being watched.
|
|
58
|
+
*/
|
|
59
|
+
executeQueue(id) {
|
|
60
|
+
const queue = this.queues.get(id);
|
|
61
|
+
if (queue) {
|
|
62
|
+
while (queue.length > 0) {
|
|
63
|
+
const fn = queue.shift();
|
|
64
|
+
if (typeof fn === "function") {
|
|
65
|
+
fn();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
this.queues.delete(id);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
module.exports = HUDTargetQueue;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trap_stevo/filetide",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.33",
|
|
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": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"license": "ISC",
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@trap_stevo/iotide": "^0.0.37",
|
|
33
|
-
"@trap_stevo/iotide-client": "^0.0.
|
|
33
|
+
"@trap_stevo/iotide-client": "^0.0.20",
|
|
34
34
|
"@trap_stevo/legendarybuilderpronodejs-utilities": "^1.0.40",
|
|
35
35
|
"chalk": "^4.1.2",
|
|
36
36
|
"readline": "^1.3.0"
|