Files
markbase/AGENTS.md
2026-05-16 17:55:25 +08:00

758 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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/<user_id>.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 <FILE> #渲染 Markdown輸出到 stdout
cargo run -- render <FILE> -o output.html #輸出到檔案
````
##架構概覽
###核心模組
|模組 |檔案 |功能 |
|------|------|------|
| CLI入口 | `main.rs` | clap指令解析 |
| Web伺服器 | `server.rs` | Axum REST API18+路由) |
|檔案樹管理 | `filetree/mod.rs` | SQLite CRUD操作 |
| Markdown渲染 | `render.rs` | pulldown-cmark轉換 |
|音訊控制 | `audio.rs` | macOS音訊裝置管理 |
|指令隊列 | `command.rs` | WebSocket指令處理 |
###資料庫結構
-位置:`data/users/<user_id>.sqlite`
-表:`file_registry`, `file_nodes`, `file_locations`
-每個 user_id獨立資料庫
---
## File Tree核心說明重點章節
###架構設計
File Tree是 MarkBase的核心模組提供檔案樹管理功能。
**核心結構:**
```rust
FileTree {
user_id: String, //用戶ID
nodes: Vec<FileNode>, //節點列表
}
````
**節點類型:**
- **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
````
###公開API13個函數
|函數名 |功能 |檔案位置 |
|--------|------|----------|
| 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 API7個路由
|路由 |方法 |功能 |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 <TOKEN>"
```
**認證說明:**
- 所有 `/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訪問APIAuthorization: Bearer <token>
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 <TOKEN>"
```
**登出:**
```bash
curl -X POST http://localhost:11438/api/v2/auth/logout \
-H "Authorization: Bearer <TOKEN>"
```
###保護範圍
**需要認證的API**
- `/api/v2/tree/*` -檔案樹管理
- `/api/v2/files/*` -檔案操作
- `/api/v2/upload/*` -檔案上傳
- `/api/v2/render/*` -檔案渲染
**公開API無需認證**
- `/` -根路徑
- `/api/v2/auth/*` -認證相關
- `/version` -版本信息
- `/api/v2/modes` -顯示模式
###擴展建議
**可擴展功能:**
1. JWT認證已添加依賴未啟用
2. 用戶管理APIcreate_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<SortOption>;
fn filter_options(&self) -> Vec<FilterOption>;
}
````
###檔案轉換功能convert.rs
**支援格式轉換:**
|工具 |支援格式 |
|------|----------|
| textutilmacOS內建 | 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_aliaszh_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);
````
---
##測試執行
###執行測試
```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.rsAPI路由 | ❌待補 | 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 <file> #渲染 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` APItest_voice |
|文檔轉換 | textutil | convert.rsdoc/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 <YOUR_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 trait43行
│ ├── 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/<RUN_ID>/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/<JOB_ID>/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 11test 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 Actionactions-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 <TOKEN>" \
<GITEA_URL>/api/v1/repos/<OWNER>/<REPO>/actions/runs
#2.獲取 Job ID
curl -H "Authorization: token <TOKEN>" \
<GITEA_URL>/api/v1/repos/<OWNER>/<REPO>/actions/runs/<RUN_ID>/jobs
#3.獲取 Job日志
curl -H "Authorization: token <TOKEN>" \
<GITEA_URL>/api/v1/repos/<OWNER>/<REPO>/actions/jobs/<JOB_ID>/logs
```
**問題診斷思路:**
```
觀察現象 →獲取日志 →定位錯誤 →分析原因 →設計方案 →實施修復 →验证結果
```
**關鍵認知:**
- GitHub Actions ≠Gitea Runner環境不完全兼容
- 原生工具安裝 >Action依賴更穩定可靠
- 環境變數載入是必要步驟(`source $HOME/.cargo/env`
**測試結果:**
-失敗→成功迭代6次失敗 →1次成功
-執行時間對比5秒失敗→5分22秒成功
-修復方案原生rustup安裝替代GitHub Action
---
**最後更新2026-05-16**
**版本1.4(部署測試經驗版)**