Spaces:
Paused
Paused
File size: 6,143 Bytes
41f0983 e094011 41f0983 e094011 41f0983 e094011 41f0983 e094011 41f0983 e094011 41f0983 e094011 41f0983 e094011 41f0983 e094011 41f0983 e094011 41f0983 e094011 41f0983 e094011 41f0983 e094011 41f0983 e094011 41f0983 e094011 41f0983 e094011 41f0983 e094011 41f0983 e094011 41f0983 e094011 41f0983 e094011 41f0983 e094011 41f0983 e094011 41f0983 e094011 41f0983 e094011 41f0983 | 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 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | // server.js
const http = require('http');
const os = require('os');
const fs = require('fs');
const { exec } = require('child_process');
// --- Hugging Face Spaces 核心配置 ---
const HOST = '0.0.0.0';
const PORT = process.env.PORT || 7860;
// --- 辅助函数:获取磁盘使用情况(需要处理 /data 可能不存在)---
function getDiskUsage() {
try {
// 获取当前工作目录所在的文件系统(通常是容器根目录)
const stats = fs.statfsSync('/');
const total = stats.bsize * stats.blocks;
const free = stats.bsize * stats.bfree;
const used = total - free;
const usagePercent = ((used / total) * 100).toFixed(2);
return {
total: (total / (1024 ** 3)).toFixed(2),
used: (used / (1024 ** 3)).toFixed(2),
free: (free / (1024 ** 3)).toFixed(2),
usagePercent: `${usagePercent}%`
};
} catch (error) {
return { error: '无法获取磁盘信息' };
}
}
// --- 辅助函数:获取出口公网IP---
function getPublicIP() {
return new Promise(resolve => {
exec('curl -s --max-time 5 ifconfig.me', (err, stdout) => {
if (err) resolve('无法获取公网IP (可能为NAT环境)');
else resolve(stdout.trim());
});
});
}
// --- 辅助函数:安全获取环境变量---
function getSafeEnvVars() {
const safeVars = {};
const sensitiveKeys = ['PASSWORD', 'SECRET', 'TOKEN', 'KEY'];
for (const [key, value] of Object.entries(process.env)) {
if (sensitiveKeys.some(k => key.toLowerCase().includes(k.toLowerCase()))) {
safeVars[key] = '****** (已隐藏)';
} else {
safeVars[key] = value;
}
}
return safeVars;
}
// --- 辅助函数:检查 /data 目录状态(兼容不存在的情况)---
function checkDataDir() {
try {
if (fs.existsSync('/data')) {
// 目录存在,检查是否可写
fs.accessSync('/data', fs.constants.W_OK);
return { exists: true, writable: true };
} else {
return { exists: false, writable: false, reason: '目录不存在(免费层未挂载持久存储)' };
}
} catch (err) {
return { exists: true, writable: false, reason: err.message };
}
}
// --- 创建 HTTP 服务器 ---
const server = http.createServer(async (req, res) => {
// 1. 动态信息
const publicIP = await getPublicIP();
const diskInfo = getDiskUsage();
const dataDirStatus = checkDataDir();
// 2. 系统与硬件信息
const cpuInfo = os.cpus()[0];
const totalMem = (os.totalmem() / (1024 ** 3)).toFixed(2);
const freeMem = (os.freemem() / (1024 ** 3)).toFixed(2);
const usedMem = (totalMem - freeMem).toFixed(2);
const systemUptime = Math.floor(os.uptime() / 60) + ' 分钟';
// 3. 用户ID和权限
let userId = null, groupId = null;
try {
userId = process.getuid();
groupId = process.getgid();
} catch(e) { /* Windows 可能不支持 */ }
// 4. 构建环境报告
const envReport = {
// 访问信息
access: {
spaceUrl: `https://${process.env.SPACE_ID?.replace('/', '-') || 'unknown'}.hf.space`,
publicIP: publicIP,
internalIP: (() => {
const interfaces = os.networkInterfaces();
for (const iface of Object.values(interfaces)) {
for (const addr of iface) {
if (addr.family === 'IPv4' && !addr.internal) return addr.address;
}
}
return '127.0.0.1';
})(),
hostname: os.hostname(),
spaceId: process.env.SPACE_ID || '未设置',
spaceRepoName: process.env.SPACE_REPO_NAME || '未设置',
},
// 硬件资源
hardware: {
cpuModel: cpuInfo?.model || '未知',
cpuCores: os.cpus().length,
totalRamGB: totalMem,
usedRamGB: usedMem,
freeRamGB: freeMem,
disk: diskInfo,
uptime: systemUptime,
expectedFreeTier: '2 vCPU / 16GB RAM / 50GB 临时磁盘'
},
// Node.js 环境
nodeEnv: {
version: process.version,
platform: process.platform,
arch: process.arch,
execPath: process.execPath,
cwd: process.cwd(),
},
// 容器与权限
container: {
userId: userId,
groupId: groupId,
expectedUserId: '通常为 1000 (node 用户)',
isDataDirPresent: dataDirStatus.exists,
isDataDirWritable: dataDirStatus.writable,
dataDirDetail: dataDirStatus.reason || (dataDirStatus.writable ? '可写' : '不可写'),
hfHome: process.env.HF_HOME || '未设置',
persistentStorageAvailable: dataDirStatus.exists && dataDirStatus.writable,
},
// 关键环境变量(安全过滤)
envVars: getSafeEnvVars(),
// 部署建议
suggestions: [
'✅ 必须监听 0.0.0.0,并在 README.md 中设置 app_port: 7860',
'✅ 强烈建议为敏感信息使用 HF Secrets,勿硬编码',
'✅ 免费层磁盘非持久化,重启会丢失数据',
'✅ 如需持久化,考虑升级 Persistent Storage 或将数据备份到 HF Dataset',
'✅ 容器默认用户 UID 可能为 1000,Dockerfile 中建议使用现有 node 用户',
]
};
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
res.end(JSON.stringify(envReport, null, 2));
});
// 启动服务器
server.listen(PORT, HOST, () => {
console.log(`🔍 Hugging Face Spaces 环境探针已启动`);
console.log(`📡 监听地址: http://${HOST}:${PORT}`);
console.log(`🌐 公网访问: https://${process.env.SPACE_ID?.replace('/', '-') || 'your-space'}.hf.space`);
console.log(`💡 提示: 访问上述 URL 即可查看详细的环境报告 JSON`);
}); |