API cho nhà phát triển tool

Mọi thứ đội phát triển cần để tự quản lý một tool: sửa thông tin hiển thị, đăng ảnh minh hoạ, viết hướng dẫn sử dụng và phát hành phiên bản mới — không cần vào trang quản trị, không cần ai mở cửa.

Trang này dành cho người dựng bản phát hành.
Nếu bạn đang nhúng phần xác thực license vào bản cài gửi cho khách thì phần bạn cần nằm ở tài liệu tích hợp cho bản cài: đăng nhập, kiểm hạn license, quản lý thiết bị.
Địa chỉ gốc
https://cyanpro.app
Xác thực
Header X-Dev-Token

Danh sách endpoint

MethodĐường dẫnDùng để
GET /api/v1/dev/product Đọc thông tin tool và bản phát hành mới nhất
PATCH /api/v1/dev/product Sửa mô tả, nền tảng, link tải, hướng dẫn sử dụng
GET /api/v1/dev/releases Lịch sử phát hành (100 bản gần nhất)
POST /api/v1/dev/releases Phát hành phiên bản mới
GET /api/v1/dev/images Danh sách ảnh minh hoạ
POST /api/v1/dev/images Thêm ảnh minh hoạ (tải tệp lên hoặc trỏ tới địa chỉ https)
DELETE /api/v1/dev/images/:id Xoá một ảnh minh hoạ
POST /api/v1/dev/icon Đặt biểu tượng phần mềm (tải tệp lên hoặc trỏ tới https)
DELETE /api/v1/dev/icon Gỡ biểu tượng, quay về chữ viết tắt
POST /api/v1/dev/cover Đặt ảnh bìa (tải tệp lên hoặc trỏ tới https)
DELETE /api/v1/dev/cover Gỡ ảnh bìa

Khoá nhà phát triển

Lấy trong Quản trị → Tool → Khoá nhà phát triển. Khoá hiện đúng một lần lúc tạo; mất thì thu hồi và tạo khoá mới chứ không xem lại được, vì hệ thống chỉ lưu bản băm.

  • Mỗi khoá gắn với đúng một tool. Đội làm tool này không chạm được vào tool khác.
  • Không bao giờ nhúng khoá này vào bản cài gửi khách. Nó sửa được nội dung và link tải; ai moi được nó ra khỏi tệp thực thi là đổi được link tải phần mềm của bạn.
  • Chỗ của nó là máy đội phát triển và biến bí mật trong hệ thống dựng bản (GitHub Actions secrets hoặc tương đương).
  • Nhánh này không nhận X-Api-Key của bản cài, và ngược lại khoá dev không đăng nhập hộ khách được. Hai cửa hoàn toàn riêng.
  • Thu hồi khoá có hiệu lực ngay. Tạo nhiều khoá cho nhiều hệ thống dựng bản để thu hồi một cái không làm chết cái còn lại.

Bắt đầu nhanh

Ba lệnh dưới đây đủ để kiểm tra khoá và đẩy một bản phát hành.

1. Đọc thông tin tool hiện tại

curl -s https://cyanpro.app/api/v1/dev/product \
  -H "X-Dev-Token: $CYAN_DEV_TOKEN"

2. Sửa một trường

curl -s -X PATCH https://cyanpro.app/api/v1/dev/product \
  -H "X-Dev-Token: $CYAN_DEV_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"tagline":"Bộ công cụ desktop cho công việc hằng ngày"}'

3. Phát hành phiên bản mới

curl -s -X POST https://cyanpro.app/api/v1/dev/releases \
  -H "X-Dev-Token: $CYAN_DEV_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "version": "1.1.0",
        "notes": "- Sửa lỗi đăng nhập\n- Tăng tốc khởi động",
        "download_url": "https://example.com/setup-1.1.0.exe"
      }'

Sửa thông tin tool

Chỉ gửi những trường muốn đổi; trường không có trong body thì giữ nguyên. Phản hồi trả về danh sách trường đã đổi để hệ thống dựng bản ghi log.

TrườngKiểuGhi chú
name string Tên tool, tối đa 120 ký tự. Không được để trống.
tagline string Mô tả một dòng, tối đa 200 ký tự. Hiện dưới tên ở trang danh mục.
tagline_en string Bản tiếng Anh của mô tả một dòng. Để trống thì trang tiếng Anh dùng bản tiếng Việt.
description string Mô tả dài, tối đa 4000 ký tự. Hiện ở trang chi tiết.
description_en string Bản tiếng Anh của mô tả dài.
guide string Hướng dẫn sử dụng đầy đủ, viết bằng Markdown, tối đa 40000 ký tự.
guide_en string Bản tiếng Anh của hướng dẫn.
icon string Một emoji hoặc 1–2 chữ cái, chỉ dùng khi chưa có ảnh biểu tượng.
icon_url string Ảnh biểu tượng, bắt buộc https. Muốn tải tệp lên thì dùng POST /dev/icon.
cover_url string Ảnh bìa, bắt buộc https. Muốn tải tệp lên thì dùng POST /dev/cover.
platforms string[] Mảng chuỗi. Chỉ nhận: Windows, macOS, Linux, Android, iOS, Web. Giá trị lạ bị bỏ qua.
download_url string Link tải mặc định. Bắt buộc là https.
PATCH https://cyanpro.app/api/v1/dev/product
X-Dev-Token: cyd_live_xxxxxxxx
Content-Type: application/json

{
  "tagline": "Bộ công cụ desktop cho công việc hằng ngày",
  "tagline_en": "A desktop toolkit for everyday work",
  "description": "Mô tả dài hơn, hiện ở trang chi tiết.",
  "platforms": ["Windows", "macOS"],
  "download_url": "https://example.com/setup.exe"
}
200 OK
{
  "ok": true,
  "updated": ["tagline", "tagline_en", "description", "platforms", "download_url"],
  "product": { "slug": "cyan-studio", "name": "CYAN Studio", ... }
}

Những trường API không sửa được

Gửi lên cũng bị bỏ qua, không báo lỗi. Đây là lựa chọn có chủ ý, không phải thiếu tính năng:

  • slug — nằm trong đường dẫn công khai /tools/<slug>. Đổi là hỏng mọi liên kết cũ và mọi kết quả tìm kiếm đã lập chỉ mục.
  • status — ẩn hay hiện một tool là quyết định kinh doanh, làm trong trang quản trị nơi có cảnh báo đi kèm.
  • kind, is_free, free_max_devices — phân loại và chính sách giá, cũng thuộc trang quản trị.

Hướng dẫn sử dụng

Trường guide là nội dung của trang /tools/<slug>/guide. Viết bằng Markdown; mục lục bên trái tự sinh từ các tiêu đề nên hãy chia bài bằng tiêu đề thay vì bằng chữ in đậm.

Cú pháp dùng được: tiêu đề, danh sách, đoạn mã, chữ đậm, chữ nghiêng, liên kết, bảng, trích dẫn.

HTML thô trong Markdown bị thoát ký tự chứ không chạy — kể cả khi bạn tin nội dung do chính mình viết, vì khoá dev có thể bị lộ và một trang chạy được script là một trang đọc được cookie phiên của khách.

Biểu tượng phần mềm

Biểu tượng là thứ đầu tiên người ta nhìn ở trang sản phẩm và ở danh mục. Chưa đặt thì hệ thống hiện chữ viết tắt của tên — đủ dùng nhưng trông như ô chưa kịp điền.

Tải tệp lên

curl -s -X POST https://cyanpro.app/api/v1/dev/icon \
  -H "X-Dev-Token: $CYAN_DEV_TOKEN" \
  -F "file=@icon-512.png"
201 Created
{ "ok": true, "icon_url": "/media/3b7e…a1.png", "uploaded": true }

Hoặc trỏ tới ảnh có sẵn

POST https://cyanpro.app/api/v1/dev/icon
X-Dev-Token: cyd_live_xxxxxxxx
Content-Type: application/json

{ "url": "https://cdn.example.com/icon-512.png" }

Gỡ biểu tượng

DELETE https://cyanpro.app/api/v1/dev/icon
X-Dev-Token: cyd_live_xxxxxxxx
  • Ảnh vuông, cạnh tối thiểu 256px. Trang sản phẩm hiện ở 88px, danh mục ở 48px, nên ảnh nhỏ hơn sẽ bị kéo nhoè.
  • Nền trong suốt (PNG) hoặc nền đặc đều được — khung bo góc 20px sẽ cắt theo.
  • Đặt biểu tượng mới là thay cái cũ. Tệp cũ được dọn khỏi hệ thống, trừ khi nó đang đồng thời là một ảnh minh hoạ.
  • Cùng giới hạn tệp với ảnh minh hoạ: PNG, JPEG, WebP, GIF và tối đa 3MB; loại tệp xác định bằng nội dung chứ không theo tên.

Ảnh bìa

Dải ảnh ngang ở đầu trang sản phẩm, và ảnh đại diện của tool trong danh mục. Khác ảnh minh hoạ: đây là ảnh dựng riêng, có tên phần mềm và khoảng thở, nên cắt kiểu gì cũng vẫn đọc được.

Tải tệp lên

curl -s -X POST https://cyanpro.app/api/v1/dev/cover \
  -H "X-Dev-Token: $CYAN_DEV_TOKEN" \
  -F "file=@cover-1024x500.png"
201 Created
{ "ok": true, "cover_url": "/media/8d12…7c.png", "uploaded": true }

Gỡ ảnh bìa

DELETE https://cyanpro.app/api/v1/dev/cover
X-Dev-Token: cyd_live_xxxxxxxx
  • Dựng đúng 1920×640 (tỉ lệ 3:1). Ảnh khác tỉ lệ vẫn nhận nhưng sẽ bị cắt cho vừa khung, vì khung có tỉ lệ cố định — để khung chạy theo ảnh thì mỗi phần mềm một chiều cao.
  • Chữ và logo nằm trong vùng giữa rộng 1140px. Khung hẹp dần theo màn hình (3:1 → 2:1 → 16:9) và luôn cắt từ hai mép vào, nên vùng giữa là phần duy nhất chắc chắn còn nguyên.
  • Chưa đặt thì đầu trang không có dải ảnh, và danh mục mượn tạm ảnh minh hoạ đầu tiên.

Ảnh minh hoạ

Ảnh hiện ở trang chi tiết tool và ảnh đầu tiên làm ảnh bìa trong danh mục. Hai cách thêm: tải tệp lên hệ thống, hoặc trỏ tới ảnh đặt ở CDN riêng của bạn.

Tải tệp lên

curl -s -X POST https://cyanpro.app/api/v1/dev/images \
  -H "X-Dev-Token: $CYAN_DEV_TOKEN" \
  -F "file=@screenshot-1.png" \
  -F "caption=Màn hình chính" \
  -F "caption_en=Main window" \
  -F "sort_order=1"

Trỏ tới ảnh đặt ở nơi khác

POST https://cyanpro.app/api/v1/dev/images
X-Dev-Token: cyd_live_xxxxxxxx
Content-Type: application/json

{
  "url": "https://cdn.example.com/shots/main.png",
  "caption": "Màn hình chính",
  "caption_en": "Main window",
  "sort_order": 1
}
201 Created
{
  "ok": true,
  "image": {
    "id": "img_7f3a…",
    "url": "/media/9c1f…c4.png",
    "caption": "Màn hình chính",
    "width": 1920,
    "height": 1200,
    "bytes": 184320,
    "sort_order": 1
  }
}
  • Chỉ nhận PNG, JPEG, WebP, GIF. Loại tệp xác định bằng nội dung, không bằng phần mở rộng hay Content-Type khai trong request.
  • Tối đa 3MB mỗi tệp và 12 ảnh mỗi tool.
  • Ảnh trỏ từ nơi khác bắt buộc là https.
  • Dùng sort_order để quyết định thứ tự hiện; số nhỏ lên trước.
  • Chú thích nên nói ảnh cho thấy điều gì — nó cũng là chữ thay thế cho người dùng trình đọc màn hình.
DELETE https://cyanpro.app/api/v1/dev/images/img_7f3a…
X-Dev-Token: cyd_live_xxxxxxxx

Xoá ảnh bằng id lấy từ GET /api/v1/dev/images. Khoá dev chỉ xoá được ảnh của đúng tool nó quản.

Phát hành phiên bản mới

Gọi từ hệ thống dựng bản sau khi đã đóng gói và tải bản cài lên nơi lưu trữ. Mỗi số hiệu chỉ phát hành một lần.

TrườngKiểuGhi chú
version string Bắt buộc. Chỉ gồm chữ, số và các dấu . - +, phải có ít nhất một chữ số. Tối đa 40 ký tự.
notes string Nội dung thay đổi, tối đa 20000 ký tự. Phần mềm hiện lại cho người dùng khi báo có bản mới.
notes_en string Bản tiếng Anh của nội dung thay đổi.
download_url string Link tải bản này. Bắt buộc https. Để trống thì dùng link tải mặc định của tool.
is_mandatory boolean true nếu bản này buộc phải cập nhật. Phần mềm nên chặn sử dụng cho tới khi cập nhật xong — chỉ dùng cho bản vá lỗi bảo mật hoặc đổi giao thức.
201 Created
{
  "ok": true,
  "release": {
    "version": "1.1.0",
    "is_latest": true,
    "is_mandatory": false,
    "download_url": "https://example.com/setup-1.1.0.exe",
    "released_at": 1792470000
  }
}

Thứ tự phiên bản được so sánh theo số

So theo từng đoạn số, nên 1.10.0 mới hơn 1.9.0 (so chuỗi thuần sẽ cho kết quả ngược). Bản thử nghiệm nhỏ hơn bản chính thức: 1.0.0-beta.1 < 1.0.0. Phần sau dấu + không tham gia so sánh.

Phát hành một bản CŨ HƠN bản hiện hành vẫn được lưu vào lịch sử nhưng không kéo lùi bản mới nhất: trả về is_latest: false kèm note giải thích. Nhờ vậy vá cho nhánh cũ không rủ khách đang chạy bản mới hạ cấp.

Lịch sử phát hành

GET https://cyanpro.app/api/v1/dev/releases
X-Dev-Token: cyd_live_xxxxxxxx

Bản mới tới tay khách thế nào

  1. Bạn gọi POST /api/v1/dev/releases. Nếu phiên bản mới hơn bản hiện hành, latest_version của tool nhảy theo ngay.
  2. Bản cài trên máy khách gọi GET /api/v1/version?current=<phiên bản đang chạy> bằng X-Api-Key của nó — endpoint duy nhất liên quan tới phát hành mà khoá của bản cài được phép gọi.
  3. Phản hồi cho biết update_available, latest_version, download_url, notes và is_mandatory.
  4. Không có hệ thống đẩy thông báo: máy khách tự hỏi. Nên gọi lúc khởi động, và thêm một lần mỗi vài giờ nếu phần mềm mở lâu.

Gắn vào hệ thống dựng bản

Ví dụ cho GitHub Actions: sau khi đóng gói, đẩy bản phát hành bằng khoá lấy từ secret. Đừng in khoá ra log — curl nhận khoá qua biến môi trường, không viết thẳng vào dòng lệnh.

# .github/workflows/release.yml
- name: Phát hành lên cyanpro.app
  env:
    CYAN_DEV_TOKEN: ${{ secrets.CYAN_DEV_TOKEN }}
    VERSION: ${{ github.ref_name }}
  run: |
    curl -sS --fail-with-body -X POST https://cyanpro.app/api/v1/dev/releases \
      -H "X-Dev-Token: $CYAN_DEV_TOKEN" \
      -H "Content-Type: application/json" \
      -d "$(jq -n \
            --arg v "${VERSION#v}" \
            --arg notes "$(cat CHANGELOG-latest.md)" \
            --arg url "https://downloads.example.com/setup-${VERSION#v}.exe" \
            '{version: $v, notes: $notes, download_url: $url}')"

Lệnh trên dừng với mã lỗi khác 0 khi máy chủ trả lỗi, nhờ --fail-with-body: một bản phát hành thất bại phải làm đỏ cả lần dựng, chứ không im lặng đi qua.

Mã lỗi

Mọi phản hồi lỗi có cùng khuôn: { "ok": false, "error": "mã_lỗi", "message": "mô tả" }. Xử lý theo error chứ đừng so khớp message — phần chữ có thể đổi, mã thì không.

MãHTTPNghĩa và cách xử lý
missing_dev_token 401 Thiếu header X-Dev-Token. Lưu ý API key của bản cài không dùng được ở nhánh này.
invalid_dev_token 401 Khoá sai hoặc đã bị thu hồi. Kiểm tra secret trong hệ thống dựng bản.
product_not_found 404 Tool gắn với khoá này đã bị xoá. Tạo khoá mới cho tool hiện tại.
invalid_json 400 Thân request không phải JSON hợp lệ.
nothing_to_update 400 Không có trường nào hợp lệ trong body. Kiểm tra lại tên trường.
invalid_name 400 Tên tool để trống.
invalid_platforms 400 platforms phải là một mảng chuỗi.
invalid_download_url 400 Link tải phải là địa chỉ https đầy đủ.
missing_version 400 Thiếu trường version.
invalid_version 400 Chuỗi phiên bản không hợp lệ.
version_exists 409 Số hiệu này đã phát hành. Dùng số mới cho bản sửa — đừng phát hành đè.
image_too_large 413 Ảnh vượt 3MB. Nén lại trước khi tải lên.
not_an_image 400 Tệp không phải ảnh thuộc các loại được nhận.
invalid_icon_url 400 Địa chỉ biểu tượng phải là https.
invalid_cover_url 400 Địa chỉ ảnh bìa phải là https.
too_many_images 409 Tool đã đủ số ảnh cho phép. Xoá ảnh cũ trước khi thêm.
image_not_found 404 Không có ảnh với id đó trong tool này.
rate_limited 429 Gọi quá nhiều. Chờ đúng số giây trong header Retry-After rồi thử lại.

Giới hạn gọi

120 lần mỗi giờ cho mỗi khoá dev. Chạm ngưỡng thì nhận HTTP 429 kèm header Retry-After tính bằng giây. Giới hạn này thưa vì đây là thao tác phát hành, không phải thao tác chạy trong vòng lặp; nếu hệ thống dựng bản của bạn chạm ngưỡng thì gần như chắc chắn nó đang gọi lặp vì một lỗi khác.

Trước khi phát hành bản đầu tiên

  • Khoá dev nằm trong secret của hệ thống dựng bản, không nằm trong mã nguồn, không nằm trong bản cài.
  • Link tải là https và tải về được từ máy ngoài mạng nội bộ.
  • Số hiệu phiên bản lớn hơn latest_version hiện tại.
  • Nội dung thay đổi viết cho người dùng cuối đọc, không phải danh sách commit.
  • Đã thử gọi GET /api/v1/version bằng API key của bản cài và thấy đúng bản vừa phát hành.

Tài liệu tích hợp cho bản cài →