網頁控制台與 Web Push
瀏覽器控制台位於 https://remote.ivjn.us/console/。它支援透過郵件驗證註冊、帳號/密碼或操作員令牌登入、工作區範圍內的裝置、流式命令、xterm PTY、Android 截圖/點選/導航/文字輸入、指標、審計記錄、裝置建立/刪除,以及主動訂閱的通知。唯讀帳號不能執行命令或檢視 Android 螢幕。離線裝置仍會顯示。
裝置通告 ubus 能力時顯示 ubus 分頁,可填寫物件、方法與選填的 JSON 物件參數(留空傳送 null)。呼叫使用密封工作階段,要求裝置上線、已啟用且具備操作權限。取消只停止等待,無法撤銷裝置已執行的方法;切換裝置或失去權限會清理待處理呼叫與結果。
註冊會建立獨立的工作區。登入前必須使用八位驗證碼驗證郵箱;控制台支援重新傳送驗證碼,並在重新整理後返回驗證流程。Exchange 必須配置 SMTP(SMTP_HOST、SMTP_PORT、SMTP_FROM,以及 .env.example 中的認證和安全設定)。郵件服務不可用時,註冊會報告錯誤,不會跳過驗證。
憑證儲存在頁面記憶體中。重新整理後需要重新登入。退出登入會關閉會話,嘗試移除此瀏覽器的推送訂閱,並撤銷透過密碼登入建立的會話。主操作員令牌仍保持原有的有效期。點選通知會聚焦已開啟的控制台,不會重新載入其憑證。
建置與部署
cd website/console
npm ci
npm test
npm run build
將 dist/ 複製到網站的 console/ 目錄。確保現有的 website/assets/ 可透過 /assets/ 訪問,以提供共享字型和標誌。提供 index.html、sw.js 和清單檔案時,應要求重新驗證或禁用快取。帶雜湊的資源可使用不可變快取。不要快取 /api/* 或 /ws/*。
控制台使用同源的 /api/* 和 /ws/browser。將這些路徑反向代理到 Exchange,保留 Host 並允許 WebSocket 升級。不要移除 /api 或 /ws 字首。Vite 開發伺服器將 API/WS 代理到 127.0.0.1:18082。Rust Exchange 預設允許同源瀏覽器存取。透過 REMOTE_CORS_ORIGINS 設定精確來源;只有明確的 * 才允許所有來源。Worker 使用 CORS_ORIGINS 及其自身預設值。Bearer 驗證和一次性 WebSocket 票證仍為必要,不啟用跨來源 Cookie。
瀏覽器認證先使用帶 Bearer 授權的 POST /api/ws-ticket,隨後請求 GET /ws/browser,併傳送首個 JSON 幀 {"ticket":"..."}。票據在 30 秒後過期,只能使用一次,使用時會重新檢查操作員的啟用狀態和憑證有效性。長期有效的憑證不會放入 WebSocket URL。原生客戶端繼續使用 /ws/operator。
所有裝置操作都使用原生密封 WebSocket 工作階段。HTTP 提供驗證、裝置中繼資料、金鑰與授權目錄,以及轉送工作階段管理;請參閱 Agent 操作的 HTTP API。
啟用 Web Push
在 Exchange 環境中設定以下兩個變數(兩個都缺失時禁用推送):
WEB_PUSH_VAPID_PRIVATE_KEY=<base64url-unpadded-32-byte-P256-private-key>
WEB_PUSH_VAPID_SUBJECT=https://remote.ivjn.us
使用 python scripts/generate-vapid-key.py 生成私鑰。將其儲存在 Git 之外、訪問受限的環境檔案中,重啟時保持不變,並安全備份。公鑰會自動推導。更換金鑰後,瀏覽器需要重新訂閱。
WEB_PUSH_ALLOWED_ORIGINS 預設僅包含以下準確的可信源地址:https://fcm.googleapis.com,https://updates.push.services.mozilla.com,https://web.push.apple.com。只有驗證了其他瀏覽器廠商的 HTTPS 推送源地址後,才能新增該廠商。客戶端提供的端點不能擴充套件此允許列表。系統不會跟隨重新導向。
需要認證的路由:
| 方法與路徑 | 用途 |
|---|---|
GET /api/webpush/config |
啟用狀態及公開的應用伺服器金鑰 |
GET/POST /api/webpush/subscriptions |
列出自己的訂閱後設資料,或訂閱/更新插入 |
DELETE /api/webpush/subscriptions/{id} |
移除自己的訂閱 |
POST /api/webpush/test |
向呼叫者的訂閱佇列加入一條可見的測試通知 |
POST /api/webpush/messages |
傳給自己,或由管理員傳給同一工作區的其他帳號 |
POST /api/webpush/devices/{id}/messages |
裝置向有管理權限且已訂閱的管理員傳送訊息 |
訂閱使用瀏覽器的 PushSubscription.toJSON()、application_server_key 和 triggers。triggers 最多包含一個 online 物件和一個 offline 物件。每個物件包含 kind、tick_count(預設 3,範圍 1–100),以及可選的 device_ids。offline 在連續 tick_count 次未收到響應後通知;online 在連續 tick_count 次成功後通知。device_ids 可以是單個裝置 ID(字串)、裝置 ID 列表,或省略/設為 null 以涵蓋工作區中的所有裝置。空的 triggers 陣列會訂閱瀏覽器,但不接收自動上線/離線通知。不接受 missed_count 和 success_count。訊息使用可選的 operator_id 和 notification,後者包含 title、body,以及可選的同源相對 url 和 tag。/webpush/ 是最小參考客戶端;生產控制台使用自己的 worker,作用域為 /console/。
遷移 0005_web_push.sql 新增訂閱和持久化發件箱。0008_online_offline_triggers.sql 將裝置通知遷移到 triggers,並移除 missed_count / success_count。202 表示已入隊,不代表已送達或顯示。任務在 15 分鐘後過期。網路錯誤、408、429 和 5xx 會重試,總計最多嘗試五次;404/410 會移除對應版本的訂閱。90 秒租約允許 worker 故障後恢復待處理工作。投遞保證至少一次,瀏覽器是否顯示取決於廠商投遞、許可權和作業系統策略。持久化入隊前的裝置事件在過載時盡力處理;已入隊的事件在重啟後仍保留。限制:每帳號 20 個訂閱,每訂閱 32 條排隊訊息,每帳號每分鐘 30 次變更操作。
iOS/iPadOS 可能要求先將控制台新增到主螢幕,才能啟用通知。瀏覽器推送服務的可達性與 Exchange 的連線情況是兩回事。
驗證
執行 cargo test --workspace、cargo clippy --workspace --all-targets -- -D warnings,以及控制台的 npm 測試和建置。進行 PostgreSQL/HTTPS 整合測試時,先安裝 psycopg[binary],將 WEB_PUSH_TEST_ADMIN_URL 設定為具有 CREATE DATABASE 許可權的測試 PostgreSQL 連線,再執行 python scripts/test-webpush.py。該指令碼會建立並刪除臨時資料庫;切勿指向具有非預期許可權的資料庫角色。
從各端傳送
Web Console 的瀏覽器通知面板可填寫訊息;行動 Console 設定提供 Web Push 傳送、測試和開啟 Web Console 入口。桌面 Agent 本機設定、Android Agent 設定和瀏覽器擴充功能設定頁均可使用裝置憑證傳送。無介面/OpenWrt Agent 使用以下命令。接收端在 Web Console/PWA 啟用通知;此功能不新增原生 FCM/APNs 接收器。
remote-cli webpush status
remote-cli webpush subscriptions
remote-cli webpush test
remote-cli webpush send --title "Backup ready" --body "Completed"
remote-cli webpush send --operator-id ACCOUNT_UUID --title "Notice" --body "Hello"
remote-agent --config /path/to/config.json webpush --title "Device ready" --body "Completed"
{"notification":{"title":"Backup ready","body":"Completed","url":"/console/","device_id":"edge-1"}}
POST /api/webpush/messages 使用操作員 Bearer 憑證。省略 operator_id 表示傳給自己;其他接收者需要管理員權限,且必須屬於同一工作區。上方 JSON 也可透過 remote-cli webpush send --json-file message.json 傳送。將裝置狀態觸發器設為空陣列可關閉自動上線/離線提醒,同時保留通用訊息接收。
POST /api/webpush/devices/{id}/messages 使用該裝置的 Bearer 權杖,直接接收通知物件(title、body、選填 tag)。只有已啟用且管理該裝置的管理員的有效訂閱會接收。伺服器固定 device_id、event=device.message、occurred_at 和 /console/,傳送者不能指定帳號或冒充其他裝置。裝置傳送共用每工作區每分鐘 30 次的額度,單次最多 256 個訂閱;帳號訂閱數和佇列限制仍生效。沒有有效接收者回傳 202 {"queued":0};queued 是訂閱數,不是送達數。