# Tonghop.io.vn — Agent API Docs Base URL: https://tonghop.io.vn ## Auth Mọi endpoint /api/agent/* yêu cầu header 'Authorization: Bearer ' (hoặc 'x-agent-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. ## Quy chuẩn SEO (áp dụng khi POST/PATCH bài viết) 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ẻ

,

,

,
    ,
      ,
    1. , , ,
      . 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 ' ``` 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 ' ``` 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 ' ``` 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 " -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":"

      Nội dung HTML...

      ","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 " -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 ' ``` Response: { "ok": true, "deleted": "" } ### 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 " -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 " -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 ' ``` ## 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. Docs HTML: /docs/api — bản text này: /docs/api.txt