{ 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./api/healthKiểm tra server sống (dùng cho healthcheck/Docker).
# 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 }/api/captchaLấ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.
# 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 = ?" }/api/trackGhi nhận analytics. Body: { "event": "pageview" }
# 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 }/api/translateDị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 }.
# 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"] }/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
}
# 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)/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" }
# 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)/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.
# 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# 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 }/api/login/verifyXác minh 2FA khi đăng nhập. Body: { "code": "123456" }
/api/meThông tin phiên hiện tại. Trả về { user: null } nếu chưa đăng nhập.
# 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" } }/api/logoutĐăng xuất, huỷ session.
# 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 }/api/token🔒 loginLấy JWT token (hạn 7 ngày) để gọi API từ app/bot: Authorization: Bearer <token>
# 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)/api/settings/password🔒 loginĐổi mật khẩu. Body: { "current", "password", "confirm" }
# 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 }/api/settings/email/start🔒 loginĐổi email — gửi mã về email MỚI. Body: { "newEmail", "password" }
# 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)/api/settings/email/verify🔒 loginXác minh mã đổi email. Body: { "newEmail", "code" }
# 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 }/api/settings/2fa/email/start🔒 loginBật 2FA email — gửi mã xác minh.
# 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)/api/settings/2fa/email/verify🔒 loginXác minh → bật 2FA email. Body: { "code" }
# 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 }/api/settings/2fa/email/disable🔒 loginTắt 2FA email. Body: { "password" }
# 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 }/api/settings/2fa/totp/setup🔒 loginTạo secret + QR (otplib). Trả về { secret, otpauth, qr } (qr là ảnh base64).
# 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,..." }/api/settings/2fa/totp/verify🔒 loginXác nhận mã từ ứng dụng → bật 2FA app. Body: { "code" }
# 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 }/api/settings/2fa/totp/disable🔒 loginTắt 2FA app. Body: { "password" }
# 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 }/api/admin/overview🛠 adminThống kê: tổng user, đăng ký hôm nay, analytics 7 ngày, trạng thái email queue, danh sách backup.
# 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": [...] }/api/admin/users?q=...🛠 adminDanh sách/tìm kiếm người dùng (100 mới nhất).
# 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": [...] }/api/admin/users/delete🛠 adminXoá người dùng. Body: { "id": 5 }
# 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 }/api/admin/queue🛠 admin50 email gần nhất trong hàng đợi (pending/sent/failed + lỗi).
# 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": [...] }/api/admin/backup🛠 adminBackup SQLite thủ công ngay lập tức.
# 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" }