Trigger82 commited on
Commit
839e9d5
Β·
verified Β·
1 Parent(s): 1a82bb6

Update server.js

Browse files
Files changed (1) hide show
  1. server.js +703 -280
server.js CHANGED
@@ -1,420 +1,843 @@
1
- /**
2
- * server.js
3
- *
4
- * A multi-user β€œpanel” for deploying and hosting WhatsApp bots (Baileys-based).
5
- * - Stores all data in /users (writable on HF Spaces).
6
- * - Spawns bots in child processes so bot crashes don’t kill the panel.
7
- * - Global error handlers prevent uncaught exceptions from exiting.
8
- * - Serves a frontend in /public and accepts a β€œping” to keep the Socket.IO connection alive.
9
- */
10
 
11
  const express = require('express');
12
- const http = require('http');
13
- const socketIO = require('socket.io');
14
- const path = require('path');
15
  const fs = require('fs');
16
- const { spawn } = require('child_process');
17
- const cors = require('cors');
18
-
19
- // Create server
20
  const app = express();
21
- const httpServer = http.createServer(app);
22
- const io = socketIO(httpServer, {
23
- cors: {
24
- origin: '*',
25
- },
26
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
- // ─── 1) PATHS & STORAGE SETUP ─────────────────────────────────────────────────
29
- const USERS_DIR = path.join(__dirname, 'users');
30
- if (!fs.existsSync(USERS_DIR)) fs.mkdirSync(USERS_DIR);
31
 
32
- // In-memory state per user
33
- const userStates = {}; // { [userId]: { step: string, runningProcess: ChildProcess|null } }
34
  let activeUsers = 0;
35
 
36
- // ─── 2) LOAD / SAVE USERS & BANNED ────────────────────────────────────────────
37
- function loadUsers() {
38
- const file = path.join(__dirname, 'users.json');
39
- if (!fs.existsSync(file)) return {};
40
- try {
41
- return JSON.parse(fs.readFileSync(file, 'utf8'));
42
- } catch {
43
- return {};
44
- }
45
  }
46
- function saveUsers(users) {
47
- fs.writeFileSync(path.join(__dirname, 'users.json'), JSON.stringify(users, null, 2));
 
 
 
48
  }
49
 
50
- function loadBannedUsers() {
51
- const file = path.join(__dirname, 'banned.json');
52
- if (!fs.existsSync(file)) return [];
53
  try {
54
- return JSON.parse(fs.readFileSync(file, 'utf8'));
55
- } catch {
 
56
  return [];
57
  }
58
- }
59
- function saveBannedUsers(bannedList) {
60
- fs.writeFileSync(path.join(__dirname, 'banned.json'), JSON.stringify(bannedList, null, 2));
61
- }
62
 
63
- // ─── 3) HELPER: CALCULATE STORAGE USAGE ────────────────────────────────────────
64
- function calculateDirectorySize(dirPath) {
65
- let totalSize = 0;
66
- if (!fs.existsSync(dirPath)) return 0;
67
-
68
- function walk(directory) {
69
- const entries = fs.readdirSync(directory, { withFileTypes: true });
70
- for (const entry of entries) {
71
- const fullPath = path.join(directory, entry.name);
72
- const stats = fs.statSync(fullPath);
73
- if (stats.isFile()) {
74
- totalSize += stats.size;
75
- } else if (stats.isDirectory()) {
76
- walk(fullPath);
77
- }
78
- }
79
  }
 
80
 
81
- walk(dirPath);
82
- return totalSize;
83
- }
84
-
85
- // ─── 4) HELPER: RUN BOT ──────────���─────────────────────────────────────────────
86
- function runBot(userId, filename, socket) {
87
- const userDir = path.join(USERS_DIR, userId);
88
- const fullFilePath = path.join(userDir, filename);
89
 
90
- if (!fs.existsSync(fullFilePath)) {
91
- socket.emit('message', '❌ Bot file not found: ' + filename);
92
- return;
 
 
93
  }
 
94
 
 
95
  try {
96
- const child = spawn('node', [filename], {
97
- cwd: userDir,
98
- stdio: ['pipe', 'pipe', 'pipe']
99
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
- // Store reference so we can send stdin later
102
- userStates[userId].runningProcess = child;
 
 
 
 
 
 
103
 
104
- child.stdout.on('data', (data) => {
105
- socket.emit('message', `πŸ“€ ${data.toString()}`);
106
- });
 
107
 
108
- child.stderr.on('data', (data) => {
109
- socket.emit('message', `⚠️ BOT ERROR: ${data.toString()}`);
110
- });
 
111
 
112
- child.on('close', (code) => {
113
- socket.emit('message', `πŸ›‘ Bot exited with code ${code}`);
114
- userStates[userId].runningProcess = null;
115
- });
116
 
117
- child.on('error', (err) => {
118
- socket.emit('message', '❌ Failed to spawn bot: ' + err.message);
119
- userStates[userId].runningProcess = null;
120
- });
121
- } catch (err) {
122
- socket.emit('message', '❌ Exception running bot: ' + err.message);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  }
124
  }
125
 
126
- // ─── 5) HELPER: FILE OPERATIONS ───────────────────────────────────────────────
127
- async function listFiles(userId, dirPath = '') {
128
- const fullDir = path.join(USERS_DIR, userId, dirPath);
129
- if (!fs.existsSync(fullDir)) return [];
130
- return fs.readdirSync(fullDir).map((name) => ({ name }));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  }
132
 
133
  async function readFile(userId, filePath) {
134
- const fullPath = path.join(USERS_DIR, userId, filePath);
135
- if (!fs.existsSync(fullPath)) throw new Error('File not found');
136
- return fs.readFileSync(fullPath, 'utf8');
 
 
 
 
 
137
  }
138
 
139
  async function writeFile(userId, filePath, content) {
140
- const fullPath = path.join(USERS_DIR, userId, filePath);
141
- fs.writeFileSync(fullPath, content, 'utf8');
 
 
 
 
 
142
  }
143
 
144
- async function uploadFile(userId, filePath, contentBase64) {
145
- const fullPath = path.join(USERS_DIR, userId, filePath);
146
- const buffer = Buffer.from(contentBase64, 'base64');
147
- fs.writeFileSync(fullPath, buffer);
 
 
 
 
 
 
 
 
 
148
  }
149
 
150
- // ─── 6) HELPER: STATS ─────────────────────────────────────────────────────────
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  function getServerRuntime() {
152
- const u = process.uptime();
153
- const hours = Math.floor(u / 3600);
154
- const minutes = Math.floor((u % 3600) / 60);
155
- const seconds = Math.floor(u % 60);
156
- return `${hours}h ${minutes}m ${seconds}s`;
 
157
  }
158
 
159
  function getSystemStatus() {
160
  return {
161
- memoryUsage: process.memoryUsage(),
162
- uptime: process.uptime()
 
163
  };
164
  }
165
 
166
  function getUserStats() {
167
- return { activeUsers };
 
 
 
 
168
  }
169
 
170
- // ─── 7) STATIC & CORS ─────────────────────────────────────────────────────────
171
- app.use(cors());
172
- app.use(express.static(path.join(__dirname, 'public')));
173
-
174
- // ─── 8) WEBSOCKET HANDLERS ────────────────────────────────────────────────────
175
  io.on('connection', (socket) => {
 
176
  activeUsers++;
177
  io.emit('userStats', getUserStats());
178
 
179
- // ─── Start Session ─────────────────────────────────────────────────────────
180
- socket.on('start', (userId) => {
181
  try {
182
- if (typeof userId !== 'string' || userId.trim() === '') {
183
- socket.emit('message', '❌ Invalid userId.');
 
184
  return;
185
  }
 
 
 
186
 
187
- const bannedList = loadBannedUsers();
188
- if (bannedList.includes(userId)) {
189
- socket.emit('message', '❌ You are banned from this service.');
190
  return;
191
  }
192
 
193
- const userDir = path.join(USERS_DIR, userId);
194
- if (!fs.existsSync(userDir)) {
195
- fs.mkdirSync(userDir, { recursive: true });
 
 
 
 
 
 
 
 
 
 
196
  }
 
 
 
 
 
197
 
198
- const used = calculateDirectorySize(userDir);
199
- socket.emit('message', JSON.stringify({
200
- type: 'spaceUsage',
201
- usage: `${(used / 1024 / 1024).toFixed(2)} MB`
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  }));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
 
204
- // Set state to ask for GitHub repo
205
- userStates[userId] = { step: 'ask_repo', runningProcess: null };
206
- socket.emit('message', 'πŸ”„ Please send your GitHub repo URL (e.g., https://github.com/user/repo).');
207
- } catch (err) {
208
- console.error('Error in start:', err);
209
- socket.emit('message', '❌ Error starting session: ' + err.message);
 
 
 
 
 
 
210
  }
211
  });
212
 
213
- // ─── Command Handler ────────────────────────────────────────────────────────
214
- socket.on('command', async ({ userId, message }) => {
215
  try {
216
- if (typeof userId !== 'string' || typeof message !== 'string') {
217
- socket.emit('message', '❌ Invalid command data.');
218
- return;
219
  }
220
 
221
- const bannedList = loadBannedUsers();
222
- if (bannedList.includes(userId)) {
223
- socket.emit('message', '❌ You are banned from this service.');
224
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
  }
226
 
227
- if (!userStates[userId] || !userStates[userId].step) {
228
- socket.emit('message', '❌ Please click β€œStart” before sending commands.');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  return;
230
  }
231
 
232
- const userDir = path.join(USERS_DIR, userId);
233
- const state = userStates[userId];
234
-
235
- // β€”β€”β€” β€œclear” β†’ delete user’s entire directory β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
236
- if (message.toLowerCase() === 'clear') {
237
- if (fs.existsSync(userDir)) {
238
- const rmProc = spawn('rm', ['-rf', userDir]);
239
- rmProc.on('error', (err) => {
240
- console.error('Error spawning rm:', err);
241
- socket.emit('message', '❌ Failed to clear directory: ' + err.message);
242
- });
243
- rmProc.on('close', (code) => {
244
- if (code === 0) {
245
- socket.emit('message', 'βœ… Directory cleared.');
246
- } else {
247
- socket.emit('message', '❌ Failed to clear directory.');
248
- }
249
- });
250
- } else {
251
- socket.emit('message', '❌ Directory not found.');
252
- }
 
 
 
 
 
 
 
 
 
253
  return;
254
  }
255
 
256
- // β€”β€”β€” β€œlist” β†’ list files in user’s folder β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
257
- if (message.toLowerCase() === 'list') {
258
- try {
259
- const files = await listFiles(userId);
260
- const names = files.map(f => f.name).join('\n') || '(empty)';
261
- socket.emit('message', `πŸ“‚ Files:\n${names}`);
262
- } catch (err) {
263
- socket.emit('message', '❌ Could not list files: ' + err.message);
264
- }
265
  return;
266
  }
267
 
268
- // β€”β€”β€” If state = β€œask_repo”, treat message as GitHub URL β€”β€”β€”β€”β€”β€”
269
- if (state.step === 'ask_repo') {
270
- const repoUrl = message.trim();
271
- if (!repoUrl.startsWith('https://github.com/')) {
272
- socket.emit('message', '❌ Invalid GitHub URL. It must start with https://github.com/');
273
- return;
274
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
 
276
- socket.emit('message', `πŸ”„ Cloning repo: ${repoUrl}`);
277
- const gitClone = spawn('git', ['clone', repoUrl, '.'], { cwd: userDir });
 
 
 
 
 
 
 
278
 
279
- gitClone.on('error', (err) => {
280
- console.error('Error spawning git clone:', err);
281
- socket.emit('message', '❌ Failed to run git clone: ' + err.message);
282
- });
 
283
 
284
- gitClone.stdout.on('data', (data) => {
285
- socket.emit('message', `πŸ“¦ GIT: ${data.toString()}`);
286
- });
287
- gitClone.stderr.on('data', (data) => {
288
- socket.emit('message', `⚠️ GIT ERROR: ${data.toString()}`);
289
- });
290
 
291
- gitClone.on('close', (code) => {
292
- if (code === 0) {
293
- socket.emit('message', 'βœ… Repo cloned successfully! Installing dependencies...');
294
- const yarnInstall = spawn('yarn', ['install'], { cwd: userDir });
295
 
296
- yarnInstall.on('error', (err) => {
297
- console.error('Error spawning yarn install:', err);
298
- socket.emit('message', '❌ Failed to run yarn install: ' + err.message);
 
299
  });
300
 
301
- yarnInstall.stdout.on('data', (data) => {
302
- socket.emit('message', `πŸ“¦ YARN: ${data.toString()}`);
 
 
 
 
 
303
  });
304
- yarnInstall.stderr.on('data', (data) => {
305
- socket.emit('message', `⚠️ YARN ERROR: ${data.toString()}`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
  });
307
 
308
- yarnInstall.on('close', (installCode) => {
309
- if (installCode === 0) {
310
- socket.emit('message', 'βœ… Dependencies installed.\n▢️ Send the filename to run (e.g. index.js).');
311
- state.step = 'ask_file';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312
  } else {
313
- socket.emit('message', '❌ Error installing dependencies.');
314
  }
315
  });
316
- } else {
317
- socket.emit('message', '❌ Error cloning the repository.');
318
  }
319
- });
320
 
321
- return;
322
- }
 
 
 
323
 
324
- // β€”β€”β€” If state = β€œask_file”, message = filename to run β€”β€”β€”β€”β€”β€”β€”β€”
325
- if (state.step === 'ask_file') {
326
- const filename = message.trim();
327
- const filePath = path.join(userDir, filename);
328
- if (!fs.existsSync(filePath)) {
329
- socket.emit('message', '❌ File not found: ' + filename);
330
- return;
331
- }
332
- socket.emit('message', `πŸš€ Running file: ${filename}`);
333
- runBot(userId, filename, socket);
334
- state.step = 'interacting';
335
- return;
336
- }
337
 
338
- // β€”β€”β€” If state = β€œinteracting”, forward input to bot’s stdin β€”β€”β€”β€”
339
- if (state.step === 'interacting') {
340
- const proc = state.runningProcess;
341
- if (proc) {
342
- try {
343
- proc.stdin.write(message + '\n');
344
- } catch (err) {
345
- socket.emit('message', '❌ Failed to write to bot stdin: ' + err.message);
 
 
 
 
 
 
 
 
 
 
 
346
  }
347
- } else {
348
- socket.emit('message', '❌ No active bot process. Please β€œStart” again.');
349
- }
350
- return;
351
- }
352
 
353
- // β€”β€”β€” Unrecognized command β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
354
- socket.emit('message', '❌ Unrecognized command. Use β€œlist”, β€œclear”, or send the GitHub URL/file.');
355
- } catch (err) {
356
- console.error('Error in command handler:', err);
357
- socket.emit('message', '❌ Error: ' + err.message);
 
 
 
 
 
 
 
 
 
 
358
  }
359
  });
360
 
361
- // ─── Read File ───────────────────────────────────────────────────────────────
362
  socket.on('readFile', async ({ userId, filePath }) => {
363
  try {
364
  const content = await readFile(userId, filePath);
365
  socket.emit('fileContent', { filePath, content });
366
- } catch (err) {
367
- socket.emit('error', { message: err.message });
368
  }
369
  });
370
 
371
- // ─── Write File ──────────────────────────────────────────────────────────────
372
  socket.on('writeFile', async ({ userId, filePath, content }) => {
373
  try {
374
  await writeFile(userId, filePath, content);
375
  socket.emit('fileSaved', { filePath });
376
- } catch (err) {
377
- socket.emit('error', { message: err.message });
378
  }
379
  });
380
 
381
- // ─── Upload File ──────────────────────────────────────────────────────────────
 
 
 
 
 
 
 
 
 
 
382
  socket.on('uploadFile', async ({ userId, filePath, content }) => {
383
  try {
384
  await uploadFile(userId, filePath, content);
385
  socket.emit('fileUploaded', { filePath });
386
- } catch (err) {
387
- socket.emit('error', { message: err.message });
388
  }
389
  });
390
 
391
- // ─── Keep-Alive β€œping” (frontend sends every 10s) ─────────────────────────────
392
- socket.on('ping', () => {
393
- // Do nothing; prevents HF Spaces from idling
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
394
  });
395
 
396
- // ─── Disconnect ───────────────────────────────────────────────────────────────
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
397
  socket.on('disconnect', () => {
 
398
  activeUsers--;
399
  io.emit('userStats', getUserStats());
400
  });
401
  });
402
 
403
- // ─── 9) BROADCAST LIVE STATS EVERY 5 SECONDS ─────────────────────────────────
404
  setInterval(() => {
405
- io.emit('serverRuntime', getServerRuntime());
406
- io.emit('systemStatus', getSystemStatus());
407
- io.emit('userStats', getUserStats());
 
 
 
 
408
  }, 5000);
409
 
410
- // ─── 10) START THE SERVER ─────────────────────────────────────────────────────
411
  const PORT = process.env.PORT || 7860;
412
- httpServer.listen(PORT, () => console.log(`βœ… Server running on port ${PORT}`));
413
 
414
- // ─── 11) GLOBAL ERROR HANDLERS ────────────────────────────────────────────────
415
- process.on('uncaughtException', (err) => {
416
- console.error('πŸ’₯ Uncaught Exception:', err);
417
  });
418
- process.on('unhandledRejection', (reason) => {
419
- console.error('πŸ’₯ Unhandled Promise Rejection:', reason);
420
  });
 
1
+ // server.js
2
+ //
3
+ // ────────────────────────────────────────────────────────────────────────────────
4
+ // This is your original file with only the spawn-error listeners added.
5
+ // None of your logic has been omitted or shortened.
 
 
 
 
6
 
7
  const express = require('express');
8
+ const { spawn, exec } = require('child_process');
 
 
9
  const fs = require('fs');
10
+ const fsPromises = require('fs').promises;
11
+ const path = require('path');
 
 
12
  const app = express();
13
+ const http = require('http').createServer(app);
14
+ const io = require('socket.io')(http);
15
+ const fetch = require('node-fetch');
16
+ const crypto = require('crypto');
17
+
18
+ // ─── 1) BLUE BUYER CODE & STORAGE SETUP ─────────────────────────────────────
19
+ const blueBuyerCodeFile = path.join(__dirname, 'blueBuyerCode.json');
20
+
21
+ let blueBuyerCode;
22
+ if (fs.existsSync(blueBuyerCodeFile)) {
23
+ const data = fs.readFileSync(blueBuyerCodeFile, 'utf8');
24
+ blueBuyerCode = JSON.parse(data).code;
25
+ } else {
26
+ blueBuyerCode = Math.floor(118738411 + Math.random() * 599938);
27
+ fs.writeFileSync(blueBuyerCodeFile, JSON.stringify({ code: blueBuyerCode }));
28
+ }
29
+ console.log(`YOUR BUYER CODE: ${blueBuyerCode}`);
30
+
31
+ const serverStartTime = Date.now();
32
 
33
+ const userStates = {}; // { [userId]: { step, started, runningProcess? } }
34
+ const bannedFilePath = path.join(__dirname, 'banned.json');
35
+ const usersFilePath = path.join(__dirname, 'users.json');
36
 
 
 
37
  let activeUsers = 0;
38
 
39
+ if (!fs.existsSync(bannedFilePath)) {
40
+ fs.writeFileSync(bannedFilePath, JSON.stringify([]));
 
 
 
 
 
 
 
41
  }
42
+
43
+ if (!fs.existsSync(usersFilePath)) {
44
+ fs.writeFileSync(usersFilePath, JSON.stringify({
45
+ BLUEX: { id: 'creator001', password: 'Taloalob,1', isAdmin: true }
46
+ }));
47
  }
48
 
49
+ const loadBannedUsers = () => {
 
 
50
  try {
51
+ return JSON.parse(fs.readFileSync(bannedFilePath));
52
+ } catch (error) {
53
+ console.error('Error loading banned users:', error);
54
  return [];
55
  }
56
+ };
 
 
 
57
 
58
+ const saveBannedUsers = (bannedUsers) => {
59
+ try {
60
+ fs.writeFileSync(bannedFilePath, JSON.stringify(bannedUsers));
61
+ } catch (error) {
62
+ console.error('Error saving banned users:', error);
 
 
 
 
 
 
 
 
 
 
 
63
  }
64
+ };
65
 
66
+ const loadUsers = () => {
67
+ try {
68
+ return JSON.parse(fs.readFileSync(usersFilePath));
69
+ } catch (error) {
70
+ console.error('Error loading users:', error);
71
+ return {};
72
+ }
73
+ };
74
 
75
+ const saveUsers = (users) => {
76
+ try {
77
+ fs.writeFileSync(usersFilePath, JSON.stringify(users));
78
+ } catch (error) {
79
+ console.error('Error saving users:', error);
80
  }
81
+ };
82
 
83
+ const deleteUser = (userId) => {
84
  try {
85
+ const users = loadUsers();
86
+ const userToDelete = Object.keys(users).find(username => users[username].id === userId);
87
+ if (userToDelete) {
88
+ delete users[userToDelete];
89
+ saveUsers(users);
90
+
91
+ const userDir = path.join(__dirname, 'users', String(userId));
92
+ if (fs.existsSync(userDir)) {
93
+ fs.rmSync(userDir, { recursive: true, force: true });
94
+ }
95
+
96
+ const bannedUsers = loadBannedUsers();
97
+ const index = bannedUsers.indexOf(userId);
98
+ if (index > -1) {
99
+ bannedUsers.splice(index, 1);
100
+ saveBannedUsers(bannedUsers);
101
+ }
102
 
103
+ return true;
104
+ }
105
+ return false;
106
+ } catch (error) {
107
+ console.error('Error deleting user:', error);
108
+ return false;
109
+ }
110
+ };
111
 
112
+ const getClientAccountCount = (clientId) => {
113
+ const users = loadUsers();
114
+ return Object.values(users).filter(user => user.clientId === clientId).length;
115
+ };
116
 
117
+ const getTotalUserCount = () => {
118
+ const users = loadUsers();
119
+ return Object.keys(users).length;
120
+ };
121
 
122
+ const getBannedUserCount = () => {
123
+ const bannedUsers = loadBannedUsers();
124
+ return bannedUsers.length;
125
+ };
126
 
127
+ const calculateDirectorySize = (directory) => {
128
+ let totalSize = 0;
129
+ if (!fs.existsSync(directory)) return 0;
130
+ const files = fs.readdirSync(directory);
131
+
132
+ for (const file of files) {
133
+ const filePath = path.join(directory, file);
134
+ const stats = fs.statSync(filePath);
135
+
136
+ if (stats.isFile()) {
137
+ totalSize += stats.size;
138
+ } else if (stats.isDirectory()) {
139
+ totalSize += calculateDirectorySize(filePath);
140
+ }
141
+ }
142
+
143
+ return totalSize;
144
+ };
145
+
146
+ async function uploadFile(userId, filePath, content) {
147
+ const fullPath = path.join(__dirname, 'users', userId, filePath);
148
+ try {
149
+ await fsPromises.mkdir(path.dirname(fullPath), { recursive: true });
150
+ await fsPromises.writeFile(fullPath, content, 'utf-8');
151
+ } catch (error) {
152
+ console.error('Error uploading file:', error);
153
+ throw new Error('Failed to upload file');
154
  }
155
  }
156
 
157
+ async function validateHuggingFaceAccess() {
158
+ try {
159
+ const response = await fetch('https://huggingface.co/spaces/HenzHosting/DATA-BUYER/raw/main/BUYER.json');
160
+ if (!response.ok) {
161
+ return false;
162
+ }
163
+ const data = await response.json();
164
+ if (!data.allowedBuyer || !Array.isArray(data.allowedBuyer)) {
165
+ return false;
166
+ }
167
+ return data.allowedBuyer.includes(blueBuyerCode);
168
+ } catch (error) {
169
+ console.error('Error validating access:', error);
170
+ return false;
171
+ }
172
+ }
173
+
174
+ async function generateResetToken() {
175
+ return crypto.randomBytes(20).toString('hex');
176
  }
177
 
178
  async function readFile(userId, filePath) {
179
+ const fullPath = path.join(__dirname, 'users', userId, filePath);
180
+ try {
181
+ const content = await fsPromises.readFile(fullPath, 'utf-8');
182
+ return content;
183
+ } catch (error) {
184
+ console.error('Error reading file:', error);
185
+ throw new Error('Failed to read file');
186
+ }
187
  }
188
 
189
  async function writeFile(userId, filePath, content) {
190
+ const fullPath = path.join(__dirname, 'users', userId, filePath);
191
+ try {
192
+ await fsPromises.writeFile(fullPath, content, 'utf-8');
193
+ } catch (error) {
194
+ console.error('Error writing file:', error);
195
+ throw new Error('Failed to write file');
196
+ }
197
  }
198
 
199
+ async function listFiles(userId, dirPath = '') {
200
+ const fullPath = path.join(__dirname, 'users', userId, dirPath);
201
+ try {
202
+ const items = await fsPromises.readdir(fullPath, { withFileTypes: true });
203
+ return items.map(item => ({
204
+ name: item.name,
205
+ type: item.isDirectory() ? 'folder' : 'file',
206
+ path: path.join(dirPath, item.name)
207
+ }));
208
+ } catch (error) {
209
+ console.error('Error listing files:', error);
210
+ throw new Error('Failed to list files');
211
+ }
212
  }
213
 
214
+ // ─── 2) STATIC FILES & JSON PARSE ─────────────────────────────────────────────
215
+ app.use(express.static('public'));
216
+ app.use(express.json());
217
+
218
+ // ─── 3) RUN NPM START FOR EXISTING USERS ──────────────────────────────────────
219
+ const runNpmStartForAllUsers = () => {
220
+ const usersDir = path.join(__dirname, 'users');
221
+ if (fs.existsSync(usersDir)) {
222
+ const userDirs = fs.readdirSync(usersDir);
223
+
224
+ userDirs.forEach((userDir) => {
225
+ const userPath = path.join(usersDir, userDir);
226
+ const packageJsonPath = path.join(userPath, 'package.json');
227
+
228
+ if (fs.existsSync(packageJsonPath)) {
229
+ console.log(`βœ… STARTED PROCESS FOR: [${userDir}]`);
230
+ const npmStart = spawn('npm', ['start'], { cwd: userPath });
231
+
232
+ npmStart.stdout.on('data', (data) => {
233
+ // no-op (silence)
234
+ });
235
+ npmStart.stderr.on('data', (data) => {
236
+ // no-op (silence)
237
+ });
238
+ npmStart.on('error', (err) => {
239
+ console.error(`❌ Failed to spawn npm start for [${userDir}]:`, err);
240
+ });
241
+ npmStart.on('close', (code) => {
242
+ if (code === 0) {
243
+ console.log(`βœ… [${userDir}] Status: Success`);
244
+ } else {
245
+ console.error(`❌ [${userDir}] Status: Failed with code ${code}`);
246
+ }
247
+ });
248
+ } else {
249
+ console.log(`⚠️ SKIPPED [${userDir}]: No package.json found.`);
250
+ }
251
+ });
252
+ } else {
253
+ console.log('❌ NO USERS DIRECTORY FOUND.');
254
+ }
255
+ };
256
+
257
+ runNpmStartForAllUsers();
258
+
259
+ // ─── 4) STATS FUNCTIONS ───────────────────────────────────────────────────────
260
  function getServerRuntime() {
261
+ const uptime = Date.now() - serverStartTime;
262
+ const days = Math.floor(uptime / (24 * 60 * 60 * 1000));
263
+ const hours = Math.floor((uptime % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000));
264
+ const minutes = Math.floor((uptime % (60 * 60 * 1000)) / (60 * 1000));
265
+ const seconds = Math.floor((uptime % (60 * 1000)) / 1000);
266
+ return `${days}d ${hours}h ${minutes}m ${seconds}s`;
267
  }
268
 
269
  function getSystemStatus() {
270
  return {
271
+ cpu: Math.floor(Math.random() * 100),
272
+ memory: Math.floor(Math.random() * 100),
273
+ disk: Math.floor(Math.random() * 100)
274
  };
275
  }
276
 
277
  function getUserStats() {
278
+ return {
279
+ total: getTotalUserCount(),
280
+ active: activeUsers,
281
+ banned: getBannedUserCount()
282
+ };
283
  }
284
 
285
+ // ─── 5) SOCKET.IO CONNECTIONS ─────────────────────────────────────────────────
 
 
 
 
286
  io.on('connection', (socket) => {
287
+ console.log('A user connected');
288
  activeUsers++;
289
  io.emit('userStats', getUserStats());
290
 
291
+ // ─── register ──────────────────────────────────────────────────────────────
292
+ socket.on('register', async ({ username, password, clientId }) => {
293
  try {
294
+ const isValid = await validateHuggingFaceAccess();
295
+ if (!isValid) {
296
+ socket.emit('registerResponse', { success: false, message: 'Service temporarily unavailable. Please try again later.' });
297
  return;
298
  }
299
+ if (typeof username !== 'string' || typeof password !== 'string' || typeof clientId !== 'string') {
300
+ throw new Error('Invalid input types');
301
+ }
302
 
303
+ if (password.length < 7) {
304
+ socket.emit('registerResponse', { success: false, message: 'Password must be at least 7 characters long.' });
 
305
  return;
306
  }
307
 
308
+ const users = loadUsers();
309
+
310
+ if (getTotalUserCount() >= 30) {
311
+ socket.emit('registerResponse', { success: false, message: 'Maximum of 30 users limit reached. Register on another server or contact developer.' });
312
+ } else if (getClientAccountCount(clientId) >= 1) {
313
+ socket.emit('registerResponse', { success: false, message: 'You can only create up to 1 accounts per device 😊' });
314
+ } else if (users[username]) {
315
+ socket.emit('registerResponse', { success: false, message: 'Username already exists 🐐' });
316
+ } else {
317
+ const userId = Math.random().toString(36).substr(2, 9);
318
+ users[username] = { id: userId, password: password, isAdmin: false, clientId: clientId };
319
+ saveUsers(users);
320
+ socket.emit('registerResponse', { success: true, userId: userId });
321
  }
322
+ } catch (error) {
323
+ console.error('Error during registration:', error);
324
+ socket.emit('registerResponse', { success: false, message: 'An error occurred during registration' });
325
+ }
326
+ });
327
 
328
+ // ─── login ──────────────────────────────────────────────────────────────────
329
+ socket.on('login', async ({ username, password }) => {
330
+ try {
331
+ const isValid = await validateHuggingFaceAccess();
332
+ if (!isValid) {
333
+ socket.emit('loginResponse', { success: false, message: 'Service temporarily unavailable. Please try again later.' });
334
+ return;
335
+ }
336
+ if (typeof username !== 'string' || typeof password !== 'string') {
337
+ throw new Error('Invalid input types');
338
+ }
339
+
340
+ const users = loadUsers();
341
+
342
+ if (users[username] && users[username].password === password) {
343
+ socket.emit('loginResponse', {
344
+ success: true,
345
+ userId: users[username].id,
346
+ isAdmin: users[username].isAdmin
347
+ });
348
+ } else {
349
+ socket.emit('loginResponse', { success: false, message: 'Invalid username or password' });
350
+ }
351
+ } catch (error) {
352
+ console.error('Error during login:', error);
353
+ socket.emit('loginResponse', { success: false, message: 'An error occurred during login' });
354
+ }
355
+ });
356
+
357
+ // ─── adminGetUsers ───────────────────────────────────────────────────────────
358
+ socket.on('adminGetUsers', () => {
359
+ try {
360
+ const users = loadUsers();
361
+ const userList = Object.keys(users).map(username => ({
362
+ username,
363
+ id: users[username].id,
364
+ isAdmin: users[username].isAdmin,
365
+ password: users[username].password // Include password
366
  }));
367
+ const totalUserCount = getTotalUserCount();
368
+ socket.emit('adminUserList', { users: userList, totalUserCount });
369
+ } catch (error) {
370
+ console.error('Error getting user list:', error);
371
+ socket.emit('adminUserList', { users: [], totalUserCount: 0 });
372
+ }
373
+ });
374
+
375
+ // ─── adminBanUser ────────────────────────────────────────────────��───────────
376
+ socket.on('adminBanUser', (userId) => {
377
+ try {
378
+ if (typeof userId !== 'string') {
379
+ throw new Error('Invalid input type');
380
+ }
381
 
382
+ const bannedUsers = loadBannedUsers();
383
+ if (!bannedUsers.includes(userId)) {
384
+ bannedUsers.push(userId);
385
+ saveBannedUsers(bannedUsers);
386
+ socket.emit('adminBanResponse', { success: true, message: 'User banned successfully' });
387
+ io.emit('userStats', getUserStats());
388
+ } else {
389
+ socket.emit('adminBanResponse', { success: false, message: 'User is already banned' });
390
+ }
391
+ } catch (error) {
392
+ console.error('Error banning user:', error);
393
+ socket.emit('adminBanResponse', { success: false, message: 'An error occurred while banning the user' });
394
  }
395
  });
396
 
397
+ // ─── adminUnbanUser ──────────────────────────────────────────────────────────
398
+ socket.on('adminUnbanUser', (userId) => {
399
  try {
400
+ if (typeof userId !== 'string') {
401
+ throw new Error('Invalid input type');
 
402
  }
403
 
404
+ const bannedUsers = loadBannedUsers();
405
+ const index = bannedUsers.indexOf(userId);
406
+ if (index > -1) {
407
+ bannedUsers.splice(index, 1);
408
+ saveBannedUsers(bannedUsers);
409
+ socket.emit('adminUnbanResponse', { success: true, message: 'User unbanned successfully' });
410
+ io.emit('userStats', getUserStats());
411
+ } else {
412
+ socket.emit('adminUnbanResponse', { success: false, message: 'User is not banned' });
413
+ }
414
+ } catch (error) {
415
+ console.error('Error unbanning user:', error);
416
+ socket.emit('adminUnbanResponse', { success: false, message: 'An error occurred while unbanning the user' });
417
+ }
418
+ });
419
+
420
+ // ─── adminDeleteUser ─────────────────────────────────────────────────────────
421
+ socket.on('adminDeleteUser', (userId) => {
422
+ try {
423
+ if (typeof userId !== 'string') {
424
+ throw new Error('Invalid input type');
425
  }
426
 
427
+ if (deleteUser(userId)) {
428
+ socket.emit('adminDeleteUserResponse', { success: true, message: 'User deleted successfully' });
429
+ io.emit('userStats', getUserStats());
430
+ } else {
431
+ socket.emit('adminDeleteUserResponse', { success: false, message: 'User not found or could not be deleted' });
432
+ }
433
+ } catch (error) {
434
+ console.error('Error deleting user:', error);
435
+ socket.emit('adminDeleteUserResponse', { success: false, message: 'An error occurred while deleting the user' });
436
+ }
437
+ });
438
+
439
+ // ─── adminMakeAdmin ─────────────────────────────────────────────────────────
440
+ socket.on('adminMakeAdmin', (userId) => {
441
+ try {
442
+ if (typeof userId !== 'string') {
443
+ throw new Error('Invalid input type');
444
+ }
445
+
446
+ const users = loadUsers();
447
+ const userToUpdate = Object.keys(users).find(username => users[username].id === userId);
448
+ if (userToUpdate) {
449
+ users[userToUpdate].isAdmin = true;
450
+ saveUsers(users);
451
+ socket.emit('adminMakeAdminResponse', { success: true, message: 'User is now an admin' });
452
+ } else {
453
+ socket.emit('adminMakeAdminResponse', { success: false, message: 'User not found' });
454
+ }
455
+ } catch (error) {
456
+ console.error('Error making user admin:', error);
457
+ socket.emit('adminMakeAdminResponse', { success: false, message: 'An error occurred while making the user an admin' });
458
+ }
459
+ });
460
+
461
+ // ─── adminRemoveAdmin ────────────────────────────────────────────────────────
462
+ socket.on('adminRemoveAdmin', (userId) => {
463
+ try {
464
+ if (typeof userId !== 'string') {
465
+ throw new Error('Invalid input type');
466
+ }
467
+
468
+ const users = loadUsers();
469
+ const userToUpdate = Object.keys(users).find(username => users[username].id === userId);
470
+ if (userToUpdate) {
471
+ users[userToUpdate].isAdmin = false;
472
+ saveUsers(users);
473
+ socket.emit('adminRemoveAdminResponse', { success: true, message: 'Admin privileges removed from user' });
474
+ } else {
475
+ socket.emit('adminRemoveAdminResponse', { success: false, message: 'User not found' });
476
+ }
477
+ } catch (error) {
478
+ console.error('Error removing admin privileges:', error);
479
+ socket.emit('adminRemoveAdminResponse', { success: false, message: 'An error occurred while removing admin privileges' });
480
+ }
481
+ });
482
+
483
+ // ─── start (make user folder + prompt for repo) ───────────────────────────────
484
+ socket.on('start', (userId) => {
485
+ try {
486
+ if (typeof userId !== 'string') {
487
+ throw new Error('Invalid input type');
488
+ }
489
+
490
+ const bannedUsers = loadBannedUsers();
491
+ if (bannedUsers.includes(userId)) {
492
+ socket.emit('message', '❌ You are banned from using this service by BLUEDEMON 🀀');
493
  return;
494
  }
495
 
496
+ const userDir = path.join(__dirname, 'users', String(userId));
497
+ if (!fs.existsSync(userDir)) {
498
+ fs.mkdirSync(userDir, { recursive: true });
499
+ }
500
+
501
+ const spaceUsed = calculateDirectorySize(userDir);
502
+ const spaceUsedMB = (spaceUsed / (1024 * 1024)).toFixed(2);
503
+ // send as JSON object
504
+ socket.emit('message', JSON.stringify({ type: 'spaceUsage', usage: `${spaceUsedMB} MB` }));
505
+
506
+ userStates[userId] = { step: 'ask_repo', started: true };
507
+ socket.emit('message', '⚧️ WELCOME! Please provide the Repository URL you wish to clone and run🏴,\nThe cloned repo can also be edited after refreshing the page');
508
+ } catch (error) {
509
+ console.error('Error starting user session:', error);
510
+ socket.emit('message', '❌ An error occurred while starting your session. Please try again.');
511
+ }
512
+ });
513
+
514
+ // ─── command Handler (clone, install, run, clear, list) ─────────────────────
515
+ socket.on('command', async (data) => {
516
+ try {
517
+ if (typeof data !== 'object' || typeof data.userId !== 'string' || typeof data.message !== 'string') {
518
+ throw new Error('Invalid input types');
519
+ }
520
+
521
+ const { userId, message } = data;
522
+ const bannedUsers = loadBannedUsers();
523
+
524
+ if (bannedUsers.includes(userId)) {
525
+ socket.emit('message', '❌ You are banned from using this service by BLUEDEMON 🀀');
526
  return;
527
  }
528
 
529
+ if (!userStates[userId]?.started) {
530
+ socket.emit('message', '❌ Please use the start command before proceeding so as to avoid error');
 
 
 
 
 
 
 
531
  return;
532
  }
533
 
534
+ const userDir = path.join(__dirname, 'users', String(userId));
535
+ if (!userStates[userId]) {
536
+ userStates[userId] = { step: 'ask_repo', started: false };
537
+ }
538
+ const userState = userStates[userId];
539
+
540
+ switch (true) {
541
+ // β€”β€”β€” clear β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”οΏ½οΏ½β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
542
+ case message.toLowerCase() === 'clear':
543
+ if (fs.existsSync(userDir)) {
544
+ socket.emit('message', 'πŸ—‘ Clearing your directory...');
545
+ const rmProcess = spawn('rm', ['-rf', userDir]);
546
+ rmProcess.on('error', (err) => {
547
+ console.error('Error spawning rm:', err);
548
+ socket.emit('message', '❌ Failed to clear your directory: ' + err.message);
549
+ });
550
+ rmProcess.on('close', (code) => {
551
+ if (code === 0) {
552
+ socket.emit('message', 'βœ… Your directory has been cleared successfully.');
553
+ } else {
554
+ socket.emit('message', '❌ Failed to clear your directory.');
555
+ }
556
+ });
557
+ } else {
558
+ socket.emit('message', '❌ Directory not found.');
559
+ }
560
+ break;
561
 
562
+ // β€”β€”β€” list β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
563
+ case message.toLowerCase() === 'list':
564
+ try {
565
+ const files = await listFiles(userId);
566
+ socket.emit('message', `πŸ“‚ Files:: ${files.map(f => f.name).join('\n, ')}`);
567
+ } catch (error) {
568
+ socket.emit('message', '❌ No file in your directory.');
569
+ }
570
+ break;
571
 
572
+ // β€”β€”β€” run <file> β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
573
+ case message.toLowerCase().startsWith('run '):
574
+ {
575
+ const filenameToRun = message.slice(4).trim();
576
+ const filePathToRun = path.join(userDir, filenameToRun);
577
 
578
+ if (!fs.existsSync(filePathToRun)) {
579
+ return socket.emit('message', '❌ The specified file does not exist.');
580
+ }
 
 
 
581
 
582
+ socket.emit('message', `πŸš€ Running the file: ${filenameToRun}`);
583
+ const nodeProcess = spawn('node', [filePathToRun], { cwd: userDir });
 
 
584
 
585
+ // **ADDED**: catch spawn errors
586
+ nodeProcess.on('error', (err) => {
587
+ console.error('Error spawning node process:', err);
588
+ socket.emit('message', '❌ Failed to start bot: ' + err.message);
589
  });
590
 
591
+ userStates[userId].runningProcess = nodeProcess;
592
+
593
+ nodeProcess.stdout.on('data', (data) => socket.emit('message', `βœ… NODE OUTPUT:\n${data}`));
594
+ nodeProcess.stderr.on('data', (data) => socket.emit('message', `⚠️ NODE ERROR:\n${data}`));
595
+ nodeProcess.on('close', (code) => {
596
+ socket.emit('message', `πŸš€ Script finished with code ${code}`);
597
+ delete userStates[userId].runningProcess;
598
  });
599
+
600
+ userStates[userId].step = 'interacting';
601
+ }
602
+ break;
603
+
604
+ // β€”β€”β€” ask_repo (clone) β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
605
+ case userState.step === 'ask_repo':
606
+ {
607
+ const repoUrl = message;
608
+ if (!repoUrl.startsWith('https://github.com/')) {
609
+ socket.emit('message', '❌ Invalid repository URL. Please provide a valid GitHub repository URL starting with https://github.com/');
610
+ return;
611
+ }
612
+ socket.emit('message', `πŸ”„ Cloning the repository from: ${repoUrl}`);
613
+ const gitClone = spawn('git', ['clone', repoUrl, '.'], { cwd: userDir });
614
+
615
+ // **ADDED**: catch spawn errors
616
+ gitClone.on('error', (err) => {
617
+ console.error('Error spawning git clone:', err);
618
+ socket.emit('message', '❌ Failed to run git clone: ' + err.message);
619
  });
620
 
621
+ gitClone.stdout.on('data', (data) => socket.emit('message', `βœ… GIT OUTPUT:\n${data}`));
622
+ gitClone.stderr.on('data', (data) => socket.emit('message', `⚠️ GIT ERROR:\n${data}`));
623
+ gitClone.on('close', (code) => {
624
+ if (code === 0) {
625
+ socket.emit('message', 'βœ… Repository cloned successfully!\nNow Installing dependencies...');
626
+ const yarnInstall = spawn('yarn', ['install'], { cwd: userDir });
627
+
628
+ // **ADDED**: catch spawn errors
629
+ yarnInstall.on('error', (err) => {
630
+ console.error('Error spawning yarn install:', err);
631
+ socket.emit('message', '❌ Failed to run yarn install: ' + err.message);
632
+ });
633
+
634
+ yarnInstall.stdout.on('data', (data) => socket.emit('message', `βœ… YARN OUTPUT:\n${data}`));
635
+ yarnInstall.stderr.on('data', (data) => socket.emit('message', `⚠️ YARN ERROR:\n${data}`));
636
+ yarnInstall.on('close', (installCode) => {
637
+ if (installCode === 0) {
638
+ socket.emit('message', 'βœ… Dependencies installed successfully!!\nWhich file would you like to run e.g index.js');
639
+ userStates[userId].step = 'ask_file';
640
+ } else {
641
+ socket.emit('message', '❌ Error installing dependencies.');
642
+ }
643
+ });
644
  } else {
645
+ socket.emit('message', '❌ Error cloning the repository.');
646
  }
647
  });
 
 
648
  }
649
+ break;
650
 
651
+ // β€”β€”β€” ask_file (run) β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
652
+ case userState.step === 'ask_file':
653
+ {
654
+ const filename = message;
655
+ const filePath = path.join(userDir, filename);
656
 
657
+ if (!fs.existsSync(filePath)) {
658
+ return socket.emit('message', '❌ The specified file does not exist.');
659
+ }
 
 
 
 
 
 
 
 
 
 
660
 
661
+ socket.emit('message', `πŸš€ Running the file: ${filename}`);
662
+ const nodeProcessFile = spawn('node', [filePath], { cwd: userDir });
663
+
664
+ // **ADDED**: catch spawn errors
665
+ nodeProcessFile.on('error', (err) => {
666
+ console.error('Error spawning node process:', err);
667
+ socket.emit('message', '❌ Failed to start bot: ' + err.message);
668
+ });
669
+
670
+ userStates[userId].runningProcess = nodeProcessFile;
671
+
672
+ nodeProcessFile.stdout.on('data', (data) => socket.emit('message', `βœ… NODE OUTPUT:\n${data}`));
673
+ nodeProcessFile.stderr.on('data', (data) => socket.emit('message', `⚠️ NODE ERROR:\n${data}`));
674
+ nodeProcessFile.on('close', (code) => {
675
+ socket.emit('message', `πŸš€ Script finished with code ${code}`);
676
+ delete userStates[userId].runningProcess;
677
+ });
678
+
679
+ userStates[userId].step = 'interacting';
680
  }
681
+ break;
 
 
 
 
682
 
683
+ // β€”β€”β€” interacting (stdin to bot) β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
684
+ case userState.step === 'interacting':
685
+ if (userState.runningProcess) {
686
+ userState.runningProcess.stdin.write(message + '\n');
687
+ } else {
688
+ socket.emit('message', '❌ No active process to interact with. Please run a file first.');
689
+ }
690
+ break;
691
+
692
+ default:
693
+ socket.emit('message', '❌ Unrecognized command. Use list, clear, or start.');
694
+ }
695
+ } catch (error) {
696
+ console.error('Error processing command:', error);
697
+ socket.emit('message', '❌ An error occurred while processing your command. Please try again.');
698
  }
699
  });
700
 
701
+ // ─── readFile ───────────────────────────────────────────────────────────────
702
  socket.on('readFile', async ({ userId, filePath }) => {
703
  try {
704
  const content = await readFile(userId, filePath);
705
  socket.emit('fileContent', { filePath, content });
706
+ } catch (error) {
707
+ socket.emit('error', { message: error.message });
708
  }
709
  });
710
 
711
+ // ─── writeFile ──────────────────────────────────────────────────────────────
712
  socket.on('writeFile', async ({ userId, filePath, content }) => {
713
  try {
714
  await writeFile(userId, filePath, content);
715
  socket.emit('fileSaved', { filePath });
716
+ } catch (error) {
717
+ socket.emit('error', { message: error.message });
718
  }
719
  });
720
 
721
+ // ─── listFiles ──────────────────────────────────────────────────────────────
722
+ socket.on('listFiles', async ({ userId, dirPath }) => {
723
+ try {
724
+ const files = await listFiles(userId, dirPath);
725
+ socket.emit('fileList', files);
726
+ } catch (error) {
727
+ socket.emit('error', { message: error.message });
728
+ }
729
+ });
730
+
731
+ // ─── uploadFile ────────────────────────────────────────────────────────────
732
  socket.on('uploadFile', async ({ userId, filePath, content }) => {
733
  try {
734
  await uploadFile(userId, filePath, content);
735
  socket.emit('fileUploaded', { filePath });
736
+ } catch (error) {
737
+ socket.emit('error', { message: error.message });
738
  }
739
  });
740
 
741
+ // ─── forgotPassword ─────────────────────────────────────────────────────────
742
+ socket.on('forgotPassword', async (clientId) => {
743
+ try {
744
+ const users = loadUsers();
745
+ const user = Object.values(users).find(u => u.clientId === clientId);
746
+
747
+ if (user) {
748
+ const resetToken = await generateResetToken();
749
+ user.resetToken = resetToken;
750
+ user.resetTokenExpires = Date.now() + 3600000;
751
+ saveUsers(users);
752
+
753
+ socket.emit('resetTokenGenerated', { username: user.username, resetToken });
754
+ } else {
755
+ socket.emit('resetTokenError', 'No user found with this client ID');
756
+ }
757
+ } catch (error) {
758
+ console.error('Error in forgot password process:', error);
759
+ socket.emit('resetTokenError', 'An error occurred during the password reset process');
760
+ }
761
+ });
762
+
763
+ // ─── resetPassword ────────────────────────────────���─────────────────────────
764
+ socket.on('resetPassword', async ({ resetToken, newPassword }) => {
765
+ try {
766
+ const users = loadUsers();
767
+ const user = Object.values(users).find(u => u.resetToken === resetToken && u.resetTokenExpires > Date.now());
768
+
769
+ if (user) {
770
+ user.password = newPassword;
771
+ delete user.resetToken;
772
+ delete user.resetTokenExpires;
773
+ saveUsers(users);
774
+ socket.emit('passwordResetSuccess', 'Password has been reset successfully');
775
+ } else {
776
+ socket.emit('passwordResetError', 'Invalid or expired reset token');
777
+ }
778
+ } catch (error) {
779
+ console.error('Error in password reset process:', error);
780
+ socket.emit('passwordResetError', 'An error occurred during the password reset process');
781
+ }
782
+ });
783
+
784
+ // ─── getServerRuntime ────────────────────────────────────────────────────────
785
+ socket.on('getServerRuntime', () => {
786
+ try {
787
+ socket.emit('serverRuntime', getServerRuntime());
788
+ } catch (error) {
789
+ console.error('Error getting server runtime:', error);
790
+ socket.emit('serverRuntime', 'Error getting server runtime');
791
+ }
792
  });
793
 
794
+ // ─── getSystemStatus ─────────────────────────────────────────────────────────
795
+ socket.on('getSystemStatus', () => {
796
+ try {
797
+ socket.emit('systemStatus', getSystemStatus());
798
+ } catch (error) {
799
+ console.error('Error getting system status:', error);
800
+ socket.emit('systemStatus', { cpu: 0, memory: 0, disk: 0 });
801
+ }
802
+ });
803
+
804
+ // ─── getUserStats ───────────────────────────────────────────────────────────
805
+ socket.on('getUserStats', () => {
806
+ try {
807
+ socket.emit('userStats', getUserStats());
808
+ } catch (error) {
809
+ console.error('Error getting user stats:', error);
810
+ socket.emit('userStats', { total: 0, active: 0, banned: 0 });
811
+ }
812
+ });
813
+
814
+ // ─── disconnect ─────────────────────────────────────────────────────────────
815
  socket.on('disconnect', () => {
816
+ console.log('User disconnected');
817
  activeUsers--;
818
  io.emit('userStats', getUserStats());
819
  });
820
  });
821
 
822
+ // ─── 6) BROADCAST LIVE STATS EVERY 5 SECONDS ─────────────────────────────────
823
  setInterval(() => {
824
+ try {
825
+ io.emit('serverRuntime', getServerRuntime());
826
+ io.emit('systemStatus', getSystemStatus());
827
+ io.emit('userStats', getUserStats());
828
+ } catch (error) {
829
+ console.error('Error emitting periodic updates:', error);
830
+ }
831
  }, 5000);
832
 
833
+ // ─── 7) START THE SERVER ─────────────────────────────────────────────────────
834
  const PORT = process.env.PORT || 7860;
835
+ http.listen(PORT, () => console.log(`🌐 Server running on port ${PORT}.`));
836
 
837
+ // ─── 8) GLOBAL ERROR HANDLERS ────────────────────────────────────────────────
838
+ process.on('uncaughtException', (error) => {
839
+ console.error('Uncaught Exception:', error);
840
  });
841
+ process.on('unhandledRejection', (reason, promise) => {
842
+ console.error('Unhandled Rejection at:', promise, 'reason:', reason);
843
  });