aigems commited on
Commit
97f9176
·
1 Parent(s): b4e225f
Files changed (6) hide show
  1. app.js +15 -12
  2. public/index.html +1 -2
  3. public/js/main.js +4 -8
  4. routes/auth.js +7 -5
  5. routes/command.js +5 -22
  6. utils/logger.js +5 -2
app.js CHANGED
@@ -1,11 +1,10 @@
1
  const express = require('express');
2
  const winston = require('winston');
3
- const fs = require('fs').promises;
4
  const path = require('path');
5
  const rateLimit = require('express-rate-limit');
6
- const { promises: fsPromises } = require('fs');
7
  const helmet = require('helmet');
8
  const jwt = require('jsonwebtoken');
 
9
 
10
  const app = express();
11
  const port = process.env.PORT || 7860;
@@ -31,11 +30,12 @@ if (process.env.NODE_ENV !== 'production') {
31
 
32
  // 安全中间件
33
  app.use(helmet());
 
34
 
35
  // 速率限制
36
  const limiter = rateLimit({
37
- windowMs: 15 * 60 * 1000, // 15分钟
38
- max: 100 // 每个IP限制100个请求
39
  });
40
  app.use('/api', limiter);
41
 
@@ -78,16 +78,19 @@ app.use((err, req, res, next) => {
78
  res.status(500).json({ error: '服务器内部错误' });
79
  });
80
 
81
- // 确保命令历史文件存在
82
- const historyFilePath = path.join(__dirname, 'data', 'command_history.json');
83
- fsPromises.access(historyFilePath)
84
- .catch(() => fsPromises.writeFile(historyFilePath, '[]'))
85
- .then(() => {
 
86
  app.listen(port, () => {
87
  logger.info(`Web 命令执行应用正在监听 http://localhost:${port}`);
88
  });
89
- })
90
- .catch(err => {
91
  logger.error('无法创建命令历史文件:', err);
92
  process.exit(1);
93
- });
 
 
 
 
1
  const express = require('express');
2
  const winston = require('winston');
 
3
  const path = require('path');
4
  const rateLimit = require('express-rate-limit');
 
5
  const helmet = require('helmet');
6
  const jwt = require('jsonwebtoken');
7
+ const cors = require('cors');
8
 
9
  const app = express();
10
  const port = process.env.PORT || 7860;
 
30
 
31
  // 安全中间件
32
  app.use(helmet());
33
+ app.use(cors());
34
 
35
  // 速率限制
36
  const limiter = rateLimit({
37
+ windowMs: 15 * 60 * 1000,
38
+ max: 100
39
  });
40
  app.use('/api', limiter);
41
 
 
78
  res.status(500).json({ error: '服务器内部错误' });
79
  });
80
 
81
+ // 启动服务器
82
+ const startServer = async () => {
83
+ try {
84
+ const historyFilePath = path.join(__dirname, 'data', 'command_history.json');
85
+ await fs.promises.access(historyFilePath).catch(() => fs.promises.writeFile(historyFilePath, '[]'));
86
+
87
  app.listen(port, () => {
88
  logger.info(`Web 命令执行应用正在监听 http://localhost:${port}`);
89
  });
90
+ } catch (err) {
 
91
  logger.error('无法创建命令历史文件:', err);
92
  process.exit(1);
93
+ }
94
+ };
95
+
96
+ startServer();
public/index.html CHANGED
@@ -7,9 +7,8 @@
7
  <title>Web 命令执行</title>
8
  <script src="https://cdn.tailwindcss.com"></script>
9
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/xss.min.js"></script>
 
10
  <style>
11
- @import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;700&display=swap');
12
-
13
  body {
14
  font-family: 'Noto Sans SC', sans-serif;
15
  }
 
7
  <title>Web 命令执行</title>
8
  <script src="https://cdn.tailwindcss.com"></script>
9
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/xss.min.js"></script>
10
+ <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;700&display=swap" rel="stylesheet">
11
  <style>
 
 
12
  body {
13
  font-family: 'Noto Sans SC', sans-serif;
14
  }
public/js/main.js CHANGED
@@ -11,8 +11,8 @@ commandInput.addEventListener('keypress', function (event) {
11
  });
12
 
13
  async function executeCommand() {
14
- const command = commandInput.value;
15
- if (!command.trim()) return;
16
 
17
  showLoading(true);
18
  output.textContent = '正在执行命令...';
@@ -30,7 +30,7 @@ async function executeCommand() {
30
  if (response.ok) {
31
  output.textContent = data.output || data.error;
32
  commandInput.value = '';
33
- loadCommandHistory();
34
  } else {
35
  output.textContent = `错误: ${data.error}`;
36
  }
@@ -83,7 +83,7 @@ async function login() {
83
  localStorage.setItem('token', data.token);
84
  document.getElementById('loginForm').style.display = 'none';
85
  document.getElementById('commandInterface').style.display = 'block';
86
- loadCommandHistory();
87
  } else {
88
  alert('登录失败: ' + data.error);
89
  }
@@ -94,8 +94,6 @@ async function login() {
94
 
95
  document.getElementById('loginButton').addEventListener('click', login);
96
 
97
- loadCommandHistory();
98
-
99
  function checkLoginStatus() {
100
  const token = localStorage.getItem('token');
101
  if (token) {
@@ -105,7 +103,5 @@ function checkLoginStatus() {
105
  }
106
  }
107
 
108
- // 在页面加载时调用此函数
109
  window.addEventListener('load', checkLoginStatus);
110
-
111
  document.getElementById('executeButton').addEventListener('click', executeCommand);
 
11
  });
12
 
13
  async function executeCommand() {
14
+ const command = commandInput.value.trim();
15
+ if (!command) return;
16
 
17
  showLoading(true);
18
  output.textContent = '正在执行命令...';
 
30
  if (response.ok) {
31
  output.textContent = data.output || data.error;
32
  commandInput.value = '';
33
+ await loadCommandHistory();
34
  } else {
35
  output.textContent = `错误: ${data.error}`;
36
  }
 
83
  localStorage.setItem('token', data.token);
84
  document.getElementById('loginForm').style.display = 'none';
85
  document.getElementById('commandInterface').style.display = 'block';
86
+ await loadCommandHistory();
87
  } else {
88
  alert('登录失败: ' + data.error);
89
  }
 
94
 
95
  document.getElementById('loginButton').addEventListener('click', login);
96
 
 
 
97
  function checkLoginStatus() {
98
  const token = localStorage.getItem('token');
99
  if (token) {
 
103
  }
104
  }
105
 
 
106
  window.addEventListener('load', checkLoginStatus);
 
107
  document.getElementById('executeButton').addEventListener('click', executeCommand);
routes/auth.js CHANGED
@@ -1,23 +1,25 @@
1
  const express = require('express');
2
  const jwt = require('jsonwebtoken');
3
  const router = express.Router();
 
4
 
5
  const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
 
6
 
7
  const users = [
8
  {
9
  id: 1,
10
  username: process.env.ADMIN_USERNAME,
11
- password: process.env.ADMIN_PASSWORD
12
  }
13
  ];
14
 
15
- router.post('/login', (req, res) => {
16
  const { username, password } = req.body;
17
- const user = users.find(u => u.username === username && u.password === password);
18
 
19
- if (user) {
20
- const token = jwt.sign({ id: user.id, username: user.username }, JWT_SECRET, { expiresIn: '1h' });
21
  res.json({ token });
22
  } else {
23
  res.status(401).json({ error: '无效的用户名或密码' });
 
1
  const express = require('express');
2
  const jwt = require('jsonwebtoken');
3
  const router = express.Router();
4
+ const bcrypt = require('bcrypt');
5
 
6
  const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
7
+ const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || '1h';
8
 
9
  const users = [
10
  {
11
  id: 1,
12
  username: process.env.ADMIN_USERNAME,
13
+ password: bcrypt.hashSync(process.env.ADMIN_PASSWORD, 10)
14
  }
15
  ];
16
 
17
+ router.post('/login', async (req, res) => {
18
  const { username, password } = req.body;
19
+ const user = users.find(u => u.username === username);
20
 
21
+ if (user && await bcrypt.compare(password, user.password)) {
22
+ const token = jwt.sign({ id: user.id, username: user.username }, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN });
23
  res.json({ token });
24
  } else {
25
  res.status(401).json({ error: '无效的用户名或密码' });
routes/command.js CHANGED
@@ -6,21 +6,13 @@ const router = express.Router();
6
 
7
  const logger = require('../utils/logger');
8
 
9
- // 命令白名单
10
  const allowedCommands = ['ls', 'pwd', 'whoami', 'date', 'echo', 'cat'];
11
-
12
- // 历史命令文件路径
13
  const historyFilePath = path.join(__dirname, '..', 'data', 'command_history.json');
14
 
15
  router.get('/command-history', async (req, res) => {
16
  try {
17
- let history = [];
18
- try {
19
- const historyData = await fs.readFile(historyFilePath, 'utf-8');
20
- history = JSON.parse(historyData);
21
- } catch (readError) {
22
- logger.warn('读取命令历史失败,使用空数组:', readError);
23
- }
24
  res.json(history);
25
  } catch (error) {
26
  logger.error('处理命令历史请求失败:', error);
@@ -32,7 +24,6 @@ router.post('/execute', async (req, res) => {
32
  const { command } = req.body;
33
  const baseCommand = command.split(' ')[0];
34
 
35
- // 白名单检查的部分
36
  // if (!allowedCommands.includes(baseCommand)) {
37
  // logger.warn(`用户 ${req.user.username} 尝试执行未授权的命令: ${command}`);
38
  // return res.status(403).json({ error: '未授权的命令' });
@@ -44,19 +35,11 @@ router.post('/execute', async (req, res) => {
44
  return res.status(500).json({ error: error.message });
45
  }
46
 
47
- // 记录命令历史
48
  try {
49
- let history = [];
50
- try {
51
- const historyData = await fs.readFile(historyFilePath, 'utf-8');
52
- history = JSON.parse(historyData);
53
- } catch (readError) {
54
- // 如果文件不存在或为空,使用空数组
55
- }
56
-
57
  history.push({ command, timestamp: new Date().toISOString(), user: req.user.username });
58
- if (history.length > 100) history.shift(); // 保留最近100条命令
59
-
60
  await fs.writeFile(historyFilePath, JSON.stringify(history, null, 2));
61
  } catch (writeError) {
62
  logger.error('写入命令历史失败:', writeError);
 
6
 
7
  const logger = require('../utils/logger');
8
 
 
9
  const allowedCommands = ['ls', 'pwd', 'whoami', 'date', 'echo', 'cat'];
 
 
10
  const historyFilePath = path.join(__dirname, '..', 'data', 'command_history.json');
11
 
12
  router.get('/command-history', async (req, res) => {
13
  try {
14
+ const historyData = await fs.readFile(historyFilePath, 'utf-8');
15
+ const history = JSON.parse(historyData);
 
 
 
 
 
16
  res.json(history);
17
  } catch (error) {
18
  logger.error('处理命令历史请求失败:', error);
 
24
  const { command } = req.body;
25
  const baseCommand = command.split(' ')[0];
26
 
 
27
  // if (!allowedCommands.includes(baseCommand)) {
28
  // logger.warn(`用户 ${req.user.username} 尝试执行未授权的命令: ${command}`);
29
  // return res.status(403).json({ error: '未授权的命令' });
 
35
  return res.status(500).json({ error: error.message });
36
  }
37
 
 
38
  try {
39
+ const historyData = await fs.readFile(historyFilePath, 'utf-8');
40
+ let history = JSON.parse(historyData);
 
 
 
 
 
 
41
  history.push({ command, timestamp: new Date().toISOString(), user: req.user.username });
42
+ history = history.slice(-100); // 保留最近100条命令
 
43
  await fs.writeFile(historyFilePath, JSON.stringify(history, null, 2));
44
  } catch (writeError) {
45
  logger.error('写入命令历史失败:', writeError);
utils/logger.js CHANGED
@@ -1,4 +1,7 @@
1
  const winston = require('winston');
 
 
 
2
 
3
  const logger = winston.createLogger({
4
  level: process.env.LOG_LEVEL || 'info',
@@ -7,8 +10,8 @@ const logger = winston.createLogger({
7
  winston.format.json()
8
  ),
9
  transports: [
10
- new winston.transports.File({ filename: 'error.log', level: 'error' }),
11
- new winston.transports.File({ filename: 'combined.log' })
12
  ]
13
  });
14
 
 
1
  const winston = require('winston');
2
+ const path = require('path');
3
+
4
+ const logDir = path.join(__dirname, '..', 'logs');
5
 
6
  const logger = winston.createLogger({
7
  level: process.env.LOG_LEVEL || 'info',
 
10
  winston.format.json()
11
  ),
12
  transports: [
13
+ new winston.transports.File({ filename: path.join(logDir, 'error.log'), level: 'error' }),
14
+ new winston.transports.File({ filename: path.join(logDir, 'combined.log') })
15
  ]
16
  });
17