|
|
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"); |
|
|
|
|
|
|
|
|
db.serialize(() => { |
|
|
|
|
|
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'"); |
|
|
|
|
|
|
|
|
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 |
|
|
)`); |
|
|
|
|
|
|
|
|
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"))); |
|
|
|
|
|
|
|
|
app.get("/ping", (req, res) => { |
|
|
res.status(200).send("pong"); |
|
|
}); |
|
|
|
|
|
|
|
|
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!" }); |
|
|
}); |
|
|
}; |
|
|
|
|
|
|
|
|
app.post("/api/register", (req, res) => { |
|
|
const { username, password, hwid } = req.body; |
|
|
const apiKey = uuidv4(); |
|
|
|
|
|
|
|
|
db.get("SELECT id FROM users WHERE hwid = ?", [hwid], (err, row) => { |
|
|
const initialCredits = row ? 0 : 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" }); |
|
|
} |
|
|
); |
|
|
}); |
|
|
|
|
|
|
|
|
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); |
|
|
} |
|
|
); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
app.post("/api/deposit", (req, res) => { |
|
|
const apiKey = req.headers["x-api-key"]; |
|
|
const { amount, method } = req.body; |
|
|
|
|
|
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"); |
|
|
}); |
|
|
stmt.finalize(); |
|
|
} |
|
|
); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app.get("/api/admin/users", requireAdmin, (req, res) => { |
|
|
db.all( |
|
|
"SELECT id, username, credits, role, api_key FROM users", |
|
|
[], |
|
|
(err, rows) => { |
|
|
res.json(rows); |
|
|
} |
|
|
); |
|
|
}); |
|
|
|
|
|
|
|
|
app.get("/api/admin/transactions", requireAdmin, (req, res) => { |
|
|
db.all("SELECT * FROM transactions ORDER BY id DESC", [], (err, rows) => { |
|
|
res.json(rows); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
app.post("/api/admin/approve", requireAdmin, (req, res) => { |
|
|
const { transId, action } = req.body; |
|
|
|
|
|
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; |
|
|
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"); |
|
|
|
|
|
io.emit("user:credit-update"); |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
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"); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
app.post("/api/admin/update-credits", requireAdmin, (req, res) => { |
|
|
const { userId, amount } = req.body; |
|
|
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"); |
|
|
} |
|
|
); |
|
|
}); |
|
|
|
|
|
|
|
|
app.post("/api/solve", async (req, res) => { |
|
|
|
|
|
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 { |
|
|
|
|
|
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(() => { |
|
|
|
|
|
server.listen(PORT, () => |
|
|
console.log(`Server running at: http://localhost:${PORT}`) |
|
|
); |
|
|
|
|
|
|
|
|
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"); |
|
|
} |
|
|
|
|
|
|
|
|
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)." |
|
|
); |
|
|
} |
|
|
}); |
|
|
|