Develop a application which I like but you may NOT
之前一直很想要開發一個 Synology 的一個替三方套件,但是因為種種原因 Padding 了。 現在火又升起來了,所以研究一下:根據 3rd-Party Package Developer Guild 上的教學,開始設計一個我喜歡但你可能不喜歡的套件。
根據 Guild 上的說法,需要先準備一個空的 .spk 檔案,其中必須要包含 INFO、scripts、package.tgz 這三個部分:
.
└── PangooStation.spk
├── INFO
├── scripts
│ ├── postinst
│ ├── postuninst
│ ├── postupgrade
│ ├── preinst
│ ├── preuninst
│ ├── preupgrade
│ └── start-stop-status
├── syno_signature.asc
└── package.tgz
└── [source code ...]
準備好下述資料之後,就可以在 Synology 上面安裝一個你自己設計的 package 了。
目前已經提供測試版的 PangooStation.spk 放到 Download Site 上了。
INFO
INFO 檔案包含了這個 Package 所需要的基本資料:很多欄位都可以使用預設值, 但是為了讓 package 擁有更好的使用體感,應該要有自己的 package name、版本號、適用平台、 描述以及開發者資訊。填好之後就可以在安裝 package 的時候看到以下這些資訊了。
package="Pangoo Station"
version="0.1"
arch="noarch"
description="Pangoo Station"
displayname="Pangoo Station"
maintainer="cmj@cmj.tw"
maintainer_url="http://cmj.tw"
fireware="5.2-5644"
startable="no"
如果你的 package 需要開啟在 DSM 上面的 UI,就需要註冊你的 package 成為一個 APP。 在這個情況下你就需要修改你的 INFO 檔,讓 synology 的 framework 知道如何在你的 package.tgz 中找到程式碼。
dsmuidir="ui"
dsmuidir 這個參數可以告訴 framework 你的 UI 是在哪個路徑之下,只要你將程式碼放在 package.tgz 中的 ui 即可。而 framework 則會到 ui/config 獲取到需要的 UI 設定。
scripts
scripts 這個資料夾包含了 7 個可以執行的 script 檔案:主要是用來在運行 package 的各個階段會執行的 script。用安裝 package 為例,當準備安裝 package 的時候,會先執行 scripts/preinst 來做所需要的預先動作。而安裝完畢之後,則會執行 scripts/postinst 來做所需要的收尾動作。
package.tgz
package.tgz 是用 gzip 壓縮後的內容,裡面的內容會在安裝好 package 之後將裡面的內容安裝到 /var/packages/[package name]/target 當中。所以主要的 package 邏輯都會放到 package.tgz 裡面, 並且在啟用 package 之後到相對的路徑下執行。
ui/config
在這個檔案中,包含的是一個 JSON 格式的檔案:
{
"main.js": {
"SYNO.APP.PangooStation": {
"type": "app",
"title": "Pangoo Station",
"version": "0.0.1",
"hidden": false,
"icon": "images/PACKAGE_ICON_{0}.PNG",
"appWindow": "SYNO.APP.PangooStation.Main",
"depend": ["SYNO.APP.PangooStation.Main"]
},
"SYNO.APP.PangooStation.Main": {
"type": "lib",
"depend": ["SYNO.SDS.AppWindow"]
}
}
}
在這個檔案當中會告訴 framework 如何來運作你的 javascript。每一個 javascript 檔案都會被當作是 JSON object 的一個 key 值,每個檔案內都會擁有若干個物件,每個物件都有自己的屬性 (type)。 在這個例子中,我們把 SYNO.APP.PangooStation 註冊為是一個 app 並且填上他所屬的 ICON, 當從 DSM UI 點選 ICON 的時候,會呼叫 appWindow 所屬的 ExtJS 的物件: SYNO.APP.PangooStation.Main。同時間我們也定義了 SYNO.APP.PangooStation.Main 這個物件, 他的屬性是 lib 而且他會呼叫到原生 Synology 提供的 ExtJS 物件 (SYNO.SDS.AppWindow)。這樣子,我們就讓 Synology 的 framework 了解怎樣來呼叫我們的 js source code。
ui/main.js
根據 ui/config 上的描述,我們告訴 framework 我們的 js source 是落在 ui/main.js 上。 而主要的實作 UI 上的邏輯,在這個例子中都是交給 main.js 來負責。如同在 config 上描述的一樣, 我們會實作兩個 ExtJS 物件:分別是 SYNO.APP.PangooStation 以及 SYNO.APP.PangooStation.Main。 第一個物件比較簡單,我們直接繼承 SYNO.SDS.AppInstance 並且設定 appWindowName 到 SYNO.APP.PangooStation.Main 上,這樣就可以在 DSM UI 上面看到我們 package 的 ICON, 並且當點選 ICON 的時候會自動呼叫 appWindowName 所屬的 ExtJS 物件。
而 SYNO.APP.PangooStation.Main 這個物件是藉由繼承 SYNO.SDS.AppWindow 來建立一個基本的物件。 可以藉由給予參數來客製化這個物件。
Ext.define("SYNO.APP.PangooStation", {
extend: "SYNO.SDS.AppInstance",
appWindowName: "SYNO.APP.PangooStation.Main",
});
Ext.define("SYNO.APP.PangooStation.Main", {
extend: "SYNO.SDS.AppWindow",
constructor: function (cfg) {
var config = Ext.apply(
{
text: "Pangoo Station",
width: 800,
height: 600,
},
cfg,
);
this.callParent([config]);
},
});
UI / ExtJS
在 Synology 的 DSM UI 中,利用 ExtJS 當作主要的前端 framework。為了能夠跟原本的 Desktop UI 結合,所以接續會用 ExtJS 來開發一個 package。
如同一開始在 config 上中描述的一樣,我們先建立一個名為 SYNO.APP.PangooStation 的 ExtJS 物件, 藉由繼承現有的 SYNO.SDS.AppInstance 就可以解決大多數的事情。
Ext.define("SYNO.APP.PangooStation", {
extend: "SYNO.SDS.AppInstance",
appWindowName: "SYNO.APP.PangooStation.Main",
});
接著,我們要產生出來一個主要的 Window 來承接實作細節中的每一個物件:利用繼承 SYNO.SDS.AppWindow,這個物件可以幫我們建立一個簡單的 Window,包含放大、改變大小等。
Ext.define("SYNO.APP.PangooStation.Main", {
extend: "SYNO.SDS.AppWindow",
constructor: function (cfg) {
this.panel = new SYNO.APP.PangooStation.Tab({
module: cfg.module,
owner: cfg.module,
});
var config = Ext.apply(
{
layout: "fit",
showHelp: false,
maxminzable: false,
resizable: false,
autoDestroy: true,
width: 1000,
autoHeight: true,
items: [this.panel],
},
cfg,
);
this.callParent([config]);
},
});
Signature
當套件開發到一定程度的時候,為了確保套件不會被惡意人士竄改,也讓 user 提升自我的安全防衛意識, 所以會將套件做簽章,而使用者也可以匯入受信任的開發者而降低安裝到惡意軟體的可能性。
Synology Package 中需要替你的套件做全方面的簽章:也就是對整個 .spk 檔案 (除了 syno_signature.asc) 之外的檔案做 GPG 簽章。可以透過簡單的方式替你的 synology package 簽章:
gpg --armor --detach-sign --out syno_signature `ls INFO pacakges.pkg scripts/* | sort`
curl -s -X POST -F "file=@syno_signature" http://timestamp.synology.com/timestamp.php > syno_signature.asc
wget -q http://timestamp.synology.com/timeserver.gpg -O pubkey
gpg --import public && gpg --verify syno_signature.asc
上面的步驟,可以將你的套件做簽章 (e.g. INFO、pacakges 以及 scripts 下的所有檔案)。按照 CodeSign.php 中的描述,他是按照字母排序後的結果依序 signing。最後,將這個簽章上傳到 timestamp.synology.com 做第二次簽章,確保你的簽章是被 Synology 所信任的。
[To be continue…]