luadao / server.js
vipsphi's picture
Update server.js
bb1d66a verified
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)."
);
}
});