📖 API Docs — SHOPGAME.VN

🛠️ Admin ← Trang chủ
Tất cả endpoint trả về JSON dạng { ok: true/false, ... }. Xác thực bằng session cookie (mặc định) hoặc JWT qua header Authorization: Bearer <token>. Các endpoint gắn 🔒 login cần đăng nhập, 🛠 admin cần quyền admin. Mỗi endpoint đều có 💻 Code ví dụ (cURL + JavaScript) — bấm vào là xem, copy chạy luôn. Mẹo cURL: đăng nhập với -c cookies.txt để lưu phiên, rồi gọi các API khác với -b cookies.txt.

Hệ thống

GET/api/health

Kiểm tra server sống (dùng cho healthcheck/Docker).

💻 Code ví dụ
# cURL
curl "http://localhost:3000/api/health"

# → { "ok": true, "uptime": 123.45 }
// JavaScript (fetch)
const r = await fetch("/api/health", {
  credentials: "include"
});
const data = await r.json();
console.log(data); // → { "ok": true, "uptime": 123.45 }
GET/api/captcha

Lấy captcha phép tính. Trả về { id, question } — gửi lại captchaId + captchaAnswer khi đăng ký. Mỗi captcha dùng 1 lần, hết hạn sau 5 phút.

💻 Code ví dụ
# cURL
curl "http://localhost:3000/api/captcha"

# → { "ok": true, "id": "abc123", "question": "3 + 6 = ?" }
// JavaScript (fetch)
const r = await fetch("/api/captcha", {
  credentials: "include"
});
const data = await r.json();
console.log(data); // → { "ok": true, "id": "abc123", "question": "3 + 6 = ?" }
POST/api/track

Ghi nhận analytics. Body: { "event": "pageview" }

💻 Code ví dụ
# cURL
curl -X POST "http://localhost:3000/api/track" \
  -H "Content-Type: application/json" \
  -d '{"event":"pageview"}'

# → { "ok": true }
// JavaScript (fetch)
const r = await fetch("/api/track", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  credentials: "include",
  body: JSON.stringify({ event: "pageview" })
});
const data = await r.json();
console.log(data); // → { "ok": true }
POST/api/translate

Dịch đa ngôn ngữ — chuỗi fallback: Google Translate (unofficial) → MyMemory → LibreTranslate. Body: { "texts": ["Xin chào"], "target": "en", "source": "vi" }. target hỗ trợ: en, vi, zh-CN, zh-TW, ko, ja. Trả về { ok, translations, providers }.

💻 Code ví dụ
# cURL
curl -X POST "http://localhost:3000/api/translate" \
  -H "Content-Type: application/json" \
  -d '{"texts":["Xin chào","Mua ngay"],"target":"en","source":"vi"}'

# → { "ok": true, "translations": ["Hello", "Buy now"], "providers": ["google", "google"] }
// JavaScript (fetch)
const r = await fetch("/api/translate", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  credentials: "include",
  body: JSON.stringify({ texts: ["Xin chào", "Mua ngay"], target: "en", source: "vi" })
});
const data = await r.json();
console.log(data); // → { "ok": true, "translations": ["Hello", "Buy now"], "providers": ["google", "google"] }

Đăng ký

POST/api/register

Bước 1 — gửi thông tin, server gửi mã 6 số về email (qua Email Queue).

{
  "username": "nguoichoi1",
  "email": "a@gmail.com",
  "password": "matkhau123",
  "confirm": "matkhau123",
  "captchaId": "<id từ /api/captcha>",
  "captchaAnswer": 9
}
💻 Code ví dụ
# cURL
curl -X POST "http://localhost:3000/api/register" \
  -H "Content-Type: application/json" \
  -d '{"username":"nguoichoi1","email":"a@gmail.com","password":"matkhau123","confirm":"matkhau123","captchaId":"abc123","captchaAnswer":9}'

# → { "ok": true } (mã 6 số đã gửi về email)
// JavaScript (fetch)
const r = await fetch("/api/register", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  credentials: "include",
  body: JSON.stringify({ username: "nguoichoi1", email: "a@gmail.com", password: "matkhau123", confirm: "matkhau123", captchaId: "abc123", captchaAnswer: 9 })
});
const data = await r.json();
console.log(data); // → { "ok": true } (mã 6 số đã gửi về email)
POST/api/register/verify

Bước 2 — xác minh mã → tạo tài khoản và đăng nhập luôn. Body: { "email", "code" }

💻 Code ví dụ
# cURL
curl -X POST "http://localhost:3000/api/register/verify" \
  -H "Content-Type: application/json" \
  -d '{"email":"a@gmail.com","code":"123456"}'

# → { "ok": true } (tài khoản đã tạo + đăng nhập luôn)
// JavaScript (fetch)
const r = await fetch("/api/register/verify", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  credentials: "include",
  body: JSON.stringify({ email: "a@gmail.com", code: "123456" })
});
const data = await r.json();
console.log(data); // → { "ok": true } (tài khoản đã tạo + đăng nhập luôn)

Đăng nhập / Phiên

POST/api/login

Body: { "username", "password" }. Nếu tài khoản bật 2FA, trả về { ok: true, twofa: "email" | "totp" } → gọi tiếp /api/login/verify.

💻 Code ví dụ
# cURL
curl -c cookies.txt -X POST "http://localhost:3000/api/login" \
  -H "Content-Type: application/json" \
  -d '{"username":"nguoichoi1","password":"matkhau123"}'

# → { "ok": true } hoặc { "ok": true, "twofa": "email" } nếu bật 2FA
// JavaScript (fetch)
const r = await fetch("/api/login", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  credentials: "include",
  body: JSON.stringify({ username: "nguoichoi1", password: "matkhau123" })
});
const data = await r.json();
console.log(data); // → { "ok": true } hoặc { "ok": true, "twofa": "email" } nếu bật 2FA
💻 Code ví dụ
# cURL
curl -b cookies.txt -X POST "http://localhost:3000/api/login/verify" \
  -H "Content-Type: application/json" \
  -d '{"code":"123456"}'

# → { "ok": true }
// JavaScript (fetch)
const r = await fetch("/api/login/verify", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  credentials: "include",
  body: JSON.stringify({ code: "123456" })
});
const data = await r.json();
console.log(data); // → { "ok": true }
POST/api/login/verify

Xác minh 2FA khi đăng nhập. Body: { "code": "123456" }

GET/api/me

Thông tin phiên hiện tại. Trả về { user: null } nếu chưa đăng nhập.

💻 Code ví dụ
# cURL
curl -b cookies.txt "http://localhost:3000/api/me"

# → { "ok": true, "user": { "id": 1, "username": "nguoichoi1", "email": "a@gmail.com" } }
// JavaScript (fetch)
const r = await fetch("/api/me", {
  credentials: "include"
});
const data = await r.json();
console.log(data); // → { "ok": true, "user": { "id": 1, "username": "nguoichoi1", "email": "a@gmail.com" } }
POST/api/logout

Đăng xuất, huỷ session.

💻 Code ví dụ
# cURL
curl -b cookies.txt -X POST "http://localhost:3000/api/logout"

# → { "ok": true }
// JavaScript (fetch)
const r = await fetch("/api/logout", {
  method: "POST",
  credentials: "include"
});
const data = await r.json();
console.log(data); // → { "ok": true }
POST/api/token🔒 login

Lấy JWT token (hạn 7 ngày) để gọi API từ app/bot: Authorization: Bearer <token>

💻 Code ví dụ
# cURL
curl -b cookies.txt -X POST "http://localhost:3000/api/token"

# → { "ok": true, "token": "eyJhbGciOiJIUzI1NiIs..." } (hạn 7 ngày)
// JavaScript (fetch)
const r = await fetch("/api/token", {
  method: "POST",
  credentials: "include"
});
const data = await r.json();
console.log(data); // → { "ok": true, "token": "eyJhbGciOiJIUzI1NiIs..." } (hạn 7 ngày)

Settings

POST/api/settings/password🔒 login

Đổi mật khẩu. Body: { "current", "password", "confirm" }

💻 Code ví dụ
# cURL
curl -b cookies.txt -X POST "http://localhost:3000/api/settings/password" \
  -H "Content-Type: application/json" \
  -d '{"current":"matkhau123","password":"matkhaumoi456","confirm":"matkhaumoi456"}'

# → { "ok": true }
// JavaScript (fetch)
const r = await fetch("/api/settings/password", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  credentials: "include",
  body: JSON.stringify({ current: "matkhau123", password: "matkhaumoi456", confirm: "matkhaumoi456" })
});
const data = await r.json();
console.log(data); // → { "ok": true }
POST/api/settings/email/start🔒 login

Đổi email — gửi mã về email MỚI. Body: { "newEmail", "password" }

💻 Code ví dụ
# cURL
curl -b cookies.txt -X POST "http://localhost:3000/api/settings/email/start" \
  -H "Content-Type: application/json" \
  -d '{"newEmail":"moi@gmail.com","password":"matkhau123"}'

# → { "ok": true } (mã gửi về email MỚI)
// JavaScript (fetch)
const r = await fetch("/api/settings/email/start", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  credentials: "include",
  body: JSON.stringify({ newEmail: "moi@gmail.com", password: "matkhau123" })
});
const data = await r.json();
console.log(data); // → { "ok": true } (mã gửi về email MỚI)
POST/api/settings/email/verify🔒 login

Xác minh mã đổi email. Body: { "newEmail", "code" }

💻 Code ví dụ
# cURL
curl -b cookies.txt -X POST "http://localhost:3000/api/settings/email/verify" \
  -H "Content-Type: application/json" \
  -d '{"newEmail":"moi@gmail.com","code":"123456"}'

# → { "ok": true }
// JavaScript (fetch)
const r = await fetch("/api/settings/email/verify", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  credentials: "include",
  body: JSON.stringify({ newEmail: "moi@gmail.com", code: "123456" })
});
const data = await r.json();
console.log(data); // → { "ok": true }
POST/api/settings/2fa/email/start🔒 login

Bật 2FA email — gửi mã xác minh.

💻 Code ví dụ
# cURL
curl -b cookies.txt -X POST "http://localhost:3000/api/settings/2fa/email/start"

# → { "ok": true } (mã xác minh đã gửi về email)
// JavaScript (fetch)
const r = await fetch("/api/settings/2fa/email/start", {
  method: "POST",
  credentials: "include"
});
const data = await r.json();
console.log(data); // → { "ok": true } (mã xác minh đã gửi về email)
POST/api/settings/2fa/email/verify🔒 login

Xác minh → bật 2FA email. Body: { "code" }

💻 Code ví dụ
# cURL
curl -b cookies.txt -X POST "http://localhost:3000/api/settings/2fa/email/verify" \
  -H "Content-Type: application/json" \
  -d '{"code":"123456"}'

# → { "ok": true }
// JavaScript (fetch)
const r = await fetch("/api/settings/2fa/email/verify", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  credentials: "include",
  body: JSON.stringify({ code: "123456" })
});
const data = await r.json();
console.log(data); // → { "ok": true }
POST/api/settings/2fa/email/disable🔒 login

Tắt 2FA email. Body: { "password" }

💻 Code ví dụ
# cURL
curl -b cookies.txt -X POST "http://localhost:3000/api/settings/2fa/email/disable" \
  -H "Content-Type: application/json" \
  -d '{"password":"matkhau123"}'

# → { "ok": true }
// JavaScript (fetch)
const r = await fetch("/api/settings/2fa/email/disable", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  credentials: "include",
  body: JSON.stringify({ password: "matkhau123" })
});
const data = await r.json();
console.log(data); // → { "ok": true }
POST/api/settings/2fa/totp/setup🔒 login

Tạo secret + QR (otplib). Trả về { secret, otpauth, qr } (qr là ảnh base64).

💻 Code ví dụ
# cURL
curl -b cookies.txt -X POST "http://localhost:3000/api/settings/2fa/totp/setup"

# → { "ok": true, "secret": "JBSWY3DP...", "otpauth": "otpauth://...", "qr": "data:image/png;base64,..." }
// JavaScript (fetch)
const r = await fetch("/api/settings/2fa/totp/setup", {
  method: "POST",
  credentials: "include"
});
const data = await r.json();
console.log(data); // → { "ok": true, "secret": "JBSWY3DP...", "otpauth": "otpauth://...", "qr": "data:image/png;base64,..." }
POST/api/settings/2fa/totp/verify🔒 login

Xác nhận mã từ ứng dụng → bật 2FA app. Body: { "code" }

💻 Code ví dụ
# cURL
curl -b cookies.txt -X POST "http://localhost:3000/api/settings/2fa/totp/verify" \
  -H "Content-Type: application/json" \
  -d '{"code":"123456"}'

# → { "ok": true }
// JavaScript (fetch)
const r = await fetch("/api/settings/2fa/totp/verify", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  credentials: "include",
  body: JSON.stringify({ code: "123456" })
});
const data = await r.json();
console.log(data); // → { "ok": true }
POST/api/settings/2fa/totp/disable🔒 login

Tắt 2FA app. Body: { "password" }

💻 Code ví dụ
# cURL
curl -b cookies.txt -X POST "http://localhost:3000/api/settings/2fa/totp/disable" \
  -H "Content-Type: application/json" \
  -d '{"password":"matkhau123"}'

# → { "ok": true }
// JavaScript (fetch)
const r = await fetch("/api/settings/2fa/totp/disable", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  credentials: "include",
  body: JSON.stringify({ password: "matkhau123" })
});
const data = await r.json();
console.log(data); // → { "ok": true }

Admin

GET/api/admin/overview🛠 admin

Thống kê: tổng user, đăng ký hôm nay, analytics 7 ngày, trạng thái email queue, danh sách backup.

💻 Code ví dụ
# cURL
curl "http://localhost:3000/api/admin/overview" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

# → { "ok": true, "totalUsers": 120, "todaySignups": 5, "queue": {...}, "backups": [...] }
// JavaScript (fetch)
const token = "eyJhbGciOiJIUzI1NiIs..."; // lấy từ POST /api/token
const r = await fetch("/api/admin/overview", {
  headers: { "Authorization": "Bearer " + token },
  credentials: "include"
});
const data = await r.json();
console.log(data); // → { "ok": true, "totalUsers": 120, "todaySignups": 5, "queue": {...}, "backups": [...] }
GET/api/admin/users?q=...🛠 admin

Danh sách/tìm kiếm người dùng (100 mới nhất).

💻 Code ví dụ
# cURL
curl "http://localhost:3000/api/admin/users?q=nguoi" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

# → { "ok": true, "users": [...] }
// JavaScript (fetch)
const token = "eyJhbGciOiJIUzI1NiIs..."; // lấy từ POST /api/token
const r = await fetch("/api/admin/users", {
  headers: { "Authorization": "Bearer " + token },
  credentials: "include"
});
const data = await r.json();
console.log(data); // → { "ok": true, "users": [...] }
POST/api/admin/users/delete🛠 admin

Xoá người dùng. Body: { "id": 5 }

💻 Code ví dụ
# cURL
curl -X POST "http://localhost:3000/api/admin/users/delete" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{"id":5}'

# → { "ok": true }
// JavaScript (fetch)
const token = "eyJhbGciOiJIUzI1NiIs..."; // lấy từ POST /api/token
const r = await fetch("/api/admin/users/delete", {
  method: "POST",
  headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token },
  credentials: "include",
  body: JSON.stringify({ id: 5 })
});
const data = await r.json();
console.log(data); // → { "ok": true }
GET/api/admin/queue🛠 admin

50 email gần nhất trong hàng đợi (pending/sent/failed + lỗi).

💻 Code ví dụ
# cURL
curl "http://localhost:3000/api/admin/queue" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

# → { "ok": true, "emails": [...] }
// JavaScript (fetch)
const token = "eyJhbGciOiJIUzI1NiIs..."; // lấy từ POST /api/token
const r = await fetch("/api/admin/queue", {
  headers: { "Authorization": "Bearer " + token },
  credentials: "include"
});
const data = await r.json();
console.log(data); // → { "ok": true, "emails": [...] }
POST/api/admin/backup🛠 admin

Backup SQLite thủ công ngay lập tức.

💻 Code ví dụ
# cURL
curl -X POST "http://localhost:3000/api/admin/backup" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

# → { "ok": true, "file": "backup-2026-07-11.sqlite" }
// JavaScript (fetch)
const token = "eyJhbGciOiJIUzI1NiIs..."; // lấy từ POST /api/token
const r = await fetch("/api/admin/backup", {
  method: "POST",
  headers: { "Authorization": "Bearer " + token },
  credentials: "include"
});
const data = await r.json();
console.log(data); // → { "ok": true, "file": "backup-2026-07-11.sqlite" }