Agent API Docs

Bản cho máy đọc: /docs/api.txt (text/markdown thuần).

Auth

Mọi endpoint /api/agent/* yêu cầu header 'Authorization: Bearer <API_KEY>' (hoặc 'x-agent-key: <API_KEY>'). API key tạo ở trang /admin/api-keys, chỉ hiện một lần lúc tạo. Sai/thiếu key trả 401, server chưa cấu hình key trả 503.

Authorization: Bearer <API_KEY>

Quy chuẩn SEO khi POST/PATCH bài

  1. title: 1 dòng, 3-300 ký tự (khuyến nghị ≤ 70 để không bị cắt trong SERP). API tự gộp whitespace/newline.
  2. excerpt: 1-2 câu, tối đa 160 ký tự, 1 dòng. Gửi thiếu thì API tự suy ra từ content. Đây là meta description của trang bài.
  3. content: HTML an toàn, CHỈ dùng thẻ <p>, <h2>, <h3>, <ul>, <ol>, <li>, <strong>, <em>, <blockquote>. 3-5 đoạn, 350-550 từ. Không markdown, không link, không script, không ảnh inline, không inline style. Mọi thứ khác bị sanitize lúc render.
  4. imageUrl: URL http(s). Ưu tiên ảnh upload qua POST /api/agent/media (URL nội bộ), không hotlink bừa từ site khác.
  5. category: một trong tech, ai, startup, dev, security, mobile, gadget, other. Sai sẽ fallback về tech.
  6. publishedAt: ISO datetime, thiếu thì lấy thời điểm hiện tại.
  7. isHot / isHidden: boolean. isHidden=false là published, true là draft/ẩn.
  8. Mọi bài tạo/sửa qua API đều được áp dụng các chuẩn trên tự động (title/excerpt 1 dòng, suy excerpt, clamp meta description ~165 ký tự).

Endpoints

GET/api/agent/stats

Thống kê tổng quan: số bài, ẩn, hot, nguồn active, nguồn lỗi, 10 log crawl gần nhất.

curl https://tonghop.io.vn/api/agent/stats -H 'Authorization: Bearer <API_KEY>'

Response: { "articles": 72, "hidden": 0, "hot": 0, "sources": 1, "sourceErrors": 0, "latestLogs": [...] }

GET/api/agent/articles

List/tìm bài viết, mới nhất trước.

Tham số: q (tìm trong title/excerpt/url), category (enum), hidden (1|0 hoặc true|false, không gửi = tất cả), page (≥1), limit (1-50, mặc định 20).

curl 'https://tonghop.io.vn/api/agent/articles?q=ai&category=tech&hidden=0&page=1&limit=20' -H 'Authorization: Bearer <API_KEY>'

Response: { "total": 192, "page": 1, "limit": 20, "items": [Article...] } — Article kèm source { name, slug }.

GET/api/agent/articles/:id

Lấy 1 bài. :id nhận cả article id hoặc slug.

curl https://tonghop.io.vn/api/agent/articles/ten-bai-viet-slug -H 'Authorization: Bearer <API_KEY>'

Response: Article object (404 nếu không có).

POST/api/agent/articles

Đăng bài mới (link-out hoặc full content viết lại). Tuân thủ Quy chuẩn SEO ở trên.

Body: { "title": string (bắt buộc), "url": string http(s) (bắt buộc, link bài gốc), "excerpt"?: string, "content"?: string HTML, "imageUrl"?: string, "author"?: string, "category"?: enum, "publishedAt"?: ISO, "isHot"?: boolean, "isHidden"?: boolean, "sourceId"?: string, "sourceSlug"?: string (mặc định "tong-hop") }

curl -X POST https://tonghop.io.vn/api/agent/articles -H "Authorization: Bearer <API_KEY>" -H "Content-Type: application/json" \
  -d '{"title":"Tiêu đề bài","url":"https://nguon.com/bai","excerpt":"Tóm tắt dưới 160 ký tự.","content":"<p>Nội dung HTML...</p>","imageUrl":"https://tonghop.io.vn/uploads/media/xxx.jpg","category":"tech"}'

Response: 201 { "ok": true, "article": {...} }. 400 nếu sai body/url.

PATCH/api/agent/articles/:id

Sửa bài (partial — gửi field nào sửa field đó). :id nhận id hoặc slug.

Body: Các field như POST, thêm: "regenerateSlug"?: boolean (tạo slug mới từ title). "imageUrl": null để XÓA ảnh bìa.

curl -X PATCH https://tonghop.io.vn/api/agent/articles/ten-bai-slug -H "Authorization: Bearer <API_KEY>" -H "Content-Type: application/json" \
  -d '{"title":"Tiêu đề mới","isHot":true}'

Response: { "ok": true, "article": {...} }. 404 nếu không tìm thấy.

DELETE/api/agent/articles/:id

Xóa vĩnh viễn 1 bài. :id nhận id hoặc slug.

curl -X DELETE https://tonghop.io.vn/api/agent/articles/ten-bai-slug -H 'Authorization: Bearer <API_KEY>'

Response: { "ok": true, "deleted": "<id>" }

POST/api/agent/moderate

Kiểm duyệt hàng loạt theo danh sách ids.

Body: { "ids": string[1..100], "action": "hide" | "show" | "hot" | "unhot" | "delete" }

curl -X POST https://tonghop.io.vn/api/agent/moderate -H "Authorization: Bearer <API_KEY>" -H "Content-Type: application/json" \
  -d '{"ids":["id1","id2"],"action":"hide"}'

Response: { "ok": true, "action": "hide", "count": 2 }

GET/api/agent/sources

List toàn bộ nguồn RSS.

Response: { "items": [Source...] }

POST/api/agent/sources

Thêm nguồn RSS mới (slug tự sinh từ name, trùng thì thêm -1, -2...).

Body: { "name": string, "rssUrl": string, "siteUrl": string, "category"?: enum, "lang"?: string, "isActive"?: boolean }

Response: 201 { "ok": true, "source": {...} }

POST/api/agent/fetch

Chạy crawl RSS toàn bộ nguồn active ngay lập tức (không đợi cron 20 phút).

Response: { "ok": true, "sources": n, "itemsNew": n, "errors": n }

POST/api/agent/media

Upload ảnh bìa, trả URL nội bộ để dùng cho article.imageUrl. Giới hạn 5MB, nhận jpg/png/gif/webp/avif (check magic bytes).

Body: multipart/form-data, field 'file'

curl -X POST https://tonghop.io.vn/api/agent/media -H "Authorization: Bearer <API_KEY>" -F "file=@cover.jpg"

Response: { "ok": true, "url": "https://tonghop.io.vn/uploads/media/...", "imageUrl": "..." }

GET/POST/api/cron/fetch và /api/cron/hotwrite

Cron nội bộ (Bearer CRON_SECRET, không dùng API key): fetch = crawl RSS; hotwrite = tìm tin hot + AI viết lại (cần OMNIROUTE_API_KEY).

curl https://tonghop.io.vn/api/cron/fetch -H 'Authorization: Bearer <CRON_SECRET>'

Lỗi chung

401 sai/thiếu key — 403 sai origin (admin) — 400 body lỗi — 413 file >5MB — 415 sai loại ảnh — 503 server chưa cấu hình.