Ứng dụng (plugin): tham chiếu
Manifest (deplao-plugin.json)
| Trường | Bắt buộc | Quy tắc |
|---|---|---|
id |
có | reverse-DNS chữ thường (vn.acme.spa); phần cuối (spa) là tiền tố node, không trùng node có sẵn (zalo, fb, trigger…) |
name, version |
có | version theo semver; cập nhật chỉ khi lớn hơn bản đã cài |
apiVersion |
có | 1 |
runtime |
có | web, local (cần backend.entry), remote (cần remote.baseUrl https) |
permissions |
có | scope API (xem "Xác thực & quyền") + quyền plugin bên dưới. Không được xin admin, plugins:manage, employees:manage, settings:write |
ui.entry |
khi có UI | file .html trong plugin; remote: URL cùng origin với remote.baseUrl |
ui.connectSrc |
không | origin https UI được fetch trực tiếp (cần quyền network) |
ui.imgSrc |
không | nguồn ảnh ngoài UI được hiển thị, https://tên-miền hoặc https://*.tên-miền (vd ảnh đại diện Zalo https://*.zadn.vn; cần quyền network) |
minDeplaoVersion |
không | semver |
contributes |
không | các điểm mở rộng bên dưới |
Quyền riêng của plugin
| Quyền | Cho phép |
|---|---|
storage |
deplao.storage (tối đa 1 MB/giá trị, 20 MB/plugin) và thư mục dữ liệu riêng |
ui:pages |
trang ở thanh điều hướng |
ui:chat-panel |
panel bên phải màn chat, biết hội thoại đang mở |
ui:composer |
nút trong ô soạn tin, deplao.ui.insertComposerText |
ui:microphone |
micro trong giao diện ứng dụng (khung được gắn allow="microphone"; thiếu quyền → trình duyệt chặn getUserMedia) |
workflow:nodes |
node và trigger workflow, deplao.workflow.trigger |
events:emit |
deplao.events.emit (tên khai báo trong contributes.events) |
network |
deplao.http.fetch ở backend, ui.connectSrc |
schedules |
contributes.schedules (cron) |
cloudflare:workers |
deplao.cloudflare ở backend (runtime local): tạo / sửa Worker và D1 tên <tiền tố> hoặc <tiền tố>-… (tiền tố = phần cuối id) trên Cloudflare đã kết nối trong Deplao; token Cloudflare không đưa cho ứng dụng |
Điểm mở rộng (contributes)
| Điểm | Khai báo | Ghi chú |
|---|---|---|
pages |
{ id, title, path } |
path là hash route trong ui.entry |
chatPanels |
{ id, title, path, width } |
nhận deplao.ui.context() và onContext |
composerButtons |
{ id, title, icon, target: 'ui' | 'backend' } |
backend: commands[id](context) trả { insertText } hoặc { toast } |
settings.schema |
JSON Schema (string, number, integer, boolean, enum; x-secret, x-multiline) |
form tự sinh ở trang Ứng dụng, API & Webhook (bấm ô ứng dụng → Cài đặt); secret được mã hoá |
workflowNodes |
{ type: '<tiền tố>.<tên>', label, configSchema, outputs } |
backend nodes[type](cfg, run, ctx); x-widget: account|thread|label|assistant (assistant = chọn trợ lý AI của Deplao, lưu id; có lựa chọn "Mặc định" = để trống), x-template |
triggers |
{ type: '<tiền tố>.trigger.<tên>', label, configSchema } |
bắn bằng deplao.workflow.trigger(type, payload); trường payload dùng như {{ $trigger.<field> }} |
routes |
{ method, path, scope } |
/api/v1/plugins/<id><path>; người gọi cần scope; body ≤ 1 MB |
schedules |
{ id, cron, handler } |
gọi handlers[handler]; lượt trước chưa xong thì bỏ lượt sau |
events |
['booking.created'] |
phát thành plugin.<id>.booking.created |
SDK deplao.*
| Nhóm | Hàm |
|---|---|
| API v1 | deplao.<nhóm>.<operationId>(params), deplao.call(operationId, params), deplao.api.request(method, path, { query, body }). Hàm trả file (downloadMessageMedia, downloadLibraryItem, downloadLibraryThumb) trả { name, mime, size, base64 } (≤ 25 MB, cần media:read; ghi âm Zalo chưa có trên máy thì app tự tải) |
storage |
get, set, delete, list(prefix); backend: dataDir() |
settings |
get(), onChange(cb) |
events |
on(type, handler) (cần events:subscribe), emit(name, data) |
workflow |
trigger(type, payload) |
media |
upload(file) → upload_id dùng trong attachments khi gửi tin (≤ 25 MB); các operationId nhóm Media gọi được như nhóm khác |
log |
debug/info/warn/error → nhật ký plugin (trang Ứng dụng, API & Webhook → bấm ô ứng dụng → Nhật ký) |
system |
info() |
ui (chỉ UI) |
context, onContext, theme, openConversation, insertComposerText, toast, navigate(pageId), openExternal(url), onCommand |
backend (chỉ UI) |
call(name, params) → commands[name] trong backend của chính plugin |
http (chỉ backend) |
fetch(url, init): cần network, chặn mạng nội bộ, ≤ 5 MB; trả text hoặc base64 theo Content-Type (thiếu thì xem nội dung), init.responseType = 'text' / 'base64' để ép |
cloudflare (chỉ backend) |
status(), d1.ensure(name), d1.migrate(name, dir), d1.query(name, sql, params), workers.get(name), workers.deployBundle({ name, bundleDir, bindings, secrets, workersDev }): cần cloudflare:workers; mỗi lời gọi tính vào ngân sách 100 request / 5 phút của app |
Lỗi trả về là Error có code (cùng mã lỗi API: FORBIDDEN_SCOPE, FORBIDDEN_ACCOUNT, VALIDATION_ERROR…) và status. deplao.cloudflare thêm PLUGIN_PERMISSION, CF_NOT_CONNECTED, CF_PERMISSION, CF_NAME_FORBIDDEN, CF_BUNDLE_INVALID, CF_API.
Backend (runtime local)
js
module.exports = {
async activate({ deplao, settings, pluginId, dataDir }) {},
async deactivate() {}, // tối đa 5 giây
async onUpgrade(prevVersion) {},
async onSettingsChanged(settings) {},
nodes: { 'shop.lookup': async (cfg, run, ctx) => ({ found: true }) }, // 60 giây
routes: { 'GET /orders/:code': async (req, ctx) => ({ status: 200, body: {} }) }, // 30 giây
handlers: { remind: async (ctx) => {} }, // lịch chạy, 120 giây
commands: { book: async (params, ctx) => ({}) }, // UI backend.call, nút soạn tin
};- Chạy trong tiến trình riêng (utilityProcess, 256 MB heap), chỉ nhận vài biến môi trường;
require('electron')vàprocess.exitbị chặn;console.*vào nhật ký plugin. - Crash thì tự khởi động lại sau 2 s, 10 s, 60 s; lần thứ 4 plugin bị tắt (
Tắt do lỗi). Không trả lời ping 3 lần liên tiếp thì bị khởi động lại. - Plugin vẫn là code Node chạy trên máy: Deplao chỉ cấp dữ liệu theo quyền đã duyệt, nhưng không cô lập hệ thống file hay mạng của tiến trình. Chỉ cài plugin từ nguồn tin cậy.
Runtime remote
- Deplao gọi
POST {remote.baseUrl}/rpcvới body{ id, method, params }và headerX-Deplao-Plugin,X-Deplao-Timestamp,X-Deplao-Signature: v1=HMAC-SHA256(secret, timestamp + "." + body). Trả về{ result }hoặc{ error: { code, message } }. method:activate,deactivate,ping,node.execute,route.handle,schedule.run,composer.command,settings.changed.- Khi cài, Deplao tạo secret và API key (gắn plugin, thu hồi khi gỡ) và hiện một lần. Server dùng key để gọi API v1, đăng ký webhook nhận sự kiện.
baseUrlphải là https (http chỉ cho127.0.0.1khi phát triển).
An toàn
- UI plugin chạy ở origin riêng
deplao-plugin://<id>trong iframe sandbox (không popup, không điều hướng trang app, không đọc được DOM, store hay IPC của app); CSP chặn script ngoài, kết nối ra ngoài trừui.connectSrcvà ảnh ngoài trừui.imgSrc. - Mọi lời gọi dữ liệu đi qua API v1 với principal
plugin:<id>: đúng scope, đúng tài khoản được cấp lúc cài, ghi nhật ký request như API key. - Gói zip được kiểm tra: tối đa 50 MB, giải nén tối đa 200 MB, không đường dẫn tuyệt đối hay
.., không symlink, CRC khớp. - Kênh quản trị plugin (
plugins:*) không gọi được qua API hay từ plugin. Kênh Cloudflare (cloudflare:*) cũng vậy, trừcloudflare:workers:*khi ứng dụng có quyềncloudflare:workers: Deplao kiểm quyền + tiền tố tên trước khi gọi.
Chưa tìm thấy câu trả lời?
Nhắn hỗ trợ qua Telegram