File size: 2,729 Bytes
42926a5
 
 
9eaf2de
940518f
b0ebb84
42926a5
 
 
9eaf2de
 
 
 
 
42926a5
9fcb962
9eaf2de
 
 
 
 
 
 
9fcb962
fe865d0
42926a5
 
b0ebb84
 
 
 
 
 
 
 
9eaf2de
 
 
 
 
9fcb962
b0ebb84
42926a5
9fcb962
42926a5
9fcb962
 
 
 
 
 
 
 
 
 
 
 
42926a5
 
9fcb962
b0ebb84
 
9eaf2de
b0ebb84
 
4798350
b0ebb84
4798350
 
b0ebb84
940518f
 
 
9fcb962
fe865d0
 
9fcb962
 
940518f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4798350
9eaf2de
 
 
42926a5
940518f
42926a5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const Convert = require('ansi-to-html');
const { exec } = require('child_process');
const path = require('path');

const app = express();
const server = http.createServer(app);
const io = new Server(server, {
  cors: { origin: "*" }
});

const PORT = 7860;

// ANSI → HTML
const convert = new Convert({
  fg: '#e2e8f0',
  bg: '#0b1020',
  newline: true,
  escapeXML: true
});

// logs memory
let logs = [];
const MAX_LOGS = 10000;

function formatLog(data) {
  return {
    html: convert.toHtml(data.message || ''),
    level: data.level || 'info',
    timestamp: new Date()
  };
}

function addLog(log) {
  logs.push(log);
  if (logs.length > MAX_LOGS) logs.shift();
}

// STATIC
app.use(express.static(path.join(__dirname, 'public')));

// ✅ PAGINATED API
app.get('/api/logs', (req, res) => {
  const limit = parseInt(req.query.limit) || 50;
  const offset = parseInt(req.query.offset) || 0;

  const start = Math.max(logs.length - offset - limit, 0);
  const end = logs.length - offset;

  const slice = logs.slice(start, end);

  res.json({
    logs: slice,
    hasMore: start > 0
  });
});

// SOCKET
io.on('connection', (socket) => {
  console.log('Client connected:', socket.id);

  socket.on('log', (data) => {
    const log = formatLog(data);
    addLog(log);
    io.emit('log', log);
  });

  socket.on('command', (data) => {
    const cmd = data.command.trim();
    if (!cmd) return;

    if (cmd === "clear") {
      logs = [];
      return;
    }

    const blocked = ['rm', 'shutdown', 'reboot', 'mkfs'];
    if (blocked.some(b => cmd.includes(b))) {
      const log = formatLog({
        message: "\x1b[31mBlocked command!\x1b[0m",
        level: "error"
      });
      addLog(log);
      return io.emit('log', log);
    }

    const cmdLog = formatLog({
      message: "\x1b[32m$ " + cmd + "\x1b[0m"
    });

    addLog(cmdLog);
    io.emit('log', cmdLog);

    exec(cmd, { timeout: 20000 }, (error, stdout, stderr) => {

      if (stdout) {
        const outLog = formatLog({ message: stdout });
        addLog(outLog);
        io.emit('log', outLog);
      }

      if (stderr) {
        const errLog = formatLog({
          message: "\x1b[31m" + stderr + "\x1b[0m",
          level: "error"
        });
        addLog(errLog);
        io.emit('log', errLog);
      }

      if (error) {
        const errLog = formatLog({
          message: "\x1b[31m" + error.message + "\x1b[0m",
          level: "error"
        });
        addLog(errLog);
        io.emit('log', errLog);
      }
    });
  });
});

server.listen(PORT, () => {
  console.log('🚀 Server running on http://localhost:' + PORT);
});