// PNG 序列动画预览工具 - 服务器主文件 // 负责启动 HTTP 服务器和静态文件服务 const http = require('http'); const fs = require('fs'); const path = require('path'); const url = require('url'); const TextureReader = require('./texture-reader'); const ZipHandler = require('./zip'); const DiskManager = require('./disk'); const ReplaceCharacterHandler = require('./replace-character'); const RemoveBackgroundBase64 = require('./remove-background-base64'); const { handleLoginRequest, handleCheckPhoneRequest } = require('./login'); const { handleRegisterRequest } = require('./register'); const PORT = 3000; const SERVER_DIR = __dirname; // Server 目录 const CLIENT_DIR = path.join(__dirname, '..', 'Client'); // Client 目录 const ADMIN_DIR = path.join(__dirname, '..', 'admin'); // Admin 目录 // MIME 类型映射 const mimeTypes = { '.html': 'text/html', '.js': 'text/javascript', '.css': 'text/css', '.png': 'image/png', '.jpg': 'image/jpeg', '.gif': 'image/gif', '.json': 'application/json', }; // 初始化 TextureReader const textureReader = new TextureReader(SERVER_DIR); // 初始化 DiskManager const diskManager = new DiskManager(); // 初始化 StoreManager const StoreManager = require('./store/store'); const storeManager = new StoreManager(); // 初始化 Pay 模块 const { handlePurchaseRequest, handleGetPurchaseHistory } = require('./pay'); // 初始化 WebSocket 服务器(用于 matting-server 连接) require('./socket-connecting'); // 创建 HTTP 服务器 const server = http.createServer((req, res) => { const parsedUrl = url.parse(req.url, true); let pathname = parsedUrl.pathname; // 添加 CORS 头(允许 file:// 协议访问) res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); // 处理 OPTIONS 预检请求 if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; } // 调试日志:记录所有API请求 if (pathname.startsWith('/api/')) { console.log(`[Server] ${req.method} ${pathname}`); } // API: 获取可用的文件夹列表 if (pathname === '/api/folders') { textureReader.handleGetFolders(res); return; } // API: 获取指定文件夹中的帧文件列表 if (pathname.startsWith('/api/frames/')) { const parsedUrl = url.parse(req.url, true); const encodedPath = pathname.replace('/api/frames/', ''); // 对路径的每一段进行解码 const folderName = encodedPath.split('/').map(seg => decodeURIComponent(seg)).join('/'); const username = parsedUrl.query.username; // 从查询参数获取用户名 console.log('[Server] API请求帧列表, 原始:', encodedPath, '解码后:', folderName, '用户名:', username); // 如果有用户名,尝试从用户网盘查找 if (username) { const { handleGetUserFrames } = require('./disk'); diskManager.handleGetUserFrames(username, folderName, res); } else { // 回退到旧的 TextureReader(兼容旧代码) textureReader.handleGetFrames(folderName, res); } return; } // API: 打包 sprite sheet 为 ZIP if (pathname === '/api/pack') { ZipHandler.handlePackRequest(req, res); return; } // API: 角色替换(图生图)- 旧版本,保留兼容 if (pathname === '/api/replace-character') { ReplaceCharacterHandler.handleReplaceRequest(req, res); return; } // API: AI生图(队列版本) if (pathname === '/api/ai/generate') { const { handleAIRequest } = require('./ai-queue'); handleAIRequest(req, res); return; } // API: AI生图重试(免费) if (pathname === '/api/ai/retry' && req.method === 'POST') { const { handleRetryRequest } = require('./ai-queue'); handleRetryRequest(req, res); return; } // API: 获取AI生成的图片 if (pathname === '/api/ai/image') { const parsedUrl = require('url').parse(req.url, true); const { username, id } = parsedUrl.query; if (!username || !id) { res.writeHead(400, { 'Content-Type': 'text/plain' }); res.end('Missing parameters'); return; } try { const fs = require('fs'); const path = require('path'); const usersDir = path.join(__dirname, 'users'); const userDir = path.join(usersDir, username.toLowerCase()); const aiDir = path.join(userDir, 'ai-images'); const imagePath = path.join(aiDir, `${id}.png`); // 安全检查 const normalizedPath = path.normalize(imagePath); const normalizedAiDir = path.normalize(aiDir); if (!normalizedPath.startsWith(normalizedAiDir)) { res.writeHead(403, { 'Content-Type': 'text/plain' }); res.end('Access denied'); return; } if (fs.existsSync(imagePath)) { const imageData = fs.readFileSync(imagePath); res.writeHead(200, { 'Content-Type': 'image/png', 'Access-Control-Allow-Origin': '*', 'Cache-Control': 'public, max-age=3600' }); res.end(imageData); } else { res.writeHead(404, { 'Content-Type': 'text/plain' }); res.end('Image not found'); } } catch (error) { console.error('[Server] 获取AI图片失败:', error); res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Internal server error'); } return; } // API: 获取AI生成预览图(原始texture) if (pathname === '/api/ai/preview') { const parsedUrl = require('url').parse(req.url, true); const { username, id } = parsedUrl.query; if (!username || !id) { res.writeHead(400, { 'Content-Type': 'text/plain' }); res.end('Missing parameters'); return; } try { const fs = require('fs'); const path = require('path'); const usersDir = path.join(__dirname, 'users'); const userDir = path.join(usersDir, username.toLowerCase()); const aiDir = path.join(userDir, 'ai-images'); const imagePath = path.join(aiDir, `${id}_preview.png`); // 安全检查 const normalizedPath = path.normalize(imagePath); const normalizedAiDir = path.normalize(aiDir); if (!normalizedPath.startsWith(normalizedAiDir)) { res.writeHead(403, { 'Content-Type': 'text/plain' }); res.end('Access denied'); return; } if (fs.existsSync(imagePath)) { const imageData = fs.readFileSync(imagePath); res.writeHead(200, { 'Content-Type': 'image/png', 'Access-Control-Allow-Origin': '*', 'Cache-Control': 'public, max-age=3600' }); res.end(imageData); } else { res.writeHead(404, { 'Content-Type': 'text/plain' }); res.end('Preview not found'); } } catch (error) { console.error('[Server] 获取AI预览图失败:', error); res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Internal server error'); } return; } // API: Base64 图片抠图 if (pathname === '/api/remove-background-base64') { RemoveBackgroundBase64.handleRequest(req, res); return; } // API: 普通抠图(使用 image-matting.py) if (pathname === '/api/matting-normal') { const MattingNormal = require('./matting-normal'); MattingNormal.handleRequest(req, res); return; } // API: VIP抠图(使用 BiRefNet) if (pathname === '/api/matting-vip') { const MattingVIP = require('./matting-vip'); MattingVIP.handleRequest(req, res); return; } // API: VIP抠图队列(异步处理) if (pathname === '/api/vip-matting/queue') { const VIPMatting = require('./vip-matting'); VIPMatting.handleQueueRequest(req, res); return; } // API: 网盘 - 获取文件列表 if (pathname === '/api/disk/list') { diskManager.handleListRequest(req, res); return; } // API: 网盘 - 上传文件 if (pathname === '/api/disk/upload') { diskManager.handleUploadRequest(req, res); return; } // API: 网盘 - 创建文件夹 if (pathname === '/api/disk/create-folder') { diskManager.handleCreateFolderRequest(req, res); return; } // API: 网盘 - 下载文件 if (pathname === '/api/disk/download') { diskManager.handleDownloadRequest(req, res); return; } // API: 网盘 - 重命名 if (pathname === '/api/disk/rename') { diskManager.handleRenameRequest(req, res); return; } // API: 网盘 - 图片预览 if (pathname === '/api/disk/preview') { diskManager.handlePreviewRequest(req, res); return; } // API: 网盘 - 移动文件/文件夹 if (pathname === '/api/disk/move') { diskManager.handleMoveRequest(req, res); return; } // API: 网盘 - 复制文件/文件夹 if (pathname === '/api/disk/copy') { diskManager.handleCopyRequest(req, res); return; } // API: 网盘 - 删除文件/文件夹 if (pathname === '/api/disk/delete') { diskManager.handleDeleteRequest(req, res); return; } // API: 网盘 - 一键抠背景 if (pathname === '/api/disk/remove-background') { diskManager.handleRemoveBackgroundRequest(req, res); return; } // API: 网盘 - 剪裁最小区域 if (pathname === '/api/disk/crop-mini') { diskManager.handleCropMiniRequest(req, res); return; } // API: 用户注册 if (pathname === '/api/register') { handleRegisterRequest(req, res); return; } // API: 用户登录 if (pathname === '/api/login') { handleLoginRequest(req, res); return; } // API: 检查手机号是否存在 if (pathname === '/api/check-phone') { handleCheckPhoneRequest(req, res); return; } // API: 获取用户点数 if (pathname === '/api/user/points') { const { handleGetUserPoints } = require('./user'); handleGetUserPoints(req, res); return; } // API: 扣除用户点数 if (pathname === '/api/user/deduct-points' && req.method === 'POST') { const { handleDeductUserPoints } = require('./user'); handleDeductUserPoints(req, res); return; } // API: 获取用户信息 if (pathname === '/api/user/info') { const { handleGetUserInfo } = require('./user'); handleGetUserInfo(req, res); return; } // API: 更新用户信息 if (pathname === '/api/user/update') { const { handleUpdateUser } = require('./user'); handleUpdateUser(req, res); return; } // API: 上传头像 if (pathname === '/api/user/avatar') { const { handleUploadAvatar } = require('./user'); handleUploadAvatar(req, res); return; } // API: 获取AI生图历史 if (pathname === '/api/ai/history') { const { handleGetAIHistory } = require('./user'); handleGetAIHistory(req, res); return; } // API: 充值 if (pathname === '/api/recharge') { const { handleRecharge } = require('./user'); handleRecharge(req, res); return; } // API: 管理后台 - 获取所有用户 if (pathname === '/api/admin/users') { const { handleGetAllUsers } = require('./admin'); handleGetAllUsers(req, res); return; } // API: 管理后台 - 更新用户 if (pathname === '/api/admin/users/update') { const { handleAdminUpdateUser } = require('./admin'); handleAdminUpdateUser(req, res); return; } // API: 管理后台 - 上传商店素材 if (pathname === '/api/admin/store/upload') { const { handleAdminUploadStore } = require('./admin'); handleAdminUploadStore(req, res); return; } // API: 管理后台 - 删除商店素材 if (pathname === '/api/admin/store/delete') { console.log('[Server] 处理删除请求'); const { handleAdminDeleteStore } = require('./admin'); handleAdminDeleteStore(req, res); // 清除分类缓存(删除可能影响分类列表) storeManager.clearCategoriesCache(); return; } // API: 管理后台 - 创建分类文件夹 if (pathname === '/api/admin/store/create-folder') { const { handleAdminCreateFolder } = require('./admin'); handleAdminCreateFolder(req, res); // 清除分类缓存 storeManager.clearCategoriesCache(); return; } // API: 管理后台 - 重命名商店素材 if (pathname === '/api/admin/store/rename') { const { handleAdminRenameStore } = require('./admin'); handleAdminRenameStore(req, res); // 清除分类缓存(重命名可能影响分类列表) storeManager.clearCategoriesCache(); return; } // API: 管理后台 - 更新资源价格 if (pathname === '/api/admin/store/update-price') { const { handleAdminUpdatePrice } = require('./admin'); handleAdminUpdatePrice(req, res); // 清除价格缓存 storeManager.clearPricesCache(); return; } // API: 管理后台 - 获取货币设置 if (pathname === '/api/admin/currency/settings' && req.method === 'GET') { const { handleGetCurrencySettings } = require('./admin'); handleGetCurrencySettings(req, res); return; } // API: 管理后台 - 保存货币设置 if (pathname === '/api/admin/currency/settings' && req.method === 'POST') { const { handleSaveCurrencySettings } = require('./admin'); handleSaveCurrencySettings(req, res); return; } // API: 管理后台 - 获取商品定价设置 if (pathname === '/api/admin/product-pricing/settings' && req.method === 'GET') { const { handleGetProductPricingSettings } = require('./admin'); handleGetProductPricingSettings(req, res); return; } // API: 管理后台 - 保存商品定价设置 if (pathname === '/api/admin/product-pricing/settings' && req.method === 'POST') { const { handleSaveProductPricingSettings } = require('./admin'); handleSaveProductPricingSettings(req, res); return; } // API: 客户端 - 获取商品定价(用于显示价格) if (pathname === '/api/product-pricing' && req.method === 'GET') { const { handleGetProductPricingSettings } = require('./admin'); handleGetProductPricingSettings(req, res); return; } // API: 管理后台 - 更新分类排序 if (pathname === '/api/admin/store/update-order') { let body = ''; req.on('data', chunk => { body += chunk.toString(); }); req.on('end', async () => { try { const data = JSON.parse(body); const { order } = data; if (!order || !Array.isArray(order)) { res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' }); res.end(JSON.stringify({ success: false, message: '缺少排序数据' })); return; } const success = await storeManager.saveCategoryOrder(order); if (success) { res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' }); res.end(JSON.stringify({ success: true, message: '排序已保存' })); } else { res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' }); res.end(JSON.stringify({ success: false, message: '保存排序失败' })); } } catch (error) { console.error('[Server] 更新分类排序失败:', error); res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' }); res.end(JSON.stringify({ success: false, message: error.message })); } }); return; } // API: 商店 - 获取分类列表 if (pathname === '/api/store/categories') { storeManager.handleGetCategories(req, res); return; } // API: 商店 - 获取资源列表 if (pathname === '/api/store/resources') { storeManager.handleGetResources(req, res); return; } // API: 商店 - 预览图 if (pathname === '/api/store/preview') { storeManager.handlePreview(req, res); return; } // API: 商店 - 获取帧列表 if (pathname === '/api/store/frames') { storeManager.handleGetFrames(req, res); return; } // API: 商店 - 获取帧图片 if (pathname === '/api/store/frame') { storeManager.handleGetFrame(req, res); return; } // API: 支付 - 购买资源 if (pathname === '/api/pay/purchase') { handlePurchaseRequest(req, res); return; } // API: 支付 - 检查资源是否存在 if (pathname === '/api/pay/check-resource') { const { handleCheckResourceRequest } = require('./pay'); handleCheckResourceRequest(req, res); return; } // API: 获取用户购买记录 if (pathname === '/api/pay/purchase-history' && req.method === 'GET') { handleGetPurchaseHistory(req, res); return; } // API: 获取默认头像列表 if (pathname === '/api/avatars/default') { const fs = require('fs'); const avatarDir = path.join(__dirname, 'avatar'); fs.readdir(avatarDir, (err, files) => { if (err) { res.writeHead(500, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }); res.end(JSON.stringify({ success: false, error: '读取头像目录失败' })); return; } // 过滤出图片文件 const imageFiles = files.filter(file => { const ext = path.extname(file).toLowerCase(); return ['.png', '.jpg', '.jpeg', '.gif'].includes(ext); }); // 返回头像文件名列表 res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }); res.end(JSON.stringify({ success: true, avatars: imageFiles })); }); return; } // 处理 favicon.ico 请求,重定向到 static/favicon.png if (pathname === '/favicon.ico') { pathname = '/static/favicon.png'; } // 默认首页 - 跳转到管理后台 if (pathname === '/') { res.writeHead(302, { 'Location': '/admin/index.html' }); res.end(); return; } // /admin 路径也跳转到管理后台 if (pathname === '/admin' || pathname === '/admin/') { res.writeHead(302, { 'Location': '/admin/index.html' }); res.end(); return; } let filePath; // 如果是 admin 相关的请求,从 admin 目录提供 if (pathname.startsWith('/admin/')) { // 移除开头的 /,然后拼接路径 const relativePath = pathname.substring(1); // 移除开头的 / // 解码 URL 编码的路径(处理中文、空格等特殊字符) const decodedPath = decodeURIComponent(relativePath); filePath = path.join(ADMIN_DIR, decodedPath.replace(/^admin\//, '')); } else if (pathname.startsWith('/texture/') || pathname.startsWith('/avatar/') || pathname.startsWith('/users/')) { // 如果是 texture、avatar 或 users 相关的请求,从 server 目录提供 // 移除开头的 /,然后拼接路径 const relativePath = pathname.substring(1); // 移除开头的 / // 解码 URL 编码的路径(处理中文、空格等特殊字符) const decodedPath = decodeURIComponent(relativePath); filePath = path.join(SERVER_DIR, decodedPath); } else { // 其他请求从 Client 目录提供 const relativePath = pathname.startsWith('/') ? pathname.substring(1) : pathname; // 解码 URL 编码的路径 const decodedPath = decodeURIComponent(relativePath); filePath = path.join(CLIENT_DIR, decodedPath); } // 检查文件是否存在并获取文件信息 fs.stat(filePath, (statErr, stats) => { if (statErr) { // 文件不存在,返回 404(静默处理) res.writeHead(404, { 'Content-Type': 'text/plain' }); res.end(''); return; } // 获取文件扩展名 const ext = path.extname(filePath).toLowerCase(); const contentType = mimeTypes[ext] || 'application/octet-stream'; // 构建响应头 const headers = { 'Content-Type': contentType }; // 如果是图片文件,添加缓存头 if (ext === '.png' || ext === '.jpg' || ext === '.jpeg' || ext === '.gif') { // 设置长期缓存(1年) headers['Cache-Control'] = 'public, max-age=31536000, immutable'; // 添加 ETag 用于缓存验证 const etag = `"${stats.mtime.getTime()}-${stats.size}"`; headers['ETag'] = etag; // 检查客户端是否发送了 If-None-Match 头(缓存验证) const ifNoneMatch = req.headers['if-none-match']; if (ifNoneMatch === etag) { // 文件未修改,返回 304 Not Modified res.writeHead(304, headers); res.end(); return; } } // 读取文件 fs.readFile(filePath, (readErr, data) => { if (readErr) { res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Internal Server Error'); return; } // 返回文件 res.writeHead(200, headers); res.end(data); }); }); }); // 启动服务器 server.listen(PORT, () => { // 立即清屏(使用 ANSI 转义序列) process.stdout.write('\x1B[2J\x1B[0f'); // 使用 setTimeout 延迟输出,确保批处理脚本的输出先完成 setTimeout(() => { console.log('========================================'); console.log(' PNG 序列动画预览工具 - 服务器'); console.log('========================================'); console.log(`服务器运行在: http://localhost:${PORT}`); console.log(`用户数据路径: ${path.join(SERVER_DIR, 'users')}`); console.log('========================================'); console.log('按 Ctrl+C 或关闭此窗口停止服务器'); console.log(''); }, 1000); // 延迟 1000ms 输出,避免与批处理脚本输出重叠 }); // 优雅关闭处理 function gracefulShutdown() { console.log('\n正在关闭服务器...'); server.close(() => { console.log('服务器已关闭'); process.exit(0); }); } process.on('SIGINT', gracefulShutdown); process.on('SIGTERM', gracefulShutdown); // Windows 下处理窗口关闭事件 if (process.platform === 'win32') { const readline = require('readline'); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); rl.on('SIGINT', () => { process.emit('SIGINT'); }); }