API 文件
Town OS systemcontroller API 的完整參考——涵蓋帳號、儲存、套件庫、套件、systemd、 設定、稽核、頁面、DNS、監控、系統服務、語言地區、虛擬機器映像檔與狀態,共 77 個端點。
概觀
systemcontroller 是 Town OS 的核心後端服務。它建構於
Echo v5 之上,正式環境中會監聽 5309 連接埠(TCP)
或一個 Unix 網域 socket。所有請求與回應主體都使用 JSON。錯誤遵循
RFC 9457
(application/problem+json)。
開發時會啟用 CORS。正式環境中,API 與介面部署在同一個來源之下。
身分驗證
呼叫 POST /account/authenticate 並附上使用者名稱與密碼即可驗證。
回應中會含有一個 Bearer 權杖。請在後續請求中帶上它:
Authorization: Bearer <token> 工作階段閒置 7 天後過期。共有五種驗證層級:
| 層級 | 說明 |
|---|---|
| 公開 | 不需要權杖。 |
| 已驗證 | 任何有效的工作階段權杖。 |
| 管理者 | 屬於管理者帳號的工作階段權杖。 |
| 授權 | 帳號上的某項特定授權可以放行非管理者。物件儲存端點使用物件儲存授權;對等裝置註冊使用 wireguard 授權,而依網路的範圍與每個對等裝置的歸屬則由處理常式本身檢查。 |
| Localhost | 來自 loopback 的請求不需驗證即可通過——能連到 loopback 本身就代表已經在這台機器上——其他來源則需要徽章旁標示的層級。systemd unit 與記錄端點使用它,因為控制器自己的工具會讀取這些端點。 |
即使分割區內部的端點並不限於管理者,建立物件儲存分割區仍然只保留給管理者: 分割區是一棵權限樹的根,並且會配置一個帶配額的 btrfs 子磁碟區, 因此授權允許你操作分割區內部的使用者,而不是允許你決定這個分割區該不該存在。
分頁
所有清單端點都接受下列查詢參數,並回傳統一的信封結構:
| 參數 | 型別 | 說明 |
|---|---|---|
sort_by | string | 用來排序的欄位名稱。 |
sort_order | string | asc 或 desc。 |
limit | int | 每頁筆數(預設 20)。 |
offset | int | 分頁位移。 |
search | string | 對所有字串欄位做不分大小寫的子字串比對。 |
回應信封
{
"entries": [...],
"has_more": true,
"total_pages": 5,
"total_count": 97
} 狀態
/status/ping 公開
健康檢查與系統概覽。未驗證的呼叫者會取得精簡回應,只含
status 與 needs_setup。已驗證的呼叫者則取得完整的儀表板資料,
包含檔案系統數量、套件計數、unit 狀態摘要、磁碟使用量、外部/內部 IP,以及是否有可用升級。
帳號
/account/authenticate 公開 以使用者名稱與密碼驗證。回傳一個工作階段權杖與該帳號物件。
| 欄位 | 型別 | 說明 |
|---|---|---|
username | string | 必填。帳號使用者名稱。 |
password | string | 必填。帳號密碼。 |
/account/create 公開
/
管理者 建立新帳號。在初始啟動模式下(尚無任何已啟用的管理者帳號),此端點是公開的; 否則需要管理者驗證。建立的第一個帳號會成為管理者。密碼至少 8 個字元。 電子郵件、電話與真實姓名為必填。
| 欄位 | 型別 | 說明 |
|---|---|---|
username | string | 必填。 |
password | string | 必填。至少 8 個字元。 |
email | string | 必填。 |
phone | string | 必填。 |
real_name | string | 必填。 |
admin | boolean | 此帳號是否具有管理者權限。 |
/account 已驗證
依使用者名稱取得單一帳號。請求主體:{"username": "alice"}。
/account 已驗證 列出所有帳號。支援分頁參數。
/account/update 已驗證
更新帳號欄位。送出 username 以指明帳號,並附上一個
fields 物件,其中可任意組合 password、
email、phone、real_name 與 admin。
只有提供的欄位會被變更。
/account/me 已驗證 回傳與 Authorization 標頭中權杖關聯的使用者名稱。
/account/sessions 已驗證 列出目前已驗證使用者的所有作用中工作階段。每個工作階段含其 ID、使用者名稱、建立時間與最後使用時間。
/account/session/revoke 已驗證
依 ID 撤銷一個工作階段。請求主體:{"session_id": "..."}。
/account/disable 管理者
停用一個帳號。請求主體:{"username": "bob"}。
/account/enable 管理者
重新啟用一個已停用的帳號。請求主體:{"username": "bob"}。
儲存
/storage 已驗證
列出檔案系統。除分頁參數外,請求主體中還可傳入選用的 name
(前綴篩選)與 state(user、installed
或 uninstalled)。
/storage/create 已驗證
建立一個新的 btrfs 子磁碟區。送出 name 與選用的 quota
(位元組)。若配額為 0 或省略,則採用系統預設值(50 GB)。保留名稱
(installed、uninstalled、archives)會被拒絕。
/storage/modify 已驗證
修改既有的檔案系統。送出 name 以指明對象,並附上一個
filesystem 物件,其中含更新後的 name 與/或 quota。
/storage/remove 已驗證
移除一個檔案系統。請求主體:{"name": "mydata"}。
/storage/upload-archive 管理者
上傳封存檔並解開到目標子磁碟區。接受 multipart/form-data,
含一個 subvolume 欄位與一個 archive 檔案。支援
.tar.gz、.tgz、.tar.bz2、.tbz2、
.tar.xz、.txz、.tar、.zip 與
.7z。
| 欄位 | 型別 | 說明 |
|---|---|---|
subvolume | string | 必填。目標子磁碟區路徑。 |
archive | file | 必填。要上傳的封存檔。 |
subpath | string | 選用。磁碟區內用來解開的相對路徑;會視需要建立。 |
stop_service | string | 選用。解開前停止、完成後重新啟動的 systemd unit 名稱。 |
| 設定 | 預設值 | 說明 |
|---|---|---|
max_archive_size | 1 GB | 上傳大小上限。 |
archive_unpack_timeout | 600 秒 | 解開封存檔的最長時間。 |
/storage/download-archive 管理者 下載子磁碟區內容的封存檔。會以所要求的格式回傳串流封存。
| 欄位 | 型別 | 說明 |
|---|---|---|
subvolume | string | 必填。來源子磁碟區路徑。 |
paths | string[] | 選用。子磁碟區內要納入的特定路徑陣列。 |
stop_service | string | 選用。封存期間停止、之後重新啟動的 systemd unit 名稱。 |
format | string | 選用。壓縮格式:tar.gz(預設)、tar.bz2 或 tar.xz。 |
filename | string | 選用。下載檔案的自訂主檔名,伺服器會補上對應副檔名。預設為 download。 |
/storage/package-volumes 已驗證 依套件分組列出套件磁碟區,並可選擇是否納入已解除安裝的磁碟區。
/storage/remove-package-volume 管理者 依內部名稱刪除某個特定的套件磁碟區。
/storage/remove-package-volume-group 管理者 一次呼叫即刪除屬於某個套件的所有磁碟區,不必逐一依內部名稱移除。
套件庫
/repository 已驗證 列出所有已設定的套件庫,含名稱、網址與任何錯誤狀態。支援分頁參數。
/repository/add 已驗證 新增一個套件庫。會立即觸發一次重新整理。
| 欄位 | 型別 | 說明 |
|---|---|---|
name | string | 必填。套件庫的顯示名稱。 |
url | string | 必填。套件庫的 Git 網址。 |
username | string | 選用。私人套件庫的驗證使用者名稱。 |
password | string | 選用。私人套件庫的驗證密碼。 |
/repository/remove 已驗證
依名稱移除一個套件庫。會立即觸發一次重新整理。
請求主體:{"name": "my-repo"}。
/repository/move 管理者
把某個套件庫移到新的位置(從 0 起算)。當套件名稱衝突時,排在後面的套件庫會覆蓋前面的。
請求主體:{"name": "my-repo", "position": 0}。
/repository/refresh 已驗證 強制立即重新整理所有套件庫的中繼資料。成功時回傳空的主體;若有套件庫失敗, 則回傳一個將套件庫名稱對應到錯誤字串的 JSON 物件。
套件
/packages 已驗證 列出所有套件庫中全部可用的套件。每筆項目含套件庫、名稱、版本、描述、supplies 標籤、 安裝狀態,以及是否有可用升級。支援分頁參數。
/packages/by-repo 已驗證
依套件庫分組列出套件。接受選用的 search 查詢參數。
回傳一個由 {"repo": "...", "packages": [...]} 組成的陣列。
/packages/installed 已驗證 列出已安裝套件的識別碼。支援分頁參數。
/packages/installed/info 已驗證
取得某個已安裝套件的詳細資訊。送出 repo、name
與 version。回傳問題、使用者回答、註記與註記型別。
/packages/responses 已驗證
取得某個已安裝套件所保存的問題回答。送出 repo、
name 與 version。回傳一份鍵值對應。
/packages/versions 已驗證
列出某個套件的可用版本。請求主體:{"name": "nginx"}。
回傳一個版本識別碼的字串陣列。
/packages/children 已驗證
列出子套件。送出 repo 與 name。回傳一個字串陣列。
/packages/questions 管理者
取得某個套件的安裝問題。請求主體:{"name": "nginx"}。
回傳一份從問題鍵到 {"query": "...", "type": "..."} 的對應。
/packages/questions/identity 管理者
取得特定套件版本的問題。送出 repo、name
與 version。
/packages/oauth/start 管理者
為某個 oauth 問題啟動 OAuth 裝置流程。送出 repo、
name、version 與 question。系統控制器
會向供應商執行該流程的起始步驟,並回傳
flow_id、approve_url(請在使用者的瀏覽器中開啟)、
選用的 user_code,以及 interval_ms——輪詢的頻率。
供應商的各個網址來自套件而非 Town OS,因此呼叫前會先檢查:
只允許 https,而且絕不能是主機自身網路上的位址。
/packages/oauth/poll 管理者
輪詢上面啟動的流程。送出 flow_id。回傳 status:
使用者尚未核准時為 pending,核准完成時為 approved
並附上 token,而流程逾時或權杖已被取走後則為 expired——
每個流程只能使用一次。取得的權杖接著會作為該問題的答案送到
/packages/install,與手動輸入的回答完全相同。
/packages/install-preview 管理者
在真正安裝之前先預覽這次安裝會做什麼。送出 repo、
name 與 version。回傳磁碟區細節、連接埠對應、
磁碟使用量、配額資訊、升級的來源版本,以及一段易讀的摘要。
/packages/install 管理者 安裝一個套件。
| 欄位 | 型別 | 說明 |
|---|---|---|
repo | string | 必填。套件庫名稱。 |
name | string | 必填。套件名稱。 |
version | string | 必填。要安裝的版本。 |
responses | object | 必填。對安裝問題的鍵值回答。 |
reuse_volumes | boolean | 重用先前安裝留下的資料磁碟區。 |
import_from_version | string | 升級時要從哪個版本匯入磁碟區。 |
/packages/uninstall 管理者
解除安裝一個套件。送出 repo、name、version,
以及選用的 purge_volumes(布林值)以刪除相關資料。
/packages/disable 管理者
停用一個已安裝的套件(停止其服務)。送出 repo 與 name。
/packages/enable 管理者
重新啟用一個已停用的套件(啟動其服務)。送出 repo 與 name。
/packages/purge-volumes 管理者
刪除某個已安裝套件的所有資料磁碟區。送出 repo 與 name。
/packages/uninstalled-volumes 管理者
檢查某個套件是否有先前安裝留下的磁碟區。送出
repo 與 name。回傳 has_uninstalled_volumes、
uninstalled_versions 與 installed_versions。
/packages/purge-uninstalled-volumes 管理者
刪除先前已解除安裝版本留下的磁碟區。送出 repo
與 name。
/packages/upgrades 已驗證
列出已安裝套件的可用升級。每筆項目含
installed_version、latest_version,以及套件定義是否
changed。
/packages/upgrades/dismiss 管理者 忽略目前的升級通知。送出一個空的 JSON 物件。
/packages/manifest 已驗證
回傳原始的 YAML 套件定義。送出 repo、name
與 version。以 Content-Type: text/x-yaml 回傳檔案內容。
若該套件檔案不存在則回傳 404。
/packages/featured 已驗證 列出所有套件庫中的精選套件。
/packages/last-responses 已驗證
取回某個套件快取的 last 回答。送出 repo 與
name。回傳上一次解除安裝時保存的回答,供重新安裝時重用。
/packages/clear-last-responses 管理者
刪除某個套件快取的 last 回答檔案。送出 repo 與
name。
/packages/rebuild-git 管理者
為某個已安裝套件中由 git 填入的磁碟區拉取最新變更,並重新啟動相依的服務。
送出 repo、name 與 version。
重新建置之前會依已保存的回答重新求值範本變數。
Systemd
/systemd/units 已驗證
/
Localhost 列出由 Town OS 管理的 systemd unit。每筆項目含 unit 名稱、描述、 load/active/sub 狀態、關聯的套件識別碼與描述,以及一個失敗旗標。支援分頁參數。
/systemd/units-tree 已驗證
/
Localhost
與扁平清單相同的 unit,但依相依關係組成樹狀:根套件在最上層,相依項逐層巢狀於其父項之下——
與 /storage/package-volumes 採用的結構相同。每一列都帶有扁平端點回傳的相同狀態資料,
因此用戶端不需要再送一次請求來補齊資訊。
/systemd/status 管理者
控制某個 systemd unit。送出 name(unit 名稱)與 action
(start、stop、restart、enable
或 disable)。
/systemd/status/tree 管理者
一次呼叫即依相依順序,對某個套件及其整棵相依樹套用同一個動作。
這裡同樣拒絕 enable 與 disable,理由與 /systemd/status 相同:
連鎖執行 enable 會把那些已透過父項連結的相依項重複啟用一次。
/systemd/logs 管理者
/
Localhost
透過 Server-Sent Events 即時串流某個 unit 的 journal 項目。傳入
unit 查詢參數;留空或使用 __system__ 會回傳全系統記錄。
每個 SSE 事件都含一筆 JSON 編碼的 journal 項目,欄位如
Message、Priority、RealtimeTimestamp 與
SystemdUnit。
/systemd/logs/tail 管理者
/
Localhost 取得一頁 journal 項目,支援以游標分頁與篩選。
| 參數 | 型別 | 說明 |
|---|---|---|
unit | string | systemd unit 名稱。留空或使用 __system__ 表示全系統記錄。 |
lines | int | 要回傳的項目數(預設 100)。 |
before | string | 游標——回傳該位置之前的項目。 |
after | string | 游標——回傳該位置之後的項目。 |
grep | string | 對訊息文字做不分大小寫的子字串篩選。 |
since | int | Unix 時間戳記——回傳該時間之後的項目。 |
until | int | Unix 時間戳記——蒐集到該時間為止。 |
priority | int | syslog 嚴重程度篩選(0 表示不篩選)。 |
回傳 entries、cursor(第一筆)與
end_cursor(最後一筆),供後續分頁使用。
/systemd/logs/tree 管理者
/
Localhost /systemd/logs 的樹狀對應端點:以一條 Server-Sent Events 串流承載某個套件
及其之下所有 unit 的記錄,並依時間順序合併。即使是沒有安裝紀錄的未知根,
也會得到一條開啟但沒有項目的串流,而不是 404,因此記錄檢視器對單一 unit 與整棵樹的處理方式完全一致。
/systemd/logs/tree/tail 管理者
/
Localhost
合併後樹狀記錄的分頁形式,接受與 /systemd/logs/tail 相同的游標、篩選與時間範圍參數。
設定
/settings 管理者 以鍵值物件取得所有設定。
/settings/get 管理者
取得單一設定項。請求主體:{"key": "default_quota"}。
回傳 key 與 value。
/settings/set 管理者
設定某個項目的值。請求主體:{"key": "default_quota", "value": "107374182400"}。
預設設定
| 鍵 | 預設值 | 說明 |
|---|---|---|
default_quota | 53687091200(50 GB) | 新檔案系統的預設配額。 |
max_archive_size | 1073741824(1 GB) | 封存檔上傳大小上限。 |
archive_unpack_timeout | 600(秒) | 解開封存檔的最長時間。 |
locale | en-US | 用於國際化的全系統語言地區。 |
proton_image | quay.io/town/proton:latest | Proton/Wine 執行器容器映像檔。 |
dns_tld | home | 本機 DNS 解析所用的頂層網域。 |
稽核記錄
/audit/log 管理者 列出稽核記錄項目。請求主體中的所有欄位皆為選填。
| 欄位 | 型別 | 說明 |
|---|---|---|
before_id | int | 鍵集分頁——回傳 ID 小於此值的項目。 |
account | string | 依帳號使用者名稱篩選。 |
sort_by | string | 用來排序的欄位。 |
sort_order | string | asc 或 desc。 |
limit | int | 每頁筆數。 |
offset | int | 分頁位移。 |
search | string | 搜尋篩選條件。 |
每筆稽核項目含 id、account、action、
path、detail、success、error
與 created_at。被稽核的動作包括:身分驗證,建立/更新/停用帳號,
撤銷工作階段,安裝/解除安裝/停用/啟用套件,建立/修改/移除檔案系統,
新增/移除/移動/重新整理套件庫,上傳/下載封存檔,更新設定,忽略升級,以及清除磁碟區。
頁面
靜態網站代管,支援三種內容來源型別:上傳封存檔、容器映像檔與 git 儲存庫。 使用者為其指定一個網域,系統則透過 Caddy 容器提供內容。 所有會造成變更的端點都需要管理者驗證;清單端點只需一般驗證。
/pages 已驗證 列出所有頁面,支援排序、搜尋與分頁。可依名稱、儲存庫網址、 分支、網域、來源型別、狀態與時間戳記排序。
/pages/create 管理者
建立一個新頁面。接受名稱、來源型別(archive、
container_image 或 git)、儲存庫網址、分支、網域、
容器映像檔與映像檔內目錄。來源型別預設為 archive。
git 與容器映像檔類型的頁面會以非同步方式佈建。
/pages/upload 管理者
為封存類型的頁面上傳內容的 tar 封存檔。接受含 name 與
archive 檔案的 multipart 表單。僅對來源型別為
archive 的頁面有效;其他來源型別會回傳 400。
/pages/update 管理者 對頁面的儲存庫網址、分支、網域、來源型別、容器映像檔或映像檔內目錄做部分更新。 只有提供的欄位會被變更。
/pages/remove 管理者 從資料庫刪除一個頁面,移除 webroot 符號連結,並刪除對應的 btrfs 子磁碟區。
/pages/rebuild 管理者
從來源重新建置頁面內容。git 頁面會拉取最新變更;容器映像檔頁面會從映像檔重新擷取。
封存頁面會回傳 400(請改用 /pages/upload 重新上傳)。
網路
網路是一個具名的 WireGuard 疊加網路,並與一個 DNS TLD 配對。 套件安裝到某個網路中,對等裝置加入這個網路,而 TLD 決定誰能解析什麼。 網路名稱必須是合法的 DNS 標籤,且不超過 32 個字元,因為它們同時會被當作 WireGuard 介面字尾與 systemd unit 名稱使用。
home 網路始終存在——它隨資料庫一併植入,而不是在開機時建立——並且在三個方面很特別:
它無法刪除,也不能被第二次建立;它只提供 DNS
(沒有 WireGuard 介面、沒有子網路、沒有對等裝置);而且在它上面註冊對等裝置會被拒絕,
回傳 400。每個帳號都屬於 home 網路,因此若在此接受註冊,單憑成員身分就等於取得了進入通道的途徑,
而且儲存下來的對等裝置所描述的通道根本不存在。
停用網路只會關掉傳輸層:WireGuard 介面不會被拉起,遠端存取因此中斷, 而本地 DNS 解析與容器本身照常執行。
/networks 已驗證 列出網路。每筆項目含名稱、TLD、子網路、本機在疊加網路中的位址、公鑰、監聽連接埠與啟用旗標。 私鑰永遠不會被序列化。
/networks/create 管理者
建立網路。子網路由本機識別種子與網路名稱以確定性方式推導而來,取自
10.64.0.0/10,以避開家用路由器常用的位址範圍。
以本機識別為依據,代表兩台都在提供對等接入的 Town OS 主機會選出不同的子網路,
因此同時加入兩者的裝置永遠不會遇到衝突。建立 home 會因 TLD 衝突檢查而回傳 409。
/networks/remove 管理者
刪除網路。會拒絕 home 網路。
/networks/enable 管理者 拉起該網路的 WireGuard 傳輸層。
/networks/disable 管理者 關掉傳輸層,同時讓 DNS 與容器繼續執行。
對等裝置
/networks/peers 已驗證 列出已在某個網路中註冊的對等裝置。
/networks/peers/connected 管理者 列出目前確實處於連線狀態的對等裝置,而不只是已註冊的。
/networks/peers/add 授權
註冊一個對等裝置。wireguard 授權是放行非管理者的依據;依網路的範圍與每個對等裝置的歸屬由處理常式檢查。
對 home 網路會回傳 400,因為它只提供 DNS。
/networks/peers/refresh 授權 在對等裝置的 TTL 到期之前續期其註冊。註冊是有時效的,回收程序會清掉已過期的項目。
/networks/peers/remove 管理者 把某個對等裝置從網路中移除。
本地 CA
/tls/ca.crt 公開 以 PEM 格式下載本機的本地憑證授權單位。Town OS 會為套件名稱簽發自己的憑證, 因此信任這張憑證正是讓那些名稱在瀏覽器中不再出現警告的關鍵。它刻意設為公開: CA 憑證本來就是拿來散布的那一部分,而且用戶端在持有任何可用於驗證的憑據之前就需要它。
物件儲存
Town OS 透過 gfeh 提供物件儲存。一個分割區包含一個 btrfs 子磁碟區、
一個 gfehd 行程、一個管理 socket,以及它自己的一套使用者。
每個 Town OS 網路恰好對應一個分割區,因此物件儲存的命名空間與 DNS、WireGuard 沿著同一條邊界劃分:
office 分割區中的主體、授權或公開曝光,在 home 中毫無意義。
每個分割區在固定的容器連接埠上提供四種 HTTP 檢視——S3 在 9000、HTTP 在 9001、雲端硬碟在 9002、IPFS 在 9003—— 並且完全不發布主機連接埠。這正是固定連接埠之所以安全的原因: 每個分割區都有自己的網路命名空間,ingress 透過容器名稱存取它,與存取套件的方式完全一樣, 所以兩個都在 9000 上提供 S3 的分割區不可能衝突。
分割區
這四條路由之所以獨立於 /storage/*,是因為 /storage/create
會無條件把送進來的名稱改寫成 user/<名稱>,因此無法產生
gfeh/ 前綴底下的磁碟區。它們的封包形態是 gfeh 用戶端會解析的公開契約,而非內部細節。
有兩處細節至關重要。前綴是不對稱的:請求帶的是裸名稱,
回應帶的是 gfeh/<名稱>,因為該前綴是 Town OS 的命名空間產物,
並不屬於分割區本身的身分。以及清單回傳的是裸 JSON 陣列,而不是分頁信封,
這與本 API 中其他所有清單端點都不同:gfeh 用戶端會直接反序列化一個普通清單,
而分頁包裝會導致解碼失敗。
| 路由 | 權限 | 請求 | 回應 |
|---|---|---|---|
POST /gfeh/partitions/create | 管理者 | name(不帶前綴)、quota | Filesystem,名稱為 gfeh/<n> |
POST /gfeh/partitions/modify | 管理者 | name、quota | Filesystem |
POST /gfeh/partitions/remove | 管理者 | name | 200,空回應 |
POST /gfeh/partitions | 已驗證 | 無請求主體 | Filesystem 的普通陣列 |
用戶端應據以分支的狀態碼:409 已存在(gfeh 的佈建是「建立或調整大小」,
正是靠這個狀態區分兩者)、404 不存在、400 名稱不合法、
403 非管理者。含有路徑分隔符號的名稱在這裡就會被拒絕,
因為 gfehd 在它自己的邊界上同樣會拒絕——雙方對「什麼是合法分割區名稱」若不一致,
就會讓 ../user/something 這樣的名稱指向物件儲存根目錄之外的磁碟區。
建立分割區僅限管理者,且無法透過授權取得: 它是一棵權限樹的根,並且會配置一個帶配額的 btrfs 子磁碟區, 因此持有授權的帳號會在任何處理常式執行之前就被拒絕。
瀏覽
/gfeh 已驗證 物件儲存總覽:存在哪些分割區,以及它們各自處於什麼狀態。
主體
分割區中的使用者。建立一個主體需要名稱、父項與權限上限——但不需要密碼,
這正是介面從不索取密碼的原因。上限遵循 gfeh 的投影規則:
Town OS 管理者為 all,其餘為讀/寫。
/gfeh/principals 已驗證 列出某個分割區中的主體。
/gfeh/principals/add 授權 在某個父項底下建立主體,並設定其權限上限。
/gfeh/principals/remove 授權 刪除一個主體。
授權
也就是存取控制清單。gfehd 會把授權收窄到主體的權限上限,
因此用戶端應顯示回傳回來的權限,而不是自己送出去的那些:
管理者必須能夠看出某項授權被收窄了。
/gfeh/grants 已驗證 列出授權,也可以只列出某一個主體的。
/gfeh/grants/add 授權 授予某個主體存取權限。回應中帶有實際儲存下來的權限。
/gfeh/grants/revoke 授權 依 id 撤銷一項授權。
公開曝光
一條已發布的檔案連結,服務於 /f/<token>。
/gfeh/exposures 已驗證 列出某個分割區中已發布的連結。
/gfeh/exposures/withdraw 授權 依 token 撤回一條已發布的連結,使該網址不再可用。
DNS
由 rolodex-dns 容器驅動的整合式本機 DNS 解析器。它為已安裝的套件
管理區域檔與記錄,並透過 gRPC Unix socket 介面提供本機名稱解析。
/dns/status 已驗證 回傳 DNS 狀態,包含啟用旗標、執行狀態、TLD 與記錄數量。
/dns/records 已驗證 列出所有 DNS 記錄。
/dns/records/add 管理者 新增一筆 DNS 記錄。接受名稱、記錄型別、值與 TTL。
/dns/records/remove 管理者 依名稱與型別移除一筆 DNS 記錄。
/dns/tld 已驗證 取得目前的頂層網域設定。
/dns/tld 管理者 設定 TLD。會變更既有的 TLD 並重新註冊所有已安裝的套件。
/dns/setup 管理者 初始化或重新啟動 DNS 伺服器,並註冊所有已安裝的套件。
封鎖清單
這是兩份彼此獨立的清單。DNSBL 以訂閱為基礎——由 rolodex 取回並套用的上游封鎖清單—— 並搭配一份放行清單,用來豁免那些無論上游清單怎麼說都希望能解析的名稱。 本地封鎖清單(RBL)則是本機自己的清單,逐筆編輯。
/dns/dnsbl 已驗證 取得 DNSBL 設定:訂閱了哪些上游封鎖清單,以及如何套用它們。
/dns/dnsbl 管理者 取代 DNSBL 設定。
/dns/dnsbl/allowlist 已驗證 列出已從訂閱封鎖清單中豁免的名稱。
/dns/dnsbl/allowlist/add 管理者 把某個名稱從訂閱的封鎖清單中豁免出來。
/dns/dnsbl/allowlist/remove 管理者 刪除一筆放行紀錄,讓訂閱的封鎖清單重新對該名稱生效。
/dns/rbl/local 已驗證 列出本機自有封鎖清單中的項目。
/dns/rbl/local/add 管理者 在本地封鎖清單中加入一個名稱。
/dns/rbl/local/remove 管理者 從本地封鎖清單移除一個名稱。
依服務發布 DNS
/dns/services 已驗證 列出已安裝的服務,以及每個服務是否發布了 DNS 名稱。
/dns/services/set 管理者 為某個服務開啟或關閉 DNS 發布,讓套件可以在不佔用網路名稱的情況下執行。
監控
整合的 Prometheus、Node Exporter 與 Grafana 堆疊,用於系統監控。
整套元件以受 systemd 監管、帶 Restart=always 的 podman 容器執行。
/monitoring/status 已驗證
回傳每個監控服務的容器狀態(名稱、映像檔、執行狀態、連接埠)。
未設定監控時會回傳 {"status": "disabled"}。
如何取得儀表板資料
系統控制器上並不存在任何反向代理。監控資料由專屬的
5308 連接埠提供,瀏覽器直接與該連接埠通訊;控制器自己的連接埠(5309)只承載
/monitoring/status。5308 上由誰監聽,取決於所設定的後端:
- uPlot 模式(預設)——由一個 socat 轉送器把 Prometheus 的 HTTP API 開在 5308 上,
介面直接查詢
/api/v1/query_range,並自行繪製圖表。 - Grafana 模式——Grafana 透過 podman 的連接埠對應直接監聽 5308,介面則以 iframe 內嵌。
TOWN_OS_MONITORING_PORT 可以更動儀表板連接埠;
TOWN_OS_PROMETHEUS_PORT 與 TOWN_OS_NODE_EXPORTER_PORT 對兩個 loopback 連接埠有同樣作用。
系統服務
系統服務是由 systemd 管理的基礎架構容器(與使用者安裝的套件服務不同)。
它們使用 town-os-system-- 作為 unit 名稱前綴。
/system-services 公開
/
已驗證 列出系統服務與其即時 unit 狀態。從本機存取時不需驗證。 每筆項目含鍵、顯示名稱、映像檔、連接埠與 systemd unit 狀態欄位。
/system-services/status 管理者
控制某個系統服務。接受 key 與 action
(start、stop 或 restart)。
/system-services/refresh 管理者 重新整理系統服務的 unit 檔案與狀態。
語言地區
系統的國際化語言地區資訊。
/locales 已驗證 回傳目前的語言地區、已填入的語言地區清單、常見語言(含其母語字體名稱) 以及延伸語言地區。使用 BCP 47 語言代碼。
虛擬機器映像檔
管理虛擬機器套件所使用、已快取的虛擬機器磁碟映像檔。遠端映像檔會被下載,
並透過 qemu-img convert 轉換成 raw 格式;轉換後的映像檔會快取在
vm-images 子磁碟區中。
/vm-images 已驗證 列出已快取的虛擬機器磁碟映像檔。回傳每個映像檔的名稱與檔案大小。
/vm-images/upload 管理者
從某個網址下載虛擬機器映像檔並轉換成 raw 格式。接受一個網址與選用的名稱。
名稱預設取自網址中的檔名,並加上 .raw 副檔名。
下載的逾時時間為 30 分鐘。
/vm-images/delete 管理者 依名稱移除一個已快取的虛擬機器映像檔。
物件儲存管理 API(gfeh)
以上都是 Town OS 的 API,通常應用程式應該使用它。在它之下,
每個 gfehd 分割區還有自己的管理介面:
以 JSON over HTTP 提供,而且只在它的 Unix socket 上,絕不監聽連接埠。
這個介面上既沒有權杖,也沒有身分驗證。socket 的檔案系統權限本身就是存取控制,
因此能夠連到它,就已經代表是這台機器上的 root。socket 位於 btrfs 磁碟區上,
因為那是 gfehd 容器與系統控制器容器都看得到的唯一一個檔案系統。
| 呼叫 | 方法與路徑 | 用途 |
|---|---|---|
Health | GET /v1/health | 存活探測,同時也用作就緒探測。 |
Names | GET /v1/names | 該分割區希望發布的名稱。 |
ListPrincipals | GET /v1/principals | 該分割區的使用者樹林。 |
CreatePrincipal | POST /v1/principals | 接受 name、parent、ceiling——不含密碼。 |
DeletePrincipal | DELETE /v1/principals/<name> | 移除一個主體。 |
ListGrants | GET /v1/grants?principal= | 存取控制清單,可只取某一個主體的。 |
CreateGrant | POST /v1/grants | 授予存取權限;會被收窄到主體的上限。 |
RevokeGrant | DELETE /v1/grants/<id> | 撤銷一項授權。 |
ListExposures | GET /v1/exposures | 已發布的 /f/<token> 連結。 |
WithdrawExposure | DELETE /v1/exposures/<token> | 停止提供某條已發布的連結。 |
gfehd 會把內部錯誤對應成 HTTP 狀態碼——404、409、400——
而 Go 用戶端再把它們對應回哨兵錯誤,因此 errors.Is 跨過 socket 邊界之後依然可用。
對於名為 <network> 的網路,其分割區的檔案位置如下:
| 內容 | 位置 |
|---|---|
| 分割區資料 | <btrfsBase>/gfeh/<network>,掛載於 /data/<network> |
| 設定 | <btrfsBase>/gfeh-control/<network>/gfehd.yaml |
| 管理 socket | <btrfsBase>/gfeh-control/<network>/run/admin.sock |
| unit | town-os-system--gfeh-<network>.service |
DNS gRPC API(rolodex)
上面的 /dns/* 端點是 Town OS 視角下的 DNS。rolodex 本身透過
gRPC 管理,開在一個 Unix socket 上(預設為
/var/run/rolodex-dns.sock),也可以選擇開在 TCP 上。
預設情況下,socket 是唯一的管理通道——grpc.tcp_bind 是空的。
只有一個服務 rolodex_dns.RolodexDnsService,包含 74 個方法。
所有路徑均為 /rolodex_dns.RolodexDnsService/<方法名稱>。
完整的訊息定義位於 rolodex-dns 儲存庫的 proto/rolodex_dns.proto;
下面的分組說明這些方法各自涵蓋什麼。
記錄與解析
| 方法 | 用途 |
|---|---|
AddRecord | 在本地資料庫加入一筆 DNS 記錄。 |
RemoveRecord | 從本地資料庫移除記錄。 |
ListRecords | 以可選的篩選條件查詢本地資料庫。 |
SetForwarders | 設定上游轉送器。 |
SetResolutionMode / GetResolutionMode | 在執行時變更並讀取解析模式。 |
GetSearchDomains | 某個用戶端 IP 對應的搜尋網域。 |
FlushCache | 清空 DNS 與封鎖清單快取。 |
權威區域
| 方法 | 用途 |
|---|---|
AddAuthoritativeZone | 把某個區域宣告為權威區域。 |
RemoveAuthoritativeZone | 把某個區域從權威清單中移除。 |
ListAuthoritativeZones | 列出所有權威區域。 |
網路範圍
範圍是 rolodex 劃分「誰能解析什麼」的方式,也是 Town OS 網路所對應到的對象。 IP 與範圍的關聯帶有 TTL,需要定期更新。
| 方法 | 用途 |
|---|---|
CreateNetworkScope / DeleteNetworkScope / ListNetworkScopes | 管理範圍。刪除一個範圍會連同它的記錄與關聯一起刪除。 |
JoinNetwork / LeaveNetwork | 把用戶端 IP 關聯到某個範圍,或解除該關聯。 |
GetNetworkAssociations | 讀取 IP 與範圍的關聯。 |
AddScopedRecord / RemoveScopedRecord / ListScopedRecords | 只在某個範圍內存在的記錄。 |
範圍 TLD
依網路歸屬的區域,在各網路之間劃分。
| 方法 | 用途 |
|---|---|
AddScopeTld / RemoveScopeTld / ListScopeTlds | 把一個全域唯一的 TLD 註冊為某個範圍所有。 |
SetScopeTldForwarders / ListScopeTldForwarders | 某個範圍的 TLD 所對應的對端轉送器。 |
ListScopeTldListeners | 繫結到某個範圍各 TLD 上的 ingress DNS 監聽器。 |
封鎖清單
| 方法 | 用途 |
|---|---|
SetDnsblConfig / GetDnsblConfig | 以訂閱為基礎的網域封鎖清單設定。 |
AddDnsblAllowlistEntry | 把某個名稱及其子網域從依名稱的封鎖檢查中豁免。 |
RemoveDnsblAllowlistEntry / ListDnsblAllowlistEntries | 管理放行清單。 |
AddLocalBlocklistEntry / RemoveLocalBlocklistEntry / ListLocalBlocklistEntries | 本機自有的封鎖清單。 |
加密傳輸
每種傳輸都有對應的設定與讀取方法。DoH 提供 HTTP/2,並在 enable_h3 開啟時,
於同一位址、同一連接埠、同一憑證上提供 HTTP/3。
| 方法 | 用途 |
|---|---|
SetDotConfig / GetDotConfig | DNS over TLS。 |
SetDohConfig / GetDohConfig | DNS over HTTPS,含 HTTP/3。 |
SetDoqConfig / GetDoqConfig | DNS over QUIC。 |
SetProxyConfig / GetProxyConfig | HTTP 代理設定。 |
DNSSEC、DANE 與 ACME
| 方法 | 用途 |
|---|---|
GenerateDnssecKey / ListDnssecKeys / DeleteDnssecKey | 依區域管理 DNSSEC 金鑰材料。 |
GetDsRecords | 某個區域的 DS 記錄。 |
SignZone | 以區域自己的 DNSSEC 金鑰為其簽章。 |
GenerateTlsaRecord / ListTlsaRecords | 由憑證產生的 TLSA 記錄。 |
GenerateDaneRootCa | 產生用於 DANE 的根 CA 憑證。 |
EnsureZoneCa | 確保某個區域擁有 CA。 |
RequestAcmeCert / GetAcmeStatus | 透過 ACME DNS-01 申請憑證,並查詢其狀態。 |
CreateEabCredential / RemoveEabCredential | 簽發限定於某個區域的外部帳戶繫結(kid 加 HMAC),供 ACME 用戶端的 newAccount 使用。 |
ListAcmeAccounts / ListAcmeCertificates | 已註冊的 ACME 帳戶與已簽發的憑證。 |
DHCP
| 方法 | 用途 |
|---|---|
AddDhcpPool / RemoveDhcpPool / ListDhcpPools | 在某個範圍內用於配發的位址池。 |
ListDhcpLeases / DeleteDhcpLease | 位址租約,依 MAC 位址刪除。 |
SetDhcpCertOption / RemoveDhcpCertOption / ListDhcpCertOptions | 為某個範圍透過 DHCP 下發給用戶端的憑證。 |
診斷與調校
| 方法 | 用途 |
|---|---|
GetCacheStats / FlushDnsCache | 快取統計,以及清空回應快取。 |
GetQueryLatencyStats | 上游查詢延遲。 |
SetTtlDriftConfig / GetTtlDriftConfig | TTL 漂移設定。 |
SetTrackedTlds / ListTrackedTlds | 依 TLD 指標背後所追蹤的 TLD 清單,包括已儲存的與實際生效的。 |
SetDns64Config / GetDns64Config | DNS64 設定。 |
用戶端函式庫
Town OS 內附 Go 與 JavaScript 用戶端函式庫,涵蓋完整的 API。 兩個用戶端遇到非 200 回應時,都會依據 RFC 9457 的問題細節拋出帶型別的錯誤。
Go 用戶端
Go 用戶端位於 src/svc/systemcontroller/client.go,實作了
Client 介面。它同時支援 Unix socket 與 HTTP 連線。
// 透過 Unix 網域 socket 連線(正式環境)
client := systemcontroller.InitClient("/run/town-os/systemcontroller.sock")
// 透過 HTTP 連線(開發 / 測試)
client := systemcontroller.FromClient(http.DefaultClient, "http://localhost:5309")
驗證完成後請設定 client.Token。所有方法的第一個參數都是
context.Context。
儲存
| 方法 | 說明 |
|---|---|
CreateFilesystem(ctx, fs) | 建立一個新的 btrfs 子磁碟區。 |
ModifyFilesystem(ctx, name, fs) | 重新命名檔案系統或調整其大小。 |
RemoveFilesystem(ctx, name) | 依名稱刪除檔案系統。 |
ListFilesystems(ctx, prefix, state, params) | 依名稱前綴與狀態("user"、"installed"、"uninstalled")篩選的分頁清單。 |
套件庫
| 方法 | 說明 |
|---|---|
AddRepository(ctx, name, rawURL, username, password) | 註冊一個套件庫,憑證選填。 |
RemoveRepository(ctx, name) | 依名稱移除套件庫。 |
MoveRepository(ctx, name, position) | 調整優先權(0 = 最高)。 |
RefreshRepositories(ctx) | 重新整理所有中繼資料。回傳錯誤對應。 |
ListRepositories(ctx, params) | 套件庫的分頁清單。 |
套件
| 方法 | 說明 |
|---|---|
ListPackages(ctx, params) | 可用套件的分頁清單。 |
ListPackagesByRepo(ctx, params) | 依套件庫分組的套件。 |
ListPackageVersions(ctx, name) | 某個套件的可用版本。 |
GetPackageQuestions(ctx, name) | 依名稱取得設定問題。 |
GetPackageQuestionsByIdentity(ctx, repo, name, version) | 特定版本的問題。 |
ListChildren(ctx, repo, name) | 子套件的名稱。 |
InstallPreview(ctx, repo, name, version) | 不實際安裝,預覽磁碟區與連接埠。 |
InstallPackage(ctx, name, version, responses, reuseVolumes, importFromVersion, skipResponseReuse) | 安裝一個套件。name 採 "repo/package" 格式。 |
UninstallPackage(ctx, repo, name, version, purgeVolumes) | 移除一個已安裝的套件。 |
DisablePackage(ctx, repo, name) | 停止服務但不解除安裝。 |
EnablePackage(ctx, repo, name) | 重新啟用一個已停用的套件。 |
PurgeVolumes(ctx, repo, name) | 刪除某個套件的所有資料磁碟區。 |
ListUninstalledVolumes(ctx, repo, name) | 檢查是否有殘留的磁碟區。 |
PurgeUninstalledVolumes(ctx, repo, name) | 刪除殘留的磁碟區。 |
ListInstalled(ctx, params) | 以 "repo/name@version" 形式列出已安裝的套件。 |
GetResponses(ctx, repo, name, version) | 已儲存的設定回答。 |
GetInstalledInfo(ctx, repo, name, version) | 詳細資訊,含問題、回答與註記。 |
Systemd
| 方法 | 說明 |
|---|---|
ListUnits(ctx, params) | systemd unit 的分頁清單。 |
SetUnitStatus(ctx, name, action) | 執行 "start"、"stop" 或 "restart"。 |
LogReplay(ctx, name) | 透過 SSE 串流 journal 項目。回傳一個 channel。 |
LogTail(ctx, params) | 一頁 journal 項目,支援游標分頁、grep、時間範圍與優先層級篩選。 |
帳號
| 方法 | 說明 |
|---|---|
Authenticate(ctx, username, password) | 回傳工作階段權杖與帳號。 |
CreateAccount(ctx, username, password, email, phone, realName, admin) | 建立一個使用者。密碼至少 8 個字元。 |
GetAccount(ctx, username) | 依使用者名稱取得帳號。 |
UpdateAccount(ctx, username, fields) | 修改帳號欄位(password、email、phone、real_name、admin)。 |
ListAccounts(ctx, params) | 帳號的分頁清單。 |
DisableAccount(ctx, username) | 阻止其進行身分驗證。 |
EnableAccount(ctx, username) | 重新啟用一個已停用的帳號。 |
ListSessions(ctx, token) | 該權杖所屬使用者的作用中工作階段。 |
SessionUsername(ctx, token) | 工作階段權杖對應的使用者名稱。 |
RevokeSession(ctx, sessionID) | 使某個工作階段失效。 |
稽核、設定與升級
| 方法 | 說明 |
|---|---|
ListAuditLog(ctx, opts, token) | 帶篩選條件的分頁稽核記錄。 |
GetSettings(ctx) | 以鍵值對應回傳所有設定。 |
GetSetting(ctx, key) | 依鍵取得單一設定。 |
SetSetting(ctx, key, value) | 更新某個設定。 |
ListUpgrades(ctx) | 有較新版本可用的套件。 |
DismissUpgrades(ctx) | 將待處理的升級標為已忽略。 |
封存檔
| 方法 | 說明 |
|---|---|
UploadArchive(ctx, subvolume, archiveReader, filename, subpath, stopService) | 上傳封存檔並解開到某個子磁碟區。格式:tar.gz、tar.bz2、tar.xz。 |
DownloadArchive(ctx, subvolume, paths, stopService, format) | 為子磁碟區內容建立封存檔。回傳一個 io.ReadCloser。 |
健康檢查
| 方法 | 說明 |
|---|---|
Ping(ctx) | 服務健康狀況與各項計數摘要。 |
JavaScript 用戶端
JavaScript 用戶端位於 ui/src/api/,由 Town OS 儀表板介面使用。
它以一組模組化的 mixin 建構在 SystemControllerClient 類別之上。
非 200 回應會拋出 ApiError,其中帶有解析後的 RFC 9457 問題細節。
import SystemControllerClient from './api/client.js';
const client = new SystemControllerClient('http://localhost:5309');
// 驗證完成後
const result = await client.authenticate('admin', 'password');
client.setToken(result.token); 儲存
| 方法 | 說明 |
|---|---|
createFilesystem(fs) | 建立一個新的 btrfs 子磁碟區。 |
modifyFilesystem(name, fs) | 重新命名檔案系統或調整其大小。 |
removeFilesystem(name) | 依名稱刪除檔案系統。 |
listFilesystems(prefix, sortBy, sortOrder, state, limit, offset, search) | 帶篩選條件的分頁清單。 |
套件庫
| 方法 | 說明 |
|---|---|
addRepository(name, url, username?, password?) | 註冊一個套件庫,憑證選填。 |
removeRepository(name) | 依名稱移除套件庫。 |
moveRepository(name, position) | 調整優先權(0 = 最高)。 |
refreshRepositories() | 重新整理所有中繼資料。回傳錯誤對應或 null。 |
listRepositories(sortBy, sortOrder, limit, offset, search) | 分頁清單。 |
套件
| 方法 | 說明 |
|---|---|
listPackages(sortBy, sortOrder, limit, offset, search) | 可用套件的分頁清單。 |
listPackagesByRepo(search) | 依套件庫分組的套件。 |
listPackageVersions(name) | 某個套件的可用版本。 |
getPackageQuestions(name) | 依名稱取得設定問題。 |
getPackageQuestionsByIdentity(repo, name, version) | 特定版本的問題。 |
installPreview(repo, name, version) | 不實際安裝,預覽磁碟區與連接埠。 |
installPackage(repo, name, version, responses, reuseVolumes?, importFromVersion?) | 帶設定回答安裝一個套件。 |
uninstallPackage(repo, name, version, purgeVolumes?) | 移除一個已安裝的套件。 |
disablePackage(repo, name) | 停止服務但不解除安裝。 |
enablePackage(repo, name) | 重新啟用一個已停用的套件。 |
purgeVolumes(repo, name) | 刪除某個套件的所有資料磁碟區。 |
listUninstalledVolumes(repo, name) | 檢查是否有殘留的磁碟區。 |
purgeUninstalledVolumes(repo, name) | 刪除殘留的磁碟區。 |
listInstalled(sortBy, sortOrder, limit, offset, search) | 以 "repo/name@version" 形式列出已安裝的套件。 |
getResponses(repo, name, version) | 已儲存的設定回答。 |
getInstalledInfo(repo, name, version) | 詳細資訊,含問題、回答與註記。 |
Systemd
| 方法 | 說明 |
|---|---|
listUnits(sortBy, sortOrder, limit, offset, search) | systemd unit 的分頁清單。 |
setUnitStatus(name, action) | 執行 "start"、"stop" 或 "restart"。 |
logReplay(unit) | 透過 SSE 串流 journal 項目。回傳一個 AsyncGenerator。 |
logTail(unit, lines?, before?, after?, grep?, since?, until?, priority?) | 一頁 journal 項目,支援游標分頁、grep、時間範圍與優先層級篩選。 |
帳號
| 方法 | 說明 |
|---|---|
authenticate(username, password) | 回傳工作階段權杖與帳號。 |
createAccount(username, password, email, phone, realName, admin) | 建立一個使用者。密碼至少 8 個字元。 |
getAccount(username) | 依使用者名稱取得帳號。 |
updateAccount(username, fields) | 修改帳號欄位。 |
listAccounts(sortBy, sortOrder, limit, offset, search) | 帳號的分頁清單。 |
disableAccount(username) | 阻止其進行身分驗證。 |
enableAccount(username) | 重新啟用一個已停用的帳號。 |
listSessions(token) | 該權杖所屬使用者的作用中工作階段。 |
sessionUsername(token) | 工作階段權杖對應的使用者名稱。 |
revokeSession(sessionID) | 使某個工作階段失效。 |
稽核、設定與升級
| 方法 | 說明 |
|---|---|
listAuditLog(opts) | 帶篩選條件的分頁稽核記錄。 |
getSettings() | 以鍵值物件回傳所有設定。 |
getSetting(key) | 依鍵取得單一設定。 |
setSetting(key, value) | 更新某個設定。 |
listUpgrades() | 有較新版本可用的套件。 |
dismissUpgrades() | 將待處理的升級標為已忽略。 |
封存檔
| 方法 | 說明 |
|---|---|
uploadArchive(subvolume, file, subpath?, stopService?) | 透過 FormData 上傳並解開封存檔。回傳 {needs_restart, message}。 |
downloadArchive(subvolume, paths?, stopService?, format?) | 下載子磁碟區封存檔。回傳原始的 Response 以便串流處理。 |
健康檢查
| 方法 | 說明 |
|---|---|
ping() | 服務健康狀況與各項計數摘要。 |
開發參考
Town OS 後端執行於 5309 連接埠,Vite 開發伺服器則在 5173 連接埠。
請用 make dev 啟動完整的開發環境。
核心目標
| 目標 | 說明 |
|---|---|
make dev | 啟動完整的開發環境(後端 + Vite 開發伺服器)。 |
make dev-stop | 停止並移除開發用的後端容器。 |
make dev-logs | 在執行中的開發容器內追蹤 journalctl。 |
make dev-clean | 停止容器並拆掉開發用的 btrfs 磁碟區。 |
測試目標
| 目標 | 說明 |
|---|---|
make test | 執行靜態檢查、Go 單元測試與 JS 單元測試。 |
make test-integration | 在特權 Podman 容器中執行 Go 整合測試。 |
make test-ui-integration | 針對後端容器執行 Bun 介面整合測試。 |
make test-full | 依序執行所有測試套組。 |
make auto-test | 監看檔案變更並自動重跑測試。 |
建置目標
| 目標 | 說明 |
|---|---|
make production-image | 建置正式環境的容器映像檔。 |
make test-image | 建置測試用的容器映像檔。 |
make pull-images | 從 Docker Hub 拉取基礎容器映像檔。 |
先決條件
- Go 1.25+
- Bun——JavaScript 執行環境
- Podman——rootful 模式,需要
sudo - btrfs-progs——
mkfs.btrfs - golangci-lint
建立一個 .env 檔案,填入儲存庫憑證:
TOWN_OS_REPO_USERNAME=<username>
TOWN_OS_REPO_PASSWORD=<password>
安裝好先決條件之後,請先執行 make pull-images,再執行其他任何目標。