const { app: electronApp } = require("electron"); const express = require("express"); const bodyParser = require("body-parser"); const cors = require("cors"); const sqlite3 = require("sqlite3").verbose(); const { v4: uuidv4 } = require("uuid"); const path = require("path"); const RecaptchaSolver = require("./solver"); const http = require("http"); const { Server } = require("socket.io"); const PORT = 7860; const app = express(); const server = http.createServer(app); const io = new Server(server, { cors: { origin: "*" }, }); const db = new sqlite3.Database("./database.sqlite"); // --- INIT DB --- db.serialize(() => { // Thêm cột role để phân quyền (admin/user) db.run(`CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE, password TEXT, api_key TEXT, credits INTEGER DEFAULT 0, role TEXT DEFAULT 'user', hwid TEXT )`); db.run("ALTER TABLE users ADD COLUMN hwid TEXT", (err) => { if (err) { if (!err.message.includes("duplicate column name")) { console.error("Migration error (hwid):", err.message); } } }); db.run("UPDATE users SET role = 'admin' WHERE username = 'admin'"); // Bảng lưu lịch sử nạp tiền chờ duyệt db.run(`CREATE TABLE IF NOT EXISTS transactions ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, username TEXT, amount INTEGER, method TEXT, status TEXT DEFAULT 'pending', -- pending, approved, rejected created_at DATETIME DEFAULT CURRENT_TIMESTAMP )`); // Bảng log lịch sử sử dụng db.run(`CREATE TABLE IF NOT EXISTS usage_logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, action TEXT, details TEXT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP )`); }); app.use(cors()); app.use(bodyParser.json()); app.use(express.static(path.join(__dirname, "public"))); // --- HEALTH CHECK --- app.get("/ping", (req, res) => { res.status(200).send("pong"); }); // --- MIDDLEWARE CHECK ADMIN --- const requireAdmin = (req, res, next) => { const apiKey = req.headers["x-api-key"]; db.get("SELECT role FROM users WHERE api_key = ?", [apiKey], (err, row) => { if (row && row.role === "admin") next(); else res.status(403).json({ error: "Bạn không phải Admin!" }); }); }; // --- AUTH --- app.post("/api/register", (req, res) => { const { username, password, hwid } = req.body; const apiKey = uuidv4(); // Kiểm tra xem HWID này đã được cộng tiền lần nào chưa db.get("SELECT id FROM users WHERE hwid = ?", [hwid], (err, row) => { const initialCredits = row ? 0 : 100; // Nếu đã tồn tại HWID thì 0, mới thì 100 const message = row ? "Đăng ký thành công! (Máy này đã nhận bonus trước đó nên không được cộng thêm)" : "Đăng ký thành công! Bạn nhận được +100 Credits bonus."; const stmt = db.prepare( "INSERT INTO users (username, password, api_key, credits, role, hwid) VALUES (?, ?, ?, ?, 'user', ?)" ); stmt.run(username, password, apiKey, initialCredits, hwid, function (err) { if (err) return res.status(400).json({ error: "User đã tồn tại" }); res.json({ success: true, message: message }); io.emit("admin:new-user"); }); stmt.finalize(); }); }); app.post("/api/login", (req, res) => { const { username, password } = req.body; db.get( "SELECT * FROM users WHERE username = ? AND password = ?", [username, password], (err, row) => { if (!row) return res.status(401).json({ error: "Sai thông tin" }); res.json({ success: true, user: row }); } ); }); app.get("/api/me", (req, res) => { const apiKey = req.headers["x-api-key"]; db.get( "SELECT username, credits, api_key, role FROM users WHERE api_key = ?", [apiKey], (err, row) => { if (row) res.json(row); else res.status(401).json({ error: "Key lỗi" }); } ); }); // --- HISTORY API --- app.get("/api/history/usage", (req, res) => { const apiKey = req.headers["x-api-key"]; db.get("SELECT id FROM users WHERE api_key = ?", [apiKey], (err, user) => { if (!user) return res.status(401).json({ error: "Auth failed" }); db.all( "SELECT * FROM usage_logs WHERE user_id = ? ORDER BY id DESC LIMIT 50", [user.id], (err, rows) => { res.json(rows); } ); }); }); app.get("/api/history/transactions", (req, res) => { const apiKey = req.headers["x-api-key"]; db.get("SELECT id FROM users WHERE api_key = ?", [apiKey], (err, user) => { if (!user) return res.status(401).json({ error: "Auth failed" }); db.all( "SELECT * FROM transactions WHERE user_id = ? ORDER BY id DESC", [user.id], (err, rows) => { res.json(rows); } ); }); }); // --- USER: TẠO YÊU CẦU NẠP TIỀN --- app.post("/api/deposit", (req, res) => { const apiKey = req.headers["x-api-key"]; const { amount, method } = req.body; // amount tính bằng USD (1, 5, 10) db.get( "SELECT id, username FROM users WHERE api_key = ?", [apiKey], (err, user) => { if (!user) return res.status(401).json({ error: "User không tồn tại" }); const stmt = db.prepare( "INSERT INTO transactions (user_id, username, amount, method) VALUES (?, ?, ?, ?)" ); stmt.run(user.id, user.username, amount, method, function (err) { if (err) return res.status(500).json({ error: "Lỗi tạo giao dịch" }); res.json({ success: true, message: "Đã gửi yêu cầu! Vui lòng đợi Admin duyệt.", }); io.emit("admin:new-transaction"); // Notify admin }); stmt.finalize(); } ); }); // --- ADMIN API --- // 1. Lấy danh sách user app.get("/api/admin/users", requireAdmin, (req, res) => { db.all( "SELECT id, username, credits, role, api_key FROM users", [], (err, rows) => { res.json(rows); } ); }); // 2. Lấy danh sách giao dịch nạp tiền app.get("/api/admin/transactions", requireAdmin, (req, res) => { db.all("SELECT * FROM transactions ORDER BY id DESC", [], (err, rows) => { res.json(rows); }); }); // 3. Duyệt hoặc Từ chối nạp tiền app.post("/api/admin/approve", requireAdmin, (req, res) => { const { transId, action } = req.body; // action: 'approve' | 'reject' db.get("SELECT * FROM transactions WHERE id = ?", [transId], (err, trans) => { if (!trans || trans.status !== "pending") return res.status(400).json({ error: "Giao dịch không hợp lệ" }); if (action === "reject") { db.run("UPDATE transactions SET status = 'rejected' WHERE id = ?", [ transId, ]); db.run("UPDATE transactions SET status = 'rejected' WHERE id = ?", [ transId, ]); io.emit("transaction-updated"); return res.json({ success: true, message: "Đã từ chối giao dịch" }); } if (action === "approve") { const creditsToAdd = trans.amount * 1000; // $1 = 1000 credits db.serialize(() => { db.run("UPDATE transactions SET status = 'approved' WHERE id = ?", [ transId, ]); db.run("UPDATE users SET credits = credits + ? WHERE id = ?", [ creditsToAdd, trans.user_id, ]); }); return res.json({ success: true, message: `Đã duyệt! Cộng ${creditsToAdd} credits cho ${trans.username}`, }); io.emit("transaction-updated"); // Notify specific user to update credits (client filters by checking if it's their update) io.emit("user:credit-update"); } }); }); // 5. Xóa user app.post("/api/admin/delete-user", requireAdmin, (req, res) => { const { userId } = req.body; db.run("DELETE FROM users WHERE id = ?", [userId], function (err) { if (err) return res.status(500).json({ error: "Lỗi xóa user" }); res.json({ success: true, message: "Đã xóa người dùng thành công." }); io.emit("admin:user-deleted"); // Notify admin to refresh list }); }); // 4. Cộng/Trừ tiền thủ công cho user app.post("/api/admin/update-credits", requireAdmin, (req, res) => { const { userId, amount } = req.body; // amount có thể là số âm để trừ db.run( "UPDATE users SET credits = credits + ? WHERE id = ?", [amount, userId], function (err) { if (err) return res.status(500).json({ error: "Lỗi database" }); res.json({ success: true, message: "Đã cập nhật số dư user." }); io.emit("user:credit-update"); } ); }); // --- API GIẢI CAPTCHA (GIỮ NGUYÊN) --- app.post("/api/solve", async (req, res) => { // const { url, siteKey, action } = req.body; const { action } = req.body; const targetUrl = "https://labs.google"; const targetKey = "6LdsFiUsAAAAAIjVDZcuLhaHiDn5nnHVXVRQGeMV"; const apiKey = req.headers["x-api-key"]; if (!apiKey) return res.status(401).json({ error: "Thiếu API Key" }); db.get( "SELECT * FROM users WHERE api_key = ?", [apiKey], async (err, user) => { if (!user) return res.status(403).json({ error: "API Key không tồn tại" }); if (user.credits <= 0) return res.status(402).json({ error: "Hết tiền rồi!" }); const solver = new RecaptchaSolver(); try { const token = await solver.getRecaptchaToken( targetUrl, targetKey, action || undefined ); if (token && !token.startsWith("ERROR")) { db.run("UPDATE users SET credits = credits - 1 WHERE id = ?", [ user.id, ]); const logDetails = JSON.stringify({ url: targetUrl, status: "Success", length: token.length, }); db.run( "INSERT INTO usage_logs (user_id, action, details) VALUES (?, ?, ?)", [user.id, "SOLVE_CAPTCHA", logDetails] ); res.json({ success: true, token: token }); io.emit("user:credit-update"); } else { // Log Failure const logDetails = JSON.stringify({ url: targetUrl, status: "Failed", error: token || "Unknown Error", length: 0, }); db.run( "INSERT INTO usage_logs (user_id, action, details) VALUES (?, ?, ?)", [user.id, "SOLVE_CAPTCHA", logDetails] ); res.status(500).json({ success: false, error: token }); } } catch (e) { res.status(500).json({ success: false, error: e.message }); } } ); }); const https = require("https"); const fs = require("fs"); electronApp.on("window-all-closed", (e) => e.preventDefault()); electronApp.whenReady().then(() => { // 1. Start Default Port 3000 server.listen(PORT, () => console.log(`Server running at: http://localhost:${PORT}`) ); // 2. Start HTTP Port 80 try { const httpServer = http.createServer(app); io.attach(httpServer); httpServer.listen(80, () => console.log("HTTP Server running on port 80")); httpServer.on("error", (e) => console.error("Error on Port 80:", e.message) ); } catch (e) { console.error("Could not start HTTP server on port 80"); } // 3. Start HTTPS Port 443 const certPath = path.join(__dirname, "cert.pem"); const keyPath = path.join(__dirname, "key.pem"); if (fs.existsSync(certPath) && fs.existsSync(keyPath)) { try { const httpsServer = https.createServer( { key: fs.readFileSync(keyPath), cert: fs.readFileSync(certPath), }, app ); io.attach(httpsServer); httpsServer.listen(443, () => console.log("HTTPS Server running on port 443") ); httpsServer.on("error", (e) => console.error("Error on Port 443:", e.message) ); } catch (e) { console.error("Could not start HTTPS server on port 443"); } } else { console.log( "No SSL certificates found (cert.pem, key.pem). Skipping HTTPS (443)." ); } });