App API 參考
原生 App 有一個 Go 後端。UI 以具名的方法呼叫它,例如 greeting.get。本頁列出呼叫格式、權限等級、模板內建的方法、新增 route 的 Go API,以及產生的 UI 中的用戶端輔助函式。
UI 呼叫 ctx.call(method, params)。DSM 會在視窗開啟時把 ctx 交給你的 UI。ctx.call 會用使用者的 DSM 工作階段,向 App 的後端送出 JSON POST。
| 項目 | 值 |
|---|---|
| 請求內容 | {"method": "app.status", "params": {}}。未知的欄位會被拒絕。 |
| 成功 | {"success": true, "data": …}。ctx.call 會以 data 完成。 |
| 錯誤 | {"success": false, "error": {"code": "…", "message": "…"}}。ctx.call 會以 {code, message} 拒絕。 |
| HTTP 狀態 | API 錯誤一律為 200,避免 DSM 取代回應內容。 |
| 來源 | 只接受同源。Content-Type 必須是 application/json。 |
錯誤碼列在錯誤碼。檔案上傳與下載不使用 ctx.call。它們使用 HTTP 傳輸,方法名稱放在查詢字串(?method=nas.files.upload 與 ?method=nas.files.download)。產生的檔案輔助函式會替你處理。
每個方法有一個權限等級。等級設在處理函式之外,也就是 backend/internal/api/permissions.json。
| 等級 | 誰可以呼叫 |
|---|---|
administrators | DSM 管理員群組的成員。這是預設值。 |
authenticated | 任何通過 entry 檢查的已登入 DSM 使用者。 |
public | 任何人,不需登入。只有 identity.* 方法使用。 |
沒有列出的方法會使用 administrators。非管理員呼叫未列出的方法,會得到 admin_required。呼叫沒有註冊的方法,所有已登入的呼叫者都會得到 unknown_method。
{ "schema": 1, "default": "administrators", "methods": { "app.status": "authenticated", "reports.list": "authenticated" }}| 規則 | 值 |
|---|---|
| 檔案結構 | 恰好是 schema、default 與 methods。default 是 administrators。 |
| 方法名稱 | 小寫的 area.action 格式,例如 reports.list。格式為 ^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$,最多 128 個字元。 |
| 大小 | 最多 1024 個方法,檔案最大 64 KiB。重複的 key 會被拒絕。 |
| 已註冊的方法 | 列出的每個方法都必須有處理函式。 |
| 能力的方法 | nas.*、bg.*、identity.*、portal.*、executor.*、root.run 與 delegate.* 的權限是固定的。你無法在 permissions.json 修改。 |
check 與 build 會驗證這個檔案。錯誤會使用 STANDALONE_API_PERMISSIONS_INVALID。請見錯誤碼。
Entry 閘門
Section titled “Entry 閘門”dsm-package.yaml 的 entry 欄位限制誰可以開啟 App。
entry | 效果 |
|---|---|
| 省略 | 沒有額外檢查。能開啟視窗的使用者都可以呼叫 authenticated 方法。 |
admin-only | 只有管理員。其他人會得到 admin_required。 |
app-privilege | 只有 DSM 應用程式權限允許的使用者。其他人會得到 app_privilege_denied。它需要至少一個不是 administrators 的方法。只有 files 或 jobs 就足夠。 |
管理員一律通過閘門。public 方法會略過它。請見 Manifest 參考。
只有在對應的能力開啟時,方法才存在。以 synopkgland add <name> 或 init --with <name> 加入能力。
| 方法 | 權限 | 用途 |
|---|---|---|
app.status | administrators | 套件名稱、版本、呼叫者(user、admin)、程序、平台與時間。App 有共用資料夾時會加上 shared_folders。 |
greeting.get | administrators | 讀取範例問候語。回傳 {text}。 |
greeting.set | administrators | 寫入範例問候語。{text} 必須是 1 到 200 個字元。 |
這三個是範例。請以你自己的方法取代它們。init --with delegate 也會把 app.status 列為 authenticated。
檔案(files)
Section titled “檔案(files)”| 方法 | 權限 | 參數 | 用途 |
|---|---|---|---|
nas.session.status | authenticated | 無 | 檔案存取開啟時,回傳 {mode: "filestation-session"}。 |
nas.files.shares | authenticated | offset | 列出使用者看得到的共用資料夾。 |
nas.files.list | authenticated | path、offset、limit(最多 100)、sortBy(name、size、type、mtime)、sortDirection(ASC、DESC) | 列出資料夾內容。 |
nas.files.info | authenticated | path | 回傳單一檔案或資料夾。 |
nas.files.create_folder | authenticated | parent、name | 建立資料夾。回傳 {created: true}。 |
nas.files.upload | authenticated | 查詢字串 parent、name。內容是檔案。 | 上傳檔案。不會覆寫,也不會建立上層資料夾。需要 Content-Length,最大 16 GiB。 |
nas.files.download | authenticated | 查詢字串 path | 以附件形式下載檔案。 |
每個呼叫都以登入使用者自己的 DSM 權限執行。路徑必須是絕對路徑、最多 4096 個字元,且不含 \、,、" 或控制字元。一頁清單最多 100 筆。
背景工作(jobs)
Section titled “背景工作(jobs)”| 方法 | 權限 | 用途 |
|---|---|---|
bg.status | authenticated | 擁有者、上限,以及排隊中與執行中的工作數量。 |
bg.browse | authenticated | 瀏覽呼叫者的資料夾,以選擇工作輸入。不需要 files。 |
bg.jobs.submit | authenticated | 將工作排入佇列。參數 key、folder、relative。回傳 {job, duplicate}。 |
bg.jobs.list | authenticated | 列出呼叫者的工作,最新的在前。 |
bg.jobs.get | authenticated | 以 id 取得一個工作。 |
bg.jobs.cancel | authenticated | 以 id 取消工作。 |
bg.jobs.result | authenticated | 已完成工作的結果,格式為 JSON。完成前會回傳 not_ready。 |
bg.jobs.purge | administrators | 清除某個使用者的工作資料(owner: {user, uid})。 |
bg.schedules.list | authenticated | 列出排程。 |
bg.schedules.create | authenticated | 重複執行工作。參數 key、jobId、intervalSeconds(3600、86400 或 604800)、consent: true。 |
bg.schedules.disable | authenticated | 以 id 停止排程。 |
bg.schedules.delete | authenticated | 以 id 刪除排程。 |
bg.schedules.enable | administrators | 重新啟動排程。參數 id、consent: true。 |
工作的 key 符合 ^[A-Za-z0-9._-]{1,64}$。工作狀態有 queued、running、succeeded、failed、canceled 與 interrupted。失敗的工作有 errorCode,請見錯誤碼。
登入(login)
Section titled “登入(login)”所有 identity.* 方法都是 public。它們需要 HTTPS 與同源的 JSON POST。
| 方法 | 參數 | 用途 |
|---|---|---|
identity.status | 無 | 回傳 {enabled: true}。 |
identity.session | bootstrap(選用) | 回傳 App 工作階段的 {user, uid, expiresAt}。使用 bootstrap: true 且沒有 App 工作階段時,會從 DSM 登入狀態建立一個。 |
identity.login | account、password、otp(選用) | 以使用者的 DSM 帳號登入,並建立 App 工作階段。 |
identity.logout | 無 | 結束 App 工作階段。回傳 {loggedOut: true}。 |
應用程式入口網站 (Application Portal)(portal)
Section titled “應用程式入口網站 (Application Portal)(portal)”| 方法 | 權限 | 用途 |
|---|---|---|
portal.status | authenticated | 讀取已儲存的入口網站別名,以及是否已啟用。 |
管理員透過 ctx.portal 從 UI 設定或清除別名。請見應用程式入口網站指南。
管理員操作(rootOps、delegate)
Section titled “管理員操作(rootOps、delegate)”| 方法 | 權限 | 參數 | 用途 |
|---|---|---|---|
executor.status | authenticated | 無 | 已核准輔助程式的狀態:digest、版本、ready、disabled,以及 tasks,即 executor 自己對核准任務的核對結果 {state, checked_at}:啟動後第一次核對結束前為 checking,之後是 ready、stale(ready 的核對已超過 15 分鐘)或 failed。executor 啟動時核對一次,通過後每 10 分鐘、失敗後每 1 分鐘再核對。executor 暫停時回傳 {ready: false, state: "paused", reason: "executor_not_listening", next}。 |
executor.control | administrators | action:start、stop 或 purge | 啟動、停止或清除輔助程式。 |
root.run | administrators | name | 以 root 執行 root/<name>.sh,並回傳 {output}。不能帶引數。 |
delegate 專案也會有 executor.*。root.run 失敗,或 executor.status 除了 executor 暫停以外的失敗,UI 會收到 internal。請見管理員操作指南。
Delegate(delegate)
Section titled “Delegate(delegate)”預設的 delegate 設定(lifetime: until-revoked)使用這些方法。
| 方法 | 權限 | 參數 | 用途 |
|---|---|---|---|
delegate.status | authenticated | 無 | 狀態(ready、disabled、needs-admin-approval、blocked、needs-scope-confirmation)、生命週期、目的地與範圍。 |
delegate.destination | authenticated | key | 單一目的地的狀態:active、missing、reselection-required、needs-scope-confirmation 或 disabled。 |
delegate.select_destination | authenticated | key、path | 讓使用者為目的地選一個資料夾。會檢查寫入權限,然後儲存授權。 |
delegate.move_from_staging | authenticated | delegation_id、job_id、staging、name、size,可選 conflict | 把完成的檔案從暫存區移到選定的資料夾。name 可以包含資料夾,例如 Album/CD1/01.flac;conflict 為 fail(預設)、overwrite 或 rename。 |
delegate.operations.get | authenticated | delegation_id、job_id | 移動的狀態。 |
delegate.operations.cancel | authenticated | delegation_id、job_id | 取消移動。 |
delegate.operations.list | authenticated | 無(送出 {}) | 列出移動。 |
delegate.example.export | authenticated | key、job_id | 稍後儲存面板使用的範例匯出。 |
delegate.disable、delegate.enable | administrators | 無 | 關閉或開啟這項整合。兩者都會結束所有授權。 |
delegate.audit | authenticated | 無 | 套件的事件。管理員看得到全部,其他人只看得到自己的。最多 256 筆。 |
使用 delegate 且沒有 lifetime 時,會改用限時方法:delegate.create(job_id、ops、path、ttl_seconds 為 1 到 3600)、delegate.list、delegate.revoke、delegate.jobs.submit、delegate.jobs.list 與 delegate.jobs.cancel。全部都是 authenticated。delegate.* 方法需要 DSM 工作階段 cookie 與 token,否則會回傳 session_required。請見 Delegate 指南。
Executor 協定
Section titled “Executor 協定”App 後端以 executor 協定 v1 的這些操作與 executor 溝通;下表由 SDK 的操作 schema 產生。以 ? 結尾的參數為選用。需要 DSM 工作階段的操作在沒有工作階段時以 AUTH_DENIED 拒絕。請見 Executor 協定錯誤碼。
| 操作 | DSM 工作階段 | 參數 |
|---|---|---|
delegate.audit | 必要 | — |
delegate.create | 必要 | job_id?, ops, scope, path, ttl_seconds?, destination?, lifetime? |
delegate.disable | 必要 | — |
delegate.enable | 必要 | — |
delegate.list | 必要 | — |
delegate.revoke | 選用 | delegation_id, job_id? |
files.cancelOperation | 選用 | delegation_id, job_id |
files.checkPermission | 選用 | delegation_id, job_id, path |
files.copy | 選用 | delegation_id, job_id, path, dest_dir, conflict? |
files.createFolder | 選用 | delegation_id, job_id, path, name, parents? |
files.delete | 選用 | delegation_id, job_id, path, recursive?, permanent? |
files.info | 選用 | delegation_id, job_id, path |
files.list | 選用 | delegation_id, job_id, path |
files.move | 選用 | delegation_id, job_id, path, dest_dir, conflict? |
files.moveFromStaging | 選用 | delegation_id, job_id, staging, name, size, conflict? |
files.operationStatus | 選用 | delegation_id, job_id |
files.read | 選用 | delegation_id, job_id, path, max_bytes? |
files.rename | 選用 | delegation_id, job_id, path, name |
files.taskCancel | 選用 | delegation_id, job_id, task_id |
files.taskStatus | 選用 | delegation_id, job_id, task_id |
files.write | 選用 | delegation_id, job_id, staging, dest_dir, name, conflict? |
portal.register | 必要 | — |
portal.unregister | 必要 | — |
root.jobs.get | 必要 | job_id |
root.jobs.list | 必要 | — |
root.jobs.stop | 必要 | job_id |
root.run | 必要 | name, params? |
root.start | 必要 | name, params? |
status | 選用 | — |
以 Go 新增 route
Section titled “以 Go 新增 route”route 在 backend/internal/app/routes.go。在那裡註冊處理函式,然後在 permissions.json 為該方法設定權限。
// routes.go: add to the list in Routesapi.HandleRequest("reports.list", cfg.listReports),package app
import ( "context" "encoding/json" "net/http"
"github.com/synopackageland/syno-pkg-land-sdk/api" "github.com/synopackageland/syno-pkg-land-sdk/filestation")
func (cfg Config) listReports(ctx context.Context, caller api.Caller, r *http.Request, params json.RawMessage) (any, error) { var in struct { Folder string `json:"folder"` } if err := decodeParams(params, &in); err != nil { return nil, err } if in.Folder == "" { return nil, api.BadRequest("folder is required") } return cfg.SessionFiles.List(ctx, caller, r, in.Folder, filestation.ListOptions{Limit: 50})}{ "schema": 1, "default": "administrators", "methods": { "reports.list": "authenticated" }}| 函式 | 處理函式簽章 | 用途 |
|---|---|---|
api.Handle(method, h) | func(ctx, caller api.Caller, params json.RawMessage) (any, error) | 大多數 route。 |
api.HandleRequest(method, h) | 在 ctx 之後加上 *http.Request。 | 以使用者工作階段呼叫 File Station 或 executor 的 route。 |
api.HandleResponse(method, h) | 加上 http.ResponseWriter 與 *http.Request。回傳 error。 | 設定 cookie 或自行寫入回應的 route。 |
api.HandleStream(method, h) | func(ctx, caller, w, r) error | 上傳與下載。方法來自查詢字串,內容不是 JSON。 |
處理函式的回傳值會成為 data。route 的權限永遠不是由你使用的函式決定,而是由 permissions.json 決定。
呼叫者與錯誤
Section titled “呼叫者與錯誤”| 項目 | 用途 |
|---|---|
caller.User() | DSM 使用者名稱。 |
caller.Admin() | DSM 管理員為 true。 |
caller.Authenticated() | 有使用者登入時為 true。 |
api.RequireAdmin(caller) | 呼叫者不是管理員時回傳 admin_required。用它保留共用 route 的一部分。 |
api.BadRequest(msg) | 回傳帶有你訊息的 bad_request。 |
&api.Error{Code: "…", Message: "…"} | 回傳你自己的錯誤碼。UI 看得到。 |
其他任何 error | 記錄在 NAS 上,並以 internal 回傳給 UI。 |
擁有者請從 caller.User() 取得。不要信任 params 裡送來的使用者名稱。
在處理函式中使用能力
Section titled “在處理函式中使用能力”| 你需要 | 使用 |
|---|---|
| 以使用者權限存取檔案 | cfg.SessionFiles.List、Info、CreateFolder、Upload 與 Download。每個都接受 ctx、caller 與請求 r。需要 files。 |
| 同時適用於即時請求與 delegate 工作的介面 | filestation.RequestContext{Caller, Request, Service}.Files(),提供 List、Info、CreateFolder、CheckPermission 與 Write。需要 delegate。 |
| delegate 工作的檔案操作 | delegate.JobContext.Files() 另外提供 CreateFolderAll、Rename、Read,以及任務操作 Copy、Move、Delete、TaskStatus 與 CancelTask。每個都需要在 delegate 的 operations 中宣告對應操作。見 Delegate 指南。 |
| 背景工作 | 從 UI 呼叫 bg.* 方法。worker 是固定的,會計算 SHA-256。請見背景工作指南。 |
| 之後在沒有使用者工作階段時儲存檔案 | Delegate。使用者在場時呼叫 service.Destination(ctx, caller, req, "save"),保留 Destination.ID,之後呼叫 service.MoveFromStaging(ctx, MoveParams{DelegationID, JobID, Staging, Name, Size})。 |
| root 操作 | 管理員的 UI 以腳本名稱呼叫 root.run。把腳本放在 root/<name>.sh。產生的 UI 都不會呼叫它,請自己呼叫 ctx.call('root.run', {name})。 |
UI 用戶端
Section titled “UI 用戶端”ctx 是 DSM 視窗傳給 UI 的 context。其他輔助函式來自 @synopackageland/sdk(headless,不含 Vue)與 @synopackageland/sdk/vue(Vue 元件與 composable;@synopackageland/sdk/vue/style.css 匯入一次)。其版本必須等於 backend/go.mod 的 SDK 版本,否則 check 回報 SDK_VERSION_MISMATCH。@synopackageland/ui 只有視覺元件,沒有後端用戶端。
| 輔助函式 | 位置 | 用途 |
|---|---|---|
ctx.call(method, params) | ctx(所有 UI) | 呼叫後端方法。以 data 完成,以 {code, message} 拒絕。 |
ctx.locale | ctx | DSM 語言,例如 en 或 zh-TW。 |
ctx.reopen(fn) | ctx | 視窗再次顯示時執行 fn。回傳一個用來移除它的函式。 |
ctx.host | ctx | 視窗與檔案管理員動作:setTitle、openApp、openURL、openFileManager、revealFile、subscribe。 |
ctx.user | ctx | 來自 DSM 的 {name, isAdmin}。只用來隱藏控制項,存取權由後端決定。 |
ctx.files | ctx | 檔案傳輸用的 {endpoint, token}。App 有 files 時才存在。 |
ctx.portal | ctx | status()、apply(alias)、disable()、openControlPanel()、mountPanel(el)。App 宣告 portal 時才存在。 |
ctx.executor | ctx | 管理員核准:status()、authorize(password, scope?)、control(action)。有 rootOps 或 delegate 時才存在。 |
ctx.t(key, params) | ctx(DSM 視窗) | 來自 i18n/messages 的翻譯文字。 |
createNasProvider(ctx) | @synopackageland/sdk | 供選擇器使用的檔案 provider:shares、list、info、createFolder、upload、download。 |
createSession(ctx) | @synopackageland/sdk/vue | 登入工作階段的狀態與呼叫:login、bootstrap、logout、call。 |
createBackgroundSaveFacade(ctx) | @synopackageland/sdk | 為稍後儲存面板包裝 delegate.* 方法。 |
FilesPage、FileBrowser、NasPicker | @synopackageland/sdk/vue | 用來瀏覽並選擇檔案與資料夾的 Vue 元件。 |
LoginPage | @synopackageland/sdk/vue | Vue 登入表單。需要 https:。 |
BackgroundPanel | @synopackageland/sdk/vue | 以 bg.browse 為基礎的工作 Vue 面板。 |
ExecutorSettings | @synopackageland/sdk/vue | 管理員核准背景存取的 Vue 面板。 |
ExecutorApproval | @synopackageland/sdk/vue | 在 executor 核准前接管 App 的頁面。加上 preload 時,頁面在第一次讀取狀態期間先隱藏掛載,自己的讀取與核對並行;狀態為 ready 就顯示同一個實例,其他狀態則丟棄。 |
useExecutorPreload() | @synopackageland/sdk/vue | 頁面預載期間為 true。狀態不是 ready 時,它會在頁面卸載前就變成 false,所以 false 不代表頁面已顯示:要開 teleport 的對話框或 toast,請用 watchPostEffect。AuthorizationGate 會自行等待。 |
PortalSettings | @synopackageland/sdk/vue | 入口網站別名的 Vue 面板。 |
// A Vue component with ctx as a propconst list = await ctx.call<{ files: { name: string; isdir: boolean }[] }>('reports.list', { folder: '/volume1/reports',})try { await ctx.call('greeting.set', { text: 'Hello' })} catch (e) { const code = (e as { code?: string }).code // for example "admin_required"}| 限制 | 值 |
|---|---|
| 請求內容 | 1 MiB |
| 單一請求的時間 | 20 秒 |
permissions.json 的方法數 | 1024,檔案最大 64 KiB |
| 清單頁 | 100 筆 |
| 上傳大小 | 16 GiB |
| App 登入工作階段 | 最長 15 分鐘 |
| 登入嘗試 | 每個帳號每分鐘 5 次,每個位址每分鐘 20 次 |