Spaces:
Paused
Paused
Update server.js
Browse files
server.js
CHANGED
|
@@ -3,26 +3,24 @@ const http = require('http');
|
|
| 3 |
const os = require('os');
|
| 4 |
const fs = require('fs');
|
| 5 |
const { exec } = require('child_process');
|
| 6 |
-
const path = require('path');
|
| 7 |
|
| 8 |
// --- Hugging Face Spaces 核心配置 ---
|
| 9 |
-
// 必须监听所有接口,以响应外部请求
|
| 10 |
const HOST = '0.0.0.0';
|
| 11 |
-
// 必须使用 Hugging Face 指定的端口
|
| 12 |
const PORT = process.env.PORT || 7860;
|
| 13 |
|
| 14 |
-
// --- 辅助函数:获取磁盘使用情况 ---
|
| 15 |
function getDiskUsage() {
|
| 16 |
try {
|
| 17 |
-
|
|
|
|
| 18 |
const total = stats.bsize * stats.blocks;
|
| 19 |
const free = stats.bsize * stats.bfree;
|
| 20 |
const used = total - free;
|
| 21 |
const usagePercent = ((used / total) * 100).toFixed(2);
|
| 22 |
return {
|
| 23 |
-
total: (total / (1024 ** 3)).toFixed(2),
|
| 24 |
-
used: (used / (1024 ** 3)).toFixed(2),
|
| 25 |
-
free: (free / (1024 ** 3)).toFixed(2),
|
| 26 |
usagePercent: `${usagePercent}%`
|
| 27 |
};
|
| 28 |
} catch (error) {
|
|
@@ -30,21 +28,17 @@ function getDiskUsage() {
|
|
| 30 |
}
|
| 31 |
}
|
| 32 |
|
| 33 |
-
// --- 辅助函数:获取出口公网IP
|
| 34 |
function getPublicIP() {
|
| 35 |
return new Promise(resolve => {
|
| 36 |
-
// Hugging Face Spaces 容器内通常可以访问外部网络
|
| 37 |
exec('curl -s --max-time 5 ifconfig.me', (err, stdout) => {
|
| 38 |
-
if (err)
|
| 39 |
-
|
| 40 |
-
} else {
|
| 41 |
-
resolve(stdout.trim());
|
| 42 |
-
}
|
| 43 |
});
|
| 44 |
});
|
| 45 |
}
|
| 46 |
|
| 47 |
-
// --- 辅助函数:获取
|
| 48 |
function getSafeEnvVars() {
|
| 49 |
const safeVars = {};
|
| 50 |
const sensitiveKeys = ['PASSWORD', 'SECRET', 'TOKEN', 'KEY'];
|
|
@@ -58,29 +52,47 @@ function getSafeEnvVars() {
|
|
| 58 |
return safeVars;
|
| 59 |
}
|
| 60 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
// --- 创建 HTTP 服务器 ---
|
| 62 |
const server = http.createServer(async (req, res) => {
|
| 63 |
-
// 1.
|
| 64 |
const publicIP = await getPublicIP();
|
| 65 |
const diskInfo = getDiskUsage();
|
|
|
|
| 66 |
|
| 67 |
-
// 2. 系统与硬件信息
|
| 68 |
const cpuInfo = os.cpus()[0];
|
| 69 |
-
const totalMem = (os.totalmem() / (1024 ** 3)).toFixed(2);
|
| 70 |
-
const freeMem = (os.freemem() / (1024 ** 3)).toFixed(2);
|
| 71 |
-
const usedMem = (totalMem - freeMem).toFixed(2);
|
| 72 |
const systemUptime = Math.floor(os.uptime() / 60) + ' 分钟';
|
| 73 |
|
| 74 |
-
// 3.
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
|
|
|
|
|
|
| 78 |
|
| 79 |
-
// 4. 构建
|
| 80 |
const envReport = {
|
| 81 |
-
//
|
| 82 |
access: {
|
| 83 |
-
spaceUrl: `https://${process.env.SPACE_ID?.replace('/', '-')
|
| 84 |
publicIP: publicIP,
|
| 85 |
internalIP: (() => {
|
| 86 |
const interfaces = os.networkInterfaces();
|
|
@@ -92,10 +104,10 @@ const server = http.createServer(async (req, res) => {
|
|
| 92 |
return '127.0.0.1';
|
| 93 |
})(),
|
| 94 |
hostname: os.hostname(),
|
| 95 |
-
spaceId: process.env.SPACE_ID || '未
|
| 96 |
-
spaceRepoName: process.env.SPACE_REPO_NAME || '未
|
| 97 |
},
|
| 98 |
-
//
|
| 99 |
hardware: {
|
| 100 |
cpuModel: cpuInfo?.model || '未知',
|
| 101 |
cpuCores: os.cpus().length,
|
|
@@ -106,38 +118,37 @@ const server = http.createServer(async (req, res) => {
|
|
| 106 |
uptime: systemUptime,
|
| 107 |
expectedFreeTier: '2 vCPU / 16GB RAM / 50GB 临时磁盘'
|
| 108 |
},
|
| 109 |
-
//
|
| 110 |
nodeEnv: {
|
| 111 |
version: process.version,
|
| 112 |
platform: process.platform,
|
| 113 |
arch: process.arch,
|
| 114 |
execPath: process.execPath,
|
| 115 |
cwd: process.cwd(),
|
| 116 |
-
nodePath: isNodePathSet ? process.env.NODE_PATH : '未设置'
|
| 117 |
},
|
| 118 |
-
//
|
| 119 |
container: {
|
| 120 |
-
userId:
|
| 121 |
-
groupId:
|
| 122 |
-
expectedUserId: '1000 (
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
|
|
|
| 127 |
},
|
| 128 |
-
//
|
| 129 |
envVars: getSafeEnvVars(),
|
| 130 |
-
//
|
| 131 |
suggestions: [
|
| 132 |
'✅ 必须监听 0.0.0.0,并在 README.md 中设置 app_port: 7860',
|
| 133 |
-
'✅ 强烈建议为敏感信息
|
| 134 |
-
'✅
|
| 135 |
-
'✅
|
| 136 |
-
'✅
|
| 137 |
]
|
| 138 |
};
|
| 139 |
|
| 140 |
-
// 5. 输出美观的 JSON 报告
|
| 141 |
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
| 142 |
res.end(JSON.stringify(envReport, null, 2));
|
| 143 |
});
|
|
|
|
| 3 |
const os = require('os');
|
| 4 |
const fs = require('fs');
|
| 5 |
const { exec } = require('child_process');
|
|
|
|
| 6 |
|
| 7 |
// --- Hugging Face Spaces 核心配置 ---
|
|
|
|
| 8 |
const HOST = '0.0.0.0';
|
|
|
|
| 9 |
const PORT = process.env.PORT || 7860;
|
| 10 |
|
| 11 |
+
// --- 辅助函数:获取磁盘使用情况(需要处理 /data 可能不存在)---
|
| 12 |
function getDiskUsage() {
|
| 13 |
try {
|
| 14 |
+
// 获取当前工作目录所在的文件系统(通常是容器根目录)
|
| 15 |
+
const stats = fs.statfsSync('/');
|
| 16 |
const total = stats.bsize * stats.blocks;
|
| 17 |
const free = stats.bsize * stats.bfree;
|
| 18 |
const used = total - free;
|
| 19 |
const usagePercent = ((used / total) * 100).toFixed(2);
|
| 20 |
return {
|
| 21 |
+
total: (total / (1024 ** 3)).toFixed(2),
|
| 22 |
+
used: (used / (1024 ** 3)).toFixed(2),
|
| 23 |
+
free: (free / (1024 ** 3)).toFixed(2),
|
| 24 |
usagePercent: `${usagePercent}%`
|
| 25 |
};
|
| 26 |
} catch (error) {
|
|
|
|
| 28 |
}
|
| 29 |
}
|
| 30 |
|
| 31 |
+
// --- 辅助函数:获取出口公网IP---
|
| 32 |
function getPublicIP() {
|
| 33 |
return new Promise(resolve => {
|
|
|
|
| 34 |
exec('curl -s --max-time 5 ifconfig.me', (err, stdout) => {
|
| 35 |
+
if (err) resolve('无法获取公网IP (可能为NAT环境)');
|
| 36 |
+
else resolve(stdout.trim());
|
|
|
|
|
|
|
|
|
|
| 37 |
});
|
| 38 |
});
|
| 39 |
}
|
| 40 |
|
| 41 |
+
// --- 辅助函数:安全获取环境变量---
|
| 42 |
function getSafeEnvVars() {
|
| 43 |
const safeVars = {};
|
| 44 |
const sensitiveKeys = ['PASSWORD', 'SECRET', 'TOKEN', 'KEY'];
|
|
|
|
| 52 |
return safeVars;
|
| 53 |
}
|
| 54 |
|
| 55 |
+
// --- 辅助函数:检查 /data 目录状态(兼容不存在的情况)---
|
| 56 |
+
function checkDataDir() {
|
| 57 |
+
try {
|
| 58 |
+
if (fs.existsSync('/data')) {
|
| 59 |
+
// 目录存在,检查是否可写
|
| 60 |
+
fs.accessSync('/data', fs.constants.W_OK);
|
| 61 |
+
return { exists: true, writable: true };
|
| 62 |
+
} else {
|
| 63 |
+
return { exists: false, writable: false, reason: '目录不存在(免费层未挂载持久存储)' };
|
| 64 |
+
}
|
| 65 |
+
} catch (err) {
|
| 66 |
+
return { exists: true, writable: false, reason: err.message };
|
| 67 |
+
}
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
// --- 创建 HTTP 服务器 ---
|
| 71 |
const server = http.createServer(async (req, res) => {
|
| 72 |
+
// 1. 动态信息
|
| 73 |
const publicIP = await getPublicIP();
|
| 74 |
const diskInfo = getDiskUsage();
|
| 75 |
+
const dataDirStatus = checkDataDir();
|
| 76 |
|
| 77 |
+
// 2. 系统与硬件信息
|
| 78 |
const cpuInfo = os.cpus()[0];
|
| 79 |
+
const totalMem = (os.totalmem() / (1024 ** 3)).toFixed(2);
|
| 80 |
+
const freeMem = (os.freemem() / (1024 ** 3)).toFixed(2);
|
| 81 |
+
const usedMem = (totalMem - freeMem).toFixed(2);
|
| 82 |
const systemUptime = Math.floor(os.uptime() / 60) + ' 分钟';
|
| 83 |
|
| 84 |
+
// 3. 用户ID和权限
|
| 85 |
+
let userId = null, groupId = null;
|
| 86 |
+
try {
|
| 87 |
+
userId = process.getuid();
|
| 88 |
+
groupId = process.getgid();
|
| 89 |
+
} catch(e) { /* Windows 可能不支持 */ }
|
| 90 |
|
| 91 |
+
// 4. 构建环境报告
|
| 92 |
const envReport = {
|
| 93 |
+
// 访问信息
|
| 94 |
access: {
|
| 95 |
+
spaceUrl: `https://${process.env.SPACE_ID?.replace('/', '-') || 'unknown'}.hf.space`,
|
| 96 |
publicIP: publicIP,
|
| 97 |
internalIP: (() => {
|
| 98 |
const interfaces = os.networkInterfaces();
|
|
|
|
| 104 |
return '127.0.0.1';
|
| 105 |
})(),
|
| 106 |
hostname: os.hostname(),
|
| 107 |
+
spaceId: process.env.SPACE_ID || '未设置',
|
| 108 |
+
spaceRepoName: process.env.SPACE_REPO_NAME || '未设置',
|
| 109 |
},
|
| 110 |
+
// 硬件资源
|
| 111 |
hardware: {
|
| 112 |
cpuModel: cpuInfo?.model || '未知',
|
| 113 |
cpuCores: os.cpus().length,
|
|
|
|
| 118 |
uptime: systemUptime,
|
| 119 |
expectedFreeTier: '2 vCPU / 16GB RAM / 50GB 临时磁盘'
|
| 120 |
},
|
| 121 |
+
// Node.js 环境
|
| 122 |
nodeEnv: {
|
| 123 |
version: process.version,
|
| 124 |
platform: process.platform,
|
| 125 |
arch: process.arch,
|
| 126 |
execPath: process.execPath,
|
| 127 |
cwd: process.cwd(),
|
|
|
|
| 128 |
},
|
| 129 |
+
// 容器与权限
|
| 130 |
container: {
|
| 131 |
+
userId: userId,
|
| 132 |
+
groupId: groupId,
|
| 133 |
+
expectedUserId: '通常为 1000 (node 用户)',
|
| 134 |
+
isDataDirPresent: dataDirStatus.exists,
|
| 135 |
+
isDataDirWritable: dataDirStatus.writable,
|
| 136 |
+
dataDirDetail: dataDirStatus.reason || (dataDirStatus.writable ? '可写' : '不可写'),
|
| 137 |
+
hfHome: process.env.HF_HOME || '未设置',
|
| 138 |
+
persistentStorageAvailable: dataDirStatus.exists && dataDirStatus.writable,
|
| 139 |
},
|
| 140 |
+
// 关键环境变量(安全过滤)
|
| 141 |
envVars: getSafeEnvVars(),
|
| 142 |
+
// 部署建议
|
| 143 |
suggestions: [
|
| 144 |
'✅ 必须监听 0.0.0.0,并在 README.md 中设置 app_port: 7860',
|
| 145 |
+
'✅ 强烈建议为敏感信息使用 HF Secrets,勿硬编码',
|
| 146 |
+
'✅ 免费层磁盘非持久化,重启会丢失数据',
|
| 147 |
+
'✅ 如需持久化,考虑升级 Persistent Storage 或将数据备份到 HF Dataset',
|
| 148 |
+
'✅ 容器默认用户 UID 可能为 1000,Dockerfile 中建议使用现有 node 用户',
|
| 149 |
]
|
| 150 |
};
|
| 151 |
|
|
|
|
| 152 |
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
| 153 |
res.end(JSON.stringify(envReport, null, 2));
|
| 154 |
});
|