@oussema_mili/test-pkg-123 1.1.32

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.
Files changed (49) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +220 -0
  3. package/auth-callback.html +97 -0
  4. package/auth.js +276 -0
  5. package/cli-commands.js +1921 -0
  6. package/containerManager.js +304 -0
  7. package/daemon/agentRunner.js +491 -0
  8. package/daemon/daemonEntry.js +64 -0
  9. package/daemon/daemonManager.js +266 -0
  10. package/daemon/logManager.js +227 -0
  11. package/dist/styles.css +504 -0
  12. package/docker-actions/apps.js +3913 -0
  13. package/docker-actions/config-transformer.js +380 -0
  14. package/docker-actions/containers.js +355 -0
  15. package/docker-actions/general.js +171 -0
  16. package/docker-actions/images.js +1128 -0
  17. package/docker-actions/logs.js +224 -0
  18. package/docker-actions/metrics.js +270 -0
  19. package/docker-actions/registry.js +1100 -0
  20. package/docker-actions/setup-tasks.js +859 -0
  21. package/docker-actions/terminal.js +247 -0
  22. package/docker-actions/volumes.js +713 -0
  23. package/helper-functions.js +193 -0
  24. package/index.html +83 -0
  25. package/index.js +341 -0
  26. package/package.json +82 -0
  27. package/postcss.config.mjs +5 -0
  28. package/scripts/release.sh +212 -0
  29. package/setup/setupWizard.js +403 -0
  30. package/store/agentSessionStore.js +51 -0
  31. package/store/agentStore.js +113 -0
  32. package/store/configStore.js +171 -0
  33. package/store/daemonStore.js +217 -0
  34. package/store/deviceCredentialStore.js +107 -0
  35. package/store/npmTokenStore.js +65 -0
  36. package/store/registryStore.js +329 -0
  37. package/store/setupState.js +147 -0
  38. package/styles.css +1 -0
  39. package/utils/appLogger.js +223 -0
  40. package/utils/deviceInfo.js +98 -0
  41. package/utils/ecrAuth.js +225 -0
  42. package/utils/encryption.js +112 -0
  43. package/utils/envSetup.js +41 -0
  44. package/utils/errorHandler.js +327 -0
  45. package/utils/portUtils.js +59 -0
  46. package/utils/prerequisites.js +323 -0
  47. package/utils/prompts.js +318 -0
  48. package/utils/ssl-certificates.js +256 -0
  49. package/websocket-server.js +415 -0
@@ -0,0 +1,415 @@
1
+ import { WebSocketServer } from "ws";
2
+ import { v4 as uuidv4 } from "uuid";
3
+ import crypto from "crypto";
4
+ import fs from "fs";
5
+ import path from "path";
6
+ import chalk from "chalk";
7
+ import os from "os";
8
+ import { loadConfig } from "./store/configStore.js";
9
+
10
+ // Import handlers
11
+ import containerHandlers from "./docker-actions/containers.js";
12
+ import imageHandlers from "./docker-actions/images.js";
13
+ import volumeHandlers from "./docker-actions/volumes.js";
14
+ import appHandlers from "./docker-actions/apps.js";
15
+ import registryHandlers from "./docker-actions/registry.js";
16
+ import logHandlers from "./docker-actions/logs.js";
17
+ import metricsHandlers from "./docker-actions/metrics.js";
18
+ import terminalHandlers from "./docker-actions/terminal.js";
19
+ import generalHandlers from "./docker-actions/general.js";
20
+ import setupTaskHandlers from "./docker-actions/setup-tasks.js";
21
+ import agentSessionStore from "./store/agentSessionStore.js";
22
+
23
+ // Store active streams and sessions
24
+ const activeStreams = new Map();
25
+ const terminalSessions = new Map();
26
+
27
+ // Load configuration
28
+ const config = loadConfig();
29
+ const AGENT_ROOT_DIR = path.join(os.homedir(), config.agentRootDir);
30
+ const WS_TOKEN_FILE = path.join(AGENT_ROOT_DIR, "ws-token");
31
+ const AUTH_TIMEOUT_MS = 30000; // 30 seconds to authenticate
32
+
33
+ // Heartbeat settings
34
+ const HEARTBEAT_INTERVAL_MS = 30000; // 30 seconds
35
+ const HEARTBEAT_TIMEOUT_MS = 35000; // 35 seconds to respond
36
+
37
+ /**
38
+ * Generate a secure random WebSocket authentication token
39
+ * @returns {string} 64-character hex token
40
+ */
41
+ function generateWsToken() {
42
+ return crypto.randomBytes(32).toString("hex");
43
+ }
44
+
45
+ /**
46
+ * Save WebSocket token to file for local-env app to read
47
+ * @param {string} token - The WebSocket authentication token
48
+ */
49
+ function saveWsToken(token) {
50
+ // Ensure directory exists
51
+ if (!fs.existsSync(AGENT_ROOT_DIR)) {
52
+ fs.mkdirSync(AGENT_ROOT_DIR, { recursive: true, mode: 0o700 });
53
+ }
54
+
55
+ // Write token to file with restricted permissions (owner only)
56
+ fs.writeFileSync(WS_TOKEN_FILE, token, { mode: 0o600 });
57
+ }
58
+
59
+ /**
60
+ * Read WebSocket token from file
61
+ * @returns {string|null} The WebSocket token or null if not found
62
+ */
63
+ function readWsToken() {
64
+ try {
65
+ if (fs.existsSync(WS_TOKEN_FILE)) {
66
+ return fs.readFileSync(WS_TOKEN_FILE, "utf-8").trim();
67
+ }
68
+ } catch (error) {
69
+ console.error("Error reading WS token:", error.message);
70
+ }
71
+ return null;
72
+ }
73
+
74
+ function setupWebSocketServer(
75
+ server,
76
+ clients,
77
+ agentId,
78
+ sessionInfo = {},
79
+ existingToken = null
80
+ ) {
81
+ // Store agent session info for use in handlers
82
+ agentSessionStore.setAgentSessionInfo({
83
+ agentId: agentId,
84
+ userEntityRef: sessionInfo.userEntityRef || null,
85
+ sessionExpiresAt: sessionInfo.expiresAt || null,
86
+ });
87
+
88
+ // Use existing token or generate a new one
89
+ const wsToken = existingToken || generateWsToken();
90
+ if (!existingToken) {
91
+ // Only save if we generated a new token
92
+ saveWsToken(wsToken);
93
+ }
94
+
95
+ const wss = new WebSocketServer({ server });
96
+
97
+ // Set up cleanup timer for inactive terminal sessions
98
+ setInterval(() => {
99
+ terminalHandlers.cleanupInactiveTerminalSessions(terminalSessions);
100
+ }, 15 * 60 * 1000); // Run every 15 minutes
101
+
102
+ // Setup heartbeat interval to check client health
103
+ const heartbeatInterval = setInterval(() => {
104
+ wss.clients.forEach((ws) => {
105
+ if (ws.isAlive === false) {
106
+ // Client didn't respond to ping, terminate
107
+ console.log(chalk.yellow(`Terminating unresponsive client`));
108
+ return ws.terminate();
109
+ }
110
+
111
+ ws.isAlive = false;
112
+ ws.ping();
113
+ });
114
+ }, HEARTBEAT_INTERVAL_MS);
115
+
116
+ // Clean up heartbeat interval when server closes
117
+ wss.on("close", () => {
118
+ clearInterval(heartbeatInterval);
119
+ });
120
+
121
+ wss.on("connection", (ws) => {
122
+ // Generate a unique client ID for each WebSocket connection
123
+ const clientId = uuidv4();
124
+ const connectionTime = Date.now();
125
+ let authenticated = false;
126
+
127
+ // Initialize heartbeat tracking
128
+ ws.isAlive = true;
129
+
130
+ // Handle pong responses (heartbeat acknowledgment)
131
+ ws.on("pong", () => {
132
+ ws.isAlive = true;
133
+ });
134
+
135
+ // Set authentication timeout
136
+ const authTimeout = setTimeout(() => {
137
+ if (!authenticated) {
138
+ ws.send(
139
+ JSON.stringify({
140
+ type: "auth_error",
141
+ error: "Authentication timeout. Connection closed.",
142
+ })
143
+ );
144
+ ws.close(4001, "Authentication timeout");
145
+ }
146
+ }, AUTH_TIMEOUT_MS);
147
+
148
+ // Send authentication challenge
149
+ ws.send(
150
+ JSON.stringify({
151
+ type: "auth_required",
152
+ message:
153
+ "Authentication required. Please send auth message with token.",
154
+ clientId,
155
+ })
156
+ );
157
+
158
+ // Handle messages from client
159
+ ws.on("message", async (message) => {
160
+ let data;
161
+ try {
162
+ data = JSON.parse(message);
163
+ } catch (parseError) {
164
+ console.error("Error parsing message: ", parseError);
165
+ ws.send(
166
+ JSON.stringify({
167
+ type: "error",
168
+ error: "Failed to parse message: " + parseError.message,
169
+ })
170
+ );
171
+ return;
172
+ }
173
+
174
+ // Handle authentication
175
+ if (!authenticated) {
176
+ if (data.type === "auth" && data.token) {
177
+ if (data.token === wsToken) {
178
+ // Authentication successful
179
+ authenticated = true;
180
+ clearTimeout(authTimeout);
181
+
182
+ // Store client info
183
+ clients.set(clientId, {
184
+ ws,
185
+ connectionTime,
186
+ lastActivity: Date.now(),
187
+ isActive: true,
188
+ authenticated: true,
189
+ });
190
+
191
+ console.log(`✅ Client authenticated: ${clientId}`);
192
+
193
+ // Send success response
194
+ ws.send(
195
+ JSON.stringify({
196
+ type: "auth_success",
197
+ clientId,
198
+ agentId: agentId,
199
+ message: "Successfully authenticated with Fenwave Agent",
200
+ })
201
+ );
202
+ } else {
203
+ // Invalid token
204
+ console.log(
205
+ chalk.red(
206
+ `❌ Invalid authentication token from client: ${clientId}`
207
+ )
208
+ );
209
+ ws.send(
210
+ JSON.stringify({
211
+ type: "auth_error",
212
+ error: "Invalid authentication token",
213
+ })
214
+ );
215
+ ws.close(4003, "Invalid authentication token");
216
+ }
217
+ } else {
218
+ // Not an auth message
219
+ ws.send(
220
+ JSON.stringify({
221
+ type: "auth_error",
222
+ error: "Authentication required before sending messages",
223
+ })
224
+ );
225
+ }
226
+ return;
227
+ }
228
+
229
+ // Update last activity time for authenticated clients
230
+ const client = clients.get(clientId);
231
+ if (client) {
232
+ client.lastActivity = Date.now();
233
+ }
234
+
235
+ // Only log received messages in debug mode
236
+ if (process.env.DEBUG === 'true' || process.env.FW_VERBOSE === 'true') {
237
+ console.log(`📨 Received message from client ${clientId}:`, data.action);
238
+ }
239
+
240
+ // Route messages to appropriate handlers
241
+ await routeMessage(ws, clientId, data);
242
+ });
243
+
244
+ // Handle client disconnect
245
+ ws.on("close", () => {
246
+ console.log(`🔴 Client disconnected: ${clientId}`);
247
+ clients.delete(clientId);
248
+
249
+ // Clean up any active streams for this client
250
+ for (const [streamId, stream] of activeStreams.entries()) {
251
+ if (streamId.startsWith(clientId)) {
252
+ if (stream.destroy) stream.destroy();
253
+ activeStreams.delete(streamId);
254
+ }
255
+ }
256
+ });
257
+ });
258
+
259
+ return wss;
260
+ }
261
+
262
+ async function routeMessage(ws, clientId, data) {
263
+ const { action, payload = {} } = data;
264
+
265
+ // Container actions
266
+ if (
267
+ action === "fetchContainers" ||
268
+ action === "startContainer" ||
269
+ action === "stopContainer" ||
270
+ action === "restartContainer" ||
271
+ action === "deleteContainer" ||
272
+ action === "createContainer" ||
273
+ action === "inspectContainer"
274
+ ) {
275
+ return await containerHandlers.handleContainerAction(ws, action, payload);
276
+ }
277
+
278
+ // Image actions
279
+ if (
280
+ action === "fetchImages" ||
281
+ action === "pullImage" ||
282
+ action === "pushImage" ||
283
+ action === "deleteImage" ||
284
+ action === "tagImage" ||
285
+ action === "inspectImage"
286
+ ) {
287
+ return await imageHandlers.handleImageAction(ws, action, payload);
288
+ }
289
+
290
+ // Volume actions
291
+ if (
292
+ action === "fetchVolumes" ||
293
+ action === "createVolume" ||
294
+ action === "deleteVolume" ||
295
+ action === "inspectVolume" ||
296
+ action === "getContainersUsingVolume" ||
297
+ action === "fetchImages" ||
298
+ action === "pullImage" ||
299
+ action === "backupVolume" ||
300
+ action === "listVolumeBackups" ||
301
+ action === "fetchImages" ||
302
+ action === "pullImage" ||
303
+ action === "restoreVolumeFromBackup" ||
304
+ action === "deleteVolumeBackup" ||
305
+ action === "getBackupDownloadUrl"
306
+ ) {
307
+ return await volumeHandlers.handleVolumeAction(ws, action, payload);
308
+ }
309
+
310
+ // App actions
311
+ if (
312
+ action === "fetchApps" ||
313
+ action === "fetchAppVersions" ||
314
+ action === "startApp" ||
315
+ action === "stopApp" ||
316
+ action === "restartApp" ||
317
+ action === "deleteApp" ||
318
+ action === "deleteAppVersions" ||
319
+ action === "createApp" ||
320
+ action === "validateCompose" ||
321
+ action === "syncApp" ||
322
+ action === "changeVersion" ||
323
+ action === "cleanApp" ||
324
+ action === "fetchAppLogs" ||
325
+ action === "fetchLogContent"
326
+ ) {
327
+ return await appHandlers.handleAppAction(ws, action, payload);
328
+ }
329
+
330
+ // Registry actions
331
+ if (
332
+ action === "fetchRegistries" ||
333
+ action === "connectRegistry" ||
334
+ action === "disconnectRegistry" ||
335
+ action === "renameRegistry" ||
336
+ action === "fetchRegistryImages" ||
337
+ action === "setActiveRegistry"
338
+ ) {
339
+ return await registryHandlers.handleRegistryAction(ws, action, payload);
340
+ }
341
+
342
+ // Log actions
343
+ if (
344
+ action === "fetchContainerLogs" ||
345
+ action === "streamContainerLogs" ||
346
+ action === "stopStreamLogs"
347
+ ) {
348
+ return await logHandlers.handleLogAction(
349
+ ws,
350
+ clientId,
351
+ action,
352
+ payload,
353
+ activeStreams
354
+ );
355
+ }
356
+
357
+ // Metrics actions
358
+ if (
359
+ action === "fetchSystemMetrics" ||
360
+ action === "fetchContainerMetrics" ||
361
+ action === "systemMetrics"
362
+ ) {
363
+ return await metricsHandlers.handleMetricsAction(ws, action, payload);
364
+ }
365
+
366
+ // Terminal actions
367
+ if (
368
+ action === "openTerminalSession" ||
369
+ action === "closeTerminalSession" ||
370
+ action === "sendTerminalInput" ||
371
+ action === "resizeTerminal"
372
+ ) {
373
+ return await terminalHandlers.handleTerminalAction(
374
+ ws,
375
+ clientId,
376
+ action,
377
+ payload,
378
+ terminalSessions
379
+ );
380
+ }
381
+
382
+ // General actions
383
+ if (["getAgentInfo", "getDockerInfo", "execInContainer"].includes(action)) {
384
+ return await generalHandlers.handleGeneralAction(
385
+ ws,
386
+ clientId,
387
+ action,
388
+ payload
389
+ );
390
+ }
391
+
392
+ // Setup task actions
393
+ if (
394
+ action === "getSetupTasks" ||
395
+ action === "getSetupStatus" ||
396
+ action === "verifySetupTask" ||
397
+ action === "executeSetupTask" ||
398
+ action === "saveSetupProgress" ||
399
+ action === "clearSetupProgress" ||
400
+ action === "browseDirectory"
401
+ ) {
402
+ return await setupTaskHandlers.handleSetupAction(ws, action, payload);
403
+ }
404
+
405
+ // Unknown action
406
+ ws.send(
407
+ JSON.stringify({
408
+ type: "error",
409
+ requestId: payload.requestId,
410
+ error: `Unknown action: ${action}`,
411
+ })
412
+ );
413
+ }
414
+
415
+ export { setupWebSocketServer, generateWsToken, saveWsToken, readWsToken };