跳到內容
Syno Package Land
語言: 繁體中文

原生 App

原生 App 是由 DSM 以 CGI 執行的 Go 後端,加上在自己的 DSM 視窗中開啟的 UI。它以未簽署的 SPK 安裝,以專屬的套件帳號執行,不需要容器,預設也沒有 root 權限。synopkgland init -t native-go 會建立可運作的專案:登入狀態、管理員可編輯的問候語,以及建置設定。

你需要安裝 synopkgland CLI 的 Node.js,以及 Go 1.25 或更新版本。

Terminal window
synopkgland init -t native-go my-app

此命令不會覆寫檔案(INIT_TARGET_NOT_EMPTY)。預設 UI 是 Vue。加上 --ui plain 可使用手寫的 ui/app.js,請見 UI 元件。加上 --with files,jobs,login 可啟用能力,請見能力。

路徑內容要編輯嗎?
dsm-package.yaml套件識別、版本、建置編號、架構、後端與 UI 位置要
backend/internal/app/你的 API:route 與 handler要
backend/internal/api/permissions.json誰可以呼叫各個方法要
frontend/src/Vue UI 外框。文字放在 frontend/src/i18n/<locale>.json要
i18n/messages/<locale>.json九種語言的套件文字要
i18n/glossary.jsonDSM 本身的用詞,以及必須使用這些用詞的訊息新增詞彙
AGENTS.md、CLAUDE.md給在專案中工作的 AI 助理的指示要
backend/internal/api/permissions.go、backend/cmd/載入權限,並組裝 CGI 與 jobs worker很少
dist/建置出的 SPK 檔案不要

請求防護、DSM 登入檢查、File Station、jobs 與 delegate 來自 SDK,不是專案檔案:Go module github.com/synopackageland/syno-pkg-land-sdk 與 npm 套件 @synopackageland/sdk。backend/go.mod 中的 require github.com/synopackageland/syno-pkg-land-sdk 是專案的 SDK 版本。frontend/package.json 必須把 @synopackageland/sdk 固定為同一版本,否則 check 會以 SDK_VERSION_MISMATCH 失敗。CLI 只建置與自己相同 minor 版本的 SDK(SDK_UNSUPPORTED)。

使用 --ui plain 時,專案是 ui/app.js 與 ui/app.css,沒有 frontend/。其餘內容由建置產生:INFO、權限(run-as: package)、生命週期腳本、圖示、視窗載入器與 CGI。

若要封裝已建置好的可執行檔而非 Go 原始碼,請使用 -t binary。binary 後端不能使用 files、jobs、login 或 delegate。若已有自己的 payload 與生命週期的產品要保留,請見既有原生產品。

route 有兩部分:在 backend/internal/app/routes.go 註冊的 Go handler,以及 backend/internal/api/permissions.json 中的一筆設定。

func Routes(cfg Config) []api.Route {
routes := cfg.delegateRoutes()
routes = append(routes, []api.Route{
api.Handle("app.status", cfg.status),
api.Handle("greeting.get", cfg.getGreeting),
api.Handle("greeting.set", cfg.setGreeting),
api.Handle("notes.list", cfg.listNotes),
}...)
// ...the rest of the function stays as generated
}
func (cfg Config) listNotes(_ context.Context, caller api.Caller, params json.RawMessage) (any, error) {
var in struct {
Limit int `json:"limit"`
}
if err := decodeParams(params, &in); err != nil {
return nil, err
}
if in.Limit < 1 || in.Limit > 100 {
return nil, api.BadRequest("limit must be 1 to 100")
}
return map[string]any{"owner": caller.User(), "notes": []string{}}, nil
}
{
"schema": 1,
"default": "administrators",
"methods": {
"notes.list": "authenticated"
}
}

接著在 backend/internal/app/routes_test.go 的 sampleParams 中,加入此 route 的有效參數,例如鍵 notes.list 搭配值 {"limit":20}。在你加入之前,產生的測試會失敗。以 (cd backend && go test ./...) 執行測試。

route 規則:

  • handler 是 func(ctx, caller, params) (any, error)。decodeParams 會拒絕未知欄位。請回傳 api.BadRequest(message) 或 &api.Error{Code, Message}。其他錯誤到達 UI 時一律是 internal。
  • handler 需要 HTTP 請求時用 api.HandleRequest,要設定 cookie 或寫入回應時用 api.HandleResponse,要做傳輸時用 api.HandleStream。
  • caller.User() 是已登入的 DSM 使用者帳號,caller.Admin() 對管理員群組成員為 true。兩者都來自 DSM,絕不取自請求。
  • 在一般使用者共用的 route 內,api.RequireAdmin(caller) 可將單一動作保留給管理員。它會回傳 admin_required。
  • 方法名稱是以句點連接的小寫單字,例如 notes.list。列在 permissions.json 的名稱必須已註冊。
  • 修改 permissions.json 需要重新建置。請求無法變更它。
permissions.json 的等級沒有工作階段一般使用者管理員
administrators(未列出的方法也是此結果)session_requiredadmin_required執行
authenticatedsession_required執行執行
public執行執行執行

default 必須保持為 administrators。只有在必須不登入也能運作的 route 才使用 public。authenticated 或 public 的 route 也會讓一般使用者能開啟 App 視窗,請見下方的 entry。

dsm-package.yaml 中可選的 entry 欄位,會在每個 route 之前加上一道檢查。

entry誰能進入拒絕時的錯誤碼
省略視窗依你的 route 而定:只要至少有一個 route(包含 files 或 jobs 加入的)不是 administrators,所有使用者都看得到依各 route
admin-only僅管理員admin_required
app-privilege管理員,以及在控制台 > 應用程式權限中獲准的帳號app_privilege_denied

app-privilege 至少需要一個 authenticated route。public route 會略過此檢查。若 DSM 無法回答權限問題,呼叫會以 auth_unavailable 失敗。

UI 會收到 context 物件 ctx。ctx.call(method, params) 以已登入的 DSM 工作階段向你的後端送出請求,並回傳結果。錯誤會以 { code, message } 拒絕。

try {
const { notes } = await ctx.call<{ notes: string[] }>('notes.list', { limit: 20 })
console.log(notes)
} catch (error) {
const { code } = error as { code?: string }
// show a message for `code`, for example admin_required or bad_request
}

要顯示僅限管理員的控制項,請用 ctx.user?.isAdmin。這只會隱藏控制項,真正決定誰能呼叫的是後端。其他 context 成員:ctx.reopen(fn) 會在 DSM 每次重新顯示視窗時執行 fn,並回傳取消訂閱的函式;ctx.locale 是視窗語言。

所有回應都使用 HTTP 200。失敗時為 {"success": false, "error": {"code", "message"}},與 DSM 自己的 Web API 相同。UI 會看到的錯誤碼列於錯誤碼。

App 提供九種語言:en、zh-TW、zh-CN、ja、ko、de、fr、es 與 it。DSM 的其他語言顯示英文。視窗會依 DSM 的語言設定切換。

  • i18n/messages/<locale>.json 存放扁平的 "dotted.key": "text" 配對。en.json 為必要。每個語言檔的鍵必須與 en.json 完全相同,{placeholder} 名稱也要相同。
  • 新增訊息時,把鍵加到 en.json 與其他八個檔案。
  • 後端會回傳的每個錯誤碼,都要加入對應的 error.<code> 訊息,UI 才能以使用者的語言顯示文字。
  • i18n/glossary.json 以九種語言列出 DSM 本身的用詞(「儲存」、「取消」),以及哪些訊息必須包含它們。要使用 DSM 用詞時,複製其文字,並把你的鍵加到該詞的 keys。
  • dsm-package.yaml 中的 i18n.info 設定套件中心依語言顯示的顯示名稱與說明。英文來自最上層欄位。
  • 在 plain UI 中,ctx.t("greeting.setBy", { text, user }) 會讀取這些檔案。Vue UI 將畫面上的文字放在 frontend/src/i18n/<locale>.json。請把新文字加到那裡的九個檔案。

check 與 build 會在鍵缺少或多出、遺漏 placeholder、語言不支援,或訊息與詞彙表不一致時失敗。

Terminal window
synopkgland check
synopkgland build
synopkgland deploy --host my-nas
synopkgland status --host my-nas
synopkgland logs --host my-nas
命令作用
check驗證 manifest、能力組合、permissions.json 與翻譯
build為每個架構寫出 dist/<package>-<version>-<build>-<arch>.spk。Vue 專案請先在 frontend/ 執行 npm install
deploy建置單一架構並安裝到 NAS。版本與建置編號的組合必須高於已安裝的(DEPLOY_VERSION_INVALID)。無法偵測 CPU 類型時請用 --arch(DEPLOY_ARCH_UNDETERMINED)
status套件狀態、開啟 App 的網址,以及各能力的就緒狀態
logs套件狀態行與套件識別。它不是 App 日誌

deploy、status 與 logs 一定要有 --host(HOST_REQUIRED)。每個要交出去的 SPK 都要遞增 build。安裝、升級與發佈請見發佈。check、build、deploy 與 status 加上 --json 會輸出單一 JSON 物件。

項目限制
套件名稱2 到 64 個英文字母與數字,以字母開頭
build1 到 9999 的整數
JSON 請求本文1 MiB
單一請求的時間20 秒
架構x86_64、aarch64、armv7(armv7 的 jobs、delegate、root 操作與 daemon 尚未在 NAS 上驗證)
最低 DSM 版本os_min_ver 預設為 7.1-42661
語言九種,見上方
症狀或錯誤碼原因解法
一般使用者收到 admin_requiredroute 是 administrators,或未列在 permissions.json請使用管理員帳號開啟。若該方法應開放一般使用者,將它設為 "authenticated" 並重新建置。
unknown_method方法未在 routes.go 註冊以 api.Handle 註冊
bad_requestdecodeParams 拒絕了未知欄位,或 handler 拒絕了某個值只傳 handler 會讀取的欄位
session_required沒有有效的 DSM 工作階段重新登入 DSM
app_privilege_deniedentry: app-privilege,而該帳號沒有此 App 的應用程式權限請管理員到「控制台 > 應用程式權限」,選擇此 App 並按「編輯」,允許你的帳號或群組使用。
INIT_TARGET_NOT_EMPTYinit 發現已有檔案使用空的目錄
新增 route 後 Go 測試失敗route 未加入 sampleParams加入有效參數

admin-only App 僅供 DSM administrators 群組的成員使用,請使用管理員帳號開啟;它不會列在應用程式權限中。遇到 app_privilege_denied 時,請依 App 的顯示名稱在應用程式權限清單中選擇。SDK 的拒絕畫面會區分這兩種錯誤碼,其他權限錯誤仍保留各自的說明。