# MarkBase開發指南 ##專案概述 **MarkBase - Momentry Display Engine** Rust Axum Web伺服器,提供 Markdown渲染與檔案樹管理功能。 -技術棧:Rust 1.92+, Axum 0.7, SQLite, pulldown-cmark -目標平台:macOS(含音訊控制功能) -資料庫:Per-user SQLite in `data/users/.sqlite` ##核心指令 ```bash #建構與測試 cargo build #建構專案 cargo test #域行所有測試 cargo test test_insert #執行特定測試 cargo clippy #代碼品質檢查 #執行伺服器 cargo run -- display #啟動顯示伺服器(預設 port 11438) cargo run -- display -p8080 #自訂 port cargo run -- display README.md #顯示指定 Markdown檔案 #渲染工具 cargo run -- render #渲染 Markdown(輸出到 stdout) cargo run -- render -o output.html #輸出到檔案 ```` ##架構概覽 ###核心模組 |模組 |檔案 |功能 | |------|------|------| | CLI入口 | `main.rs` | clap指令解析 | | Web伺服器 | `server.rs` | Axum REST API(18+路由) | |檔案樹管理 | `filetree/mod.rs` | SQLite CRUD操作 | | Markdown渲染 | `render.rs` | pulldown-cmark轉換 | |音訊控制 | `audio.rs` | macOS音訊裝置管理 | |指令隊列 | `command.rs` | WebSocket指令處理 | ###資料庫結構 -位置:`data/users/.sqlite` -表:`file_registry`, `file_nodes`, `file_locations` -每個 user_id獨立資料庫 --- ## File Tree核心說明(重點章節) ###架構設計 File Tree是 MarkBase的核心模組,提供檔案樹管理功能。 **核心結構:** ```rust FileTree { user_id: String, //用戶ID nodes: Vec, //節點列表 } ```` **節點類型:** - **Folder** -資料夾節點(可包含子節點) - **File** -檔案節點(指向實體檔案) ###資料庫設計 **SQLite表結構:** |表名 |功能 | |------|------| | file_registry |檔案註冊資訊 | | file_nodes |檔案樹節點 | | file_locations |檔案位置記錄 | **節點欄位:** ``` node_id, label, aliases_json, file_uuid, sha256, parent_id, children_json, node_type, icon, color, bg_color, file_size, registered_at, created_at, updated_at, sort_order ```` ###公開API(13個函數) |函數名 |功能 |檔案位置 | |--------|------|----------| | user_db_path | 取得DB路徑 | mod.rs:58 | | init_user_db | 初始化DB | mod.rs:62 | | open_user_db | 開啟DB | mod.rs:74 | | load | 載入檔案樹 | mod.rs:79 | | insert_node | 插入節點 | mod.rs:112 | | update_node | 更新節點 | mod.rs:149 | | update_node_alias | 更新別名 | mod.rs:187 | | delete_node | 刪除節點 | mod.rs:214 | | move_node | 移動節點 | mod.rs:220 | | build_tree | 建立樹狀結構 | mod.rs:240 | | new_folder | 建立資料夾節點 | node.rs:27 | | new_file_node | 建立檔案節點 | node.rs:300 | | add_location | 新增檔案位置 | mod.rs:346 | ### REST API(7個路由) |路由 |方法 |功能 |server.rs行號 | |------|------|------|--------------| | `/api/v2/auth/login` | POST | 登入(返回token) | 1203 | | `/api/v2/auth/logout` | POST | 登出(需Bearer token) | 1220 | | `/api/v2/auth/verify` | GET | 验证token有效性 | 1238 | | `/api/v2/tree/:user_id` | GET | 取得檔案樹(需認證) | 395 | | `/api/v2/tree/:user_id` | DELETE | 刪除所有節點(需認證) | 64 | | `/api/v2/tree/:user_id/node` | POST | 建立節點(需認證) | 62 | | `/api/v2/tree/:user_id/node/:node_id` | PUT | 更新節點(需認證) | 63 | | `/api/v2/tree/:user_id/node/:node_id` | DELETE | 刪除節點 | 63 | | `/api/v2/tree/:user_id/node/:node_id/move` | PUT | 移動節點 | 71 | | `/api/v2/tree/:user_id/node/:node_id/alias` | PATCH | 更新別名 | 72 | | `/api/v2/tree/:user_id/restore` | POST | 從外部API恢復 | 65 | **Query參數:** - `mode` -顯示模式 **使用範例:** ```bash #登入取得token curl -X POST http://localhost:11438/api/v2/auth/login \ -H "Content-Type: application/json" \ -d '{"username":"demo","password":"demo123"}' #返回:{"token":"xxx","expires_at":"...","user_id":"demo"} #使用token訪問檔案樹 curl http://localhost:11438/api/v2/tree/demo \ -H "Authorization: Bearer " ``` **認證說明:** - 所有 `/api/v2/tree/*` 和 `/api/v2/files/*` API 需要認證 - Token有效期:24小時 - 默認用戶:`demo` /密碼:`demo123` ```` --- ##認證系統(Authentication) ###認證方式 **Token-BasedAuthentication:** - UUID Token(簡單可靠) - 24小時有效期 - Session管理(in-memory) **APIEndpoints:** |Endpoint |方法 |功能 | |---------|------|------| | `/api/v2/auth/login` | POST | 登入(返回token) | | `/api/v2/auth/logout` | POST | 登出(需Bearer token) | | `/api/v2/auth/verify` | GET | 验证token | **使用流程:** ``` 1.登入 →取得token 2.使用token訪問API(Authorization: Bearer ) 3.Token过期 →重新登入 ``` ###默認用戶 **測試用戶:** - Username: `demo` - Password: `demo123` - UserID: `demo` **密碼安全:** - bcrypt加密(DEFAULT_COST) - 密碼不以明文儲存 ###認證範例 **登入:** ```bash curl -X POST http://localhost:11438/api/v2/auth/login \ -H "Content-Type: application/json" \ -d '{"username":"demo","password":"demo123"}' ``` **返回:** ```json { "token": "b2060c13-2a2d-4d5c-82e9-985bbdadbea2", "expires_at": "2026-05-17T09:53:51Z", "user_id": "demo" } ``` **使用Token訪問:** ```bash curl http://localhost:11438/api/v2/tree/demo \ -H "Authorization: Bearer b2060c13-2a2d-4d5c-82e9-985bbdadbea2" ``` **验证Token:** ```bash curl http://localhost:11438/api/v2/auth/verify \ -H "Authorization: Bearer " ``` **登出:** ```bash curl -X POST http://localhost:11438/api/v2/auth/logout \ -H "Authorization: Bearer " ``` ###保護範圍 **需要認證的API:** - `/api/v2/tree/*` -檔案樹管理 - `/api/v2/files/*` -檔案操作 - `/api/v2/upload/*` -檔案上傳 - `/api/v2/render/*` -檔案渲染 **公開API(無需認證):** - `/` -根路徑 - `/api/v2/auth/*` -認證相關 - `/version` -版本信息 - `/api/v2/modes` -顯示模式 ###擴展建議 **可擴展功能:** 1. JWT認證(已添加依賴,未啟用) 2. 用戶管理API(create_user已實現) 3.持久化Session(目前in-memory) 4. RBAC權限控制 --- ### DisplayMode(顯示模式) **顯示模式選項:** |模式 |檔案 |用途 | |------|------|------| | tree | `modes/tree.rs` |樹狀顯示 | | list | `modes/list.rs` |列表顯示 | | grid_sm | `modes/grid_sm.rs` |小格狀顯示 | | grid_lg | `modes/grid_lg.rs` |大格狀顯示 | **DisplayMode trait定義(mode.rs:19):** ```rust pub trait DisplayMode: Send + Sync { fn name(&self) -> &'static str; fn render(&self, tree: &FileTree) -> Value; fn sort_options(&self) -> Vec; fn filter_options(&self) -> Vec; } ```` ###檔案轉換功能(convert.rs) **支援格式轉換:** |工具 |支援格式 | |------|----------| | textutil(macOS內建) | doc, docx, rtf | | macOS工具 | pages, key, numbers | | soffice/qlmanage | pptx, ppt, xlsx, xls, odt, epub | **核心函數:** - `is_doc_ext(ext)` - 檢查是否為文檔格式 - `get_cached_preview(file, ext)` - 生成緩存預覽 **緩存目錄:** `data/cache/` ###測試覆蓋現況 **已測試(7個):** - ✅ init_and_load_empty_tree - ✅ insert_and_load_node - ✅ update_node - ✅ delete_node - ✅ move_node - ✅ update_alias(zh_tw等多語言別名) - ✅ build_tree(樹狀結構) **待補測試:** - ❌ convert.rs - 檔案轉換功能 - ❌ modes/*.rs - DisplayMode渲染 - ❌ API路由 - REST endpoint測試 ###開發範例 **新增節點範例:** ```rust //建立資料夾 let folder = FileTree::new_folder("Videos", None); tree.insert_node(&conn, &folder)?; //建立檔案節點 let (file_node, register_sql) = FileTree::new_file_node( "demo.mp4", "abc123def456...", Some("sha256hash"), "demo.mp4", Some(1024000), Some("video/mp4"), None, Some(folder.node_id), ); tree.insert_node(&conn, &file_node)?; ```` **查詢範例:** ```rust //載入檔案樹 let tree = FileTree::load(&conn, &user_id)?; //建立樹狀結構(parent-child關係) let roots = tree.build_tree(); //取得特定顯示模式 let mode = filetree::mode::get_mode("tree"); let rendered = mode.render(&tree); ```` --- ## SFTPGo同步系統(Authentication Sync) ###系統架構 **同步流程:** ``` SFTPGo (PostgreSQL) → MarkBase (auth.sqlite) users表 → sftpgo_users表 groups表 → sftpgo_groups表 users_groups_mapping表 → users_groups_mapping表 ``` **同步策略:** -啟動時同步(server.rs startup task) -每小時自動同步(tokio interval task) -手動同步API(/api/v2/admin/sync) -失敗時使用緩存fallback ###數據庫結構 **auth.sqlite位置:** `data/auth.sqlite` **表結構:** |表名 |功能 | |------|------| | sftpgo_users |用戶信息(username, password_hash, status, permissions) | | sftpgo_groups |群組信息(name, description) | | users_groups_mapping |用戶-群組映射(username, group_name) | | sync_log |同步日志(sync_type, sync_time, status) | ### PostgreSQL連接配置 **默認配置(pg_client.rs):** ``` host: 127.0.0.1 port: 5432 user: sftpgo password: sftpgo_pass_2026 database: sftpgo ``` **環境變數配置(優先):** ```bash export PG_HOST=127.0.0.1 export PG_PORT=5432 export PG_USER=sftpgo export PG_PASSWORD=sftpgo_pass_2026 export PG_DATABASE=sftpgo ``` ###同步API **手動同步:** ```bash curl -X POST http://localhost:11438/api/v2/admin/sync ``` **返回示例:** ```json { "users_synced": 3, "groups_synced": 1, "mappings_synced": 0, "status": "success" } ``` **同步狀態查詢:** ```bash curl http://localhost:11438/api/v2/admin/sync/status ``` **返回示例:** ```json { "status": "ok", "latest_sync": { "sync_type": "full", "sync_time": 1778927765, "users_synced": 3, "groups_synced": 1, "mappings_synced": 0, "status": "success" } } ``` ###認證流程 **登入流程:** ``` 1.客戶端發送登入請求(username, password) 2.服務器從auth.sqlite查詢用戶 3.bcrypt驗證密碼 4.生成UUID token(24小時有效) 5.返回token、expires_at、groups、permissions ``` **登入API:** ```bash curl -X POST http://localhost:11438/api/v2/auth/login \ -H "Content-Type: application/json" \ -d '{"username":"demo","password":"your_password"}' ``` **返回示例:** ```json { "token": "8d85c37d-8cc2-4633-a838-5400bb88dc6f", "expires_at": "2026-05-17T10:39:05Z", "user_id": "demo", "groups": [], "permissions": "{\"/*\": [\"*\"]}" } ``` ###測試用戶 **SFTPGo用戶(狀態=1):** - warren(原始密碼hash:\$2a\$10\$TpGOufSlx...) - momentry(原始密碼hash:\$2a\$10\$Yn/43aBY...) - demo(原始密碼hash:\$2a\$10\$wCQC0wGRe...) **測試密碼:** 為測試目的,可設置統一密碼: ```bash # 在PostgreSQL中更新密碼為demo123 psql -h 127.0.0.1 -p 5432 -U sftpgo -d sftpgo -c " UPDATE users SET password = '\$2b\$10\$ha5wU.mOi8fHLJCfun860u2cfVopa04jwe/q82IKOwqp5uG70qsH6' WHERE username IN ('warren', 'momentry', 'demo'); " # 重新同步 curl -X POST http://localhost:11438/api/v2/admin/sync #測試登入 curl -X POST http://localhost:11438/api/v2/auth/login \ -H "Content-Type: application/json" \ -d '{"username":"warren","password":"demo123"}' ``` ###同步模組 **sync.rs:** - `AuthDb` - auth.sqlite操作(get_user, get_user_groups, save_user) - `SyncResult` -同步結果統計(users_synced, groups_synced, status) - `PgUser, PgGroup, PgUserGroupMapping` - PostgreSQL數據結構 **pg_client.rs:** - `PgClient` - PostgreSQL連接客戶端 - `SftpGoSync` -同步器(fetch_users, fetch_groups, full_sync) **server.rs:** - `/api/v2/admin/sync` -手動同步endpoint - `/api/v2/admin/sync/status` -同步狀態endpoint - startup sync task -啟動時同步 - hourly sync task -每小時同步(tokio::spawn) ###測試結果(2026-05-16) **測試時間:**18:39 **測試狀態:** ✅ 全部成功 **測試項目:** - ✅ PostgreSQL連接成功 - ✅ 手動同步API成功(3用戶, 1群組) - ✅ 登入測試成功(demo/momentry/warren) - ✅ Token驗證成功 - ✅ 保護API訪問成功 **密碼測試:** 使用統一密碼demo123測試,bcrypt驗證成功。 --- ##測試執行 ###執行測試 ```bash cargo test #域行所有測試 cargo test test_insert #執行特定測試 cargo test -- --nocapture #顯示詳細輸出 rm data/users/test_*.sqlite #清理暫存資料庫 ```` ###測試現況 |模組 |狀態 |說明 | |------|------|------| | filetree/mod.rs | ✅已測試 | 7個 CRUD測試 | | filetree/convert.rs | ❌待補 | 檔案轉換測試 | | filetree/modes/*.rs | ❌待補 | DisplayMode測試 | | server.rs(API路由) | ❌待補 | API handler測試 | | render.rs | ❌待補 | Markdown渲染測試 | | audio.rs | ❌待補 | macOS音訊功能測試 | ###測試清理 測試會產生暫存資料庫:`data/users/test_*.sqlite` 階段性任務結束後應手動清除: ```bash rm data/users/test_*.sqlite ```` --- ##展示執行 ###啟動伺服器 ```bash cargo run -- display #啟動(自動開啟瀏覽器) cargo run -- render #渲染 Markdown檔案 ```` ###Demo資料 - `data/users/demo.sqlite` - 50節點範例資料 - 5個 Folder節點 - 45個 File節點 - `data/cache/` -範例檔案 - 29ffd4c12ef6481da6bee7ae4c36a89f.jpg - 2c62f90aacc542a9bcfa0c65b63be02a.txt ###Demo資料庫結構 ``` Home(根資料夾) ├── Movies(子資料夾,包含影片檔案) ├── Marketing(子資料夾,包含行銷素材) ├── Cartoons(子資料夾,包含動畫檔案) └── Other(子資料夾,包含其他檔案) ```` --- ## macOS環境需求 ###必要工具 - **SwitchAudioSource** -音訊裝置切換CLI ```bash brew install switchaudio-source SwitchAudioSource -a #列出所有音訊裝置 ```` ### macOS限定功能 |功能 |依賴 |說明 | |------|------|------| |音訊裝置切換 | SwitchAudioSource | `/devices` API | |音量控制 | osascript | `/volume` API | |語音測試 | say命令 | `/command` API(test_voice) | |文檔轉換 | textutil | convert.rs(doc/rtf轉換) | --- ## CI/CD配置(Gitea Actions) ### Access Token 配置 **Token資訊:** - **Token名稱:** OpenCode_M4Mini - **建立日期:** 2026-05-16 - **擁有者:** warren (Warren Lo) - **權限範圍:** repo (完整倉庫權限) - **用途:** CI/CD部署、倉庫管理、自動化操作 **Token值:** `c5e025496ebc3c7408a971d64a33bd56aac9186c` **使用方式:** ```bash # API認證 curl -H "Authorization: token c5e025496ebc3c7408a971d64a33bd56aac9186c" \ https://gitea.momentry.ddns.net/api/v1/user # Git推送(需配置遠端) git remote set-url origin https://oauth2:c5e025496ebc3c7408a971d64a33bd56aac9186c@gitea.momentry.ddns.net/warren/markbase.git ``` **安全提醒:** - ⚠️ 此Token僅用於本機開發環境 - ⚠️ 請勿提交到公開倉庫 - ⚠️ 定期更換Token(建議每90天) - ⚠️ 如需撤銷:Gitea → Settings → Applications → Access Tokens → Delete ###環境資訊 - **Gitea Server**: https://gitea.momentry.ddns.net - **Gitea版本**: 1.25.3(支援 Actions) - **Runner**: 本機Mac(實機測試) - **Workflow**: `.gitea/workflows/*.yml` ### Runner配置步驟 **1. 取得 Runner Token** - 登入 Gitea: https://gitea.momentry.ddns.net - Settings → Actions → Runners →建立新 Runner **2. 下載並安裝 Runner** ```bash # macOS ARM版本 wget https://dl.gitea.com/act_runner/latest/act_runner-darwin-arm64 chmod +x act_runner-darwin-arm64 sudo mv act_runner-darwin-arm64 /usr/local/bin/act_runner ```` **3. 註冊 Runner** ```bash act_runner register --instance https://gitea.momentry.ddns.net --token ```` **4.啟動 Runner** ```bash act_runner daemon ```` ###Workflow範例 ```yaml # .gitea/workflows/test.yml name: Test on: [push, pull_request] jobs: test: runs-on: macos-latest steps: - uses: actions/checkout@v3 - name: Setup Rust uses: actions-rust-lang/setup-rust-toolchain@v1 - name: Install SwitchAudioSource run: brew install switchaudio-source - name: Run tests run: cargo test --all - name: Run clippy run: cargo clippy --all-targets --all-features -- -D warnings - name: Check formatting run: cargo fmt -- --check - name: Clean test databases run: rm -f data/users/test_*.sqlite ```` --- ##開發環境設定 ###開發環境API Key `server.rs:192`包含開發環境 API key: ```rust let api_key = "muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"; let api_url = "http://localhost:3002/api/v1/files"; ```` 用途:`restore_tree`功能從外部 API恢復檔案樹。 **改進建議:** -應改用環境變數配置 - 建立 `.env.example`範例 ###環境變數配置(待實作) ```bash # .env(未來配置) RESTORE_API_KEY=muser_your_api_key_here RESTORE_API_URL=http://localhost:3002 SERVER_PORT=11438 DB_DIR=data/users ```` --- ##代碼風格 ###Rust標準工具 ```bash cargo fmt #代碼格式化 cargo clippy #代碼品質檢查 ```` **現有clippy警告:** - server.rs:609 -未使用變數 `state` - server.rs:1020 -未使用變數 `pg_url` --- ##專案結構 ``` markbase/ ├── src/ │ ├── main.rs # CLI入口 │ ├── lib.rs #模組宣告 │ ├── server.rs # Web伺服器(18+路由) │ ├── render.rs # Markdown渲染 │ ├── audio.rs # macOS音訊 │ ├── command.rs #指令隊列 │ ├── page.html # HTML模板 │ └── filetree/ │ ├── mod.rs #檔案樹核心(553行) │ ├── convert.rs #檔案轉換(253行) │ ├── mode.rs # DisplayMode trait(43行) │ ├── node.rs #節點定義(82行) │ └── modes/ │ ├── tree.rs #樹狀模式(57行) │ ├── list.rs #列表模式(87行) │ ├── grid_sm.rs #小格狀模式(72行) │ ├── grid_lg.rs #大格狀模式(83行) │ └── mod.rs #模式匯出(4行) ├── data/ │ ├── users/ # SQLite資料庫 │ │ ├── demo.sqlite # Demo資料(50節點) │ │ └── test_*.sqlite #測試暫存(已清理) │ └── cache/ #檔案緩存 │ ├── *.jpg #圖片緩存 │ └── *.txt #文本緩存 ├── tests/ #整合測試(待建立) ├── examples/ #範例檔案(待建立) ├── .gitea/ │ └ workflows/ │ ├── test.yml #測試workflow(待建立) │ ├── build.yml #建構workflow(待建立) │ └── release.yml #發布workflow(待建立) ├── Cargo.toml # Rust配置 ├── Cargo.lock #依賴鎖定 └── AGENTS.md # 本文件 ```` --- ##常見問題 ###測試暫存檔清理 ```bash rm data/users/test_*.sqlite ```` ###音訊功能無效 確認 `SwitchAudioSource`已安裝: ```bash brew install switchaudio-source SwitchAudioSource -a #列出音訊裝置 ```` ###File Tree API測試 ```bash curl http://localhost:11438/api/v2/tree/demo curl http://localhost:11438/api/v2/tree/demo?mode=tree ```` ###CI/CD Runner連接失敗 確認 Runner已配置並啟動: ```bash act_runner list #查看 Runner狀態 act_runner daemon #啟動 Runner ``` ###CI/CD日志查看方式 **方法1:通過 Gitea Web UI查看(推薦)** ``` https://gitea.momentry.ddns.net/warren/markbase/actions ``` **方法2:通過 API獲取詳細日志** **步驟:** 1. **獲取所有 Actions Runs** ```bash curl -H "Authorization: token c5e025496ebc3c7408a971d64a33bd56aac9186c" \ https://gitea.momentry.ddns.net/api/v1/repos/warren/markbase/actions/runs | jq '.workflow_runs[] | {id, status, conclusion}' ``` 2. **獲取特定 Run的所有 Jobs** ```bash curl -H "Authorization: token c5e025496ebc3c7408a971d64a33bd56aac9186c" \ https://gitea.momentry.ddns.net/api/v1/repos/warren/markbase/actions/runs//jobs | jq '.jobs[] | {id, name, status, conclusion}' ``` 3. **獲取 Job詳細日志** ```bash curl -H "Authorization: token c5e025496ebc3c7408a971d64a33bd56aac9186c" \ https://gitea.momentry.ddns.net/api/v1/repos/warren/markbase/actions/jobs//logs ``` **範例:查看 Run ID 6的 test job日志** ```bash # Step 1: 獲取 jobs列表 curl -s -H "Authorization: token c5e025496ebc3c7408a971d64a33bd56aac9186c" \ https://gitea.momentry.ddns.net/api/v1/repos/warren/markbase/actions/runs/6/jobs | jq '.jobs[] | {id, name}' # Step 2:獲取 job 11(test job)的完整日志 curl -s -H "Authorization: token c5e025496ebc3c7408a971d64a33bd56aac9186c" \ https://gitea.momentry.ddns.net/api/v1/repos/warren/markbase/actions/jobs/11/logs ``` **日志格式:** - 每行以時間戳開頭:`2026-05-16T08:26:07.5859940Z` - 包含完整執行過程、命令輸出、錯誤信息 - 錯誤行通常包含 `##[error]`標記 **常見錯誤診斷:** - `conditional binary operator expected` → bash腳本語法問題 - `Process completed with exit code 2` →腳本執行失敗 - `Unable to pull refs/heads/v3` → Git clone問題(通常可忽略)` --- ##部署狀況記錄(2026-05-16) ###已完成部署 **倉庫建立:** - ✅ warren/markbase 倉庫已建立(ID: 27) - ✅ URL: https://gitea.momentry.ddns.net/warren/markbase - ✅ 代碼已推送(4 commits) **Runner配置:** - ✅ Runner已註冊(ID: 2) - ✅ Runner名稱: accusys-Mac-mini-M4-2.local - ✅ Runner標籤: macos-latest:host, macos-arm64:host - ✅ Runner進程運行中(PID: 90905) **Actions狀態:** - ✅ Actions已觸發並執行 - ✅ Run ID 7: 成功(test + build jobs均通過) ← 最新 - ⚠️ Run ID 1-6: 失敗(Setup Rust bash兼容性問題) - ✅ 本地測試全部通過(62 tests) - ✅ Clippy檢查已通過 - ✅ 格式檢查已通過 ###CI問題已解決(2026-05-16) **問題根本原因:** - GitHub Action(actions-rust-lang/setup-rust-toolchain@v1)與Gitea Runner bash環境不兼容 - 錯誤:`conditional binary operator expected`(line 2) **解決方案:** - 替換為原生rustup安裝:`curl | sh.rustup.rs` - 在所有cargo命令前添加:`source $HOME/.cargo/env` **結果:** - ✅ Run ID 7: 成功(5分22秒,正常執行) - ✅ test job: success - ✅ build job: success - ✅ CI/CD完全正常運行 **Commits記錄:** - 87a3eea: 記錄CI日志獲取方式和問題診斷 - 34b6839: 替換GitHub Action為原生rustup安裝 ←修復提交 --- ##部署測試經驗(2026-05-16) ###核心方法論 **CI日志獲取方法:** ```bash #1.獲取 Run列表 curl -H "Authorization: token " \ /api/v1/repos///actions/runs #2.獲取 Job ID curl -H "Authorization: token " \ /api/v1/repos///actions/runs//jobs #3.獲取 Job日志 curl -H "Authorization: token " \ /api/v1/repos///actions/jobs//logs ``` **問題診斷思路:** ``` 觀察現象 →獲取日志 →定位錯誤 →分析原因 →設計方案 →實施修復 →验证結果 ``` **關鍵認知:** - GitHub Actions ≠Gitea Runner環境(不完全兼容) - 原生工具安裝 >Action依賴(更穩定可靠) - 環境變數載入是必要步驟(`source $HOME/.cargo/env`) **測試結果:** -失敗→成功迭代:6次失敗 →1次成功 -執行時間對比:5秒(失敗)→5分22秒(成功) -修復方案:原生rustup安裝替代GitHub Action --- **最後更新:2026-05-16** **版本:1.4(部署測試經驗版)** --- ## 配置系统 ### 配置文件位置 **默认位置:** `config/markbase.toml` **创建配置文件:** ```bash cargo run -- config init # 创建默认配置文件 cargo run -- config init --force # 强制覆盖现有配置 ``` ### 配置参数说明 #### Server配置 - **host**: 服务器监听地址(默认:127.0.0.1) - **port**: 服务器端口(默认:11438,范围:>=1024) - **log_level**: 日志级别 - **auth_db_path**: 认证数据库路径(默认:data/auth.sqlite) - **users_db_dir**: 用户数据库目录(默认:data/users) #### PostgreSQL配置 - **host**: PostgreSQL服务器地址(默认:127.0.0.1) - **port**: PostgreSQL端口(默认:5432) - **user**: PostgreSQL用户名(默认:sftpgo) - **password**: PostgreSQL密码(默认:sftpgo_pass_2026) - **database**: PostgreSQL数据库(默认:sftpgo) - **connection_pool_size**: 连接池大小(默认:5) #### Authentication配置 - **bcrypt_cost**: bcrypt加密强度(默认:10,范围:4-31) - **token_validity_hours**: Token有效期(默认:24小时) - **session_storage**: Session存储方式(默认:memory) - **max_sessions_per_user**: 每用户最大Session数(默认:5) - **default_user**: 默认用户(默认:demo) - **default_password**: 默认密码(默认:demo123) #### Test配置 - **users**: 测试用户列表(默认:["warren", "momentry", "demo"]) - **password**: 测试统一密码(默认:demo123) - **login_test_iterations**: Login性能测试迭代次数(默认:10) - **verify_test_iterations**: Token验证测试迭代次数(默认:100) - **api_test_iterations**: Protected API测试迭代次数(默认:50) - **performance_report**: 是否生成性能报告(默认:true) - **output_format**: 输出格式(默认:markdown) #### Logging配置 - **level**: 日志级别(默认:info) - **file_path**: 日志文件路径(默认:logs/markbase.log) - **console_output**: 是否输出到控制台(默认:true) - **structured_logging**: 是否使用结构化日志(默认:false) ### CLI Config命令 **查看配置:** ```bash cargo run -- config show # 显示所有配置 cargo run -- config show --section server # 显示server配置 cargo run -- config show --section postgresql cargo run -- config show --section authentication ``` **编辑配置:** ```bash cargo run -- config edit --key server.port --value 8080 cargo run -- config edit --key postgresql.password --value new_pass cargo run -- config edit --key authentication.token_validity_hours --value 48 ``` **验证配置:** ```bash cargo run -- config validate ``` ### UI Settings面板 **访问方式:** 1. 打开浏览器访问 http://localhost:11438/ 2. 点击底部栏⚙️Settings按钮 3. Settings面板将从顶部滑入 **功能:** - **查看配置**:显示所有5个配置section(server, postgresql, authentication, test, logging) - **编辑配置**:点击每个参数旁的Edit按钮,修改值后Save保存 - **验证配置**:点击Validate按钮验证配置有效性 - **Toast提示**:操作成功/失败显示提示信息 **API端点:** |端点 |方法 |功能 | |------|------|------| | `/api/v2/config` | GET | 获取完整配置JSON | | `/api/v2/config/edit` | POST | 编辑配置参数 | | `/api/v2/config/validate` | GET | 验证配置有效性 | **编辑示例:** ```bash # 使用API修改配置 curl -X POST "http://localhost:11438/api/v2/config/edit?key=server.port&value=8080" # 返回:{"ok":true} # 验证配置 curl http://localhost:11438/api/v2/config/validate # 返回:{"ok":true} ``` ### 配置优先级 默认值 → 配置文件 → 环境变量 → CLI参数 **环境变量命名:** - Server: `MB_HOST`, `MB_PORT`, `MB_LOG_LEVEL` - PostgreSQL: `PG_HOST`, `PG_PORT`, `PG_USER`, `PG_PASSWORD` - Authentication: `MB_BCRYPT_COST`, `MB_TOKEN_VALIDITY_HOURS` --- ## FUSE Virtual File System(2026-05-17新增) ### 功能概述 **虚拟文件系统挂载:** - FUSE-T技术(Kext-less设计) - Backend选择:NFSv4(稳定)或FSKit(macOS 26+,最快) - 支持多用户并发挂载(10 users) - 性能目标:600MB/s sustained write per user ### 设计文档 **架构概览:** ``` MarkBase FUSE System ├── src/fuse/mod.rs # FUSE核心模組 │ ├── BackendType # NFSv4/FSKit选择 │ ├── select_backend() # 自动检测backend │ └── mount_hello_fs() # POC placeholder ├── docs/FUSE_DESIGN.md # 完整设计文档 └ docs/FUSE_POC_TEST.md # POC测试计划 ``` **Backend选择逻辑:** - macOS 26+:FSKit(native,direct path,minimal overhead) - macOS <26:NFSv4(stable,TCP/IP overhead ~5-10%) ### CLI命令 **POC测试命令:** ```bash # 检测backend cargo run -- fuse detect-backend # 输出:macOS 26.4.1 → Recommended: fskit # POC placeholder mount cargo run -- fuse poc --dir /tmp/fuse_test --backend auto cargo run -- fuse poc --backend fskit # 手動指定 FSKit cargo run -- fuse poc --backend nfs # 手動指定 NFSv4 ``` **未来完整命令(Phase 2+):** ```bash # 单user挂载 cargo run -- fuse --mount --user warren --dir /Volumes/MarkBase_warren # 多user并发挂载 cargo run -- fuse --mount --all --dir /Volumes/ # 卸載 cargo run -- fuse --unmount --dir /Volumes/MarkBase_warren cargo run -- fuse --unmount --all ``` ### POC测试结果(2026-05-17) **测试环境:** - 硬體:M4 Mac mini, 16GB RAM, NVMe 2TB - OS:macOS 26.4.1 - Backend:FSKit(自动检测) **测试項目(7項全通過):** - ✅ Backend detection(macOS 26.4.1 → FSKit) - ✅ Auto backend selection(FSKit) - ✅ Manual backend selection(FSKit/NFSv4) - ✅ Error handling(invalid backend) - ✅ Compilation check - ✅ Unit tests(7 passed) - ✅ CLI commands functional **Unit Tests结果:** ``` running 7 tests test fuse::backend::tests::test_backend_support ... ok test fuse::backend::tests::test_backend_type_name ... ok test fuse::backend::tests::test_manual_backend_selection ... ok test fuse::poc_hello::tests::test_hello_fs_creation ... ok test fuse::backend::tests::test_select_backend_macos_25 ... ok test fuse::poc_hello::tests::test_mount_placeholder ... ok test fuse::backend::tests::test_select_backend_macos_26 ... ok test result: ok. 7 passed; 0 failed; 0 ignored ``` ### FUSE-T vs macFUSE比較 |特性 |FUSE-T |macFUSE | |------|--------|---------| |Kernel Design |Kext-less(userspace)|Kernel Extension + FSKit | |Backend Protocol |NFSv4 / SMB3 / FSKit |Direct kernel FUSE API | |安装难度 |简单(brew install)|需System Settings設定 | |稳定性 |穩定(userspace server)|可能kernel crash | |授權 |免費個人,商業需授權 |開源(BSD)| |macOS支援 |全版本 |macOS 12+ | |App Store |可嵌入 |需特殊處理 | |效能(FSKit) |~600-700 MB/s |~650-750 MB/s | **推薦:FUSE-T** - 稳定性优先(避免kernel panic) - 部署友善(无需Security Settings) - macOS 26支援FSKit backend(与macFUSE相同效能) ### 效能目标 |Metric |Target |Measurement Method | |--------|--------|-------------------| |Write throughput |>=600MB/s |AJA System Test 4K ProRes 4444 | |Read throughput |>=800MB/s |AJA System Test 4K ProRes 422 HQ | |Mount latency(single) |<100ms |Timing measurement | |Mount latency(10 users) |<2s |Parallel mount timing | |Concurrent writes |10 × 600MB/s |AJA concurrent test | |Uptime stability |24h no crash |Stability test | |Cache hit rate |>=90% |Cache statistics | ### 实作阶段 **Phase 1:POC验证(已完成 Day 1)** - ✅ 建立fuse module结构 - ✅ Backend detection implementation - ✅ CLI commands functional - ✅ Unit tests pass(7 tests) - ⏳ FUSE-T安装(需手动sudo) - ⏳ AJA System Test下载(需手动) **Phase 2:SQLite-backed FUSE(Day 3-5)** - ❌ MarkBaseFs implementation - ❌ FUSE operations(getattr, read, write) - ❌ warren user test(12659 nodes) - ❌ LRU caching **Phase 3:Multi-user Concurrent(Day 6-8)** - ❌ MountManager implementation - ❌ 10 user parallel mount - ❌ AJA concurrent write test - ❌ 24h stability test **Phase 4:Performance Optimization(Day 9-12)** - ❌ Write buffering(64KB chunks) - ❌ FSKit backend optimization - ❌ 600MB/s validation - ❌ Final documentation ### 手动安装步骤 **FUSE-T安装:** ```bash # 检查下载的PKG ls -lh ~/Downloads/fuse-t-1.2.6.pkg # 23MB # 安装(需要sudo密码) sudo installer -pkg ~/Downloads/fuse-t-1.2.6.pkg -target / # 验证安装 ls -la /usr/local/bin/fuse-t fuse-t --version # Expected: 1.2.6 ``` **AJA System Test安装:** ```bash # 手动下载(浏览器访问) https://www.aja.com/en/products/aja-system-test # 安装DMG hdiutil attach ~/Downloads/AJA_System_Test.dmg cp -R /Volumes/AJA\ System\ Test/*.app /Applications/ hdiutil detach /Volumes/AJA\ System\ Test ``` ### 相关文档 |文档 |位置 |说明 | |------|------|------| |FUSE_DESIGN.md |docs/ |完整设计文档(架构、backend、性能)| |FUSE_POC_TEST.md |docs/ |POC测试计划(7项测试)| |FUSE_POC_REPORT.md |docs/ |POC测试报告(结果、下一步)| |fuse_poc_test.sh |tests/ |自动化测试脚本 | --- **最后更新:** 2026-05-17 10:20 **版本:** 1.8(FUSE Virtual File System版) --- ## File Scan System(2026-05-17新增) ### 功能概述 **异步导入系统:** - 快速导入(skip_hash=true):立即显示文件树 - 后台hash:异步计算SHA256,不影响前端使用 ### CLI命令 **scan命令:** ```bash # 快速导入(默认skip_hash=true) cargo run -- scan --user warren --dir /Users/accusys/momentry/var/sftpgo/data/warren # 导入并计算hash(skip_hash=false) cargo run -- scan --user warren --dir --skip-hash false --threads 4 # 参数说明: --user 用户ID --dir 扫描目录 --batch 批量插入大小(默认:100) --skip-hash 跳过hash计算(默认:true) --threads hash计算线程数(默认:4) ```` **hash命令:** ```bash # 后台计算hash cargo run -- hash --user warren --threads 4 # 参数说明: --user 用户ID --threads 并行线程数(默认:4) ```` ### 效能测试结果(2026-05-17) **测试配置:** - 文件总数:11857 files + 801 folders = 12658 nodes - 目录深度:多层子目录(Accusys/Accusys_FAE/VolPack_ME5012...) - CPU线程:4 threads (M4 Mac mini) **第一阶段:快速导入(skip_hash=true)** - 目录扫描:0.10s - ID生成:0.57s(主要瓶颈) - 数据库插入:0.21s - 总时间:0.89s - 速度:14243 nodes/sec **第二阶段:SHA256计算(4 threads)** - 文件数:11857 - 总时间:417.58s - 速度:28 files/sec **效能瓶颈分析:** 1. ID生成(64%)- SHA256计算UUID耗时 2. 数据库插入(24%)- 批量插入优化 3. 目录扫描(11%)- fast, no bottleneck 4. Hash计算 - IO瓶颈(多线程未提速) ### 设计决策 **UUID生成策略:** - 算法:SHA256(path|filename|mac|mtime).chars().take(32) - 特性:确定性UUID(同一文件 = 同一UUID) - 优势:无需外部API,支持增量导入 **路径存储方案:** - 存储位置:aliases.json(临时方案) - 格式:`{"path": "/full/path/to/file"}` - 原因:file_nodes表无path字段 - 改进:未来添加file_locations表填充 **异步hash设计:** - 导入优先:用户立即查看文件树 - hash后台:不影响前端使用 - 多线程:并行计算(IO瓶颈限制) - 增量:只计算缺失hash的文件 ### 数据库结构(warren.sqlite) **节点统计:** - Folders: 801 - Files: 11857 - Total: 12658 **示例节点:** ``` node_id: 8b1ede3cd6970f02fa85b8e34b682caf label: Test_Plan_ME5.docx aliases_json: {"path":"/Users/accusys/.../Test_Plan_ME5.docx"} sha256: 355a063b697a812742fae2a021cdda5c node_type: file parent_id: (folder UUID) ```` ### 使用流程 **完整导入流程:** ``` 1. cargo run -- scan --user warren --dir # 快速导入(0.89s) 2. cargo run -- hash --user warren --threads 4 # 后台hash(417s) 3. 查看文件树:http://localhost:11438/ → File Tree → Login ```` **增量导入流程:** ``` 1. cargo run -- scan --user warren --dir # 导入新文件 2. cargo run -- hash --user warren # 计算新hash(只计算缺失的) ```` ### 相关文件 |文件 |功能 | |------|------| | src/scan.rs | 扫描导入 + hash计算(499行)| | src/main.rs | CLI命令定义(scan/hash)| | src/filetree/mod.rs | FileTree::init_user_db | | data/users/warren.sqlite | warren用户数据库(12658 nodes)| --- **最后更新:** 2026-05-17 02:15 **版本:** 1.7(File Scan System版)