File size: 12,223 Bytes
741c4a4
f252513
 
 
741c4a4
f252513
 
 
 
 
 
bb1d66a
f252513
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c2204ae
 
 
 
 
f252513
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
741c4a4
f252513
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
741c4a4
 
 
f252513
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
741c4a4
f252513
741c4a4
 
f252513
 
 
 
 
 
 
 
 
 
741c4a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f252513
 
 
 
 
 
 
 
 
 
 
741c4a4
f252513
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
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)."
    );
  }
});