const express = require('express'); const { Low } = require('lowdb'); const { JSONFile } = require('lowdb/node'); const bodyParser = require('body-parser'); const cors = require('cors'); const crypto = require('crypto'); const fs = require('fs'); const path = require('path'); const multer = require('multer'); const port = process.env.PORT || 7860; const app = express(); // 管理员固定账号密码 const ADMIN_USER = "admin"; const ADMIN_PWD = "admin123"; // 数据库初始化 const dbPath = path.join(__dirname, 'db.json'); if (!fs.existsSync(dbPath)) { fs.writeFileSync(dbPath, JSON.stringify({ users: [], sessions: [] }, null, 2)); } const adapter = new JSONFile(dbPath); const db = new Low(adapter, { users: [], sessions: [] }); app.use(cors()); app.use(bodyParser.json()); app.use(express.static('public')); // 密码加密 function encryptPassword(pwd) { return crypto.createHash('sha256').update(pwd).digest('hex'); } // ===================== 用户登录接口(仅普通用户使用,管理员无需登录进后台) ===================== app.post('/api/login', async (req, res) => { const { username, password } = req.body; const hash = encryptPassword(password); await db.read(); // 管理员账号登录(仅用于网站普通功能登录,后台独立密码) if (username === ADMIN_USER && hash === encryptPassword(ADMIN_PWD)) { const token = crypto.randomBytes(16).toString('hex'); db.data.sessions.push({ token, username, expireTime: Date.now() + 86400000 }); await db.write(); return res.json({ code: 200, msg: "管理员登录成功", data: { token, username, isAdmin: true } }); } // 普通用户 const user = db.data.users.find(u => u.username === username && u.password === hash); if (!user) return res.json({ code: 400, msg: "用户名或密码错误" }); if (user.status !== "pass") return res.json({ code: 400, msg: "账号未通过审核,无法登录" }); const token = crypto.randomBytes(16).toString('hex'); db.data.sessions.push({ token, username, expireTime: Date.now() + 86400000 }); await db.write(); return res.json({ code: 200, msg: "登录成功", data: { token, username, isAdmin: false } }); }); // ===================== 用户注册接口 ===================== app.post('/api/register', async (req, res) => { const { username, password, phone, qq, wechat } = req.body; const regTime = new Date().toLocaleString(); if (!username || !password) return res.json({ code: 400, msg: "用户名、密码必填" }); if (password.length < 6) return res.json({ code: 400, msg: "密码至少6位" }); const fillList = [phone, qq, wechat].filter(v => v?.trim()); if (fillList.length === 0) return res.json({ code: 400, msg: "手机号/QQ/微信至少填写一项" }); await db.read(); if (db.data.users.find(u => u.username === username)) return res.json({ code: 400, msg: "用户名已被注册" }); const p = phone?.trim(), q = qq?.trim(), w = wechat?.trim(); if (p && db.data.users.find(u => u.phone === p)) return res.json({ code: 400, msg: "该手机号已注册" }); if (q && db.data.users.find(u => u.qq === q)) return res.json({ code: 400, msg: "该QQ已注册" }); if (w && db.data.users.find(u => u.wechat === w)) return res.json({ code: 400, msg: "该微信号已注册" }); db.data.users.push({ username, password: encryptPassword(password), phone: p || "", qq: q || "", wechat: w || "", status: "pending", registerTime: regTime }); await db.write(); return res.json({ code: 200, msg: "注册成功,等待管理员审核" }); }); // ===================== 校验管理员密码(主页点击管理按钮调用) ===================== app.post('/api/verifyAdminPwd', (req, res) => { const { inputPwd } = req.body; if (inputPwd === ADMIN_PWD) { return res.json({ code: 200, msg: "密码正确,可进入后台" }); } else { return res.json({ code: 400, msg: "管理员密码错误" }); } }); // ===================== 管理员后台接口(全部接收前端传入的管理员密码校验,无session读取) ===================== // 获取全部用户 app.get('/api/admin/userlist', async (req, res) => { const pwd = req.query.adminPwd; if (pwd !== ADMIN_PWD) return res.json({ code: 403, msg: "无管理员权限" }); await db.read(); res.json({ code: 200, data: db.data.users }); }); // 审核通过用户 app.post('/api/admin/pass', async (req, res) => { const { adminPwd, username } = req.body; if (adminPwd !== ADMIN_PWD) return res.json({ code: 403, msg: "无管理员权限" }); await db.read(); const user = db.data.users.find(u => u.username === username); if (!user) return res.json({ code: 400, msg: "用户不存在" }); user.status = "pass"; await db.write(); res.json({ code: 200, msg: "审核通过" }); }); // 删除用户 app.post('/api/admin/del', async (req, res) => { const { adminPwd, username } = req.body; if (adminPwd !== ADMIN_PWD) return res.json({ code: 403, msg: "无管理员权限" }); await db.read(); db.data.users = db.data.users.filter(u => u.username !== username); await db.write(); res.json({ code: 200, msg: "已删除用户" }); }); // ===================== 云电脑、网盘(仅区分普通用户审核状态,管理员无需登录也可访问) ===================== app.get('/api/cloudpc', async (req, res) => { const token = req.headers.authorization?.split(' ')[1]; await db.read(); // 管理员直接放行 if (token) { const session = db.data.sessions.find(s => s.token === token); if (session && session.username === ADMIN_USER) { return res.json({ code: 200, msg: "云电脑访问成功(管理员)" }); } } // 普通用户校验审核状态 if (!token) return res.json({ code: 401, msg: "请先登录账号" }); const session = db.data.sessions.find(s => s.token === token); if (!session) return res.json({ code: 401, msg: "登录过期" }); const user = db.data.users.find(u => u.username === session.username); if (!user || user.status !== "pass") return res.json({ code: 403, msg: "账号未审核,禁止访问" }); res.json({ code: 200, msg: "云电脑访问成功" }); }); app.get('/api/pan', async (req, res) => { const token = req.headers.authorization?.split(' ')[1]; await db.read(); if (token) { const session = db.data.sessions.find(s => s.token === token); if (session && session.username === ADMIN_USER) { return res.json({ code: 200, msg: "网盘访问成功(管理员)" }); } } if (!token) return res.json({ code: 401, msg: "请先登录账号" }); const session = db.data.sessions.find(s => s.token === token); if (!session) return res.json({ code: 401, msg: "登录过期" }); const user = db.data.users.find(u => u.username === session.username); if (!user || user.status !== "pass") return res.json({ code: 403, msg: "账号未审核,禁止访问" }); res.json({ code: 200, msg: "网盘访问成功" }); }); // 登录状态校验(仅前台展示欢迎文字,做完备判空) app.get('/api/checkLogin', async (req, res) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.json({ code: 401, msg: "未登录" }); await db.read(); const session = db.data.sessions.find(s => s.token === token); if (!session || !session.username) return res.json({ code: 401, msg: "登录过期" }); const isAdmin = session.username === ADMIN_USER; res.json({ code: 200, data: { username: session.username, isAdmin } }); }); // ========== 云盘模块依赖、工具函数、存储配置 ========== const CLOUD_DISK_PATH = path.join(__dirname, 'cloud-storage'); const USERS_FILE = path.join(CLOUD_DISK_PATH, 'users.json'); const MAX_STORAGE = 1 * 1024 * 1024 * 1024; // 1GB // 初始化云盘存储目录 if (!fs.existsSync(CLOUD_DISK_PATH)) fs.mkdirSync(CLOUD_DISK_PATH, { recursive: true }); if (!fs.existsSync(path.join(CLOUD_DISK_PATH, 'users'))) fs.mkdirSync(path.join(CLOUD_DISK_PATH, 'users'), { recursive: true }); if (!fs.existsSync(USERS_FILE)) fs.writeFileSync(USERS_FILE, '{}', 'utf8'); // 文件大小格式化 function formatSize(sizeBytes) { if (sizeBytes === 0) return '0 B'; const units = ['B', 'KB', 'MB', 'GB', 'TB']; let size = sizeBytes; for (let unit of units) { if (size < 1024) return size.toFixed(2) + ' ' + unit; size /= 1024; } return size.toFixed(2) + ' PB'; } // 获取用户存储空间占用 // 异步计算存储空间,不会卡死服务 async function getUserTotalSize(username) { const userDir = path.join(CLOUD_DISK_PATH, 'users', username); if (!fs.existsSync(userDir)) return 0; let total = 0; const scanDir = async (dir) => { const files = await fs.promises.readdir(dir); for (const name of files) { const fullPath = path.join(dir, name); const stat = await fs.promises.stat(fullPath); if (stat.isDirectory()) await scanDir(fullPath); else total += stat.size; } }; await scanDir(userDir); return total; } // multer上传配置 const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: MAX_STORAGE } }); // ========== 云盘接口路由 ========== // 获取用户信息、存储容量 app.get('/api/cloud/user-info', async (req, res) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.json({ code: 401, msg: "请先登录账号" }); await db.read(); const session = db.data.sessions.find(s => s.token === token); if (!session) return res.json({ code: 401, msg: "登录过期" }); const username = session.username; const userDir = path.join(CLOUD_DISK_PATH, 'users', username); if (!fs.existsSync(userDir)) fs.mkdirSync(userDir, { recursive: true }); // 注释掉同步阻塞的空间计算函数 // const used = getUserTotalSize(username); const used = 0; res.json({ code: 200, data: { username, used, max: MAX_STORAGE, usedText: "0 B", maxText: "1.00 GB" } }); }); // 获取文件列表 app.get('/api/cloud/list', async (req, res) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.json({ code: 401, msg: "请先登录账号" }); await db.read(); const session = db.data.sessions.find(s => s.token === token); if (!session) return res.json({ code: 401, msg: "登录过期" }); // 临时直接返回空数组,不读取本地文件夹,彻底消除同步阻塞 res.json({ code: 200, data: [] }); }); // 新建文件夹 app.post('/api/cloud/mkdir', async (req, res) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.json({ code: 401, msg: "请先登录账号" }); await db.read(); const session = db.data.sessions.find(s => s.token === token); if (!session) return res.json({ code: 401, msg: "登录过期" }); const username = session.username; const { path: dirPath, name } = req.body; const safePath = path.join(CLOUD_DISK_PATH, 'users', username, dirPath, name); if (!safePath.startsWith(path.join(CLOUD_DISK_PATH, 'users', username))) { return res.json({ code: 400, msg: "非法路径" }); } if (fs.existsSync(safePath)) return res.json({ code: 400, msg: "文件夹已存在" }); fs.mkdirSync(safePath, { recursive: true }); res.json({ code: 200, msg: "创建成功" }); }); // 文件上传 app.post('/api/cloud/upload', upload.single('file'), async (req, res) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.json({ code: 401, msg: "请先登录账号" }); await db.read(); const session = db.data.sessions.find(s => s.token === token); if (!session) return res.json({ code: 401, msg: "登录过期" }); const username = session.username; const { path: savePath } = req.body; const file = req.file; const totalUsed = getUserTotalSize(username); if (totalUsed + file.size > MAX_STORAGE) { return res.json({ code: 400, msg: "存储空间不足" }); } const targetDir = path.join(CLOUD_DISK_PATH, 'users', username, savePath); if (!targetDir.startsWith(path.join(CLOUD_DISK_PATH, 'users', username))) { return res.json({ code: 400, msg: "非法路径" }); } if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true }); const fullFile = path.join(targetDir, file.originalname); fs.writeFileSync(fullFile, file.buffer); res.json({ code: 200, msg: "上传成功" }); }); // 删除文件/文件夹 app.post('/api/cloud/delete', async (req, res) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.json({ code: 401, msg: "请先登录账号" }); await db.read(); const session = db.data.sessions.find(s => s.token === token); if (!session) return res.json({ code: 401, msg: "登录过期" }); const username = session.username; const { path: delPath, name } = req.body; const target = path.join(CLOUD_DISK_PATH, 'users', username, delPath, name); if (!target.startsWith(path.join(CLOUD_DISK_PATH, 'users', username))) { return res.json({ code: 400, msg: "非法路径" }); } if (!fs.existsSync(target)) return res.json({ code: 400, msg: "文件不存在" }); function rmDir(dir) { if (fs.statSync(dir).isDirectory()) { fs.readdirSync(dir).forEach(f => rmDir(path.join(dir, f))); fs.rmdirSync(dir); } else fs.unlinkSync(dir); } rmDir(target); res.json({ code: 200, msg: "删除成功" }); }); // 文件下载 app.get('/api/cloud/download', async (req, res) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.json({ code: 401, msg: "请先登录账号" }); await db.read(); const session = db.data.sessions.find(s => s.token === token); if (!session) return res.json({ code: 401, msg: "登录过期" }); const username = session.username; let filePath = req.query.path || ''; let fileName = req.query.name; const target = path.join(CLOUD_DISK_PATH, 'users', username, filePath, fileName); if (!target.startsWith(path.join(CLOUD_DISK_PATH, 'users', username))) { return res.json({ code: 400, msg: "非法路径" }); } if (!fs.existsSync(target) || fs.statSync(target).isDirectory()) { return res.json({ code: 400, msg: "文件不存在" }); } res.download(target, fileName); }); app.listen(port, "0.0.0.0", async () => { await db.read(); console.log(`服务启动,端口:${port}`); });