nbugs commited on
Commit
90ba9d4
·
verified ·
1 Parent(s): c452165

Update server.js

Browse files
Files changed (1) hide show
  1. server.js +47 -14
server.js CHANGED
@@ -9,6 +9,14 @@ const port = process.env.PORT || 8080;
9
  app.use(express.json());
10
  app.use(express.urlencoded({ extended: true }));
11
 
 
 
 
 
 
 
 
 
12
  // 从环境变量获取 HuggingFace 用户名和对应的 API Token 映射
13
  const userTokenMapping = {};
14
  const usernames = [];
@@ -35,7 +43,7 @@ const ADMIN_PASSWORD = process.env.USER_PASSWORD || 'password';
35
  const sessions = new Map();
36
  const SESSION_TIMEOUT = 24 * 60 * 60 * 1000; // 24小时超时
37
 
38
- // 缓存管理
39
  class SpaceCache {
40
  constructor() {
41
  this.spaces = {};
@@ -50,15 +58,34 @@ class SpaceCache {
50
  getAll() {
51
  return Object.values(this.spaces);
52
  }
 
 
 
 
 
 
 
 
53
 
54
  isExpired(expireMinutes = 5) {
55
  if (!this.lastUpdate) return true;
56
  return (Date.now() - this.lastUpdate) > (expireMinutes * 60 * 1000);
57
  }
 
 
 
 
 
58
  }
59
 
60
  const spaceCache = new SpaceCache();
61
 
 
 
 
 
 
 
62
  // 提供静态文件(前端文件)
63
  app.use(express.static(path.join(__dirname, 'public')));
64
 
@@ -126,12 +153,13 @@ const authenticateToken = (req, res, next) => {
126
  }
127
  };
128
 
129
- // 获取所有 spaces 列表(包括私有)
130
  app.get('/api/proxy/spaces', async (req, res) => {
131
  try {
132
  if (!spaceCache.isExpired()) {
133
  console.log('从缓存获取 Spaces 数据');
134
- return res.json(spaceCache.getAll());
 
135
  }
136
 
137
  const allSpaces = [];
@@ -155,12 +183,13 @@ app.get('/api/proxy/spaces', async (req, res) => {
155
  const spaceInfo = spaceInfoResponse.data;
156
  const spaceRuntime = spaceInfo.runtime || {};
157
 
 
158
  allSpaces.push({
159
  repo_id: spaceInfo.id,
160
  name: spaceInfo.cardData?.title || spaceInfo.id.split('/')[1],
161
  owner: spaceInfo.author,
162
  username: username,
163
- token: token || '',
164
  url: `https://${spaceInfo.author}-${spaceInfo.id.split('/')[1]}.hf.space`,
165
  status: spaceRuntime.stage || 'unknown',
166
  last_modified: spaceInfo.lastModified || 'unknown',
@@ -182,7 +211,9 @@ app.get('/api/proxy/spaces', async (req, res) => {
182
  allSpaces.sort((a, b) => a.name.localeCompare(b.name));
183
  spaceCache.updateAll(allSpaces);
184
  console.log(`总共获取到 ${allSpaces.length} 个 Spaces`);
185
- res.json(allSpaces);
 
 
186
  } catch (error) {
187
  console.error(`代理获取 spaces 列表失败:`, error.message);
188
  res.status(500).json({ error: '获取 spaces 列表失败', details: error.message });
@@ -194,14 +225,15 @@ app.post('/api/proxy/restart/:repoId(*)', authenticateToken, async (req, res) =>
194
  try {
195
  const { repoId } = req.params;
196
  console.log(`尝试重启 Space: ${repoId}`);
197
- const spaces = spaceCache.getAll();
198
- const space = spaces.find(s => s.repo_id === repoId);
199
- if (!space || !space.token) {
 
200
  console.error(`Space ${repoId} 未找到或无 Token 配置`);
201
  return res.status(404).json({ error: 'Space 未找到或无 Token 配置' });
202
  }
203
 
204
- const headers = { 'Authorization': `Bearer ${space.token}`, 'Content-Type': 'application/json' };
205
  const response = await axios.post(`https://huggingface.co/api/spaces/${repoId}/restart`, {}, { headers });
206
  console.log(`重启 Space ${repoId} 成功,状态码: ${response.status}`);
207
  res.json({ success: true, message: `Space ${repoId} 重启成功` });
@@ -221,14 +253,15 @@ app.post('/api/proxy/rebuild/:repoId(*)', authenticateToken, async (req, res) =>
221
  try {
222
  const { repoId } = req.params;
223
  console.log(`尝试重建 Space: ${repoId}`);
224
- const spaces = spaceCache.getAll();
225
- const space = spaces.find(s => s.repo_id === repoId);
226
- if (!space || !space.token) {
 
227
  console.error(`Space ${repoId} 未找到或无 Token 配置`);
228
  return res.status(404).json({ error: 'Space 未找到或无 Token 配置' });
229
  }
230
 
231
- const headers = { 'Authorization': `Bearer ${space.token}`, 'Content-Type': 'application/json' };
232
  // 将 factory_reboot 参数作为查询参数传递,而非请求体
233
  const response = await axios.post(
234
  `https://huggingface.co/api/spaces/${repoId}/restart?factory=true`,
@@ -426,4 +459,4 @@ app.listen(port, () => {
426
  console.log(`Server running on port ${port}`);
427
  console.log(`User configurations:`, usernames.map(user => `${user}: ${userTokenMapping[user] ? 'Token Configured' : 'No Token'}`).join(', ') || 'None');
428
  console.log(`Admin login enabled: Username=${ADMIN_USERNAME}, Password=${ADMIN_PASSWORD ? 'Configured' : 'Not Configured'}`);
429
- });
 
9
  app.use(express.json());
10
  app.use(express.urlencoded({ extended: true }));
11
 
12
+ // 添加安全响应头
13
+ app.use((req, res, next) => {
14
+ res.setHeader('X-Content-Type-Options', 'nosniff');
15
+ res.setHeader('X-Frame-Options', 'DENY');
16
+ res.setHeader('X-XSS-Protection', '1; mode=block');
17
+ next();
18
+ });
19
+
20
  // 从环境变量获取 HuggingFace 用户名和对应的 API Token 映射
21
  const userTokenMapping = {};
22
  const usernames = [];
 
43
  const sessions = new Map();
44
  const SESSION_TIMEOUT = 24 * 60 * 60 * 1000; // 24小时超时
45
 
46
+ // 缓存管理 - 修改后的类
47
  class SpaceCache {
48
  constructor() {
49
  this.spaces = {};
 
58
  getAll() {
59
  return Object.values(this.spaces);
60
  }
61
+
62
+ // 新增:获取安全的空间数据(不包含token)
63
+ getSafeSpaces() {
64
+ return Object.values(this.spaces).map(space => {
65
+ const { token, ...safeSpace } = space;
66
+ return safeSpace;
67
+ });
68
+ }
69
 
70
  isExpired(expireMinutes = 5) {
71
  if (!this.lastUpdate) return true;
72
  return (Date.now() - this.lastUpdate) > (expireMinutes * 60 * 1000);
73
  }
74
+
75
+ // 根据repo_id获取单个space的token
76
+ getToken(repoId) {
77
+ return this.spaces[repoId]?.token || '';
78
+ }
79
  }
80
 
81
  const spaceCache = new SpaceCache();
82
 
83
+ // 添加API请求日志中间件
84
+ app.use('/api/proxy', (req, res, next) => {
85
+ console.log(`[${new Date().toISOString()}] ${req.method} ${req.originalUrl} - IP: ${req.ip}`);
86
+ next();
87
+ });
88
+
89
  // 提供静态文件(前端文件)
90
  app.use(express.static(path.join(__dirname, 'public')));
91
 
 
153
  }
154
  };
155
 
156
+ // 获取所有 spaces 列表(包括私有)- 修改后的路由处理函数
157
  app.get('/api/proxy/spaces', async (req, res) => {
158
  try {
159
  if (!spaceCache.isExpired()) {
160
  console.log('从缓存获取 Spaces 数据');
161
+ // 返回不包含token的安全数据
162
+ return res.json(spaceCache.getSafeSpaces());
163
  }
164
 
165
  const allSpaces = [];
 
183
  const spaceInfo = spaceInfoResponse.data;
184
  const spaceRuntime = spaceInfo.runtime || {};
185
 
186
+ // 存储完整信息到缓存(包括token)
187
  allSpaces.push({
188
  repo_id: spaceInfo.id,
189
  name: spaceInfo.cardData?.title || spaceInfo.id.split('/')[1],
190
  owner: spaceInfo.author,
191
  username: username,
192
+ token: token || '', // 仍然存储token到缓存中,用于后续API调用
193
  url: `https://${spaceInfo.author}-${spaceInfo.id.split('/')[1]}.hf.space`,
194
  status: spaceRuntime.stage || 'unknown',
195
  last_modified: spaceInfo.lastModified || 'unknown',
 
211
  allSpaces.sort((a, b) => a.name.localeCompare(b.name));
212
  spaceCache.updateAll(allSpaces);
213
  console.log(`总共获取到 ${allSpaces.length} 个 Spaces`);
214
+
215
+ // 返回不包含token的安全数据
216
+ res.json(spaceCache.getSafeSpaces());
217
  } catch (error) {
218
  console.error(`代理获取 spaces 列表失败:`, error.message);
219
  res.status(500).json({ error: '获取 spaces 列表失败', details: error.message });
 
225
  try {
226
  const { repoId } = req.params;
227
  console.log(`尝试重启 Space: ${repoId}`);
228
+
229
+ // 从缓存中获取token,而不是完整的space对象
230
+ const token = spaceCache.getToken(repoId);
231
+ if (!token) {
232
  console.error(`Space ${repoId} 未找到或无 Token 配置`);
233
  return res.status(404).json({ error: 'Space 未找到或无 Token 配置' });
234
  }
235
 
236
+ const headers = { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' };
237
  const response = await axios.post(`https://huggingface.co/api/spaces/${repoId}/restart`, {}, { headers });
238
  console.log(`重启 Space ${repoId} 成功,状态码: ${response.status}`);
239
  res.json({ success: true, message: `Space ${repoId} 重启成功` });
 
253
  try {
254
  const { repoId } = req.params;
255
  console.log(`尝试重建 Space: ${repoId}`);
256
+
257
+ // 从缓存中获取token,而不是完整的space对象
258
+ const token = spaceCache.getToken(repoId);
259
+ if (!token) {
260
  console.error(`Space ${repoId} 未找到或无 Token 配置`);
261
  return res.status(404).json({ error: 'Space 未找到或无 Token 配置' });
262
  }
263
 
264
+ const headers = { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' };
265
  // 将 factory_reboot 参数作为查询参数传递,而非请求体
266
  const response = await axios.post(
267
  `https://huggingface.co/api/spaces/${repoId}/restart?factory=true`,
 
459
  console.log(`Server running on port ${port}`);
460
  console.log(`User configurations:`, usernames.map(user => `${user}: ${userTokenMapping[user] ? 'Token Configured' : 'No Token'}`).join(', ') || 'None');
461
  console.log(`Admin login enabled: Username=${ADMIN_USERNAME}, Password=${ADMIN_PASSWORD ? 'Configured' : 'Not Configured'}`);
462
+ });