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