Spaces:
Paused
Paused
| // 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`); | |
| }); |