feat: update core API, database layer, and worker modules

- Remove unused imports (n8n_search, universal_search, Client, Arc, etc.)
- Update API endpoints for identity, face recognition, search
- Fix postgres_db.rs search_videos parent_uuid column
- Add snapshot API and identity agent API
- Clean up backup files (.bak, .bak2)
This commit is contained in:
Warren
2026-04-30 15:07:02 +08:00
parent 8f2208dd63
commit 2b23d1cfbd
148 changed files with 8553 additions and 48637 deletions

View File

@@ -1,230 +0,0 @@
# Momentry Core API 存取指南
| 項目 | 內容 |
|------|------|
| 版本 | V1.3 |
| 日期 | 2026-03-25 |
| 用途 | API 存取方式、端點與整合指南 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.3 | 2026-03-25 | 更新: n8n 搜尋回傳 `file_path` 取代 `media_url`,新增 API Key 驗證說明 | OpenCode | deepseek-reasoner |
| V1.2 | 2026-03-24 | 更新網址與服務列表 | Warren | OpenCode / MiniMax M2.5 |
| V1.1 | 2026-03-23 | 初始版本 | Warren | OpenCode / MiniMax M2.5 |
---
## 基本網址
| 環境 | URL | 說明 |
|------|-----|------|
| **本地開發** | `http://localhost:3002` | 直接訪問 API繞過反向代理 |
| **外部訪問** | `https://api.momentry.ddns.net` | 通過 Caddy 反向代理訪問,需網路可達 |
### 何時使用哪個 URL
**使用 `localhost:3002`**
- 開發/測試環境
- 直接在伺服器上操作
- 當反向代理有問題時
**使用 `api.momentry.ddns.net`**
- n8n workflow 中呼叫 API
- 外部系統整合
- 生產環境
## 認證
所有 `/api/v1/*` 端點(除了健康檢查 `/health``/health/detailed`)都需要 API Key 認證。
請在請求標頭中加入:
```
X-API-Key: YOUR_API_KEY
```
**目前示範使用的 API Key**: `demo_api_key_12345`
> **注意**: 正式環境請使用安全的 API Key 管理機制,避免在客戶端暴露 API Key。
---
## 影片搜尋 API
### 語意搜尋
**端點:** `POST /api/v1/search`
**請求:**
```json
{
"query": "charade",
"limit": 5,
"uuid": "a1b10138a6bbb0cd"
}
```
| 欄位 | 類型 | 必填 | 說明 |
|------|------|------|------|
| `query` | 字串 | 是 | 搜尋文字 |
| `limit` | 整數 | 否 | 最大回傳結果數(預設 10 |
| `uuid` | 字串 | 否 | 依影片 UUID 過濾 |
**回應:**
```json
{
"results": [
{
"uuid": "a1b10138a6bbb0cd",
"chunk_id": "sentence_0006",
"chunk_type": "sentence",
"start_time": 48.8,
"end_time": 55.44,
"text": "fun plot twists, Woody Dialog and charming performances...",
"score": 0.526
}
],
"query": "charade"
}
```
---
### n8n 整合搜尋
**端點:** `POST /api/v1/n8n/search`
**請求:**
```json
{
"query": "charade",
"limit": 5
}
```
**回應:**
```json
{
"query": "charade",
"count": 5,
"hits": [
{
"id": "sentence_0006",
"vid": "a1b10138a6bbb0cd",
"start": 48.8,
"end": 55.44,
"title": "Chunk sentence_0006",
"text": "fun plot twists...",
"score": 0.526,
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
}
]
}
```
> **注意**: API 現在返回 `file_path`(檔案系統路徑)而非 `media_url`(網頁 URL。如需在網頁中播放影片請將檔案路徑轉換為可訪問的 URL例如透過 SFTPGo 分享連結)。
---
## 影片管理 API
### 列出所有影片
**端點:** `GET /api/v1/videos`
### 查詢影片資訊
**端點:** `GET /api/v1/lookup?uuid={uuid}``GET /api/v1/lookup?path={path}`
### 取得處理進度
**端點:** `GET /api/v1/progress/{uuid}`
---
## 區塊資料結構
每個搜尋結果包含影片播放的時間資訊:
| 欄位 | 說明 |
|------|------|
| `uuid` | 影片識別碼 |
| `chunk_id` | 區塊唯一識別碼 |
| `chunk_type` | 類型:`sentence``cut``time_based` |
| `start_time` | 開始時間(秒) |
| `end_time` | 結束時間(秒) |
| `text` | 語音轉文字內容 |
| `score` | 相關性分數0-1 |
---
## 整合範例
### JavaScript/fetch
```javascript
const response = await fetch('http://localhost:3002/api/v1/search', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'YOUR_API_KEY' // 替換為實際的 API Key
},
body: JSON.stringify({ query: 'charade', limit: 5 })
});
const data = await response.json();
console.log(data.results);
```
### PHP/cURL
```php
$ch = curl_init('http://localhost:3002/api/v1/search');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'query' => 'charade',
'limit' => 5
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'X-API-Key: YOUR_API_KEY' // 替換為實際的 API Key
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
```
---
## 影片嵌入網址
> **重要**: API 現在返回 `file_path`(檔案系統路徑),而非直接可訪問的網址。您需要將檔案路徑轉換為 SFTPGo 分享連結才能嵌入影片。
**檔案路徑轉換為網址:**
- API 返回的 `file_path` 範例:`/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4`
- 對應的 SFTPGo 分享連結:`https://wp.momentry.ddns.net/demo/video.mp4`
- 轉換方式:移除 `/Users/accusys/momentry/var/sftpgo/data/` 前綴,將剩餘路徑附加到 `https://wp.momentry.ddns.net/`
**手動建立分享連結:**
1. 開啟 SFTPGo Web UI`http://localhost:8080`
2. 使用帳號 `demo` / 密碼 `demopassword123` 登入
3. 導航至 `Files` → 選擇影片檔案
4. 點擊 `Share``Create Link`
5. 複製產生的分享連結
使用搜尋結果中的 `start_time``end_time` 來嵌入影片片段。
---
## 服務列表
| 服務 | 網址 | 用途 |
|------|------|------|
| Momentry API | `http://localhost:3002` | 核心 API |
| SFTPGo | `http://localhost:8080` | 檔案儲存 |
| Qdrant | `http://localhost:6333` | 向量搜尋 |
| PostgreSQL | `localhost:5432` | 資料庫 |
---
## 示範影片
- **檔案:** `Old_Time_Movie_Show_-_Charade_1963.HD.mov`
- **UUID** `a1b10138a6bbb0cd`
- **長度:** 約 6879 秒(約 1.9 小時)
- **區塊數:** 3886 個(句子 + 場景 + 時間)

View File

@@ -1,586 +0,0 @@
# Momentry API 使用說明 (curl 範例)
| 項目 | 內容 |
|------|------|
| 版本 | V1.4 |
| 日期 | 2026-03-26 |
| Base URL | `http://localhost:3002` |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.4 | 2026-03-26 | 新增: 任務管理端點 (`/api/v1/jobs`, `/api/v1/jobs/:uuid`),更新註冊端點回應格式 | OpenCode | deepseek-reasoner |
| V1.3 | 2026-03-25 | 更新: n8n 搜尋回傳 `file_path` 取代 `media_url`,新增 API Key 驗證說明 | OpenCode | deepseek-reasoner |
| V1.2 | 2026-03-23 | 建立 curl 範例文件 | Warren | OpenCode / MiniMax M2.5 |
| V1.1 | 2026-03-18 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
---
> **狀態說明**:
> - ✅ **已實作**: 健康檢查、搜尋、影片管理端點
> - ⚠️ **規劃中**: API Key 管理功能
> - 🔧 **CLI**: 部分功能需使用命令列工具
---
## 目錄
1. [已實作端點](#1-已實作端點)
2. [API Key 管理](#2-api-key-管理-規劃中)
3. [影片管理](#3-影片管理)
4. [查詢與搜索](#4-查詢與搜索)
5. [系統狀態](#5-系統狀態)
---
## URL 選擇指南
### 兩種 URL 的使用情境
| 環境 | URL | 說明 |
|------|-----|------|
| **本地開發** | `http://localhost:3002` | 直接訪問 API繞過反向代理 |
| **外部訪問** | `https://api.momentry.ddns.net` | 通過 Caddy 反向代理訪問,需網路可達 |
### 何時使用 localhost:3002
- ✅ 開發/測試環境
- ✅ 直接在伺服器上操作
- ✅ 當 Caddy/反向代理有問題時
- ✅ 需要快速除錯時
### 何時使用 api.momentry.ddns.net
- ✅ n8n workflow 中呼叫 API
- ✅ 外部系統整合
- ✅ 生產環境
- ✅ 從其他機器訪問
### 快速切換範例
```bash
# 本地測試
curl http://localhost:3002/health
# 外部測試(功能相同)
curl https://api.momentry.ddns.net/health
```
### 常見問題
**Q: 為什麼有兩個 URL**
A: `localhost:3002` 是直接訪問,`api.momentry.ddns.net` 通過 Caddy 反向代理。
**Q: 兩者功能相同嗎?**
A: 是的,所有端點和功能完全相同。
**Q: 502 錯誤時怎麼辦?**
A: 如果 `api.momentry.ddns.net` 返回 502檢查 Momentry API 服務是否運行:
```bash
launchctl list | grep momentry.api
# 如果未運行
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
```
---
## API 認證
所有 `/api/v1/*` 端點(除了健康檢查)都需要 API Key 認證。請在請求標頭中加入:
```
-H "X-API-Key: YOUR_API_KEY"
```
**目前示範使用的 API Key**: `demo_api_key_12345`
> **注意**: 正式環境請使用安全的 API Key 管理機制。
---
## 1. 已實作端點
### 健康檢查
```bash
curl http://localhost:3002/health
```
**回應**:
```json
{"status":"ok","version":"0.1.0","uptime_ms":123456}
```
### 詳細健康檢查
```bash
curl http://localhost:3002/health/detailed
```
---
## 2. API Key 管理 *(規劃中)*
> ⚠️ **此功能尚未實作**。以下為規劃中的 API 說明,僅供參考。
### 2.1 建立 API Key
```bash
curl -X POST http://localhost:3002/api/v1/api-keys \
-H "Content-Type: application/json" \
-H "X-API-Key: your-admin-key" \
-d '{
"name": "my-service-key",
"key_type": "service",
"permissions": ["read", "write"],
"ttl_days": 90
}'
```
### 2.2 列出所有 API Keys
```bash
curl -X GET http://localhost:3002/api/v1/api-keys \
-H "X-API-Key: your-admin-key"
```
### 2.3 驗證 API Key
```bash
curl -X GET http://localhost:3002/api/v1/api-keys/validate \
-H "X-API-Key: key-to-validate"
```
### 2.4 撤銷 API Key
```bash
curl -X DELETE http://localhost:3002/api/v1/api-keys/msvc_a1b2c3d4_... \
-H "X-API-Key: your-admin-key"
```
### 2.5 請求 Key 輪換
```bash
curl -X POST http://localhost:3002/api/v1/api-keys/msvc_a1b2c3d4_.../rotate \
-H "X-API-Key: your-admin-key" \
-H "Content-Type: application/json" \
-d '{"reason": "scheduled_rotation"}'
```
### 2.6 取得統計資訊
```bash
curl -X GET http://localhost:3002/api/v1/api-keys/stats \
-H "X-API-Key: your-admin-key"
```
---
## 3. 影片管理
### 3.1 註冊影片 ✅
```bash
curl -X POST http://localhost:3002/api/v1/register \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"path": "/path/to/video.mp4"}'
```
**回應範例**:
```json
{
"uuid": "a1b2c3d4e5f6g7h8",
"video_id": 1,
"job_id": 123,
"file_name": "video.mp4",
"duration": 120.5,
"width": 1920,
"height": 1080,
"already_exists": false
}
```
### 3.2 列出所有影片 ✅
```bash
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos
```
### 3.3 查詢影片 ✅
```bash
# 依 UUID 查詢
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3002/api/v1/lookup?uuid=a1b2c3d4e5f6g7h8"
# 依路徑查詢
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3002/api/v1/lookup?path=/path/to/video.mp4"
```
### 3.4 處理影片 🔧 *(CLI - 非 API)*
影片處理需要使用 CLI 命令:
```bash
# 處理影片(生成 ASR, CUT, YOLO, OCR, Face, Pose 資料)
cargo run --bin momentry -- process <uuid>
# 或處理多個影片
cargo run --bin momentry -- process <uuid1> <uuid2> <uuid3>
```
### 3.5 取得處理進度 ✅
```bash
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/progress/<uuid>
```
**回應範例**:
```json
{
"uuid": "a1b2c3d4e5f6g7h8",
"overall_progress": 75,
"processors": [
{
"name": "asr",
"status": "complete",
"current": 100,
"total": 100,
"progress": 100,
"message": "7 segments"
},
{
"name": "cut",
"status": "complete",
"current": 134,
"total": 134,
"progress": 100,
"message": "134 scenes"
},
{
"name": "yolo",
"status": "progress",
"current": 5000,
"total": 14315,
"progress": 35,
"message": "frame 5000"
}
]
}
```
### 3.6 任務管理 ✅
```bash
# 列出所有任務
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/jobs
# 取得特定任務詳情
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/jobs/<uuid>
```
**任務列表回應範例**:
```json
{
"jobs": [
{
"id": 123,
"uuid": "a1b2c3d4e5f6g7h8",
"status": "pending",
"current_processor": null,
"progress_current": 0,
"progress_total": 100,
"created_at": "2026-03-26 10:30:00",
"started_at": null
}
]
}
```
**任務詳情回應範例**:
```json
{
"id": 123,
"uuid": "a1b2c3d4e5f6g7h8",
"status": "processing",
"current_processor": "asr",
"progress_current": 50,
"progress_total": 100,
"processors": [
{
"processor_type": "asr",
"status": "complete",
"started_at": "2026-03-26 10:30:00",
"completed_at": "2026-03-26 10:35:00",
"duration_secs": 300.5,
"error_message": null
},
{
"processor_type": "cut",
"status": "pending",
"started_at": null,
"completed_at": null,
"duration_secs": null,
"error_message": null
}
],
"created_at": "2026-03-26 10:30:00",
"started_at": "2026-03-26 10:30:00",
"updated_at": "2026-03-26 10:35:00"
}
```
---
## 4. 查詢與搜索
### 4.1 語意搜尋 ✅
```bash
curl -X POST http://localhost:3002/api/v1/search \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"query": "測試關鍵字",
"limit": 5
}'
```
**回應範例**:
```json
{
"results": [
{
"uuid": "a1b2c3d4e5f6g7h8",
"chunk_id": "sentence_0006",
"chunk_type": "sentence",
"start_time": 48.8,
"end_time": 55.44,
"text": "fun plot twists...",
"score": 0.526
}
],
"query": "測試關鍵字"
}
```
### 4.2 n8n 格式搜尋 ✅
```bash
curl -X POST http://localhost:3002/api/v1/n8n/search \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"query": "測試關鍵字",
"limit": 5
}'
```
**回應範例**:
```json
{
"query": "測試關鍵字",
"count": 2,
"hits": [
{
"id": "c_001",
"vid": "a1b2c3d4e5f6g7h8",
"start": 48.8,
"end": 55.44,
"title": "Chunk sentence_0006",
"text": "fun plot twists...",
"score": 0.92,
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
}
]
}
```
### 4.3 混合搜尋 ✅
```bash
curl -X POST http://localhost:3002/api/v1/search/hybrid \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"query": "測試關鍵字",
"limit": 5
}'
```
---
## 5. 系統狀態
### 5.1 健康檢查 ✅
```bash
curl http://localhost:3002/health
```
**回應**:
```json
{"status":"ok","version":"0.1.0","uptime_ms":123456}
```
### 5.2 詳細健康檢查 ✅
```bash
curl http://localhost:3002/health/detailed
```
**回應範例**:
```json
{
"status":"ok",
"version":"0.1.0",
"uptime_ms":123456,
"services":{
"postgres":{"status":"ok","latency_ms":42,"error":null},
"redis":{"status":"ok","latency_ms":0,"error":null},
"qdrant":{"status":"ok","latency_ms":15,"error":null}
}
}
```
---
## 6. n8n Webhook 測試
### 測試 n8n Workflow
**重要**: 測試前請先在 n8n UI 中點擊 "Execute workflow" 按鈕
```bash
# 測試 Video RAG Workflow (Test Mode)
curl -X POST http://localhost:5678/webhook-test/video-rag-mcp \
-H "Content-Type: application/json" \
-d '{"query":"charade","limit":3}'
# 帶有 UUID 過濾的搜尋
curl -X POST http://localhost:5678/webhook-test/video-rag-mcp \
-H "Content-Type: application/json" \
-d '{"query":"woody","limit":5,"uuid":"a1b10138a6bbb0cd"}'
```
### 生產環境 Webhook
**注意**: 工作流程必須處於 Active 狀態
```bash
curl -X POST http://localhost:5678/webhook/video-rag-mcp \
-H "Content-Type: application/json" \
-d '{"query":"charade","limit":3}'
```
### n8n Webhook 常見問題
**Q: webhook-test 返回 404**
A: 需要在 n8n UI 中點擊 "Execute workflow" 按鈕後才能使用 test webhook
**Q: webhook (生產環境) 返回 404**
A: 需要將工作流程切換為 Active 狀態 (右上角開關)
---
## 附錄
### A. 服務 URL 列表
| 服務 | URL |
|------|-----|
| Momentry API (本地) | `http://localhost:3002` |
| Momentry API (外部) | `https://api.momentry.ddns.net` |
| n8n Web UI | `https://n8n.momentry.ddns.net` |
| n8n Webhook Test | `http://localhost:5678/webhook-test/{workflow-name}` |
| n8n Webhook Prod | `http://localhost:5678/webhook/{workflow-name}` |
### B. 所有可用端點
| 端點 | 方法 | 狀態 | 說明 |
|------|------|------|------|
| `/health` | GET | ✅ | 健康檢查 |
| `/health/detailed` | GET | ✅ | 詳細健康檢查 |
| `/api/v1/register` | POST | ✅ | 註冊影片 |
| `/api/v1/search` | POST | ✅ | 語意搜尋 |
| `/api/v1/n8n/search` | POST | ✅ | n8n 格式搜尋 |
| `/api/v1/search/hybrid` | POST | ✅ | 混合搜尋 |
| `/api/v1/lookup` | GET | ✅ | 查詢影片 |
| `/api/v1/videos` | GET | ✅ | 列出所有影片 |
| `/api/v1/progress/:uuid` | GET | ✅ | 處理進度 |
| `/api/v1/jobs` | GET | ✅ | 任務列表 |
| `/api/v1/jobs/:uuid` | GET | ✅ | 任務詳情 |
| `/api/v1/api-keys` | * | ⚠️ | API Key 管理 (規劃中) |
### C. 常見錯誤
| HTTP 狀態 | 說明 | 解決方式 |
|-----------|------|----------|
| 200 | 成功 | - |
| 400 | 請求格式錯誤 | 檢查 JSON 格式 |
| 404 | 端點不存在或資源未找到 | 確認端點 URL 正確 |
| 500 | 伺服器內部錯誤 | 檢查 API 服務日誌 |
| **502** | **Bad Gateway** | **API 服務未啟動,見下方說明** |
#### 502 Bad Gateway 錯誤
**問題**: 外部 URL `https://api.momentry.ddns.net` 返回 502
**原因**: Momentry Core API 服務未啟動
**解決方式**:
```bash
# 1. 檢查服務狀態
launchctl list | grep momentry.api
# 2. 如果未啟動,手動啟動
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
# 3. 或使用本地測試(繞過反向代理)
curl http://localhost:3002/health
# 4. 檢查日誌
tail -50 /Users/accusys/momentry/log/momentry_api.error.log
```
### D. 範例腳本
```bash
#!/bin/bash
# api_test.sh - API 測試腳本
API_URL="http://localhost:3002"
# 健康檢查
echo "=== Health Check ==="
curl -s "$API_URL/health" | jq .
# 搜尋
echo -e "\n=== Search ==="
curl -s -X POST "$API_URL/api/v1/search" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"query": "test", "limit": 3}' | jq .
# 列出影片
echo -e "\n=== Videos ==="
curl -s -H "X-API-Key: YOUR_API_KEY" "$API_URL/api/v1/videos" | jq '.videos | length'
```
---
## 相關文件
- [API_INDEX.md](./API_INDEX.md) - 文件總覽(起點)
- [API_ENDPOINTS.md](./API_ENDPOINTS.md) - 端點完整說明
- [API_N8N_GUIDE.md](./API_N8N_GUIDE.md) - n8n 使用範例
- [API_WORDPRESS_GUIDE.md](./API_WORDPRESS_GUIDE.md) - WordPress 使用範例

View File

@@ -1,321 +0,0 @@
# Momentry Core API 端點總覽
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-18 |
| 文件版本 | V1.3 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 |
|------|------|------|--------|
| V1.0 | 2026-03-18 | 創建文件 | OpenCode |
| V1.1 | 2026-03-23 | 更新端點與實際一致 | OpenCode |
| V1.2 | 2026-03-25 | 新增快取/刪除 API | OpenCode |
| V1.3 | 2026-03-26 | 更新API回應格式 (media_url→file_path) | OpenCode |
---
## Base URL
| 環境 | URL |
|------|-----|
| 本地 | `http://localhost:3002` |
| 外部 | `https://api.momentry.ddns.net` |
---
## 認證
除健康檢查端點外,所有 API 端點都需要 API Key。
### Header 方式
```bash
curl -H "X-API-Key: your-api-key" http://localhost:3002/api/v1/videos
```
### 響應
- `401 Unauthorized` - 缺少或無效的 API Key
- `200 OK` - 認證成功
### 取得 API Key
使用 CLI 建立:
```bash
./target/release/momentry api-key create "My API Key" --key-type user
```
---
## 端點列表
### 健康檢查(公開)
| 方法 | 端點 | 說明 |
|------|------|------|
| GET | `/health` | 基本健康檢查 |
| GET | `/health/detailed` | 詳細健康檢查(含服務狀態) |
**範例**:
```bash
curl http://localhost:3002/health
# {"status":"ok","version":"0.1.0","uptime_ms":123456}
```
---
### 影片搜尋
| 方法 | 端點 | 說明 |
|------|------|------|
| POST | `/api/v1/search` | 語意搜尋(標準格式) |
| POST | `/api/v1/n8n/search` | 語意搜尋n8n 專用格式) |
| POST | `/api/v1/search/hybrid` | 混合搜尋 |
**標準搜尋** (`/api/v1/search`):
```bash
curl -X POST http://localhost:3002/api/v1/search \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"query": "test", "limit": 10}'
```
**n8n 格式搜尋** (`/api/v1/n8n/search`):
```bash
curl -X POST http://localhost:3002/api/v1/n8n/search \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"query": "test", "limit": 10}'
```
---
### 影片管理
| 方法 | 端點 | 說明 |
|------|------|------|
| POST | `/api/v1/register` | 註冊影片 |
| POST | `/api/v1/probe` | 探測影片資訊(不註冊) |
| GET | `/api/v1/videos` | 列出所有影片 |
| GET | `/api/v1/lookup` | 查詢影片資訊 |
| GET | `/api/v1/progress/:uuid` | 取得處理進度 |
**註冊影片**:
```bash
curl -X POST http://localhost:3002/api/v1/register \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"path": "/path/to/video.mp4"}'
```
**註冊回應範例**:
```json
{
"uuid": "a1b10138a6bbb0cd",
"video_id": 1,
"job_id": 10,
"file_name": "video.mp4",
"duration": 120.5,
"width": 1920,
"height": 1080,
"already_exists": false
}
```
**探測影片** (不註冊,只取得影片資訊):
```bash
curl -X POST http://localhost:3002/api/v1/probe \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"path": "./demo/video.mp4"}'
```
**Probe 回應範例**:
```json
{
"uuid": "a1b10138a6bbb0cd",
"file_name": "video.mp4",
"duration": 120.5,
"width": 1920,
"height": 1080,
"fps": 30.0,
"cached": false,
"format": {
"filename": "/path/to/video.mp4",
"format_name": "mov,mp4,m4a,3gp,3g2,mj2",
"duration": "120.5",
"size": "12345678",
"bit_rate": "819200"
},
"streams": [
{
"index": 0,
"codec_name": "h264",
"codec_type": "video",
"width": 1920,
"height": 1080,
"r_frame_rate": "30/1",
"duration": "120.5"
}
]
}
```
**列出影片**:
```bash
curl -H "X-API-Key: your-api-key" http://localhost:3002/api/v1/videos
```
**查詢影片**:
```bash
curl -H "X-API-Key: your-api-key" "http://localhost:3002/api/v1/lookup?uuid=5dea6618a606e7c7"
```
**處理進度**:
```bash
curl -H "X-API-Key: your-api-key" http://localhost:3002/api/v1/progress/5dea6618a606e7c7
```
---
### 工作管理
| 方法 | 端點 | 說明 |
|------|------|------|
| GET | `/api/v1/jobs` | 列出所有工作 |
| GET | `/api/v1/jobs/:uuid` | 取得指定工作的詳細資訊 |
**列出工作**:
```bash
curl -H "X-API-Key: your-api-key" http://localhost:3002/api/v1/jobs
```
**取得工作詳細資訊**:
```bash
curl -H "X-API-Key: your-api-key" http://localhost:3002/api/v1/jobs/a03485a40b2df2d3
```
---
### 系統管理
| 方法 | 端點 | 說明 |
|------|------|------|
| POST | `/api/v1/config/cache` | 切換快取功能(管理員) |
| POST | `/api/v1/unregister` | 刪除影片及其所有資料(管理員) |
**快取設定**:
```bash
curl -X POST http://localhost:3002/api/v1/config/cache \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"enabled": true}'
```
**刪除影片**:
```bash
curl -X POST http://localhost:3002/api/v1/unregister \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"uuid": "5dea6618a606e7c7"}'
```
---
## 端點對照表
| 功能 | n8n 使用 | WordPress 使用 | curl 測試 |
|------|-----------|----------------|------------|
| 健康檢查 | ✓ | ✓ | ✓ |
| 語意搜尋 | ✓ (n8n格式) | ✓ (標準格式) | ✓ |
| 影片探測 | ✓ | ✓ | ✓ |
| 註冊影片 | ✓ | ✓ | ✓ |
| 列出影片 | ✓ | ✓ | ✓ |
| 查詢影片 | ✓ | ✓ | ✓ |
| 處理進度 | ✓ | ✓ | ✓ |
| 工作管理 | ✓ | ✓ | ✓ |
| 快取設定 | ✓ (管理員) | ✓ (管理員) | ✓ (管理員) |
| 刪除影片 | ✓ (管理員) | ✓ (管理員) | ✓ (管理員) |
---
## 回應格式
### n8n 格式 (`/api/v1/n8n/search`)
```json
{
"query": "charade",
"count": 10,
"hits": [
{
"id": "sentence_0001",
"vid": "a1b10138a6bbb0cd",
"start": 48.8,
"end": 55.44,
"title": "Chunk sentence_0001",
"text": "...",
"score": 0.92,
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
}
]
}
```
### 標準格式 (`/api/v1/search`)
```json
{
"results": [
{
"uuid": "a1b10138a6bbb0cd",
"chunk_id": "sentence_0001",
"chunk_type": "sentence",
"start_time": 48.8,
"end_time": 55.44,
"text": "...",
"score": 0.92
}
],
"query": "charade"
}
```
---
## HTTP 狀態碼
| 狀態 | 說明 |
|------|------|
| 200 | 成功 |
| 400 | 請求格式錯誤 |
| 404 | 端點或資源不存在 |
| 500 | 伺服器內部錯誤 |
| 502 | API 服務未啟動 |
---
## 錯誤處理
### 502 Bad Gateway
**原因**: Momentry API 服務未啟動
**解決**:
```bash
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
```
---
## 相關文件
- [API_INDEX.md](./API_INDEX.md) - 文件總覽(起點)
- [API_EXAMPLES.md](./API_EXAMPLES.md) - **完整範例總覽curl / n8n / WordPress**
- [API_N8N_GUIDE.md](./API_N8N_GUIDE.md) - n8n 詳細指南
- [API_WORDPRESS_GUIDE.md](./API_WORDPRESS_GUIDE.md) - WordPress 詳細指南
- [API_CURL_EXAMPLES.md](./API_CURL_EXAMPLES.md) - curl 範例

View File

@@ -1,771 +0,0 @@
# Momentry Core API 使用範例總覽
| 項目 | 內容 |
|------|------|
| 版本 | V2.1 |
| 日期 | 2026-03-26 |
| Base URL (本地) | `http://localhost:3002` |
| Base URL (外部) | `https://api.momentry.ddns.net` |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 |
|------|------|------|--------|
| V2.0 | 2026-03-25 | 創建完整範例總覽 | OpenCode |
| V2.1 | 2026-03-26 | 更新API回應格式 (media_url→file_path) 與認證標頭 | OpenCode |
---
## 快速參考
### 環境 URL 選擇
| 環境 | URL | 用途 |
|------|-----|------|
| **本地開發** | `http://localhost:3002` | 開發/測試,直接訪問 API |
| **外部訪問** | `https://api.momentry.ddns.net` | n8n、WordPress、curl 生產環境 |
### 所有可用端點
| 方法 | 端點 | 說明 |
|------|------|------|
| GET | `/health` | 健康檢查 |
| GET | `/health/detailed` | 詳細健康檢查 |
| POST | `/api/v1/search` | 語意搜尋(標準格式) |
| POST | `/api/v1/n8n/search` | 語意搜尋n8n 格式) |
| POST | `/api/v1/search/hybrid` | 混合搜尋 |
| POST | `/api/v1/register` | 註冊影片 |
| POST | `/api/v1/probe` | 探測影片資訊 |
| GET | `/api/v1/videos` | 列出所有影片 |
| GET | `/api/v1/lookup` | 查詢影片 |
| GET | `/api/v1/progress/:uuid` | 處理進度 |
| GET | `/api/v1/jobs` | 任務列表 |
| GET | `/api/v1/jobs/:uuid` | 任務詳情 |
---
## 認證
### API Key
所有 `/api/v1/*` 端點需要 API Key 認證。
```bash
# 添加 API Key Header
curl -H "X-API-Key: your-api-key" http://localhost:3002/api/v1/videos
# 範例
curl -H "X-API-Key: muser_f08e13ba967e4d8ea8fc542ad9f99ac8_1774416728_90472a35" \
http://localhost:3002/api/v1/videos
```
### 響應狀態
| 狀態碼 | 說明 |
|--------|------|
| 200 | 成功 |
| 401 | 未授權(缺少或無效 API Key |
| 500 | 伺服器錯誤 |
### 建立 API Key
```bash
./target/release/momentry api-key create "My Key" --key-type user
```
---
## 1. curl 範例
### 基本語法
```bash
# 格式
curl [OPTIONS] URL
# 常用選項
-X METHOD # HTTP 方法 (GET, POST, etc.)
-H HEADER # 添加 HTTP 標頭
-d DATA # POST 請求體
-s # 靜默模式
-w FORMAT # 輸出額外信息
```
### 1.1 健康檢查
```bash
# 基本健康檢查
curl http://localhost:3002/health
# 詳細健康檢查
curl http://localhost:3002/health/detailed
```
**回應**:
```json
{"status":"ok","version":"0.1.0","uptime_ms":123456}
```
### 1.2 語意搜尋
```bash
# 標準格式搜尋
curl -X POST http://localhost:3002/api/v1/search \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"query": "charade", "limit": 5}'
# n8n 格式搜尋(推薦)
curl -X POST http://localhost:3002/api/v1/n8n/search \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"query": "charade", "limit": 5}'
# 混合搜尋
curl -X POST http://localhost:3002/api/v1/search/hybrid \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"query": "charade", "limit": 5}'
```
**標準格式回應**:
```json
{
"results": [
{
"uuid": "a1b10138a6bbb0cd",
"chunk_id": "sentence_0001",
"chunk_type": "sentence",
"start_time": 48.8,
"end_time": 55.44,
"text": "fun plot twists...",
"score": 0.92
}
],
"query": "charade"
}
```
**n8n 格式回應**:
```json
{
"query": "charade",
"count": 1,
"hits": [
{
"id": "sentence_0001",
"vid": "a1b10138a6bbb0cd",
"start": 48.8,
"end": 55.44,
"title": "Chunk sentence_0001",
"text": "fun plot twists...",
"score": 0.92,
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
}
]
}
```
### 1.3 影片管理
```bash
# 列出所有影片
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos
# 查詢特定影片(依 UUID
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3002/api/v1/lookup?uuid=a1b10138a6bbb0cd"
# 查詢特定影片(依路徑)
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3002/api/v1/lookup?path=/path/to/video.mp4"
# 取得處理進度
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/progress/a1b10138a6bbb0cd
# 探測影片(不註冊)
curl -X POST http://localhost:3002/api/v1/probe \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"path": "/path/to/video.mp4"}'
# 註冊影片
curl -X POST http://localhost:3002/api/v1/register \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"path": "/path/to/video.mp4"}'
```
### 1.4 批次測試腳本
```bash
#!/bin/bash
# api_test.sh - API 測試腳本
API_URL="http://localhost:3002"
echo "=== 健康檢查 ==="
curl -s "$API_URL/health" | jq .
echo -e "\n=== 語意搜尋 ==="
curl -s -X POST "$API_URL/api/v1/search" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"query": "charade", "limit": 3}' | jq .
echo -e "\n=== 影片列表 ==="
curl -s -H "X-API-Key: YOUR_API_KEY" "$API_URL/api/v1/videos" | jq '.videos | length'
```
### 1.5 外部 URL 範例
```bash
# 使用外部 URL需網路可達
curl https://api.momentry.ddns.net/health
# 外部搜尋
curl -X POST https://api.momentry.ddns.net/api/v1/n8n/search \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"query": "charade", "limit": 5}'
```
---
## 2. n8n 範例
### 2.1 HTTP Request Node 設定
```
Node: HTTP Request
├── URL: https://api.momentry.ddns.net/api/v1/n8n/search
├── Method: POST
├── Authentication: None
├── Send Body: ✓ (checked)
├── Content Type: JSON
├── Body:
│ {
│ "query": "={{ $json.query }}",
│ "limit": "={{ $json.limit || 10 }}"
│ }
├── Send Headers: ✓ (checked)
└── Header Parameters:
└── X-API-Key: {{ $env.MOMENTRY_API_KEY }}
```
### 2.2 基本搜尋 Workflow
```json
{
"name": "Momentry Video Search",
"nodes": [
{
"parameters": {},
"name": "Manual Trigger",
"type": "n8n-nodes-base.manualTrigger",
"position": [250, 300]
},
{
"parameters": {
"url": "https://api.momentry.ddns.net/api/v1/n8n/search",
"method": "POST",
"sendBody": true,
"contentType": "json",
"body": {
"query": "charade",
"limit": 3
}
},
"name": "Search Video API",
"type": "n8n-nodes-base.httpRequest",
"position": [450, 300]
}
],
"connections": {
"Manual Trigger": {
"main": [[{"node": "Search Video API"}]]
}
}
}
```
### 2.3 Webhook 動態搜尋
```json
{
"name": "Momentry Dynamic Search",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "search",
"responseMode": "lastNode"
},
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"position": [250, 300]
},
{
"parameters": {
"url": "https://api.momentry.ddns.net/api/v1/n8n/search",
"method": "POST",
"sendBody": true,
"contentType": "json",
"body": {
"query": "={{ JSON.stringify($json.body.query) }}",
"limit": "={{ $json.body.limit || 5 }}"
}
},
"name": "Search API",
"type": "n8n-nodes-base.httpRequest",
"position": [450, 300]
}
],
"connections": {
"Webhook": {
"main": [[{"node": "Search API"}]]
}
}
}
```
### 2.4 測試 Webhook
```bash
# 測試模式(需先在 n8n UI 點擊 Execute
curl -X POST http://localhost:5678/webhook-test/video-rag-mcp \
-H "Content-Type: application/json" \
-d '{"query":"charade","limit":3}'
# 生產環境(需 workflow 為 Active 狀態)
curl -X POST http://localhost:5678/webhook/video-rag-mcp \
-H "Content-Type: application/json" \
-d '{"query":"charade","limit":3}'
```
### 2.5 健康檢查 Workflow
```json
{
"name": "Momentry Health Check",
"nodes": [
{
"parameters": {},
"name": "Manual Trigger",
"type": "n8n-nodes-base.manualTrigger",
"position": [250, 300]
},
{
"parameters": {
"url": "https://api.momentry.ddns.net/health",
"method": "GET"
},
"name": "Health Check",
"type": "n8n-nodes-base.httpRequest",
"position": [450, 300]
}
],
"connections": {
"Manual Trigger": {
"main": [[{"node": "Health Check"}]]
}
}
}
```
### 2.6 錯誤處理
| 錯誤 | 原因 | 解決 |
|------|------|------|
| 502 Bad Gateway | API 服務未啟動 | `sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist` |
| "Your request is invalid" | Body 格式設定錯誤 | 確認 Content Type: JSONBody 為有效 JSON |
| 404 on webhook-test | 未執行 workflow | 在 n8n UI 點擊 "Execute workflow" |
---
## 3. WordPress 範例
### 3.1 PHP 基本用法
```php
<?php
// 搜尋影片
$api_url = 'https://api.momentry.ddns.net/api/v1/n8n/search';
$data = [
'query' => 'charade',
'limit' => 10
];
$response = wp_remote_post($api_url, [
'headers' => ['Content-Type' => 'application/json'],
'body' => json_encode($data),
'timeout' => 30
]);
if (is_wp_error($response)) {
echo '錯誤: ' . $response->get_error_message();
} else {
$body = json_decode(wp_remote_retrieve_body($response), true);
print_r($body['hits']);
}
?>
```
### 3.2 列出影片
```php
<?php
$api_url = 'https://api.momentry.ddns.net/api/v1/videos';
$response = wp_remote_get($api_url, ['timeout' => 30]);
if (!is_wp_error($response)) {
$body = json_decode(wp_remote_retrieve_body($response), true);
foreach ($body['videos'] as $video) {
echo $video['file_name'] . "\n";
}
}
?>
```
### 3.3 查詢特定影片
```php
<?php
$uuid = 'a1b10138a6bbb0cd';
$api_url = 'https://api.momentry.ddns.net/api/v1/lookup?uuid=' . $uuid;
$response = wp_remote_get($api_url, ['timeout' => 30]);
if (!is_wp_error($response)) {
$video = json_decode(wp_remote_retrieve_body($response), true);
echo '檔案: ' . $video['file_name'] . "\n";
echo '時長: ' . $video['duration'] . ' 秒';
}
?>
```
### 3.4 JavaScript fetch
```javascript
// 搜尋影片
async function searchVideos(query, limit = 10) {
const response = await fetch('https://api.momentry.ddns.net/api/v1/n8n/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query, limit })
});
if (!response.ok) {
throw new Error('API 請求失敗');
}
return await response.json();
}
// 使用範例
searchVideos('charade', 5)
.then(data => {
data.hits.forEach(hit => {
console.log(`${hit.text} (score: ${hit.score})`);
});
});
```
### 3.5 WordPress Shortcode
`functions.php` 中註冊短碼:
```php
<?php
// 將文件路徑轉換為可訪問的 URL
function convert_file_path_to_url($file_path) {
// 範例: 將 SFTPGo 文件路徑轉換為 web URL
// /Users/accusys/momentry/var/sftpgo/data/demo/video.mp4
// → https://sftpgo.example.com/demo/video.mp4
// 移除基本路徑
$base_path = '/Users/accusys/momentry/var/sftpgo/data/';
if (strpos($file_path, $base_path) === 0) {
$relative_path = substr($file_path, strlen($base_path));
// 替換為實際的 SFTPGo web URL
return 'https://sftpgo.example.com/' . $relative_path;
}
// 如果無法轉換,返回原始路徑
return $file_path;
}
// 註冊短碼
add_shortcode('momentry_search', function($atts) {
$atts = shortcode_atts([
'query' => '',
'limit' => '10'
], $atts);
if (empty($atts['query'])) {
return '<p>請提供搜尋關鍵字</p>';
}
$response = wp_remote_post('https://api.momentry.ddns.net/api/v1/n8n/search', [
'headers' => [
'Content-Type' => 'application/json',
'X-API-Key' => 'YOUR_API_KEY' // 替換為實際的 API Key
],
'body' => json_encode([
'query' => $atts['query'],
'limit' => (int)$atts['limit']
]),
'timeout' => 30
]);
if (is_wp_error($response)) {
return '<p>搜尋服務暫時無法使用</p>';
}
$data = json_decode(wp_remote_retrieve_body($response), true);
if (empty($data['hits'])) {
return '<p>找不到相關結果</p>';
}
$output = '<ul class="momentry-results">';
foreach ($data['hits'] as $hit) {
// 注意: API 現在返回 file_path 而非 media_url
// 需要將文件路徑轉換為可訪問的 URL
$file_path = $hit['file_path'];
$video_url = convert_file_path_to_url($file_path); // 需要實作此函數
$output .= sprintf(
'<li>%s <a href="%s?start=%s">播放</a></li>',
esc_html($hit['text']),
$video_url,
$hit['start']
);
}
$output .= '</ul>';
return $output;
});
?>
```
**使用方式**:
```
[momentry_search query="charade" limit="5"]
```
### 3.6 WordPress REST API Endpoint
在 WordPress REST API 中註冊自定義端點:
```php
<?php
// 註冊 REST API 端點
add_action('rest_api_init', function() {
register_rest_route('momentry/v1', '/search', [
'methods' => 'POST',
'callback' => function($request) {
$response = wp_remote_post(
'https://api.momentry.ddns.net/api/v1/n8n/search',
[
'headers' => ['Content-Type' => 'application/json'],
'body' => json_encode([
'query' => $request->get_param('query'),
'limit' => $request->get_param('limit', 10)
])
]
);
if (is_wp_error($response)) {
return new WP_Error('api_error', 'API 請求失敗');
}
return json_decode(wp_remote_retrieve_body($response));
}
]);
});
?>
```
**呼叫方式**:
```
POST /wp-json/momentry/v1/search
Body: {"query": "charade", "limit": 5}
```
---
## 4. 回應格式說明
### 4.1 n8n 格式 (`/api/v1/n8n/search`)
```json
{
"query": "charade",
"count": 10,
"hits": [
{
"id": "sentence_0001",
"vid": "a1b10138a6bbb0cd",
"start": 48.8,
"end": 55.44,
"title": "Chunk sentence_0001",
"text": "fun plot twists...",
"score": 0.92,
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
}
]
}
```
### 4.2 標準格式 (`/api/v1/search`)
```json
{
"results": [
{
"uuid": "a1b10138a6bbb0cd",
"chunk_id": "sentence_0001",
"chunk_type": "sentence",
"start_time": 48.8,
"end_time": 55.44,
"text": "fun plot twists...",
"score": 0.92
}
],
"query": "charade"
}
```
### 4.3 健康檢查
```json
{
"status": "ok",
"version": "0.1.0",
"uptime_ms": 123456
}
```
### 4.4 詳細健康檢查
```json
{
"status": "ok",
"version": "0.1.0",
"uptime_ms": 123456,
"services": {
"postgres": {"status": "ok", "latency_ms": 42, "error": null},
"redis": {"status": "ok", "latency_ms": 0, "error": null},
"qdrant": {"status": "ok", "latency_ms": 15, "error": null},
"mongodb": {"status": "ok", "latency_ms": 0, "error": null}
}
}
```
### 4.5 處理進度
```json
{
"uuid": "a1b10138a6bbb0cd",
"file_name": "video.mp4",
"duration": 120.5,
"overall_progress": 75,
"processors": [
{"name": "asr", "status": "complete", "progress": 100},
{"name": "cut", "status": "complete", "progress": 100},
{"name": "yolo", "status": "progress", "progress": 35}
]
}
```
### 4.6 Probe 回應
```json
{
"uuid": "a1b10138a6bbb0cd",
"file_name": "video.mp4",
"duration": 120.5,
"width": 1920,
"height": 1080,
"fps": 30.0,
"cached": false,
"format": {
"filename": "/path/to/video.mp4",
"format_name": "mov,mp4,m4a,3gp,3g2,mj2",
"duration": "120.5",
"size": "12345678",
"bit_rate": "819200"
},
"streams": [
{
"index": 0,
"codec_name": "h264",
"codec_type": "video",
"width": 1920,
"height": 1080,
"r_frame_rate": "30/1",
"duration": "120.5"
}
]
}
```
---
## 5. HTTP 狀態碼
| 狀態 | 說明 | 解決 |
|------|------|------|
| 200 | 成功 | - |
| 400 | 請求格式錯誤 | 檢查 JSON 格式 |
| 404 | 端點或資源不存在 | 確認 URL 正確 |
| 500 | 伺服器內部錯誤 | 檢查 API 服務日誌 |
| 502 | API 服務未啟動 | 見下方說明 |
### 502 Bad Gateway 解決
```bash
# 檢查服務狀態
launchctl list | grep momentry.api
# 啟動服務
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
# 或使用本地測試
curl http://localhost:3002/health
```
---
## 6. 常見問題
### Q: 為什麼有兩個 URL
| URL | 用途 |
|-----|------|
| `localhost:3002` | 直接訪問,繞過反向代理 |
| `api.momentry.ddns.net` | 通過 Caddy 反向代理 |
### Q: 兩者功能相同嗎?
是的,所有端點和功能完全相同。
### Q: n8n webhook-test 返回 404
需在 n8n UI 中點擊 "Execute workflow" 按鈕後才能使用測試 Webhook。
### Q: 生產環境 webhook 返回 404
需將 workflow 切換為 Active 狀態(右上角開關)。
---
## 相關文件
- [API_INDEX.md](./API_INDEX.md) - 文件總覽
- [API_ENDPOINTS.md](./API_ENDPOINTS.md) - 端點完整說明
- [API_N8N_GUIDE.md](./API_N8N_GUIDE.md) - n8n 詳細指南
- [API_WORDPRESS_GUIDE.md](./API_WORDPRESS_GUIDE.md) - WordPress 詳細指南

View File

@@ -1,119 +0,0 @@
# Momentry Core API 文件總覽
| 項目 | 內容 |
|------|------|
| 建立者 | OpenCode |
| 建立時間 | 2026-03-25 |
| 文件版本 | V2.2 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V2.0 | 2026-03-22 | 創建 API 文件總覽 | Warren | OpenCode |
| V2.1 | 2026-03-24 | 新增文件分類與快速選擇指南 | OpenCode | deepseek-reasoner |
| V2.2 | 2026-03-25 | 更新 API Key 驗證說明與文件連結 | OpenCode | deepseek-reasoner |
---
## 文件架構
```
docs/
├── API_INDEX.md ← 本文件:總覽與入口
├── API_ENDPOINTS.md ← API 端點完整說明
├── API_EXAMPLES.md ← 完整範例總覽curl / n8n / WordPress
├── API_REFERENCE.md ← 詳細技術參考
├── DEMO_MANUAL.md ← ⭐ 示範手冊(含 Demo API Key
├── API_N8N_GUIDE.md ← n8n 詳細指南
├── API_WORDPRESS_GUIDE.md ← WordPress 詳細指南
├── API_CURL_EXAMPLES.md ← curl 快速範例
├── API_TRAINING_MARCOM.md ← ⭐ marcom 團隊教育訓練手冊
├── API_WORKFLOW_WORDPRESS_N8N.md ← WordPress/n8n 完整工作流程
└── CHUNK_DATA_STRUCTURE.md ← Chunk 資料結構說明
```
---
## 快速選擇指南
| 需求 | 閱讀文件 |
|------|----------|
| **我要快速開始測試** | ⭐ [DEMO_MANUAL.md](./DEMO_MANUAL.md) |
| **我要查看所有範例** | [API_EXAMPLES.md](./API_EXAMPLES.md) |
| **我是 marcom 團隊** | ⭐ [API_TRAINING_MARCOM.md](./API_TRAINING_MARCOM.md) |
| 我想了解有哪些 API 端點 | [API_ENDPOINTS.md](./API_ENDPOINTS.md) |
| 我要整合 WordPress/n8n | [API_WORKFLOW_WORDPRESS_N8N.md](./API_WORKFLOW_WORDPRESS_N8N.md) |
| 我要在 n8n workflow 中呼叫 API | [DEMO_MANUAL.md](./DEMO_MANUAL.md#2-n8n-範例) |
| 我要在 WordPress 中呼叫 API | [DEMO_MANUAL.md](./DEMO_MANUAL.md#3-wordpress-範例) |
| 我要用 curl 快速測試 | [DEMO_MANUAL.md](./DEMO_MANUAL.md#1-curl-範例) |
---
## 認證
### Demo API Key
```
API Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69
Key ID: muser_68600856036340bcafc01930eb4bd839
過期日: 2027-03-25
```
### 使用方式
```bash
curl -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
http://localhost:3002/api/v1/videos
```
---
## API URL 選擇
| 環境 | URL | 使用時機 |
|------|-----|----------|
| **本地開發** | `http://localhost:3002` | 開發/測試、繞過反向代理 |
| **外部訪問** | `https://api.momentry.ddns.net` | n8n、WordPress、遠端系統 |
### 何時用哪個
**使用 `localhost:3002`**
- 本地終端機測試
- 當反向代理有問題時
- 快速除錯
**使用 `api.momentry.ddns.net`**
- n8n workflow
- WordPress 網站
- 外部系統整合
---
## 常見問題
### Q: API 返回 401 錯誤?
API Key 無效或過期。請使用 Demo API Key 或建立新的 API Key。
### Q: API 返回 502 錯誤?
```bash
# 檢查服務狀態
launchctl list | grep momentry.api
# 如未啟動
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
```
### Q: 兩個 URL 功能相同嗎?
是的,所有端點完全相同,只是訪問路徑不同。
---
## 相關文件
- [DEMO_MANUAL.md](./DEMO_MANUAL.md) - ⭐ 示範手冊(推薦新手)
- [INSTALL_MOMENTRY_API.md](./INSTALL_MOMENTRY_API.md) - API 服務安裝指南
- [PENDING_ISSUES.md](./PENDING_ISSUES.md) - 待解決問題追蹤

View File

@@ -1,195 +0,0 @@
# API Key Management System Architecture
## System Overview
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ API Key Management System │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ CLI │ │ HTTP API │ │ Service │ │ External │ │
│ │ Layer │────▶│ Layer │────▶│ Layer │────▶│ Services │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ Core Modules │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Service │ │Validator│ │ Anomaly │ │Rotation │ │ Cleanup │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Webhook │ │Encrypt │ │Blacklist│ │ Report │ │ Error │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ PostgreSQL │ │ Redis │ │ External │ │
│ │ (Storage) │ │ (Cache) │ │ (Gitea/n8n)│ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
```
## Module Dependencies
```
┌──────────────┐
│ models.rs │
│ (Types) │
└──────┬───────┘
┌──────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ service.rs │ │ error.rs │ │ validator.rs │
│ (Core CRUD) │ │ (Errors) │ │ (Cache+Rate) │
└───────┬───────┘ └───────────────┘ └───────────────┘
│ ┌───────────────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ anomaly.rs │ │ rotation.rs │ │ blacklist.rs │
│ (Detection) │ │ (Rotation) │ │ (IP Block) │
└───────────────┘ └───────────────┘ └───────────────┘
```
## Request Flow
```
Client Request
┌─────────────┐
│ CLI/API │
└──────┬──────┘
┌─────────────┐ ┌─────────────┐
│ Rate Limit │────▶│ IP Blacklist│
│ Check │ │ Check │
└──────┬──────┘ └──────┬──────┘
│ │
└─────────┬─────────┘
┌───────────────┐
│ Hash API Key │
└───────┬───────┘
┌───────────────┐ ┌───────────────┐
│ Cache Lookup │────▶│ PostgreSQL │
└───────┬───────┘ │ Lookup │
│ └───────┬───────┘
│ │
└──────────┬──────────┘
┌───────────────┐
│ Validate │
│ (Status, │
│ Expiry) │
└───────┬───────┘
┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Valid │ │ Invalid │ │ Error │
│ Response│ │ Response │ │ Response │
└──────────┘ └──────────┘ └──────────┘
```
## Database Schema
```
┌─────────────────────────────────────────────────────────────────┐
│ PostgreSQL │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ api_keys │ │ api_key_audit_ │ │
│ ├─────────────────┤ │ log │ │
│ │ id │ ├─────────────────┤ │
│ │ key_id │─────▶│ id │ │
│ │ key_hash │ │ key_id (FK) │ │
│ │ name │ │ action │ │
│ │ key_type │ │ ip_address │ │
│ │ status │ │ details │ │
│ │ expires_at │ └─────────────────┘ │
│ │ ... │ │
│ └─────────────────┘ ┌─────────────────┐ │
│ │ api_key_anomalies│ │
│ ┌─────────────────┐ ├─────────────────┤ │
│ │ gitea_tokens │ │ id │ │
│ ├─────────────────┤ │ key_id (FK) │ │
│ │ id │ │ anomaly_type │ │
│ │ gitea_token_id │ │ severity │ │
│ │ token_name │ │ details │ │
│ │ scopes │ └─────────────────┘ │
│ └─────────────────┘ │
│ │
│ ┌─────────────────┐ │
│ │ n8n_api_keys │ │
│ ├─────────────────┤ │
│ │ id │ │
│ │ n8n_key_id │ │
│ │ label │ │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
## External Integrations
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ External Integrations │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Gitea │ │ n8n │ │ Webhook │ │
│ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤ │
│ │ • Create Token │ │ • Create API Key│ │ • Key Created │ │
│ │ • List Tokens │ │ • List API Keys │ │ • Key Revoked │ │
│ │ • Delete Token │ │ • Delete API Key│ │ • Anomaly │ │
│ │ • Verify Token │ │ • Verify │ │ • Rate Limited │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
```
## Security Layers
```
┌─────────────────────────────────────────────────────────────────┐
│ Security Layers │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Layer 1: Network │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ • IP Blacklist │ │
│ │ • Rate Limiting │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Layer 2: Authentication │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ • API Key Hash (SHA256) │ │
│ │ • Constant-time Comparison │ │
│ │ • Key Validation (Status, Expiry) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Layer 3: Monitoring │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ • Anomaly Detection │ │
│ │ • Audit Logging (Encrypted) │ │
│ │ • Webhook Notifications │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```

View File

@@ -1,236 +0,0 @@
# API Key Management Integration Tests
## Test Environment Setup
### Prerequisites
```bash
# Start services
sudo launchctl load /Library/LaunchDaemons/com.momentry.postgresql.plist
sudo launchctl load /Library/LaunchDaemons/com.momentry.redis.plist
# Set environment variables
export DATABASE_URL="postgres://accusys@localhost:5432/momentry"
export REDIS_URL="redis://:accusys@localhost:6379"
export GITEA_URL="http://localhost:3000"
export N8N_URL="https://n8n.momentry.ddns.net"
```
### Run Tests
```bash
# Run all unit tests
cargo test --lib
# Run API key specific tests
cargo test --lib api_key
# Run with output
cargo test --lib -- --nocapture
```
---
## Test Cases
### 1. API Key Creation
```bash
# Test: Create a service key
momentry api-key create test-key --key-type service --ttl 90
# Expected Output:
# ✅ API Key created successfully!
# Key ID: msvc_...
# API Key: msvc_...
# Expires: 2026-06-19
```
### 2. API Key Validation
```bash
# Test: Validate the created key
momentry api-key validate --key "msvc_..."
# Expected Output:
# ✅ API Key is valid
# Key ID: msvc_...
# Name: test-key
# Type: service
```
### 3. API Key Listing
```bash
# Test: List all keys
momentry api-key list
# Expected Output:
# 📋 API Key List
# ┌────────────────────────────────────────────────────────────────────────────┐
# │ Status │ Name │ Type │ Usage │ Last Used │
# ├────────────────────────────────────────────────────────────────────────────┤
# │ ✓ active │ test-key │ "service" │ 0 │ never │
# └────────────────────────────────────────────────────────────────────────────┘
```
### 4. API Key Statistics
```bash
# Test: Show statistics
momentry api-key stats
# Expected Output:
# 📊 API Key Statistics
# ┌─────────────────────────────────────────┐
# │ Total Keys: 1 │
# │ Active Keys: 1 │
# │ Expired Keys: 0 │
# └─────────────────────────────────────────┘
```
### 5. Gitea Token Creation
```bash
# Test: Create Gitea token
momentry gitea create \
--username admin \
--password "Test3200Test3200Test3200" \
--token-name "test-token" \
--scopes "read:repository,write:repository"
# Expected Output:
# ✅ Gitea Token created successfully!
# Token ID: ...
# SHA1: ...
```
### 6. n8n API Key Creation
```bash
# Test: Create n8n API key
momentry n8n create \
--api-key "existing-n8n-key" \
--label "test-key" \
--expires-in-days 90
# Expected Output:
# ✅ n8n API Key created successfully!
# Key ID: ...
# API Key: ...
```
---
## Automated Test Script
```bash
#!/bin/bash
# integration_test.sh
set -e
echo "=== API Key Integration Tests ==="
# 1. Create API key
echo "1. Testing API key creation..."
momentry api-key create integration-test --key-type service --ttl 30
echo "✅ API key created"
# 2. List keys
echo "2. Testing API key listing..."
momentry api-key list
echo "✅ API key list OK"
# 3. Show stats
echo "3. Testing statistics..."
momentry api-key stats
echo "✅ Statistics OK"
# 4. Test Gitea integration
echo "4. Testing Gitea integration..."
GITEA_URL="http://localhost:3000" \
momentry gitea list --username admin --password "Test3200Test3200Test3200"
echo "✅ Gitea integration OK"
echo ""
echo "=== All Tests Passed ==="
```
---
## Unit Test Coverage
| Module | Tests | Status |
|--------|-------|--------|
| `models.rs` | 0 | ✅ |
| `service.rs` | 5 | ✅ |
| `validator.rs` | 2 | ✅ |
| `gitea.rs` | 3 | ✅ |
| `n8n.rs` | 2 | ✅ |
| `rotation.rs` | 4 | ✅ |
| `anomaly.rs` | 0 | ✅ |
| `blacklist.rs` | 5 | ✅ |
| `encryption.rs` | 2 | ✅ |
| `webhook.rs` | 2 | ✅ |
| `error.rs` | 3 | ✅ |
| `report.rs` | 1 | ✅ |
| `cleanup.rs` | 1 | ✅ |
| **Total** | **30** | **✅** |
---
## CI/CD Integration
### GitHub Actions / Gitea Actions
```yaml
name: API Key Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_USER: accusys
POSTGRES_DB: momentry_test
ports:
- 5432:5432
redis:
image: redis:7
ports:
- 6379:6379
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo test --lib api_key
```
---
## Troubleshooting
### Common Issues
1. **Database connection failed**
```bash
# Check PostgreSQL status
pg_isready -h localhost -p 5432
```
2. **Redis connection failed**
```bash
# Check Redis status
redis-cli -a accusys ping
```
3. **Gitea authentication failed**
```bash
# Verify credentials
curl -u admin:password http://localhost:3000/api/v1/user
```

View File

@@ -1,713 +0,0 @@
# Momentry API Key 管理系統設計
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-21 |
| 文件版本 | V1.2 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-18 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
| V1.1 | 2026-03-20 | 新增 Key 類型與管理流程 | Warren | OpenCode |
| V1.2 | 2026-03-21 | 更新 API Key 格式與驗證流程 | Warren | OpenCode |
---
**狀態**: 開發中
---
## 1. 概述
### 1.1 目標
建立安全的 API Key 管理機制,支援:
- 多類型 API Key系統、用戶、服務
- 自動過期與輪換
- 異常使用偵測
- 強制更新機制
- 完整審計日誌
- Gitea Token 整合
- n8n API Key 整合
### 1.2 設計原則
| 原則 | 說明 |
|------|------|
| 最小權限 | 每個 Key 僅授予必要權限 |
| 定期輪換 | 自動過期強制更新 |
| 追蹤可審 | 所有操作都有日誌 |
| 分離儲存 | Key 與使用者資料分離 |
---
## 2. API Key 類型
### 2.1 Key 類型矩陣
| 類型 | 前綴 | 用途 | 預設有效期 | 輪換方式 |
|------|------|------|------------|----------|
| `system` | `msys_` | 系統內部服務 | 365 天 | 手動 |
| `user` | `muser_` | 個人用戶 | 90 天 | 自動 |
| `service` | `msvc_` | 服務間通訊 | 180 天 | 自動 |
| `integration` | `mint_` | 第三方整合 | 30 天 | 強制更新 |
| `emergency` | `memg_` | 緊急存取 | 24 小時 | 一次性 |
### 2.2 Key 格式
```
{prefix}{uuid_v4}_{timestamp}_{checksum}
```
**範例:**
```
msys_a1b2c3d4-e5f6-7890-abcd-ef1234567890_1710998400_sha256
```
---
## 3. 資料庫 Schema
### 3.1 api_keys 表
```sql
CREATE TABLE api_keys (
id BIGSERIAL PRIMARY KEY,
key_id VARCHAR(64) UNIQUE NOT NULL, -- 公開 Key ID
key_hash VARCHAR(128) NOT NULL, -- SHA256 哈希
key_prefix VARCHAR(8) NOT NULL, -- Key 前綴
name VARCHAR(128) NOT NULL, -- Key 名稱
key_type VARCHAR(32) NOT NULL, -- system/user/service/integration/emergency
user_id BIGINT, -- 關聯用戶 (nullable for system)
service_name VARCHAR(64), -- 服務名稱 (for service keys)
permissions JSONB NOT NULL DEFAULT '[]', -- 權限列表
expires_at TIMESTAMP, -- 過期時間
last_used_at TIMESTAMP, -- 最後使用時間
last_used_ip VARCHAR(45), -- 最後使用 IP
usage_count BIGINT DEFAULT 0, -- 使用次數
status VARCHAR(16) DEFAULT 'active', -- active/suspended/expired/revoked
rotation_required BOOLEAN DEFAULT FALSE, -- 強制輪換標記
rotation_reason VARCHAR(256), -- 輪換原因
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_api_keys_key_id ON api_keys(key_id);
CREATE INDEX idx_api_keys_user_id ON api_keys(user_id);
CREATE INDEX idx_api_keys_type ON api_keys(key_type);
CREATE INDEX idx_api_keys_status ON api_keys(status);
CREATE INDEX idx_api_keys_expires ON api_keys(expires_at);
```
### 3.2 api_key_audit_log 表
```sql
CREATE TABLE api_key_audit_log (
id BIGSERIAL PRIMARY KEY,
key_id VARCHAR(64) NOT NULL,
action VARCHAR(32) NOT NULL, -- created/used/rotated/revoked/expired/suspended
actor VARCHAR(64), -- 操作者 (user_id or 'system')
ip_address VARCHAR(45),
user_agent VARCHAR(512),
request_path VARCHAR(256),
response_code INTEGER,
details JSONB,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_audit_key_id ON api_key_audit_log(key_id);
CREATE INDEX idx_audit_action ON api_key_audit_log(action);
CREATE INDEX idx_audit_created ON api_key_audit_log(created_at);
```
### 3.3 api_key_rotation_log 表
```sql
CREATE TABLE api_key_rotation_log (
id BIGSERIAL PRIMARY KEY,
key_id VARCHAR(64) NOT NULL,
old_key_id VARCHAR(64),
new_key_id VARCHAR(64),
rotation_type VARCHAR(32) NOT NULL, -- scheduled/manual/forced/emergency
reason VARCHAR(256),
triggered_by VARCHAR(64), -- system/user/scheduler
grace_period_end TIMESTAMP, -- 寬限期結束時間
created_at TIMESTAMP DEFAULT NOW()
);
```
---
## 4. API Key 狀態機
```
┌──────────────┐
│ created │
└──────┬───────┘
┌────────────────────┐
│ active │◄─────────────┐
└─────────┬──────────┘ │
│ │
┌─────────────┼─────────────┐ │
│ │ │ │
▼ ▼ ▼ │
┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ suspended │ │ expired │ │ revoked │─────┘
└──────────┘ └──────────┘ └──────────┘
```
### 狀態轉換規則
| 從 | 到 | 觸發條件 |
|----|----|----------|
| created | active | 啟用 Key |
| active | suspended | 異常使用偵測 |
| active | expired | 達到過期時間 |
| active | revoked | 手動撤銷 |
| suspended | active | 解除鎖定 |
| suspended | revoked | 確認異常 |
| expired | active | 重新啟用 |
---
## 5. 異常偵測機制
### 5.1 異常指標
| 指標 | 閾值 | 處置 |
|------|------|------|
| 每分鐘請求數 | > 1000 | 警告 |
| 每小時請求數 | > 10000 | 鎖定 |
| 錯誤率 | > 50% | 警告 |
| 不同 IP 數 | > 5/小時 | 警告 |
| 非工作時間使用 | 深夜請求 | 警告 |
| 異常模式 | 暴力破解 | 鎖定 |
### 5.2 異常處理流程
```
異常偵測
┌─────────┐
│ 分析 │──→ 排除正常流量
└────┬────┘
┌─────────┐
│ 評估 │──→ 輕微 → 警告
└────┬────┘
┌─────────┐
│ 處置 │──→ 嚴重 → 鎖定 + 輪換
└─────────┘
```
---
## 6. 強制更新機制
### 6.1 觸發條件
| 條件 | 嚴重性 | 動作 |
|------|--------|------|
| 疑似洩露 | 高 | 立即停用 + 強制輪換 |
| 異常使用 | 中 | 警告 + 建議輪換 |
| 計劃性維護 | 低 | 通知 + 排程輪換 |
| 政策要求 | 高 | 強制輪換 |
| 過期 | 低 | 停用 + 通知 |
### 6.2 強制輪換流程
```
1. 系統偵測到需要強制更新
2. 建立新 Key保留舊 Key 在寬限期內)
3. 發送通知Email/Slack/Redis PubSub
4. 寬限期開始(預設 24 小時)
├── 在寬限期內更新 → 完成輪換
└── 寬限期結束 → 舊 Key 停用
```
### 6.3 寬限期配置
| Key 類型 | 寬限期 |
|----------|--------|
| system | 72 小時 |
| user | 24 小時 |
| service | 48 小時 |
| integration | 24 小時 |
| emergency | 0 小時 |
---
## 7. CLI 管理命令
### 7.1 命令列表
```bash
# Key 管理
momentry api-key create --name "My Key" --type user --permissions read,write
momentry api-key list --type user
momentry api-key info <key_id>
momentry api-key revoke <key_id> --reason "安全原因"
# 輪換管理
momentry api-key rotate <key_id> # 正常輪換
momentry api-key force-rotate <key_id> # 強制輪換
momentry api-key rotation-status <key_id> # 查看輪換狀態
# 異常管理
momentry api-key suspend <key_id> --reason "異常使用"
momentry api-key unsuspend <key_id>
momentry api-key blacklist <key_id> # 列入黑名單
# 審計
momentry api-key audit <key_id> --since 7d
momentry api-key stats --type service --period 30d
```
### 7.2 輸出範例
```bash
$ momentry api-key list --type service
┌────────────────────────────────────┬─────────┬──────────────┬────────────────┐
│ Key ID │ Name │ Status │ Expires │
├────────────────────────────────────┼─────────┼──────────────┼────────────────┤
│ msvc_a1b2c3d4_1710998400_sha256 │ N8N │ active │ 2026-09-21 │
│ msvc_e5f6g7h8_1713600000_sha256 │ OpenCode│ rotation_req │ 2026-09-21 │
└────────────────────────────────────┴─────────┴──────────────┴────────────────┘
⚠️ 1 個 Key 需要輪換
```
---
## 8. 實現計畫
### Phase 1: 核心功能
- [ ] 資料庫 Schema
- [ ] Key 生成與哈希
- [ ] 基本 CRUD API
- [ ] 過期檢查
### Phase 2: 安全機制
- [ ] 異常偵測
- [ ] 自動鎖定
- [ ] 強制輪換
- [ ] 寬限期管理
### Phase 3: 管理工具
- [ ] CLI 命令
- [ ] 審計日誌
- [ ] 統計報表
- [ ] 通知系統
### Phase 4: 自動化
- [ ] 定時輪換排程
- [ ] Prometheus 指標
- [ ] Alertmanager 整合
- [ ] 自動化回應
---
## 9. 安全考量
### 9.1 Key 儲存
- 明文 Key 只顯示一次(創建時)
- 儲存時使用 SHA256 哈希
- 使用 Fernet 對稱加密敏感配置
### 9.2 傳輸安全
- 所有 API 必須使用 HTTPS
- Key 在 Header 中傳輸X-API-Key
- 避免 Key 在 URL 中
### 9.3 存取控制
- 只有管理員可創建/撤銷 Key
- 用戶只能管理自己的 Key
- 系統 Key 需要特殊權限
---
## 10. 環境變數配置
```bash
# API Key 管理
MOMENTRY_API_KEY_GRACE_PERIOD=86400 # 寬限期(秒)
MOMENTRY_API_KEY_MAX_PER_USER=5 # 每用戶最大 Key 數
MOMENTRY_API_KEY_ROTATION_DAYS=90 # 自動輪換天數
# 異常偵測
MOMENTRY_API_KEY_RATE_LIMIT=1000 # 每分鐘限制
MOMENTRY_API_KEY_ERROR_THRESHOLD=0.5 # 錯誤率閾值
MOMENTRY_API_KEY_IP_LIMIT=5 # 每小時 IP 限制
# 通知
MOMENTRY_API_KEY_ALERT_WEBHOOK= # 異常通知 webhook
```
---
## 11. Gitea API Token 整合
### 11.1 概述
支援透過 API Key 管理系統建立和管理 Gitea Personal Access Tokens採用「建立時納管」模式。
### 11.2 納管模式
```
使用者提供帳號密碼 → 呼叫 Gitea API 建立 Token → 明文只顯示一次 → 同步儲存至管理系統
```
**特點:**
- Token 明文僅在建立時取得
- 管理系統記錄 Token 元數據(不含明文)
- 支援本地查詢和刪除
### 11.3 資料庫結構
```sql
CREATE TABLE gitea_tokens (
id SERIAL PRIMARY KEY,
gitea_token_id BIGINT NOT NULL, -- Gitea 內部 Token ID
gitea_user VARCHAR(128) NOT NULL, -- Gitea 用戶名
token_name VARCHAR(128) NOT NULL, -- Token 名稱
token_last_eight VARCHAR(8) NOT NULL, -- SHA1 最後 8 碼(顯示用)
scopes JSONB DEFAULT '[]', -- 權限範圍
api_key_id VARCHAR(48), -- 關聯的 API Key ID可選
last_verified TIMESTAMP, -- 最後驗證時間
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(gitea_user, token_name)
);
```
### 11.4 Token 權限範圍
| 範圍 | 說明 |
|------|------|
| `read:repository` | 讀取倉庫 |
| `write:repository` | 寫入倉庫 |
| `read:issue` | 讀取議題 |
| `write:issue` | 寫入議題 |
| `read:user` | 讀取用戶資訊 |
| `write:write` | 修改用戶資訊 |
| `read:organization` | 讀取組織 |
| `write:organization` | 修改組織 |
| `read:package` | 讀取套件 |
| `write:package` | 發布套件 |
| `read:notification` | 讀取通知 |
| `write:notification` | 修改通知 |
| `read:admin` | 管理員讀取 |
| `write:admin` | 管理員寫入 |
### 11.5 CLI 命令
#### 建立 Token
```bash
# 基本用法
momentry gitea create \
--username <gitea_user> \
--password <gitea_password> \
--token-name <token_name> \
--scopes "read:repository,write:repository"
# 範例:建立整合用 Token
momentry gitea create \
--username admin \
--password "MyPassword123" \
--token-name "ci-pipeline" \
--scopes "read:repository,write:repository,read:issue,write:issue"
```
**輸出範例:**
```
✅ Gitea Token created successfully!
┌─────────────────────────────────────────────────────────────────────────────┐
│ ⚠️ IMPORTANT: Save this token now - it will not be shown again! │
└─────────────────────────────────────────────────────────────────────────────┘
Token ID: 9
Token Name: ci-pipeline
SHA1: 9a4f282e9ba817b430082e6bff2c18e2ae38e480
Last 8: ae38e480
Authorization Header:
Authorization: token 9a4f282e9ba817b430082e6bff2c18e2ae38e480
```
#### 列出 Token
```bash
# 列出用戶的所有 Token
momentry gitea list \
--username <gitea_user> \
--password <gitea_password>
```
**輸出範例:**
```
📋 Gitea Tokens for user: admin
┌────────────────────────────────────────────────────────────────────────────┐
│ ID │ Name │ Last 8 │ Registered │
├────────────────────────────────────────────────────────────────────────────┤
│ 9 │ ci-pipeline │ ae38e480 │ ✓ │
│ 8 │ dev-token │ 1234abcd │ - │
└────────────────────────────────────────────────────────────────────────────┘
Total: 2 token(s)
```
#### 刪除 Token
```bash
# 刪除指定 Token
momentry gitea delete \
--username <gitea_user> \
--password <gitea_password> \
--token-name <token_name>
```
#### 查詢本地記錄
```bash
# 查詢已納管的 Token 記錄
momentry gitea verify --token-name <token_name>
```
**輸出範例:**
```
📋 Gitea Token: ci-pipeline
User: admin
Token ID: 9
Last 8: ae38e480
Scopes: ["read:repository","write:repository"]
Created: 2026-03-21 06:44:55.577586 UTC
Last Verified: never
```
### 11.6 使用範圍
#### 適用場景
| 場景 | 說明 |
|------|------|
| CI/CD 整合 | 建立專用 Token 用於自動化流程 |
| 服務間通訊 | 建立 Token 供其他服務存取 Gitea API |
| 開發環境 | 為開發者建立短期 Token |
| 監控整合 | 建立只讀 Token 用於監控和報告 |
#### 限制
| 限制 | 說明 |
|------|------|
| 明文 Token | 僅在建立時取得,無法再次查詢 |
| 管理 API | 需要帳號密碼BasicAuth |
| Token 驗證 | 只能透過 API 呼叫驗證有效性 |
| 同步刪除 | 本地刪除不會自動同步到 Gitea |
### 11.7 環境變數
```bash
# Gitea 連線設定
GITEA_URL=http://localhost:3000 # Gitea API URL
```
### 11.8 安全考量
| 項目 | 措施 |
|------|------|
| 密碼傳輸 | 僅在 CLI 命令中使用,不儲存 |
| Token 儲存 | 本地僅存元數據,不含明文 |
| 權限最小化 | 建議僅授予必要權限 |
| 定期輪換 | 建議定期更新 Token |
---
## 12. n8n API Key 整合
### 12.1 概述
支援透過 API Key 管理系統建立和管理 n8n API Keys採用「建立時納管」模式。
### 12.2 納管模式
```
使用者提供現有 n8n API Key → 呼叫 n8n API 建立新 Key → 明文只顯示一次 → 同步儲存至管理系統
```
**特點:**
- 需要一個現有的 n8n API Key 作為管理憑證
- API Key 明文僅在建立時取得
- 管理系統記錄 Key 元數據(不含明文)
- 支援本地查詢和刪除
### 12.3 資料庫結構
```sql
CREATE TABLE n8n_api_keys (
id SERIAL PRIMARY KEY,
n8n_key_id VARCHAR(64) UNIQUE NOT NULL, -- n8n 內部 Key ID
label VARCHAR(100) NOT NULL, -- Key 標籤
api_key_last_eight VARCHAR(8) NOT NULL, -- API Key 最後 8 碼(顯示用)
momentry_api_key_id VARCHAR(48), -- 關聯的 API Key ID可選
expires_at TIMESTAMP WITH TIME ZONE, -- 過期時間
last_verified TIMESTAMP WITH TIME ZONE, -- 最後驗證時間
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
```
### 12.4 認證方式
n8n 使用 JWT-based API Key透過 `X-N8N-API-KEY` Header 認證:
```bash
curl -H "X-N8N-API-KEY: <your-api-key>" https://n8n.example.com/api/v1/workflows
```
### 12.5 CLI 命令
#### 建立 API Key
```bash
# 基本用法
momentry n8n create \
--api-key <existing_n8n_api_key> \
--label <key_label> \
--expires-in-days <days>
# 範例:建立 CI/CD 用 Key
momentry n8n create \
--api_key "n8n_api_xxxxxxxxxxxx" \
--label "ci-pipeline" \
--expires-in-days 90
```
**輸出範例:**
```
✅ n8n API Key created successfully!
┌─────────────────────────────────────────────────────────────────────────────┐
│ ⚠️ IMPORTANT: Save this API key now - it will not be shown again! │
└─────────────────────────────────────────────────────────────────────────────┘
Key ID: abc123-def456
Label: ci-pipeline
API Key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Usage:
curl -H 'X-N8N-API-KEY: eyJhbGciOiJIUz...' https://n8n.momentry.ddns.net/api/v1/workflows
```
#### 列出 API Keys
```bash
# 列出所有 API Keys
momentry n8n list --api-key <existing_n8n_api_key>
```
**輸出範例:**
```
📋 n8n API Keys
┌────────────────────────────────────────────────────────────────────────────┐
│ Label │ ID │
├────────────────────────────────────────────────────────────────────────────┤
│ ci-pipeline │ abc123-def456-789 │
│ monitoring │ xyz789-abc123-456 │
└────────────────────────────────────────────────────────────────────────────┘
Total: 2 key(s)
```
#### 刪除 API Key
```bash
# 刪除指定 API Key
momentry n8n delete \
--api-key <existing_n8n_api_key> \
--label <key_label>
```
#### 查詢本地記錄
```bash
# 查詢已納管的 API Key 記錄
momentry n8n verify --label <key_label>
```
**輸出範例:**
```
📋 n8n API Key: ci-pipeline
Key ID: abc123-def456
Last 8: ...JVCJ9
Created: 2026-03-21 06:44:55.577586 UTC
Expires: 2026-06-19 06:44:55.577586 UTC
Last Verified: never
```
### 12.6 使用範圍
#### 適用場景
| 場景 | 說明 |
|------|------|
| CI/CD 整合 | 建立專用 Key 用於自動化流程 |
| 監控整合 | 建立只讀 Key 用於監控工作流狀態 |
| 服務間通訊 | 建立 Key 供其他服務呼叫 n8n API |
| 開發環境 | 為開發者建立短期 Key |
#### 限制
| 限制 | 說明 |
|------|------|
| 明文 API Key | 僅在建立時取得,無法再次查詢 |
| 管理憑證 | 需要一個現有的 n8n API Key |
| 本地刪除 | 不會自動同步到 n8n |
| 權限範圍 | 非 Enterprise 版無細粒度權限 |
### 12.7 環境變數
```bash
# n8n 連線設定
N8N_URL=https://n8n.momentry.ddns.net # n8n API URL
```
### 12.8 安全考量
| 項目 | 措施 |
|------|------|
| 管理 Key | 需妥善保管,作為管理其他 Key 的憑證 |
| API Key 儲存 | 本地僅存元數據,不含明文 |
| 過期機制 | 建議設定過期時間 |
| 定期輪換 | 建議定期更新 Key |
---
## 13. 參考文檔
- PostgreSQL Schema
- Redis Key 設計( MOMENTRY_CORE_REDIS_KEYS.md
- 監控系統MOMENTRY_CORE_MONITORING.md
- Gitea 安裝指南INSTALL_GITEA.md
- n8n API 文件https://docs.n8n.io/api/authentication/

View File

@@ -1,399 +0,0 @@
# API Key Management 優化計畫
| 項目 | 內容 |
|------|------|
| 版本 | V1.0 |
| 日期 | 2026-03-21 |
| 狀態 | 規劃中 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-21 | 創建優化計畫 | OpenCode | - |
---
## 任務編碼規則
```
AKO-{類別}-{序號}
AKO = API Key Optimization
類別:
- CODE = 程式碼品質
- PERF = 效能優化
- SEC = 安全性
- FEAT = 功能增強
- DOC = 文件
```
---
## Phase 1: 程式碼品質 (CODE)
| 編碼 | 任務 | 描述 | 優先級 | 預估工時 | 狀態 |
|------|------|------|--------|----------|------|
| AKO-CODE-01 | 修復 from_str 警告 | 重命名為 `parse_scope` 或實作 `FromStr` trait | 🔴 高 | 0.5h | ⏳ 待辦 |
| AKO-CODE-02 | 函數參數重構 | 使用 Config struct 減少參數數量 | 🔴 高 | 1h | ⏳ 待辦 |
| AKO-CODE-03 | 抽象 CRUD Trait | 建立 `ExternalTokenStore` trait 統一 Gitea/n8n | 🟡 中 | 3h | ⏳ 待辦 |
| AKO-CODE-04 | 錯誤處理統一 | 使用 `thiserror` 定義自訂錯誤類型 | 🟡 中 | 2h | ⏳ 待辦 |
### AKO-CODE-01 細節
```rust
// Before
impl GiteaScope {
pub fn from_str(s: &str) -> Option<Self> { ... }
}
// After: Option A - Rename
impl GiteaScope {
pub fn parse(s: &str) -> Option<Self> { ... }
}
// After: Option B - Implement FromStr
impl std::str::FromStr for GiteaScope {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> { ... }
}
```
### AKO-CODE-02 細節
```rust
// Before
pub async fn create_api_key(
&self,
key_id: &str,
key_hash: &str,
key_prefix: &str,
name: &str,
key_type: &str,
user_id: Option<i64>,
service_name: Option<&str>,
permissions: &serde_json::Value,
expires_at: Option<DateTime<Utc>>,
) -> Result<i64>
// After
pub struct CreateApiKeyConfig<'a> {
pub key_id: &'a str,
pub key_hash: &'a str,
pub key_prefix: &'a str,
pub name: &'a str,
pub key_type: &'a str,
pub user_id: Option<i64>,
pub service_name: Option<&'a str>,
pub permissions: &'a serde_json::Value,
pub expires_at: Option<DateTime<Utc>>,
}
pub async fn create_api_key(&self, config: CreateApiKeyConfig<'_>) -> Result<i64>
```
### AKO-CODE-03 細節
```rust
#[async_trait]
pub trait ExternalTokenStore<T> {
async fn create(&self, record: T) -> Result<i64>;
async fn get_by_label(&self, label: &str) -> Result<Option<T>>;
async fn list(&self) -> Result<Vec<T>>;
async fn delete(&self, label: &str) -> Result<()>;
async fn update_verification(&self, label: &str) -> Result<()>;
}
```
---
## Phase 2: 效能優化 (PERF)
| 編碼 | 任務 | 描述 | 優先級 | 預估工時 | 狀態 |
|------|------|------|--------|----------|------|
| AKO-PERF-01 | 連線池配置外部化 | 使用環境變數控制 max_connections | 🟡 中 | 0.5h | ⏳ 待辦 |
| AKO-PERF-02 | API Key 驗證快取 | 使用 Moka 快取減少資料庫查詢 | 🔴 高 | 2h | ⏳ 待辦 |
| AKO-PERF-03 | 批次查詢優化 | 合併多次查詢為單一 SQL | 🟡 中 | 1h | ⏳ 待辦 |
| AKO-PERF-04 | 非同步日誌寫入 | 使用 channel 非同步寫入審計日誌 | 🟢 低 | 2h | ⏳ 待辦 |
### AKO-PERF-01 細節
```rust
// Before
let pool_options = PgPoolOptions::new()
.max_connections(10)
.acquire_timeout(std::time::Duration::from_secs(60));
// After
let max_conn = std::env::var("DB_MAX_CONNECTIONS")
.unwrap_or_else(|_| "10".to_string())
.parse()
.unwrap_or(10);
let pool_options = PgPoolOptions::new()
.max_connections(max_conn)
.acquire_timeout(std::time::Duration::from_secs(60));
```
### AKO-PERF-02 細節
```rust
use moka::future::Cache;
use std::time::Duration;
pub struct ApiKeyCache {
cache: Cache<String, CachedApiKey>,
}
pub struct CachedApiKey {
pub record: ApiKeyRecord,
pub cached_at: chrono::DateTime<chrono::Utc>,
}
impl ApiKeyCache {
pub fn new(ttl_seconds: u64, max_capacity: u64) -> Self {
Self {
cache: Cache::builder()
.time_to_live(Duration::from_secs(ttl_seconds))
.max_capacity(max_capacity)
.build(),
}
}
pub async fn get(&self, key_hash: &str) -> Option<ApiKeyRecord> {
self.cache.get(key_hash).await.map(|c| c.record)
}
pub async fn insert(&self, key_hash: String, record: ApiKeyRecord) {
self.cache.insert(key_hash, CachedApiKey {
record,
cached_at: chrono::Utc::now(),
}).await;
}
pub async fn invalidate(&self, key_hash: &str) {
self.cache.invalidate(key_hash).await;
}
}
```
---
## Phase 3: 安全性 (SEC)
| 編碼 | 任務 | 描述 | 優先級 | 預估工時 | 狀態 |
|------|------|------|--------|----------|------|
| AKO-SEC-01 | Constant-time 比較 | 使用 `subtle` crate 防止 timing attack | 🔴 高 | 0.5h | ⏳ 待辦 |
| AKO-SEC-02 | Rate Limiter | 限制驗證失敗重試次數 | 🔴 高 | 2h | ⏳ 待辦 |
| AKO-SEC-03 | IP 黑名單 | 支援封鎖特定 IP | 🟡 中 | 1.5h | ⏳ 待辦 |
| AKO-SEC-04 | 審計日誌加密 | 敏感欄位加密儲存 | 🟡 中 | 2h | ⏳ 待辦 |
| AKO-SEC-05 | Key 強度檢查 | 驗證建立的 Key 符合強度要求 | 🟢 低 | 1h | ⏳ 待辦 |
### AKO-SEC-01 細節
```rust
use subtle::ConstantTimeEq;
// Before
if stored_hash == computed_hash {
// valid
}
// After
if bool::from(stored_hash.as_bytes().ct_eq(computed_hash.as_bytes())) {
// valid
}
```
### AKO-SEC-02 細節
```rust
use moka::future::Cache;
pub struct RateLimiter {
attempts: Cache<String, AttemptInfo>,
max_attempts: u32,
window_seconds: u64,
}
pub struct AttemptInfo {
pub count: u32,
pub first_attempt: chrono::DateTime<chrono::Utc>,
pub locked_until: Option<chrono::DateTime<chrono::Utc>>,
}
impl RateLimiter {
pub async fn check(&self, identifier: &str) -> Result<()> {
if let Some(info) = self.attempts.get(identifier).await {
if let Some(locked_until) = info.locked_until {
if chrono::Utc::now() < locked_until {
anyhow::bail!("Account locked until {}", locked_until);
}
}
}
Ok(())
}
pub async fn record_failure(&self, identifier: &str) -> Result<()> {
let mut info = self.attempts.get(identifier).await
.unwrap_or(AttemptInfo {
count: 0,
first_attempt: chrono::Utc::now(),
locked_until: None,
});
info.count += 1;
if info.count >= self.max_attempts {
info.locked_until = Some(
chrono::Utc::now() + chrono::Duration::seconds(self.window_seconds as i64)
);
}
self.attempts.insert(identifier.to_string(), info).await;
Ok(())
}
pub async fn record_success(&self, identifier: &str) {
self.attempts.invalidate(identifier).await;
}
}
```
---
## Phase 4: 功能增強 (FEAT)
| 編碼 | 任務 | 描述 | 優先級 | 預估工時 | 狀態 |
|------|------|------|--------|----------|------|
| AKO-FEAT-01 | 批量建立 Key | 支援 JSON 檔案批量匯入 | 🟡 中 | 3h | ⏳ 待辦 |
| AKO-FEAT-02 | 批量撤銷 Key | 支援條件式批量撤銷 | 🟡 中 | 2h | ⏳ 待辦 |
| AKO-FEAT-03 | Key 匯出 | 匯出 Key 列表(不含明文) | 🟢 低 | 1.5h | ⏳ 待辦 |
| AKO-FEAT-04 | Key 匯入 | 匯入 Key 元數據 | 🟢 低 | 1.5h | ⏳ 待辦 |
| AKO-FEAT-05 | Webhook 通知 | 異常發生時發送 Webhook | 🟡 中 | 3h | ⏳ 待辦 |
| AKO-FEAT-06 | Email 通知 | Key 到期前提醒 | 🟢 低 | 4h | ⏳ 待辦 |
| AKO-FEAT-07 | 統計報表 | 生成使用統計報表 | 🟢 低 | 2h | ⏳ 待辦 |
| AKO-FEAT-08 | 清理過期記錄 | 自動清理過期的 Key 記錄 | 🟢 低 | 1h | ⏳ 待辦 |
### AKO-FEAT-01 細節
```json
// keys.json
{
"keys": [
{
"name": "ci-service-1",
"key_type": "service",
"permissions": ["read", "write"],
"ttl_days": 90
},
{
"name": "ci-service-2",
"key_type": "service",
"permissions": ["read"],
"ttl_days": 180
}
]
}
```
```bash
momentry api-key batch-create --file keys.json
```
### AKO-FEAT-05 細節
```rust
pub struct WebhookConfig {
pub url: String,
pub secret: String,
pub events: Vec<WebhookEvent>,
}
pub enum WebhookEvent {
KeyCreated,
KeyRevoked,
KeyExpired,
AnomalyDetected,
RotationRequired,
}
pub struct WebhookNotifier {
client: Client,
config: WebhookConfig,
}
impl WebhookNotifier {
pub async fn notify(&self, event: WebhookEvent, payload: serde_json::Value) -> Result<()> {
if !self.config.events.contains(&event) {
return Ok(());
}
let signature = self.sign(&payload);
self.client.post(&self.config.url)
.header("X-Webhook-Signature", signature)
.json(&serde_json::json!({
"event": event,
"timestamp": chrono::Utc::now(),
"payload": payload,
}))
.send()
.await?;
Ok(())
}
}
```
---
## Phase 5: 文件 (DOC)
| 編碼 | 任務 | 描述 | 優先級 | 預估工時 | 狀態 |
|------|------|------|--------|----------|------|
| AKO-DOC-01 | API 文件自動生成 | 使用 `utoipa` 生成 OpenAPI | 🟢 低 | 3h | ⏳ 待辦 |
| AKO-DOC-02 | CHANGELOG.md | 建立變更日誌 | 🟢 低 | 1h | ⏳ 待辦 |
| AKO-DOC-03 | 架構圖 | 添加系統架構圖 | 🟢 低 | 2h | ⏳ 待辦 |
| AKO-DOC-04 | 整合測試文件 | 記錄整合測試流程 | 🟢 低 | 1h | ⏳ 待辦 |
---
## 總工時估算
| Phase | 工時 | 任務數 |
|-------|------|--------|
| CODE | 6.5h | 4 |
| PERF | 5.5h | 4 |
| SEC | 7h | 5 |
| FEAT | 18h | 8 |
| DOC | 7h | 4 |
| **總計** | **44h** | **25** |
---
## 環境變數
```bash
# 效能
DB_MAX_CONNECTIONS=10
CACHE_TTL_SECONDS=300
CACHE_MAX_CAPACITY=10000
# 安全
RATE_LIMIT_MAX_ATTEMPTS=5
RATE_LIMIT_WINDOW_SECONDS=900
# 通知
WEBHOOK_URL=https://example.com/webhook
WEBHOOK_SECRET=your-secret
```
---
## 參考文件
- `docs/API_KEY_MANAGEMENT.md` - API Key 管理系統設計
- `docs/PENDING_ISSUES.md` - 待解決問題追蹤
- `src/core/api_key/` - API Key 模組

View File

@@ -1,222 +0,0 @@
# n8n 呼叫 Momentry API 指南
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-23 |
| 文件版本 | V1.1 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-23 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
| V1.1 | 2026-03-26 | 新增 API Key 驗證說明,更新 HTTP Request Node 設定 | OpenCode | deepseek-reasoner |
---
**用途**: 在 n8n workflow 中呼叫 Momentry API
---
## API URL
在 n8n HTTP Request Node 中,**請使用外部 URL**
```
https://api.momentry.ddns.net
```
> ⚠️ **不要使用** `localhost:3002`,因為 n8n 需要從外部訪問 API。
---
## 常用端點
| 方法 | 端點 | 說明 |
|------|------|------|
| GET | `/health` | 健康檢查 |
| POST | `/api/v1/n8n/search` | 語意搜尋(推薦) |
| GET | `/api/v1/videos` | 列出所有影片 |
| GET | `/api/v1/lookup` | 查詢影片 |
| GET | `/api/v1/progress/:uuid` | 處理進度 |
| GET | `/api/v1/jobs` | 任務列表 |
| GET | `/api/v1/jobs/:uuid` | 任務詳情 |
---
## HTTP Request Node 設定
### 語意搜尋(推薦)
```
Node: HTTP Request
├── URL: https://api.momentry.ddns.net/api/v1/n8n/search
├── Method: POST
├── Authentication: None
├── Send Body: ✓ (checked)
├── Content Type: JSON
├── Body:
│ {
│ "query": "={{ $json.query }}",
│ "limit": "={{ $json.limit || 10 }}"
│ }
├── Send Headers: ✓ (checked)
└── Header Parameters:
└── X-API-Key: {{ $env.MOMENTRY_API_KEY }}
```
### 測試用(固定關鍵字)
```
Node: HTTP Request
├── URL: https://api.momentry.ddns.net/api/v1/n8n/search
├── Method: POST
├── Send Body: ✓
├── Content Type: JSON
├── Body:
│ {
│ "query": "charade",
│ "limit": 3
│ }
├── Send Headers: ✓ (checked)
└── Header Parameters:
└── X-API-Key: {{ $env.MOMENTRY_API_KEY }}
```
---
## 完整 Workflow 範例
### 基本搜尋 Workflow
```json
{
"name": "Momentry Video Search",
"nodes": [
{
"parameters": {},
"name": "Manual Trigger",
"type": "n8n-nodes-base.manualTrigger",
"position": [250, 300]
},
{
"parameters": {
"url": "https://api.momentry.ddns.net/api/v1/n8n/search",
"method": "POST",
"sendBody": true,
"contentType": "json",
"body": {
"query": "charade",
"limit": 3
}
},
"name": "Search Video API",
"type": "n8n-nodes-base.httpRequest",
"position": [450, 300]
}
],
"connections": {
"Manual Trigger": {
"main": [[{"node": "Search Video API"}]]
}
}
}
```
---
## 動態查詢 Workflow
```json
{
"name": "Momentry Dynamic Search",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "search",
"responseMode": "lastNode"
},
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"position": [250, 300]
},
{
"parameters": {
"url": "https://api.momentry.ddns.net/api/v1/n8n/search",
"method": "POST",
"sendBody": true,
"contentType": "json",
"body": {
"query": "={{ JSON.stringify($json.body.query) }}",
"limit": "={{ $json.body.limit || 5 }}"
}
},
"name": "Search API",
"type": "n8n-nodes-base.httpRequest",
"position": [450, 300]
}
],
"connections": {
"Webhook": {
"main": [[{"node": "Search API"}]]
}
}
}
```
---
## 常見錯誤
### 錯誤: 502 Bad Gateway
**原因**: API 服務未啟動
**解決**:
```bash
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
```
### 錯誤: "Your request is invalid"
**原因**: Body 格式設定錯誤
**正確設定**:
- `Content Type`: JSON
- `Body`: 必須是有效的 JSON 物件
---
## curl 測試
在終端機中測試 API
> **注意**: 所有 `/api/v1/*` 端點都需要 API Key 驗證。請設定環境變數或直接替換 API Key。
```bash
# 設定環境變數(使用您的 API Key
export MOMENTRY_API_KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
```
```bash
# 健康檢查
curl https://api.momentry.ddns.net/health
# 搜尋測試 (需要 API Key)
curl -X POST https://api.momentry.ddns.net/api/v1/n8n/search \
-H "Content-Type: application/json" \
-H "X-API-Key: $MOMENTRY_API_KEY" \
-d '{"query":"charade","limit":3}'
```
---
## 相關文件
- [API_INDEX.md](./API_INDEX.md) - 文件總覽
- [API_ENDPOINTS.md](./API_ENDPOINTS.md) - 端點完整說明
- [N8N_HTTP_REQUEST_GUIDE.md](./N8N_HTTP_REQUEST_GUIDE.md) - HTTP Request 詳細設定

View File

@@ -1,532 +0,0 @@
# Momentry Core API 快速查詢表
| 版本 | 日期 | 建立者 |
|------|------|--------|
| V1.0 | 2026-03-26 | OpenCode |
---
## 📋 快速導覽
| 類別 | 端點數量 | 說明 |
|------|----------|------|
| 健康檢查 | 2 | 系統狀態監控 |
| 影片管理 | 5 | 影片註冊、查詢、刪除 |
| 搜尋功能 | 3 | 語意搜尋、混合搜尋 |
| 任務管理 | 2 | 處理任務狀態查詢 |
| 系統管理 | 2 | 快取設定、進度查詢 |
---
## 🔐 認證
所有 `/api/v1/*` 端點需要 `X-API-Key` header
```bash
curl -H "X-API-Key: YOUR_API_KEY" ...
```
**公開端點(無需認證):**
- `GET /health`
- `GET /health/detailed`
---
## 📊 端點總表
### 健康檢查
| 方法 | 端點 | 認證 | 描述 |
|------|------|------|------|
| GET | `/health` | 公開 | 基本健康檢查 |
| GET | `/health/detailed` | 公開 | 詳細健康檢查(包含所有服務狀態) |
### 影片管理
| 方法 | 端點 | 認證 | 描述 |
|------|------|------|------|
| POST | `/api/v1/register` | 需要 | 註冊影片並開始處理 |
| POST | `/api/v1/unregister` | 需要 | 刪除影片及其所有資料 |
| POST | `/api/v1/probe` | 需要 | 探測影片資訊(不註冊) |
| GET | `/api/v1/videos` | 需要 | 列出所有已註冊影片 |
| GET | `/api/v1/lookup` | 需要 | 查詢影片資訊 |
### 搜尋功能
| 方法 | 端點 | 認證 | 描述 |
|------|------|------|------|
| POST | `/api/v1/search` | 需要 | 語意搜尋(標準格式) |
| POST | `/api/v1/n8n/search` | 需要 | 語意搜尋n8n 格式) |
| POST | `/api/v1/search/hybrid` | 需要 | 混合搜尋(向量 + 關鍵字) |
### 任務管理
| 方法 | 端點 | 認證 | 描述 |
|------|------|------|------|
| GET | `/api/v1/jobs` | 需要 | 列出所有處理任務 |
| GET | `/api/v1/jobs/:uuid` | 需要 | 取得特定任務詳情 |
### 系統管理
| 方法 | 端點 | 認證 | 描述 |
|------|------|------|------|
| GET | `/api/v1/progress/:uuid` | 需要 | 取得影片處理進度 |
| POST | `/api/v1/config/cache` | 需要 | 切換快取功能 |
---
## 🔧 詳細端點說明
### 1. 健康檢查
#### GET /health
**基本健康檢查**
```bash
curl http://localhost:3002/health
```
**回應:**
```json
{
"status": "ok",
"version": "0.1.0",
"uptime_ms": 14426558
}
```
#### GET /health/detailed
**詳細健康檢查**
```bash
curl http://localhost:3002/health/detailed
```
**回應:**
```json
{
"status": "ok",
"version": "0.1.0",
"uptime_ms": 14441362,
"services": {
"postgres": {"status": "ok", "latency_ms": 50, "error": null},
"redis": {"status": "ok", "latency_ms": 0, "error": null},
"qdrant": {"status": "ok", "latency_ms": 2, "error": null},
"mongodb": {"status": "ok", "latency_ms": 2, "error": null}
}
}
```
### 2. 影片管理
#### POST /api/v1/register
**註冊影片並開始處理**
```bash
curl -X POST http://localhost:3002/api/v1/register \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"path": "/path/to/video.mp4"}'
```
**請求:**
```json
{
"path": "/path/to/video.mp4"
}
```
**回應:**
```json
{
"uuid": "5dea6618a606e7c7",
"video_id": 10,
"job_id": 1,
"file_name": "video.mp4",
"duration": 596.458333,
"width": 320,
"height": 180,
"already_exists": false
}
```
#### POST /api/v1/unregister
**刪除影片及其所有資料**
```bash
curl -X POST http://localhost:3002/api/v1/unregister \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"uuid": "5dea6618a606e7c7"}'
```
**請求:**
```json
{
"uuid": "5dea6618a606e7c7"
}
```
**回應:**
```json
{
"success": true,
"uuid": "5dea6618a606e7c7",
"message": "Video unregistered successfully"
}
```
#### POST /api/v1/probe
**探測影片資訊(不註冊)**
```bash
curl -X POST http://localhost:3002/api/v1/probe \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"path": "/path/to/video.mp4"}'
```
**請求:**
```json
{
"path": "/path/to/video.mp4"
}
```
**回應:**
```json
{
"uuid": "5dea6618a606e7c7",
"file_name": "video.mp4",
"duration": 596.458333,
"width": 320,
"height": 180,
"fps": 24.0,
"cached": true,
"format": {...},
"streams": [...]
}
```
#### GET /api/v1/videos
**列出所有已註冊影片**
```bash
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos
```
**回應:**
```json
{
"videos": [
{
"uuid": "a03485a40b2df2d3",
"file_path": "/path/to/video.mp4",
"file_name": "video.mp4",
"duration": 596.458333,
"width": 320,
"height": 180
}
]
}
```
#### GET /api/v1/lookup
**查詢影片資訊**
```bash
# 依 UUID 查詢
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3002/api/v1/lookup?uuid=a03485a40b2df2d3"
# 依路徑查詢
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3002/api/v1/lookup?path=/path/to/video.mp4"
```
**回應:**
```json
{
"uuid": "a03485a40b2df2d3",
"file_path": "/path/to/video.mp4",
"file_name": "video.mp4",
"duration": 596.458333
}
```
### 3. 搜尋功能
#### POST /api/v1/search
**語意搜尋(標準格式)**
```bash
curl -X POST http://localhost:3002/api/v1/search \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"query": "search term", "limit": 5}'
```
**請求:**
```json
{
"query": "search term",
"limit": 5
}
```
**回應:**
```json
{
"results": [
{
"uuid": "a1b10138a6bbb0cd",
"chunk_id": "sentence_0001",
"chunk_type": "sentence",
"start_time": 10.5,
"end_time": 15.2,
"text": "Found text matching query",
"score": 0.85
}
],
"query": "search term"
}
```
#### POST /api/v1/n8n/search
**語意搜尋n8n 格式)**
```bash
curl -X POST http://localhost:3002/api/v1/n8n/search \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"query": "search term", "limit": 5}'
```
**回應:**
```json
{
"query": "search term",
"count": 1,
"hits": [
{
"id": "sentence_0001",
"vid": "a1b10138a6bbb0cd",
"start_time": 10.5,
"end_time": 15.2,
"title": "Chunk sentence_0001",
"text": "Found text matching query",
"score": 0.85,
"file_path": "/path/to/video.mp4"
}
]
}
```
#### POST /api/v1/search/hybrid
**混合搜尋(向量 + 關鍵字)**
```bash
curl -X POST http://localhost:3002/api/v1/search/hybrid \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"query": "search term", "limit": 5}'
```
**請求:**
```json
{
"query": "search term",
"limit": 5
}
```
**回應:**`/api/v1/search` 相同格式
### 4. 任務管理
#### GET /api/v1/jobs
**列出所有處理任務**
```bash
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/jobs
```
**回應:**
```json
{
"jobs": [
{
"id": 10,
"uuid": "a03485a40b2df2d3",
"status": "running",
"current_processor": null,
"progress_current": 0,
"progress_total": 0,
"created_at": "2026-03-26 13:39:37.830465",
"started_at": null
}
]
}
```
#### GET /api/v1/jobs/:uuid
**取得特定任務詳情**
```bash
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/jobs/a03485a40b2df2d3
```
**回應:**
```json
{
"id": 10,
"uuid": "a03485a40b2df2d3",
"status": "running",
"current_processor": null,
"progress_current": 0,
"progress_total": 0,
"processors": [
{
"processor_type": "asr",
"status": "completed",
"started_at": "2026-03-26 05:39:40.275468",
"completed_at": "2026-03-26 07:19:43.166613",
"duration_secs": 6002.891145,
"error_message": null
},
// ... 其他處理器
],
"created_at": "2026-03-26 13:39:37.830465",
"started_at": null,
"updated_at": "2026-03-26 07:19:16.338406"
}
```
### 5. 系統管理
#### GET /api/v1/progress/:uuid
**取得影片處理進度**
```bash
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/progress/a03485a40b2df2d3
```
**回應:**
```json
{
"uuid": "a03485a40b2df2d3",
"user": null,
"group": null,
"file_name": "video.mp4",
"duration": 596.458333,
"overall_progress": 0,
"cpu_percent": 0.2,
"gpu_percent": null,
"memory_percent": 0.1,
"memory_mb": 16720,
"processors": [
{
"name": "asr",
"status": "pending",
"current": 0,
"total": 0,
"progress": 0,
"message": ""
},
// ... 其他處理器
]
}
```
#### POST /api/v1/config/cache
**切換快取功能**
```bash
curl -X POST http://localhost:3002/api/v1/config/cache \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"enabled": true}'
```
**請求:**
```json
{
"enabled": true
}
```
**回應:**
```json
{
"success": true,
"cache_enabled": true,
"message": "Cache enabled"
}
```
---
## 🚀 快速工作流程
### 1. 註冊並處理影片
```bash
# 1. 註冊影片
curl -X POST http://localhost:3002/api/v1/register \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"path": "/path/to/video.mp4"}'
# 回應包含 UUID: 5dea6618a606e7c7
# 2. 監控進度
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/progress/5dea6618a606e7c7
# 3. 查看任務狀態
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/jobs/5dea6618a606e7c7
```
### 2. 搜尋影片內容
```bash
# 1. 列出所有影片
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos
# 2. 搜尋內容
curl -X POST http://localhost:3002/api/v1/n8n/search \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"query": "charade scene", "limit": 10}'
```
### 3. 系統管理
```bash
# 1. 檢查系統健康
curl http://localhost:3002/health/detailed
# 2. 管理快取
curl -X POST http://localhost:3002/api/v1/config/cache \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"enabled": false}'
# 3. 刪除影片(需要時)
curl -X POST http://localhost:3002/api/v1/unregister \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"uuid": "5dea6618a606e7c7"}'
```
---
## 📝 注意事項
1. **API Key 格式:**
- 使用完整 API Key`muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69`
- 系統存儲的是 SHA256 哈希值
2. **路徑格式:**
- 絕對路徑:`/Users/accusys/test_video/video.mp4`
- 相對路徑:`./demo/video.mp4`(相對於 SFTPGo 資料目錄)
3. **回應時間:**
- 健康檢查:< 100ms
- 搜尋:取決於查詢複雜度,通常 100-500ms
- 影片註冊:立即返回,背景處理可能需要數分鐘到數小時
4. **錯誤處理:**
- 401: 認證失敗
- 404: 資源不存在
- 500: 伺服器內部錯誤
---
## 🔗 相關文件
- [API 參考指南](./API_REFERENCE.md) - 詳細 API 說明
- [API 範例總覽](./API_EXAMPLES.md) - 完整使用範例
- [API 端點列表](./API_ENDPOINTS.md) - 端點簡介
- [Curl 範例指南](./API_CURL_EXAMPLES.md) - curl 命令範例
- [n8n 整合指南](./API_N8N_GUIDE.md) - n8n 工作流程整合

View File

@@ -1,528 +0,0 @@
# Momentry Core API 安裝指南
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-18 |
| 文件版本 | V1.3 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-18 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
| V1.1 | 2026-03-23 | 更新端點與實際一致 | OpenCode | - |
| V1.2 | 2026-03-25 | 新增快取/刪除 API | OpenCode | - |
| V1.3 | 2026-03-26 | 修正認證聲明與API回應格式 | OpenCode | - |
---
## Base URL
| 環境 | URL | 說明 |
|------|-----|------|
| **本地開發** | `http://localhost:3002` | 直接訪問 API繞過反向代理 |
| **外部訪問** | `https://api.momentry.ddns.net` | 通過 Caddy 反向代理訪問,需網路可達 |
> **Note:** Port 3000 is used by Gitea. Momentry API server runs on **port 3002**.
### URL 使用時機
| 情境 | 建議 URL |
|------|----------|
| 本地開發/測試 | `http://localhost:3002` |
| n8n workflow | `https://api.momentry.ddns.net` |
| 外部系統整合 | `https://api.momentry.ddns.net` |
| 反向代理有問題時 | `http://localhost:3002` (繞過代理) |
## Authentication
**API Key 認證:**
所有 `/api/v1/*` 端點需要 `X-API-Key` header 進行認證。
**公開端點:**
- `GET /health` - 健康檢查
- `GET /health/detailed` - 詳細健康檢查
**認證格式:**
```bash
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos
```
**API Key 管理:**
- 使用 `/api/v1/api-keys` 端點管理 API Keys
- 詳細說明請參考 [API Key Management Guide](../docs/API_KEY_MANAGEMENT.md)
---
## Endpoints
### 1. Register Video
Register a video file to the system.
**Endpoint:** `POST /api/v1/register`
**Request Body:**
```json
{
"path": "/path/to/video.mp4"
}
```
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `path` | string | Yes | Absolute path to video file |
**Response (200):**
```json
{
"uuid": "5dea6618a606e7c7",
"video_id": 1,
"job_id": 10,
"file_name": "video.mp4",
"duration": 120.5,
"width": 1920,
"height": 1080,
"already_exists": false
}
```
**Example:**
```bash
curl -X POST http://localhost:3002/api/v1/register \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"path": "/Users/accusys/test_video/BigBuckBunny_320x180.mp4"}'
```
---
### 2. Process Video (CLI)
Process video to generate ASR, CUT, YOLO, OCR, Face, Pose data.
**Note:** This is a CLI command, not an HTTP endpoint.
```bash
# Process video by UUID
cargo run --bin momentry -- process 5dea6618a606e7c7
# Or process by file path
cargo run --bin momentry -- process /path/to/video.mp4
```
---
### 3. Get Progress
Get real-time processing progress via Redis.
**Endpoint:** `GET /api/v1/progress/:uuid`
| Parameter | Type | Description |
|-----------|------|-------------|
| `uuid` | path | Video UUID (16 characters) |
**Response (200):**
```json
{
"uuid": "5dea6618a606e7c7",
"processors": [
{
"name": "asr",
"status": "complete",
"current": 0,
"total": 0,
"message": "7 segments"
},
{
"name": "cut",
"status": "complete",
"current": 134,
"total": 134,
"message": "134 scenes"
},
{
"name": "yolo",
"status": "progress",
"current": 5000,
"total": 14315,
"message": "frame 5000"
},
{
"name": "ocr",
"status": "pending",
"current": 0,
"total": 0,
"message": ""
}
]
}
```
**Processor Status Values:**
- `pending` - Not started
- `info` - Starting/info message
- `progress` - In progress
- `complete` - Finished
- `error` - Failed
**Example:**
```bash
# Get progress for specific video
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/progress/5dea6618a606e7c7
```
---
### 4. Natural Language Search
Search video chunks using natural language queries (RAG).
**Endpoint:** `POST /api/v1/search`
**Request Body:**
```json
{
"query": "What is the person saying about machine learning?",
"limit": 10,
"uuid": "5dea6618a606e7c7"
}
```
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `query` | string | Yes | Natural language search query |
| `limit` | integer | No | Max results (default: 10) |
| `uuid` | string | No | Filter by specific video UUID |
**Response (200):**
```json
{
"results": [
{
"uuid": "5dea6618a606e7c7",
"chunk_id": "0",
"chunk_type": "sentence",
"start_time": 5.5,
"end_time": 8.2,
"text": "Machine learning is a subset of artificial intelligence...",
"score": 0.85
}
],
"query": "What is the person saying about machine learning?"
}
```
**Example:**
```bash
curl -X POST http://localhost:3002/api/v1/search \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"query": "machine learning", "limit": 5}'
```
---
### 4a. N8N Search (n8n Workflow Integration)
N8n-compatible search endpoint with standardized response format for direct workflow integration.
**Endpoint:** `POST /api/v1/n8n/search`
**Request Body:**
```json
{
"query": "sunset",
"limit": 10,
"uuid": "5dea6618a606e7c7"
}
```
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `query` | string | Yes | Natural language search query |
| `limit` | integer | No | Max results (default: 10) |
| `uuid` | string | No | Filter by specific video UUID |
**Response (200):**
```json
{
"query": "sunset",
"count": 2,
"hits": [
{
"id": "c_001",
"vid": "5dea6618a606e7c7",
"start": 5.5,
"end": 8.2,
"title": "Sunset Scene",
"text": "The sun slowly sets over the ocean...",
"score": 0.92,
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
}
]
}
```
| Field | Type | Description |
|-------|------|-------------|
| `query` | string | Original search query |
| `count` | integer | Number of results |
| `hits[].id` | string | Chunk ID |
| `hits[].vid` | string | Video UUID |
| `hits[].start` | number | Start time in seconds |
| `hits[].end` | number | End time in seconds |
| `hits[].title` | string | Chunk title (from metadata or auto-generated) |
| `hits[].text` | string | Text content |
| `hits[].score` | number | Relevance score (0-1) |
| `hits[].file_path` | string | Full file path to video file |
**Example:**
```bash
curl -X POST http://localhost:3002/api/v1/n8n/search \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"query": "sunset", "limit": 5}'
```
**Environment Variables:**
| Variable | Default | Description |
|----------|---------|-------------|
| `MOMENTRY_MEDIA_BASE_URL` | `https://wp.momentry.ddns.net` | Base URL for constructing media URLs |
---
### 5. Lookup Video
Lookup video UUID by path or get video details by UUID.
**Endpoint:** `GET /api/v1/lookup`
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `path` | query | No* | Video file path |
| `uuid` | query | No* | Video UUID |
*One of `path` or `uuid` is required.
**Response (200):**
```json
{
"uuid": "5dea6618a606e7c7",
"file_path": "/path/to/video.mp4",
"file_name": "video.mp4",
"duration": 120.5
}
```
**Example:**
```bash
# Lookup by path
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3002/api/v1/lookup?path=/path/to/video.mp4"
# Lookup by UUID
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3002/api/v1/lookup?uuid=5dea6618a606e7c7"
```
---
### 6. List Videos
List all registered videos.
**Endpoint:** `GET /api/v1/videos`
**Response (200):**
```json
{
"videos": [
{
"uuid": "5dea6618a606e7c7",
"file_path": "/path/to/video.mp4",
"file_name": "video.mp4",
"duration": 120.5,
"width": 1920,
"height": 1080
}
]
}
```
**Example:**
```bash
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos
```
---
## Data Flow
```
┌─────────────────────────────────────────────────────────────────────┐
│ 完整工作流程 │
└─────────────────────────────────────────────────────────────────────┘
1. Register Video
POST /api/v1/register
└── UUID: 5dea6618a606e7c7
2. Process Video (CLI)
cargo run -- process 5dea6618a606e7c7
├── ASR (WhisperX) → 7 segments
├── CUT (PySceneDetect) → 134 scenes
├── YOLO (YOLOv8) → 10483 frames with objects
├── OCR (EasyOCR) → 40 frames with text
├── Face (OpenCV) → 44 frames with faces
└── Pose (YOLOv8-Pose) → 14315 frames
3. Monitor Progress (Real-time)
GET /api/v1/progress/:uuid
└── Redis Pub/Sub + Hash
4. Chunk (CLI)
cargo run -- chunk 5dea6618a606e7c7
└── Create chunks in database
5. Vectorize (CLI)
cargo run -- vectorize 5dea6618a606e7c7
└── Generate embeddings in Qdrant
6. Search (API)
POST /api/v1/search
└── Natural language query
```
---
## Processor Reference
| Processor | Model | Description |
|-----------|-------|-------------|
| **ASR** | WhisperX (faster-whisper) | Speech recognition + diarization |
| **CUT** | PySceneDetect | Scene detection/segmentation |
| **ASRX** | WhisperX | Speaker diarization |
| **YOLO** | YOLOv8n | Object detection |
| **OCR** | EasyOCR | Text recognition |
| **Face** | OpenCV Haar Cascade | Face detection |
| **Pose** | YOLOv8n-Pose | Pose estimation |
---
## Cache Toggle
Toggle caching at runtime.
**Endpoint:** `POST /api/v1/config/cache`
**Request Body:**
```json
{
"enabled": true
}
```
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `enabled` | boolean | Yes | Enable (true) or disable (false) cache |
**Response (200):**
```json
{
"cache_enabled": true,
"message": "Cache toggled successfully"
}
```
---
## Unregister Video
Delete a video and all associated data (chunks, processor results, thumbnails).
**Endpoint:** `POST /api/v1/unregister`
**Request Body:**
```json
{
"uuid": "5dea6618a606e7c7"
}
```
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `uuid` | string | Yes | Video UUID (16 character hex) |
**Response (200):**
```json
{
"success": true,
"message": "Video unregistered successfully",
"uuid": "5dea6618a606e7c7"
}
```
**Warning:** This operation is irreversible and will delete all associated chunks, processor results, and thumbnails.
---
## Error Responses
**400 Bad Request**
```json
{
"error": "Invalid request body"
}
```
**404 Not Found**
```json
{
"error": "Resource not found"
}
```
**500 Internal Server Error**
```json
{
"error": "Internal server error"
}
```
---
## Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| `DATABASE_URL` | `postgres://accusys@localhost:5432/momentry` | PostgreSQL connection |
| `REDIS_URL` | `redis://localhost:6379` | Redis connection |
| `REDIS_PASSWORD` | `accusys` | Redis password |
| `QDRANT_URL` | `http://localhost:6333` | Qdrant vector DB URL |
| `QDRANT_API_KEY` | - | Qdrant API key |
| `QDRANT_COLLECTION` | `chunks` | Qdrant collection name |
| `MOMENTRY_MEDIA_BASE_URL` | `https://wp.momentry.ddns.net` | Base URL for n8n search media URLs |
---
## Starting the Server
```bash
# Default (port 3002, since 3000 is Gitea)
cargo run --bin momentry -- server
# Custom host and port
cargo run --bin momentry -- server --host 127.0.0.1 --port 3002
```
---
## Quick Reference
| Task | Command |
|------|---------|
| Register video | `POST /api/v1/register` |
| Process video | `cargo run -- process <uuid>` |
| Check progress | `GET /api/v1/progress/<uuid>` |
| Search | `POST /api/v1/search` |
| List videos | `GET /api/v1/videos` |
| Lookup | `GET /api/v1/lookup?uuid=<uuid>` |
| Toggle cache | `POST /api/v1/config/cache` |
| Delete video | `POST /api/v1/unregister` |

View File

@@ -1,391 +0,0 @@
# Momentry Core API 教育訓練手冊
> **對象**: marcom 團隊
> **版本**: V1.4 | **日期**: 2026-03-25
---
## 1. 快速開始
### 基本資訊
| 項目 | 值 |
|------|-----|
| API 網址 | `https://api.momentry.ddns.net` |
| 認證方式 | Header `X-API-Key` |
| 格式 | JSON |
### Demo 測試帳號
#### API Key用於 API 認證)
```
X-API-Key: muser_68600856036340bcafc01930eb4bd839
```
#### SFTPGo用於影片上傳
| 項目 | 值 |
|------|-----|
| SFTP 主機 | `sftpgo.momentry.ddns.net` |
| SFTP 連接埠 | `2022` |
| 用戶名 | `demo` |
| 密碼 | `demopassword123` |
| Web 管理介面 | `https://sftpgo.momentry.ddns.net` |
**使用方式**:透過 SFTP 上傳影片,系統會自動註冊並處理。
---
## 2. 快速範例
### 查詢所有影片
```bash
curl -s -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
"https://api.momentry.ddns.net/api/v1/videos"
```
### 查詢單一影片
```bash
curl -s -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
"https://api.momentry.ddns.net/api/v1/videos/{uuid}"
```
### 查詢處理任務狀態
```bash
curl -s -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
"https://api.momentry.ddns.net/api/v1/jobs/{uuid}"
```
---
## 3. API 端點說明
### 3.1 影片相關
#### GET /api/v1/videos
取得所有影片列表
**回應範例**:
```json
{
"videos": [
{
"uuid": "5dea6618a606e7c7",
"filename": "demo_video.mp4",
"duration": 123.45,
"status": "ready",
"created_at": "2026-03-25T10:00:00Z"
}
]
}
```
#### GET /api/v1/videos/:uuid
取得單一影片詳情
### 3.2 搜尋與分段查詢
#### POST /api/v1/search
向量搜尋查詢分段Chunk詳情
**請求參數**:
| 參數 | 類型 | 必填 | 說明 |
|------|------|------|------|
| `query` | string | 是 | 搜尋關鍵字 |
| `limit` | number | 否 | 回傳數量(預設 10 |
| `uuid` | string | 否 | 只搜尋指定影片 |
**請求範例**:
```json
{
"query": "天氣",
"limit": 10,
"uuid": "5dea6618a606e7c7"
}
```
**回應範例**:
```json
{
"results": [
{
"uuid": "39567a0eb16f39fd",
"chunk_id": "sentence_1471",
"chunk_type": "sentence",
"start_time": 5309.08,
"end_time": 5311.08,
"text": "influenced by a vital way,",
"score": 0.68
}
],
"query": "天氣"
}
```
**Chunk 欄位說明**:
| 欄位 | 說明 | 範例 |
|------|------|------|
| `uuid` | 影片唯一識別碼 | `39567a0eb16f39fd` |
| `chunk_id` | 分段識別碼 | `sentence_1471` |
| `chunk_type` | 分段類型 | `sentence` / `cut` / `time` / `trace` / `story` |
| `start_time` | 開始時間(秒) | `5309.08` |
| `end_time` | 結束時間(秒) | `5311.08` |
| `text` | 內容文字 | `influenced by a vital way` |
| `score` | 相似度分數0-1 | `0.68` |
**Chunk 類型說明**:
| 類型 | 說明 | 來源 |
|------|------|------|
| `sentence` | 語音轉文字片段 | ASR 處理 |
| `cut` | 場景變化片段 | CUT 處理 |
| `time` | 固定時間分段 | 系統自動切割 |
| `trace` | 軌跡追蹤片段 | YOLO 追蹤 |
| `story` | 故事線片段(父子關係) | Story 分析 |
#### POST /api/v1/n8n/search
n8n 專用搜尋(包含完整影片檔案路徑 file_path
**請求參數**: 與 `/search` 相同
**回應範例**:
```json
{
"query": "天氣",
"count": 2,
"hits": [
{
"id": "sentence_1471",
"vid": "39567a0eb16f39fd",
"chunk_type": "sentence",
"start_frame": 318545,
"end_frame": 318665,
"fps": 59.94,
"start_time": 5314.31,
"end_time": 5316.32,
"text": "influenced by a vital way,",
"score": 0.68
}
]
}
```
**與 /search 的差異**:
| 欄位 | `/search` | `/n8n/search` |
|------|-----------|----------------|
| 影片 UUID | `uuid` | `vid` |
| Chunk ID | `chunk_id` | `id` |
| 開始時間 | `start_time` | `start_time` |
| 結束時間 | `end_time` | `end_time` |
| 相似度分數 | `score` | `score` |
| **檔案路徑** | ❌ | ✅ `file_path` |
> **注意**: `file_path` 是影片的實際路徑,可用於本地播放。
### 3.3 任務相關
### 3.4 任務相關
#### GET /api/v1/jobs/:uuid
查詢處理任務狀態
**回應範例**:
```json
{
"uuid": "9760d0820f0cf9a7",
"video_uuid": "5dea6618a606e7c7",
"status": "completed",
"progress": 100,
"created_at": "2026-03-25T10:00:00Z",
"completed_at": "2026-03-25T10:05:00Z"
}
```
#### GET /api/v1/jobs
查詢所有任務
**查詢參數**:
| 參數 | 說明 | 範例 |
|------|------|------|
| `status` | 篩選狀態 | `pending`, `processing`, `completed`, `failed` |
| `limit` | 回傳數量 | `10` |
**範例**:
```bash
curl -s -H "X-API-Key: ..." \
"https://api.momentry.ddns.net/api/v1/jobs?status=completed&limit=5"
```
### 3.5 系統管理
#### POST /api/v1/config/cache
切換快取功能(管理員專用)
**請求範例**:
```json
{
"enabled": true
}
```
**回應範例**:
```json
{
"cache_enabled": true,
"message": "Cache toggled successfully"
}
```
#### POST /api/v1/unregister
刪除影片及其所有關聯資料(管理員專用)
**請求範例**:
```json
{
"uuid": "5dea6618a606e7c7"
}
```
**回應範例**:
```json
{
"success": true,
"message": "Video unregistered successfully",
"uuid": "5dea6618a606e7c7"
}
```
**注意**: 此操作會刪除影片及其所有分段、處理結果、縮圖等關聯資料,**無法復原**。
### 3.6 健康檢查
#### GET /health
服務健康狀態(**無需認證**
**回應**:
```json
{
"status": "ok",
"version": "0.9.20260325_144654"
}
```
---
## 4. n8n Workflow 範例
### 4.1 基本設定
在 n8n workflow 中使用 HTTP Request 節點:
```
┌─────────────────┐
│ HTTP Request │
├─────────────────┤
│ Method: GET │
│ URL: https://api.momentry.ddns.net/api/v1/videos
│ Headers: │
│ X-API-Key: │
│ [YOUR_KEY] │
└────────┬────────┘
┌─────────────────┐
│ 處理回應資料 │
└─────────────────┘
```
### 4.2 範例:檢查任務狀態
```javascript
// n8n Function Node 範例
const jobUuid = $input.item.json.uuid;
return [{
json: {
method: "GET",
url: `https://api.momentry.ddns.net/api/v1/jobs/${jobUuid}`,
headers: {
"X-API-Key": "YOUR_API_KEY"
}
}
}];
```
---
## 5. 常見問題
### Q: 返回 401 錯誤怎麼辦?
確認 Header 中有正確的 `X-API-Key`
### Q: 如何確認影片處理完成?
```
GET /api/v1/jobs/{uuid}
```
檢查 `status` 是否為 `completed`
### Q: 查不到資料?
確認 UUID 格式正確16碼 hex 字串)
---
## 6. 快速參考卡
```
┌─────────────────────────────────────────────────────────────┐
│ Momentry API 速查 │
├─────────────────────────────────────────────────────────────┤
│ 查詢所有影片 GET /api/v1/videos │
│ 查詢單一影片 GET /api/v1/videos/:uuid │
│ 向量搜尋 POST /api/v1/search │
│ n8n 搜尋 POST /api/v1/n8n/search │
│ 查詢任務狀態 GET /api/v1/jobs/:uuid │
│ 查詢所有任務 GET /api/v1/jobs │
│ 快取設定 POST /api/v1/config/cache (管理員) │
│ 刪除影片 POST /api/v1/unregister (管理員) │
│ 健康檢查 GET /health (免認證) │
├─────────────────────────────────────────────────────────────┤
│ Header: X-API-Key: [YOUR_KEY] │
│ URL: https://api.momentry.ddns.net │
└─────────────────────────────────────────────────────────────┘
```
---
## 附錄:回應狀態說明
### 任務狀態 (status)
| 狀態 | 說明 |
|------|------|
| `pending` | 等待處理 |
| `processing` | 處理中 |
| `completed` | 已完成 |
| `failed` | 處理失敗 |
### 影片狀態 (status)
| 狀態 | 說明 |
|------|------|
| `uploading` | 上傳中 |
| `pending` | 等待處理 |
| `processing` | 處理中 |
| `ready` | 已就緒 |
| `error` | 錯誤 |
---
## 附錄:版本歷史
| 版本 | 日期 | 內容 | 操作人 |
|------|------|------|--------|
| V1.0 | 2026-03-25 | 初版建立 | OpenCode |
| V1.1 | 2026-03-25 | 新增快取/刪除 API、搜尋端點文件 | OpenCode |
| V1.2 | 2026-03-25 | 新增 Chunk 欄位說明、類型、播放方式 | OpenCode |
| V1.3 | 2026-03-25 | 新增 Demo 測試帳號SFTPGo| OpenCode |
| V1.4 | 2026-03-25 | 更新 n8n 搜尋回傳欄位說明 (media_url→file_path) | OpenCode |
| V1.5 | 2026-04-17 | 修正 API Key 格式、統一 n8n/search 欄位名稱 (start/end → start_time/end_time) | OpenCode |

View File

@@ -1,325 +0,0 @@
# WordPress 呼叫 Momentry API 指南
| 項目 | 內容 |
|------|------|
| 版本 | V1.1 |
| 日期 | 2026-03-25 |
| 用途 | 在 WordPress 中呼叫 Momentry API |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.1 | 2026-03-25 | 更新: n8n 搜尋回傳 `file_path` 取代 `media_url`,新增 API Key 驗證說明 | OpenCode | deepseek-reasoner |
| V1.0 | 2026-03-23 | 創建 WordPress API 指南 | Warren | OpenCode / MiniMax M2.5 |
---
## API URL
在 WordPress 中呼叫 API**請使用外部 URL**
```
https://api.momentry.ddns.net
```
> ⚠️ WordPress 運行於瀏覽器端,無法直接訪問 `localhost`。
---
## API 認證
所有 `/api/v1/*` 端點(除了健康檢查)都需要 API Key 認證。請在請求標頭中加入:
```
'headers' => ['Content-Type' => 'application/json', 'X-API-Key' => 'YOUR_API_KEY']
```
**目前示範使用的 API Key**: `demo_api_key_12345`
> **注意**: 正式環境請使用安全的 API Key 管理機制,避免在客戶端 JavaScript 中暴露 API Key。
---
## 常用端點
| 方法 | 端點 | 說明 |
|------|------|------|
| GET | `/health` | 健康檢查 |
| POST | `/api/v1/search` | 語意搜尋(標準格式) |
| GET | `/api/v1/videos` | 列出所有影片 |
| GET | `/api/v1/lookup` | 查詢影片 |
---
## PHP 範例
### 基本搜尋
```php
<?php
$api_url = 'https://api.momentry.ddns.net/api/v1/n8n/search';
$data = [
'query' => 'charade',
'limit' => 10
];
$response = wp_remote_post($api_url, [
'headers' => ['Content-Type' => 'application/json', 'X-API-Key' => 'YOUR_API_KEY'],
'body' => json_encode($data),
'timeout' => 30
]);
if (is_wp_error($response)) {
echo '錯誤: ' . $response->get_error_message();
} else {
$body = json_decode(wp_remote_retrieve_body($response), true);
print_r($body['hits']);
}
?>
```
### 列出所有影片
```php
<?php
$api_url = 'https://api.momentry.ddns.net/api/v1/videos';
$response = wp_remote_get($api_url, [
'headers' => ['X-API-Key' => 'YOUR_API_KEY'],
'timeout' => 30
]);
if (!is_wp_error($response)) {
$body = json_decode(wp_remote_retrieve_body($response), true);
foreach ($body['videos'] as $video) {
echo $video['file_name'] . "\n";
}
}
?>
```
### 查詢特定影片
```php
<?php
$uuid = '5dea6618a606e7c7';
$api_url = 'https://api.momentry.ddns.net/api/v1/lookup?uuid=' . $uuid;
$response = wp_remote_get($api_url, [
'headers' => ['X-API-Key' => 'YOUR_API_KEY'],
'timeout' => 30
]);
if (!is_wp_error($response)) {
$video = json_decode(wp_remote_retrieve_body($response), true);
echo '檔案: ' . $video['file_name'] . "\n";
echo '時長: ' . $video['duration'] . ' 秒';
}
?>
```
---
## JavaScript 範例
### 使用 fetch
```javascript
// 搜尋影片
async function searchVideos(query, limit = 10) {
const response = await fetch('https://api.momentry.ddns.net/api/v1/n8n/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-API-Key': 'YOUR_API_KEY' },
body: JSON.stringify({ query, limit })
});
if (!response.ok) {
throw new Error('API 請求失敗');
}
return await response.json();
}
// 使用範例
searchVideos('charade', 5)
.then(data => {
data.hits.forEach(hit => {
console.log(`${hit.text} (score: ${hit.score})`);
});
});
```
---
## WordPress Shortcode 範例
`functions.php` 中註冊短碼:
```php
<?php
// 將文件路徑轉換為可訪問的 URL
function convert_file_path_to_url($file_path) {
// 範例: 將 SFTPGo 文件路徑轉換為 web URL
// /Users/accusys/momentry/var/sftpgo/data/demo/video.mp4
// → https://sftpgo.example.com/demo/video.mp4
// 移除基本路徑
$base_path = '/Users/accusys/momentry/var/sftpgo/data/';
if (strpos($file_path, $base_path) === 0) {
$relative_path = substr($file_path, strlen($base_path));
// 替換為實際的 SFTPGo web URL
return 'https://sftpgo.example.com/' . $relative_path;
}
// 如果無法轉換,返回原始路徑
return $file_path;
}
// 註冊短碼
add_shortcode('momentry_search', function($atts) {
$atts = shortcode_atts([
'query' => '',
'limit' => '10'
], $atts);
if (empty($atts['query'])) {
return '<p>請提供搜尋關鍵字</p>';
}
$response = wp_remote_post('https://api.momentry.ddns.net/api/v1/n8n/search', [
'headers' => [
'Content-Type' => 'application/json',
'X-API-Key' => 'YOUR_API_KEY' // 替換為實際的 API Key
],
'body' => json_encode([
'query' => $atts['query'],
'limit' => (int)$atts['limit']
]),
'timeout' => 30
]);
if (is_wp_error($response)) {
return '<p>搜尋服務暫時無法使用</p>';
}
$data = json_decode(wp_remote_retrieve_body($response), true);
if (empty($data['hits'])) {
return '<p>找不到相關結果</p>';
}
$output = '<ul class="momentry-results">';
foreach ($data['hits'] as $hit) {
// 注意: API 現在返回 file_path 而非 media_url
// 需要將文件路徑轉換為可訪問的 URL
$file_path = $hit['file_path'];
$video_url = convert_file_path_to_url($file_path); // 需要實作此函數
$output .= sprintf(
'<li>%s <a href="%s?start=%s">播放</a></li>',
esc_html($hit['text']),
$video_url,
$hit['start']
);
}
$output .= '</ul>';
return $output;
});
?>
```
**使用方式**:
```
[momentry_search query="charade" limit="5"]
```
---
## REST API Endpoint (WP >= 5.0)
在 WordPress REST API 中註冊自定義端點:
```php
<?php
// 註冊 REST API 端點
add_action('rest_api_init', function() {
register_rest_route('momentry/v1', '/search', [
'methods' => 'POST',
'callback' => function($request) {
$response = wp_remote_post(
'https://api.momentry.ddns.net/api/v1/n8n/search',
[
'headers' => ['Content-Type' => 'application/json', 'X-API-Key' => 'YOUR_API_KEY'],
'body' => json_encode([
'query' => $request->get_param('query'),
'limit' => $request->get_param('limit', 10)
])
]
);
if (is_wp_error($response)) {
return new WP_Error('api_error', 'API 請求失敗');
}
return json_decode(wp_remote_retrieve_body($response));
}
]);
});
?>
```
**呼叫方式**:
```
POST /wp-json/momentry/v1/search
Body: {"query": "charade", "limit": 5}
```
---
## 常見錯誤
### 錯誤: cURL error 7
**原因**: 無法連接到 API
**檢查**:
1. API 服務是否啟動
2. 網路是否可達
### 錯誤: 502 Bad Gateway
**原因**: API 服務未啟動
**解決**:
```bash
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
```
---
## curl 測試
在終端機中測試:
```bash
# 健康檢查
curl https://api.momentry.ddns.net/health
# 搜尋測試
curl -X POST https://api.momentry.ddns.net/api/v1/n8n/search \
-H "Content-Type: application/json" \
-d '{"query":"charade","limit":5}'
```
---
## 相關文件
- [API_INDEX.md](./API_INDEX.md) - 文件總覽
- [API_ENDPOINTS.md](./API_ENDPOINTS.md) - 端點完整說明
- [API_N8N_GUIDE.md](./API_N8N_GUIDE.md) - n8n 使用範例

View File

@@ -1,461 +0,0 @@
# Momentry API 使用流程
> **目標**: 從影片上傳到搜尋的完整流程
> **適用**: WordPress / n8n 整合
> **版本**: V1.0 | **日期**: 2026-03-25
---
## 流程總覽
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 1. 上傳 │ → │ 2. 註冊 │ → │ 3. 確認 │ → │ 4. 處理 │ → │ 5. 搜尋 │
│ SFTPGo │ │ 自動完成 │ │ UUID │ │ 查詢進度 │ │ 測試 │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
```
---
## Step 1: 上傳影片
### 方式 A: SFTP 上傳(推薦)
```bash
# 連線資訊
主機: sftpgo.momentry.ddns.net
連接埠: 2022
用戶名: demo
密碼: demopassword123
```
使用 FileZilla 或 SFTP 客戶端上傳到 `/` 目錄
### 方式 B: SFTP 命令列
```bash
sshpass -p "demopassword123" sftp -P 2022 demo@sftpgo.momentry.ddns.net
```
上傳後確認檔案在 SFTPGo 中的位置
---
## Step 2: 自動註冊
上傳後,系統會自動:
1. 偵測新檔案
2. 計算 UUIDSHA256
3. 建立資料庫記錄
**無需手動操作**
---
## Step 3: 確認註冊成功
### 查詢所有影片
```bash
curl -s -H "X-API-Key: YOUR_API_KEY" \
"https://api.momentry.ddns.net/api/v1/videos" | jq '.videos | length'
```
### 查詢特定檔案
```bash
curl -s -H "X-API-Key: YOUR_API_KEY" \
"https://api.momentry.ddns.net/api/v1/videos" | jq '.videos[] | select(.file_name | contains("你的檔案名"))'
```
### 預期回應
```json
{
"uuid": "952f5854b9febad1",
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/你的檔案.mp4",
"file_name": "你的檔案.mp4",
"duration": 123.45,
"width": 1920,
"height": 1080
}
```
**確認要點**:
- ✅ UUID 已產生16位 hex
-`file_path` 正確
-`duration` > 0
---
## Step 4: 查詢處理進度
### 取得任務 UUID
```bash
# 從影片資訊取得 job_id
curl -s -H "X-API-Key: YOUR_API_KEY" \
"https://api.momentry.ddns.net/api/v1/videos" | \
jq '.videos[] | select(.file_name == "你的檔案.mp4") | {uuid, job_id}'
```
### 查詢任務狀態
```bash
curl -s -H "X-API-Key: YOUR_API_KEY" \
"https://api.momentry.ddns.net/api/v1/jobs/{uuid}"
```
### 任務狀態說明
| status | 說明 | 動作 |
|--------|------|------|
| `pending` | 等待處理 | 等待中 |
| `processing` | 處理中 | 繼續輪詢 |
| `completed` | 已完成 | 可進入 Step 5 |
| `failed` | 處理失敗 | 檢查錯誤 |
### n8n 輪詢範例
```javascript
// n8n Workflow: 檢查處理狀態
const jobUuid = $input.item.json.job_uuid;
const response = await fetch(
`https://api.momentry.ddns.net/api/v1/jobs/${jobUuid}`,
{
headers: {
"X-API-Key": "YOUR_API_KEY"
}
}
);
const job = await response.json();
// 狀態檢查
if (job.status === 'completed') {
return [{ json: { done: true, video_uuid: job.video_uuid } }];
} else {
return [{ json: { done: false, status: job.status } }];
}
```
---
## Step 5: 搜尋測試
處理完成後,資料會入庫到向量資料庫,可進行搜尋測試。
### 測試向量搜尋
```bash
curl -s -X POST "https://api.momentry.ddns.net/api/v1/search" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "測試關鍵字",
"limit": 5
}'
```
### 取得分段Chunk內容
搜尋結果會返回影片分段Chunk包含可播放的時間軸資訊
```json
{
"results": [
{
"uuid": "39567a0eb16f39fd",
"chunk_id": "sentence_1471",
"chunk_type": "sentence",
"start_time": 5309.08,
"end_time": 5311.08,
"text": "influenced by a vital way,",
"score": 0.68
}
]
}
```
**Chunk 欄位說明**:
| 欄位 | 說明 |
|------|------|
| `uuid` | 影片 UUID用於取得影片網址 |
| `chunk_id` | 分段 ID |
| `chunk_type` | 分段類型sentence/cut/time/trace/story |
| `start_time` | 開始時間(秒) |
| `end_time` | 結束時間(秒) |
| `text` | 語音內容文字 |
| `score` | 相似度分數0-1 |
### 播放分段
取得 Chunk 後可組合成播放網址:
```
影片網址?start={start_time}&end={end_time}
```
範例:
```
https://wp.momentry.ddns.net/video.mp4?start=5309.08&end=5311.08
```
---
## 完整 n8n Workflow 範例
```
┌──────────────┐
│ 觸發 (定時) │
└──────┬───────┘
┌──────────────┐ ┌──────────────┐
│ 查詢影片 │────►│ 比對新檔案 │
│ /videos │ │ │
└──────┬───────┘ └──────────────┘
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ 等待處理 │◄────│ 輪詢任務狀態 │
│ /jobs/:uuid │ │ /jobs/:uuid │
└──────┬───────┘ └──────────────┘
▼ (completed)
┌──────────────┐
│ 搜尋測試 │
│ /search │
└──────────────┘
```
---
## 快速參考
| 步驟 | API | 用途 |
|------|-----|------|
| 查詢影片 | `GET /api/v1/videos` | 確認上傳成功 |
| 查詢任務 | `GET /api/v1/jobs/:uuid` | 查看處理進度 |
| 搜尋內容 | `POST /api/v1/search` | 測試搜尋功能 |
---
## WordPress PHP 範例
### 基本設定
```php
<?php
class Momentry_API {
private const API_URL = 'https://api.momentry.ddns.net';
private const API_KEY = 'YOUR_API_KEY';
public static function request(string $method, string $endpoint, ?array $data = null): array {
$url = self::API_URL . $endpoint;
$args = [
'method' => $method,
'headers' => [
'X-API-Key' => self::API_KEY,
'Content-Type' => 'application/json',
],
'timeout' => 30,
];
if ($data !== null) {
$args['body'] = json_encode($data);
}
$response = wp_remote_request($url, $args);
if (is_wp_error($response)) {
throw new Exception($response->get_error_message());
}
return json_decode(wp_remote_retrieve_body($response), true);
}
public static function getVideos(): array {
return self::request('GET', '/api/v1/videos');
}
public static function getVideo(string $uuid): array {
return self::request('GET', "/api/v1/videos/{$uuid}");
}
public static function getJob(string $uuid): array {
return self::request('GET', "/api/v1/jobs/{$uuid}");
}
public static function search(string $query, int $topK = 5): array {
return self::request('POST', '/api/v1/search', [
'query' => $query,
'top_k' => $topK,
]);
}
}
```
### Step 3: 確認註冊成功
```php
<?php
// 查詢所有影片
$videos = Momentry_API::getVideos();
foreach ($videos['videos'] as $video) {
echo "UUID: " . $video['uuid'] . "\n";
echo "檔案: " . $video['file_name'] . "\n";
echo "時長: " . $video['duration'] . "\n";
echo "---\n";
}
// 查詢特定影片
$video = Momentry_API::getVideo('952f5854b9febad1');
print_r($video);
```
### Step 4: 查詢處理進度
```php
<?php
// 取得任務狀態
$job = Momentry_API::getJob('9760d0820f0cf9a7');
switch ($job['status']) {
case 'pending':
echo "等待處理中...\n";
break;
case 'processing':
echo "處理中: " . $job['progress'] . "%\n";
break;
case 'completed':
echo "處理完成!\n";
break;
case 'failed':
echo "處理失敗: " . ($job['error'] ?? '未知錯誤') . "\n";
break;
}
```
### Step 5: 搜尋內容並取得 Chunk
```php
<?php
// 搜尋相關片段
$results = Momentry_API::search('測試關鍵字', 5);
foreach ($results['results'] as $result) {
echo "影片 UUID: " . $result['uuid'] . "\n";
echo "Chunk ID: " . $result['chunk_id'] . "\n";
echo "類型: " . $result['chunk_type'] . "\n";
echo "開始: " . $result['start_time'] . "s\n";
echo "結束: " . $result['end_time'] . "s\n";
echo "內容: " . ($result['text'] ?? '') . "\n";
echo "相似度: " . $result['score'] . "\n";
echo "---\n";
}
```
### WordPress Shortcode 範例(可點擊播放)
```php
<?php
// 在 functions.php 中加入
add_shortcode('momentry_search', function($atts) {
$atts = shortcode_atts([
'query' => '',
'limit' => 10,
], $atts);
if (empty($atts['query'])) {
return '<p>請輸入搜尋關鍵字</p>';
}
try {
$results = Momentry_API::search($atts['query'], $atts['limit']);
if (empty($results['results'])) {
return '<p>找不到相關結果</p>';
}
$html = '<div class="momentry-results">';
$html .= '<h3>搜尋結果: ' . esc_html($atts['query']) . '</h3>';
$html .= '<ul>';
foreach ($results['results'] as $result) {
$video_uuid = $result['uuid'];
$start = $result['start_time'] ?? 0;
$end = $result['end_time'] ?? 0;
$text = $result['text'] ?? '無文字描述';
$html .= '<li>';
$html .= '<a href="/player?uuid=' . esc_attr($video_uuid) .
'&start=' . esc_attr($start) .
'&end=' . esc_attr($end) . '">';
$html .= '播放 ' . $start . 's - ' . $end . 's';
$html .= '</a>';
$html .= '<br>';
$html .= '<small>相似度: ' . round($result['score'] * 100) . '%</small>';
$html .= '<br>';
$html .= esc_html($text);
$html .= '</li>';
}
$html .= '</ul></div>';
return $html;
} catch (Exception $e) {
return '<p>搜尋服務暫時無法使用</p>';
}
});
```
**使用方式**:
```html
[momentry_search query="關鍵字" limit="5"]
```
---
## 完整 n8n Workflow 範例
```
┌──────────────┐
│ 觸發 (定時) │
└──────┬───────┘
┌──────────────┐ ┌──────────────┐
│ 查詢影片 │────►│ 比對新檔案 │
│ /videos │ │ │
└──────┬───────┘ └──────────────┘
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ 等待處理 │◄────│ 輪詢任務狀態 │
│ /jobs/:uuid │ │ /jobs/:uuid │
└──────┬───────┘ └──────────────┘
▼ (completed)
┌──────────────┐
│ 搜尋測試 │
│ /search │
└──────────────┘
```
---
**注意**:
- 處理時間視影片長度而定1分鐘影片約需 2-5 分鐘處理)
- 大量影片時建議分批上傳
---
## 附錄:版本歷史
| 版本 | 日期 | 內容 | 操作人 |
|------|------|------|--------|
| V1.0 | 2026-03-25 | 初版建立 | OpenCode |
| V1.1 | 2026-03-25 | 新增 Chunk 取得與播放說明、Shortcode 範例 | OpenCode |
| V1.2 | 2026-03-25 | 修正 SFTPGo 主機名稱為 sftpgo.momentry.ddns.net | OpenCode |

View File

@@ -1,331 +0,0 @@
# 架構優化待評估事項
| 項目 | 內容 |
|------|------|
| 建立者 | OpenCode |
| 建立時間 | 2026-03-21 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 |
|------|------|------|--------|
| V1.0 | 2026-03-21 | 創建文件 | OpenCode |
| V1.1 | 2026-03-22 | 新增 TigerGraph/GraphRAG 說故事評估 | OpenCode |
---
## 架構優化項目
### 1. PostgreSQL → Redis 故障轉移
**說明**: 當 PostgreSQL 不可用時,降級到 Redis 作為臨時存儲
**複雜度**: 中
**影響範圍**:
- `src/core/db/postgres_db.rs`
- `src/core/db/redis_client.rs`
**風險**:
- 數據一致性問題
- 需要定義轉移策略
**優先級**: 待評估
---
### 2. 連接池監控
**說明**: 添加 PostgreSQL 和 Redis 連接池指標到 Prometheus
**複雜度**: 低
**影響範圍**:
- `src/core/db/postgres_db.rs`
- `src/core/db/redis_client.rs`
- `src/api/` (新增 metrics endpoint)
**風險**: 低
**優先級**: 待評估
---
### 3. Processor 重試機制
**說明**: 當 processor 失敗時自動重試
**複雜度**: 中
**影響範圍**:
- `src/core/processor/executor.rs` (新增 `run_with_retry` 方法)
- `src/core/processor/mod.rs` (導出 `RetryConfig`)
**風險**:
- 無限重試風險 → 已通過 `max_attempts` 控制
- 需要指數退避 → 已實現
**優先級**: ✅ 已完成 (2026-03-21)
**實作內容**:
- `RetryConfig` 結構體 (可配置重試次數、初始延遲、最大延遲、退避倍數)
- `run_with_retry()` 方法 (自動重試 + 指數退避)
- 單元測試覆蓋
**使用範例**:
```rust
use crate::core::processor::{PythonExecutor, RetryConfig};
let executor = PythonExecutor::new()?;
let config = RetryConfig::new(3).with_delay(1000).with_max_delay(30000);
executor.run_with_retry(
"asr_processor.py",
&["--input", "/path/to/video"],
Some(&uuid),
"asr",
Some(Duration::from_secs(3600)),
Some(config),
).await?;
```
---
### 4. PyO3 整合
**說明**: Python/Rust 直接調用,移除子進程調用
**複雜度**: 高
**影響範圍**:
- `src/core/processor/executor.rs` (重寫)
- Python 模組 (修改為可直接 import)
**風險**:
- Python GIL 問題
- 依賴版本兼容性
- 需要大量重寫
**優先級**: 低 (長期目標)
---
### 5. HTTP 健康端點
**說明**: 添加 `/health` API 用於外部監控
**複雜度**: 低
**影響範圍**:
- `src/api/server.rs` (新增路由)
**風險**: 低
**優先級**: ✅ 已完成 (2026-03-21)
**實作內容**:
- `GET /health` - 基本健康檢查 (status, version, uptime)
- `GET /health/detailed` - 詳細健康檢查 (PostgreSQL, Redis, Qdrant 狀態和延遲)
---
### 6. Gitea Actions CI/CD
**說明**: 配置 Gitea Actions 自動化 CI/CD在合併前執行檢查
**複雜度**: 中
**影響範圍**:
- `.gitea/workflows/` (新增 workflow 文件)
**優點**:
- 強制執行檢查,無法跳過
- 跨設備一致
- PR 審查前自動檢查
**風險**: 低
**優先級**: 待評估
---
### 7. Commit Message Lint
**說明**: 規範化提交訊息格式 (Conventional Commits)
**複雜度**: 低
**影響範圍**:
- `.git/hooks/commit-msg` (新增 hook)
- `~/dotfiles/hooks/commit-msg`
**風險**: 低
**優先級**: ✅ 已完成 (2026-03-21)
**實作內容**:
- 驗證格式: `<type>(<scope>): <description>`
- 有效類型: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert
- 警告: 第一行超過 72 字符
**範例**:
```
feat(api): add health check endpoint
fix(db): resolve connection pool issue
docs: update README
```
---
### 8. 自動化安裝腳本
**說明**: 創建腳本一次安裝所有開發工具
**複雜度**: 低
**影響範圍**:
- `scripts/install-dev-tools.sh` (新增)
**風險**: 低
**優先級**: 待評估
---
## 評估標準
| 標準 | 說明 |
|------|------|
| 業務價值 | 對用戶有何幫助 |
| 技術風險 | 實現難度和潛在問題 |
| 維護成本 | 未來維護負擔 |
| 依賴性 | 對其他系統的影響 |
---
## 評估記錄
| 項目 | 評估日期 | 決策 | 原因 |
|------|----------|------|------|
| PostgreSQL → Redis 故障轉移 | 待評估 | - | - |
| 連接池監控 | 待評估 | - | - |
| Processor 重試機制 | 2026-03-21 | 已完成 | - |
| PyO3 整合 | 待評估 | - | - |
| HTTP 健康端點 | 2026-03-21 | 已完成 | - |
| Gitea Actions CI/CD | 待評估 | - | - |
| Commit Message Lint | 2026-03-21 | 已完成 | - |
| 自動化安裝腳本 | 待評估 | - | - |
---
## 9. TigerGraph / Knowledge Graph 圖譜說故事
**說明**: 使用知識圖譜 (Knowledge Graph) 增強視頻敘事 (Storytelling) 和 RAG 檢索
**複雜度**: 高
**研究來源**:
- [TigerGraph Agentic GraphRAG](https://www.tigergraph.com/blog/agentic-graphrag-gives-ai-a-playbook-for-smarter-retrieval/) (2025-12-15)
- [TigerGraph GraphRAG GitHub](https://github.com/tigergraph/graphrag) (v1.2.0, 2026-03-11)
- [GraphRAG in 2026: Practitioner's Guide](https://medium.com/graph-praxis/graph-rag-in-2026-a-practitioners-guide-to-what-actually-works-dca4962e7517) (2026-02-22)
- [GraphRAG Complete Guide](https://medium.com/@brian-curry-research/graphrag-the-complete-guide-to-graph-powered-retrieval-augmented-generation-eeb58a6bb4d1) (2026-02-11)
### 核心概念
| 概念 | 說明 |
|------|------|
| **GraphRAG** | 結合知識圖譜與 RAG比傳統向量檢索更智能 |
| **知識圖譜** | 實體 (Entity) + 關係 (Relationship) 的結構化表示 |
| **多跳推理** | Multi-hop traversal可連接多個相關節點 |
| **混合檢索** | Graph traversal + Vector similarity 結合 |
### 對 Momentry 的潛在應用
```
視頻場景 → 實體識別 → 關係建立 → 故事圖譜
↓ ↓ ↓ ↓
CUT [人物, 物品, 動作] [誰做了什麼, 什麼導致什麼] [敘事鏈]
```
**1. 敘事圖譜構建 (Narrative Graph)**
- 從 Story/Chunks 模組提取實體
- 建立場景之間的因果關係
- 追蹤角色互動和情節發展
**2. 故事檢索增強**
```python
# 現有: Parent-child chunks
parent_chunk: "場景描述"
child_chunks: [詳細內容]
# 加入圖譜:
場景A --led_to--> 場景B
角色X --interacted_with--> 角色Y
主題Y --related_to--> 主題Z
```
**3. 查詢模式**
| 查詢類型 | 傳統 RAG | GraphRAG |
|----------|----------|----------|
| 事實查找 | ✅ "這個場景在說什麼" | ✅ |
| 主題推理 | ❌ "這個視頻的主要情節" | ✅ Global search |
| 多跳關係 | ❌ | ✅ "A導致BB導致C" |
| 可解釋性 | ❌ | ✅ 關係路徑可追溯 |
### 實作方案
**方案 A: TigerGraph Cloud (推薦)**
- ✅ 原生 Graph + Vector 混合查詢
- ✅ GraphRAG 官方支援
- ✅ 200GB 免費額度
- ❌ 雲端依賴,延遲敏感場景需考慮
**方案 B: Neo4j + Qdrant**
- ✅ 成熟開源生態
- ✅ LangChain/LlamaIndex 整合
- ❌ 需要維護兩個系統
**方案 C: 自建混合架構**
- PostgreSQL + Neo4j (或Typesense)
- 利用現有 BM25 + 向量檢索基礎
- ❌ 開發成本高
### 技術棧整合建議
```rust
// 現有架構
Vector Search (Qdrant) BM25 (PostgreSQL)
// 加入 GraphRAG
Knowledge Graph (TigerGraph/Neo4j)
Vector + Graph traversal
```
### 優先級: 待評估
**考慮因素**:
- 用戶是否需要複雜的故事情節查詢?
- 實體識別 (NER) 成本是否可以接受?
- 與現有 BM25 + Vector 混合搜索的比較優勢?
---
## 10. LazyGraphRAG / FastGraphRAG 成本優化
**說明**: GraphRAG 索引成本高昂LazyGraphRAG 推遲圖譜構建到查詢時
**來源**: [GraphRAG in 2026](https://medium.com/graph-praxis/graph-rag-in-2026-a-practitioners-guide-to-what-actually-works-dca4962e7517)
**Microsoft GraphRAG 問題**: $33K 索引大型數據集
**替代方案**:
- **LazyGraphRAG**: 按需構建,查詢時再建立子圖
- **FastGraphRAG**: 優化索引管道10-90% 成本節省
- **HippoRAG**: 使用 Personalised PageRank 優化遍歷
**優先級**: 待評估 (作為 GraphRAG 的一部分)

View File

@@ -1,450 +0,0 @@
# Momentry 備份版本管理規範
| 項目 | 內容 |
|------|------|
| 建立者 | Warren / OpenCode |
| 建立時間 | 2026-03-25 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 |
|------|------|------|--------|
| V1.0 | 2026-03-25 | 建立備份版本管理規範 | OpenCode |
---
## 1. 概述
本文檔定義 Momentry 系統的備份版本管理規範,確保新舊架構之間的回滾相容性。
### 1.1 版本定義
| 版本 | 日期 | 說明 |
|------|------|------|
| v1 | 2026-03-18 | 初始備份架構(不包含新架構組件)|
| v2 | 2026-03-25 | 新架構備份(包含 monitor_jobs, processor_results, Output 目錄)|
### 1.2 備份版本格式
| 版本 | 檔案命名格式 |
|------|-------------|
| v1 | `{service}_{type}_{YYYYMMDD}_{HHMMSS}.{ext}` |
| v2 | `{service}_{type}_v2_{YYYYMMDD}_{HHMMSS}.{ext}` |
### 1.3 各版本涵蓋範圍
| 組件 | v1 | v2 |
|------|-----|-----|
| PostgreSQL (videos, chunks) | ✅ | ✅ |
| PostgreSQL (monitor_jobs) | ❌ | ✅ |
| PostgreSQL (processor_results) | ❌ | ✅ |
| Redis | ✅ | ✅ |
| MongoDB Cache | ⚠️ | ⚠️ |
| Output (probe.json) | ❌ | ✅ |
> ⚠️ MongoDB 備份目前存在路徑問題,正在修復中
---
## 2. 備份版本識別
### 2.1 檔名識別
```bash
# 識別版本
detect_version() {
local backup_file=$1
if echo "$backup_file" | grep -q "_v2_"; then
echo "v2"
else
echo "v1"
fi
}
# 使用範例
detect_version "postgresql_db_momentry_v2_20260325_030000.sql.gz"
# 輸出: v2
detect_version "postgresql_db_momentry_20260324_030000.sql.gz"
# 輸出: v1
```
### 2.2 內容識別
```bash
# 檢查是否為 v2 備份
is_v2_backup() {
local backup_file=$1
gzip -dc "$backup_file" 2>/dev/null | grep -q "monitor_jobs" && echo "yes" || echo "no"
}
# 檢查是否包含 processor_results
has_processor_results() {
local backup_file=$1
gzip -dc "$backup_file" 2>/dev/null | grep -q "processor_results" && echo "yes" || echo "no"
}
```
### 2.3 檔案大小比較
| 版本 | PostgreSQL 備份大小 | 說明 |
|------|---------------------|------|
| v1 | ~18-19 MB | 基本資料表 |
| v2 | >19 MB | 包含新表格和索引 |
---
## 3. 回滾策略
### 3.1 回滾流程圖
```
┌─────────────────────────────────────────┐
│ 選擇還原目標 │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ 選擇備份版本 │
│ ┌───────────┐ ┌───────────┐ │
│ │ v1 備份 │ │ v2 備份 │ │
│ └───────────┘ └───────────┘ │
└─────────────────────────────────────────┘
┌─────────┴─────────┐
▼ ▼
┌──────────┐ ┌──────────┐
│ v1 回滾 │ │ v2 回滾 │
└──────────┘ └──────────┘
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ 基本資料庫 │ │ 完整還原 │
└──────────┘ └──────────┘
```
### 3.2 回滾矩陣
| 還原目標 | v1 備份 | v2 備份 |
|----------|---------|---------|
| 基本資料庫 | ✅ | ✅ |
| + monitor_jobs | ❌ | ✅ |
| + processor_results | ❌ | ✅ |
| + Output 檔案 | ❌ | ✅ |
| + MongoDB Cache | ⚠️ | ⚠️ |
### 3.3 回滾相容性說明
#### v1 → v2支援
- v1 備份可以還原到 v2 架構
- 新架構組件會從空白狀態開始
- 不會造成資料損壞
#### v2 → v1 警告)
```
⚠️ v2 回滾到 v1 可能導致資料丟失
影響範圍:
- monitor_jobs 資料會消失
- processor_results 資料會消失
- Output 檔案參照可能失效
建議:
1. 在還原前建立 v2 快照
2. 或使用隔離還原staging restore
```
---
## 4. 還原腳本保護機制
### 4.1 還原前檢查
```bash
# 還原前檢查版本相容性
pre_restore_check() {
local backup_file=$1
local version=$(detect_version "$backup_file")
local current_db_version=$(check_current_db_version)
echo "備份版本: $version"
echo "目前版本: $current_db_version"
# v2 → v1: 警告但允許(使用者需確認)
if [ "$version" = "v1" ] && [ "$current_db_version" = "v2" ]; then
echo "⚠️ 警告:即將回滾到 v1"
echo "影響monitor_jobs 和 processor_results 資料將被清除"
read -p "確認繼續?(y/N): " confirm
[ "$confirm" != "y" ] && exit 1
fi
# v1 → v2: 直接允許
if [ "$version" = "v1" ] && [ "$current_db_version" = "v2" ]; then
echo " 提示:新架構組件將重新初始化"
fi
# v2 → v2: 直接允許
# v1 → v1: 直接允許
}
```
### 4.2 隔離還原Staging Restore
```bash
# 只還原到暫存資料庫,不影響生產
restore_to_staging() {
local backup_file=$1
local version=$(detect_version "$backup_file")
echo "執行隔離還原..."
echo "版本: $version"
# 建立暫存資料庫
PGPASSWORD="$PG_PASSWORD" psql -U "$PG_USER" -d postgres << EOF
DROP DATABASE IF EXISTS momentry_staging;
CREATE DATABASE momentry_staging;
EOF
# 還原到暫存資料庫
PGPASSWORD="$PG_PASSWORD" pg_restore -U "$PG_USER" -d "momentry_staging" \
--no-owner --no-acl "$backup_file"
echo "✅ 還原完成momentry_staging"
echo "驗證命令psql -U accusys -d momentry_staging -c '\\dt'"
}
```
### 4.3 版本驗證命令
```bash
# 識別所有備份版本
ls /Users/accusys/momentry/backup/daily/postgresql/*.sql.gz | \
xargs -I {} sh -c 'echo "{}: $(detect_version {})"'
# 驗證 v2 備份內容
verify_v2_backup() {
local backup_file=$1
echo "驗證備份: $backup_file"
# 檢查 monitor_jobs
if gzip -dc "$backup_file" | grep -q "monitor_jobs"; then
echo "✅ 包含 monitor_jobs"
else
echo "❌ 缺少 monitor_jobs"
return 1
fi
# 檢查 processor_results
if gzip -dc "$backup_file" | grep -q "processor_results"; then
echo "✅ 包含 processor_results"
else
echo "❌ 缺少 processor_results"
return 1
fi
echo "✅ v2 備份驗證通過"
}
```
---
## 5. 版本遷移
### 5.1 v1 → v2 遷移步驟
| 步驟 | 說明 | 驗證 |
|------|------|------|
| 1 | 確認所有 v1 備份已完成 | `ls *.sql.gz \| grep -v v2` |
| 2 | 修改 `backup_all.sh` 加入 v2 標記 | 確認 TIMESTAMP 包含 `v2_` |
| 3 | 修正 MongoDB 路徑 | 確認指向正確目錄 |
| 4 | 新增 Output 目錄備份 | 確認 probe.json 被備份 |
| 5 | 執行測試備份 | 驗證命名格式正確 |
| 6 | 驗證 v2 備份完整性 | `verify_v2_backup` |
| 7 | 正式啟用 v2 備份 | 確認 crontab 使用新版 |
### 5.2 遷移驗證清單
```bash
#!/bin/bash
# verify_v2_migration.sh
echo "=== v2 遷移驗證 ==="
# 1. 檢查備份腳本
echo "1. 檢查備份腳本..."
if grep -q "v2_" /Users/accusys/momentry/scripts/backup_all.sh; then
echo " ✅ 版本標記已啟用"
else
echo " ❌ 版本標記未啟用"
fi
# 2. 檢查 MongoDB 路徑
echo "2. 檢查 MongoDB 路徑..."
if grep -q "/opt/homebrew/var/mongodb" /Users/accusys/momentry/scripts/backup_all.sh; then
echo " ✅ MongoDB 路徑已修正"
else
echo " ❌ MongoDB 路徑未修正"
fi
# 3. 檢查 Output 目錄備份
echo "3. 檢查 Output 目錄備份..."
if grep -q "momentry_output" /Users/accusys/momentry/scripts/backup_all.sh; then
echo " ✅ Output 目錄備份已啟用"
else
echo " ❌ Output 目錄備份未啟用"
fi
# 4. 檢查最新備份
echo "4. 檢查最新備份..."
latest_backup=$(ls -t /Users/accusys/momentry/backup/daily/postgresql/*.sql.gz 2>/dev/null | head -1)
if [ -n "$latest_backup" ]; then
version=$(detect_version "$latest_backup")
echo " 最新備份: $(basename $latest_backup)"
echo " 版本: $version"
if [ "$version" = "v2" ]; then
verify_v2_backup "$latest_backup"
fi
fi
echo "=== 驗證完成 ==="
```
---
## 6. 疑難排解
### 6.1 常見問題
| 問題 | 原因 | 解決方案 |
|------|------|----------|
| 無法識別版本 | 檔名被修改 | 使用內容分析 `gzip -dc \| grep "monitor_jobs"` |
| v2 備份還原失敗 | 磁碟空間不足 | 清理空間後重試 |
| v1 還原覆蓋 v2 | 操作失誤 | 使用隔離還原保護生產資料 |
| MongoDB 備份為空 | 路徑錯誤 | 修正為 `/opt/homebrew/var/mongodb` |
### 6.2 緊急回滾流程
```bash
#!/bin/bash
# emergency_restore.sh
set -e
BACKUP_FILE=$1
VERSION=$2
echo "=== 緊急回滾 ==="
echo "備份檔案: $BACKUP_FILE"
echo "目標版本: $VERSION"
# 1. 建立當前狀態快照
echo "1. 建立當前狀態快照..."
NOW=$(date +%Y%m%d_%H%M%S)
pg_dump -U accusys -d momentry | gzip > "/tmp/momentry_emergency_$NOW.sql.gz"
echo " 快照: /tmp/momentry_emergency_$NOW.sql.gz"
# 2. 執行還原
echo "2. 執行還原..."
gunzip -c "$BACKUP_FILE" | psql -U accusys -d momentry
# 3. 驗證
echo "3. 驗證還原..."
psql -U accusys -d momentry -c "SELECT COUNT(*) FROM monitor_jobs;"
echo "=== 回滾完成 ==="
```
---
## 7. 備份清單v2
### 7.1 每日備份v2 格式)
| 服務 | 備份項目 | 檔案命名 | 說明 |
|------|----------|----------|------|
| PostgreSQL | momentry | `postgresql_db_momentry_v2_{date}_{time}.sql.gz` | 完整資料庫 |
| PostgreSQL | video_register | `postgresql_db_video_register_v2_{date}_{time}.sql.gz` | 影片註冊資料 |
| Redis | RDB | `redis_rdb_v2_{date}_{time}.rdb` | Redis 快照 |
| MongoDB | 資料 | `mongodb_data_v2_{date}_{time}.tar.gz` | MongoDB 資料 |
| n8n | 資料+DB | `n8n_{date}_{time}.tar.gz`, `n8n_db_{date}_{time}.sql.gz` | n8n 完整 |
| SFTPGo | 配置+DB | `sftpgo_{date}_{time}.tar.gz`, `sftpgo_db_{date}_{time}.sql.gz` | SFTPGo |
| Gitea | 資料 | `gitea_{date}_{time}.tar.gz` | Gitea |
| Output | 檔案 | `momentry_output_v2_{date}_{time}.tar.gz` | probe.json 等 |
### 7.2 備份保留策略
| 類型 | 保留期限 | 位置 |
|------|----------|------|
| 每日備份 | 7 天 | `backup/daily/` |
| 每週備份 | 4 週 | `backup/weekly/` |
| 每月備份 | 12 個月 | `backup/monthly/` |
| 歸檔 | 1 年+ | `backup/archive/` |
---
## 8. 相關文件
| 文件 | 說明 |
|------|------|
| [SERVICES.md](./SERVICES.md) | 服務說明 |
| [MOMENTRY_CORE_MONITORING.md](./MOMENTRY_CORE_MONITORING.md) | 監控規範 |
| `/Users/accusys/momentry/scripts/backup_all.sh` | 備份腳本 |
| `/Users/accusys/momentry/scripts/restore_all.sh` | 還原腳本 |
| `/Users/accusys/momentry_core_0.1/monitor/storage/backup_monitor.sh` | 備份監控 |
---
## 附錄 Av2 備份完整性檢查清單
```bash
#!/bin/bash
# check_v2_integrity.sh
BACKUP_DIR="/Users/accusys/momentry/backup/daily"
echo "=== v2 備份完整性檢查 ==="
# 檢查 PostgreSQL
echo "1. PostgreSQL..."
pg_backup=$(ls -t "$BACKUP_DIR/postgresql"/postgresql_db_momentry_v2_*.sql.gz 2>/dev/null | head -1)
if [ -n "$pg_backup" ]; then
echo " 備份: $(basename $pg_backup)"
verify_v2_backup "$pg_backup"
else
echo " ❌ 未找到 v2 備份"
fi
# 檢查 Output
echo "2. Output 目錄..."
output_backup=$(ls -t "$BACKUP_DIR/momentry"/momentry_output_v2_*.tar.gz 2>/dev/null | head -1)
if [ -n "$output_backup" ]; then
echo " 備份: $(basename $output_backup)"
echo " ✅ Output 備份已存在"
else
echo " ❌ 未找到 Output 備份"
fi
# 檢查 MongoDB
echo "3. MongoDB..."
mongo_backup=$(ls -t "$BACKUP_DIR/mongodb"/mongodb_data_v2_*.tar.gz 2>/dev/null | head -1)
if [ -n "$mongo_backup" ]; then
size=$(stat -f%z "$mongo_backup" 2>/dev/null || stat -c%s "$mongo_backup" 2>/dev/null)
echo " 備份: $(basename $mongo_backup)"
echo " 大小: $size bytes"
if [ "$size" -gt 1000 ]; then
echo " ✅ MongoDB 備份有效"
else
echo " ⚠️ MongoDB 備份可能為空"
fi
else
echo " ❌ 未找到 MongoDB 備份"
fi
echo "=== 檢查完成 ==="
```

View File

@@ -1,667 +0,0 @@
# Momentry Core 版本紀錄
## 版本命名規則
### Main Version (主版本)
```
v{major}.{minor}
例: v0.1, v0.2, v1.0
```
### Build Version (建置版本)
```
v{major}.{minor}.{YYYYMMDD_HHMMSS}
例: v0.1.20260325_143000
```
---
## 版本紀錄存放位置
```
/Users/accusys/momentry/versions/
├── current/ # 目前使用版本
│ ├── binary # 當前 binary
│ └── version.json # 版本資訊
├── releases/ # Release 版本存放
│ ├── v0.1/
│ │ ├── v0.1.20260325_143000/
│ │ │ ├── binary
│ │ │ └── version.json
│ │ ├── v0.1.20260324_100000/
│ │ │ └── ...
│ │ └── release.json # v0.1 版本總覽
│ │
│ └── v0.2/
│ └── ...
└── changelog.json # 全域版本變更記錄
```
---
## version.json 格式
```json
{
"version": "v0.1.20260325_143000",
"main_version": "v0.1",
"build_timestamp": "2026-03-25T14:30:00+08:00",
"git_commit": "83ae050",
"git_branch": "main",
"git_message": "fix: save probe.json to OUTPUT_DIR instead of current directory",
"features": [
"API Key Authentication",
"Job Worker System"
],
"fixes": [
"get_processor_results_by_job column mapping"
],
"deployed_at": "2026-03-25T15:00:00+08:00",
"deployed_by": "opencode",
"status": "production"
}
```
---
## release.json 格式 (主版本總覽)
```json
{
"version": "v0.1",
"status": "production",
"created_at": "2026-03-14T10:00:00+08:00",
"current_build": "v0.1.20260325_143000",
"builds": [
{
"build": "v0.1.20260325_143000",
"date": "2026-03-25",
"commits": ["83ae050", "171c36a"],
"summary": "fix: save probe.json, add v2 backup versioning"
},
{
"build": "v0.1.20260324_100000",
"date": "2026-03-24",
"commits": ["89fbfd6", "3edaf01"],
"summary": "feat: add POST /api/v1/probe endpoint"
}
],
"changelog": [
"## v0.1.20260325_143000",
"- 修復 processor_results 欄位映射錯誤",
"- 添加 API Key 認證",
"",
"## v0.1.20260324_100000",
"- 新增 Probe API"
]
}
```
---
## changelog.json 格式 (全域變更記錄)
```json
{
"updated_at": "2026-03-25T14:30:00+08:00",
"versions": {
"v0.1": {
"status": "production",
"current_build": "v0.1.20260325_143000",
"build_count": 12
},
"v0.0": {
"status": "deprecated",
"final_build": "v0.0.20260310_090000"
}
},
"recent_changes": [
{
"version": "v0.1.20260325_143000",
"date": "2026-03-25",
"changes": [
{"type": "fix", "description": "get_processor_results_by_job column mapping"},
{"type": "feat", "description": "API Key Authentication"}
]
}
]
}
```
---
## Release Script
### /Users/accusys/momentry/scripts/release.sh
```bash
#!/bin/bash
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="/Users/accusys/momentry_core_0.1"
VERSIONS_DIR="/Users/accusys/momentry/versions"
BACKUP_DIR="/Users/accusys/momentry/backup/bin"
CURRENT_BIN="/Users/accusys/momentry/bin/momentry"
# 顏色輸出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# 解析命令列參數
MAIN_VERSION=""
while [[ $# -gt 0 ]]; do
case $1 in
-v|--version)
MAIN_VERSION="$2"
shift 2
;;
*)
log_error "Unknown option: $1"
exit 1
;;
esac
done
if [ -z "$MAIN_VERSION" ]; then
log_error "請指定主版本: ./release.sh -v v0.1"
exit 1
fi
log_info "開始 Release ${MAIN_VERSION}..."
# 1. 取得 Git 資訊
GIT_COMMIT=$(git -C "$PROJECT_DIR" rev-parse --short HEAD)
GIT_BRANCH=$(git -C "$PROJECT_DIR" rev-parse --abbrev-ref HEAD)
GIT_MESSAGE=$(git -C "$PROJECT_DIR" log -1 --pretty=%s)
BUILD_TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BUILD_VERSION="${MAIN_VERSION}.${BUILD_TIMESTAMP}"
log_info "Build Version: ${BUILD_VERSION}"
log_info "Git Commit: ${GIT_COMMIT}"
# 2. 創建版本目錄
BUILD_DIR="${VERSIONS_DIR}/releases/${MAIN_VERSION}/${BUILD_VERSION}"
mkdir -p "$BUILD_DIR"
mkdir -p "${VERSIONS_DIR}/current"
mkdir -p "$BACKUP_DIR"
# 3. 停止 Production Service
log_info "停止 Production Service..."
sudo launchctl unload /Library/LaunchDaemons/com.momentry.api.plist 2>/dev/null || true
# 4. 備份當前 Binary
if [ -f "$CURRENT_BIN" ]; then
OLD_VERSION=$(cat "${VERSIONS_DIR}/current/version.json" 2>/dev/null | jq -r '.version // "unknown"')
log_info "備份當前版本: $OLD_VERSION"
cp "$CURRENT_BIN" "${BACKUP_DIR}/momentry_${OLD_VERSION}_$(date +%Y%m%d_%H%M%S)"
fi
# 5. 編譯 Release 版本
log_info "編譯 Release 版本..."
cd "$PROJECT_DIR"
cargo build --release --bin momentry
# 6. 複製到版本目錄
log_info "複製到版本目錄..."
cp target/release/momentry "${BUILD_DIR}/binary"
cp target/release/momentry "$CURRENT_BIN"
# 7. 生成 version.json
cat > "${BUILD_DIR}/version.json" << EOF
{
"version": "${BUILD_VERSION}",
"main_version": "${MAIN_VERSION}",
"build_timestamp": "$(date -Iseconds)",
"git_commit": "${GIT_COMMIT}",
"git_branch": "${GIT_BRANCH}",
"git_message": "${GIT_MESSAGE}",
"features": [],
"fixes": [],
"deployed_at": null,
"deployed_by": null,
"status": "built"
}
EOF
# 8. 更新 current
cp "${BUILD_DIR}/version.json" "${VERSIONS_DIR}/current/version.json"
# 9. 更新 changelog.json
UPDATE_CHANGELOG="
import json
from datetime import datetime
changelog_path = '${VERSIONS_DIR}/changelog.json'
build_info = {
'version': '${BUILD_VERSION}',
'date': datetime.now().strftime('%Y-%m-%d'),
'commit': '${GIT_COMMIT}',
'message': '${GIT_MESSAGE}'
}
try:
with open(changelog_path, 'r') as f:
changelog = json.load(f)
except FileNotFoundError:
changelog = {'updated_at': '', 'versions': {}, 'recent_changes': []}
changelog['updated_at'] = datetime.now().isoformat()
if '${MAIN_VERSION}' not in changelog['versions']:
changelog['versions']['${MAIN_VERSION}'] = {'status': 'building', 'build_count': 0}
changelog['versions']['${MAIN_VERSION}']['build_count'] += 1
changelog['versions']['${MAIN_VERSION}']['current_build'] = '${BUILD_VERSION}'
changelog['recent_changes'].insert(0, build_info)
with open(changelog_path, 'w') as f:
json.dump(changelog, f, indent=2, ensure_ascii=False)
"
python3 -c "$UPDATE_CHANGELOG"
# 10. 啟動 Production Service
log_info "啟動 Production Service..."
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
# 11. 驗證
sleep 3
if curl -s http://localhost:3002/health > /dev/null; then
log_info "✓ Release 成功!"
log_info "版本: ${BUILD_VERSION}"
log_info "目錄: ${BUILD_DIR}"
else
log_error "✗ Release 失敗!請檢查服務狀態。"
exit 1
fi
```
---
## 查詢版本指令
### 查詢目前版本
```bash
cat /Users/accusys/momentry/versions/current/version.json
```
### 查詢所有 Release
```bash
ls -la /Users/accusys/momentry/versions/releases/
```
### 查詢版本歷史
```bash
cat /Users/accusys/momentry/versions/changelog.json | python3 -m json.tool
```
### 查詢特定主版本
```bash
ls /Users/accusys/momentry/versions/releases/v0.1/
```
---
## 版本狀態
| 狀態 | 說明 |
|------|------|
| `building` | 建置中 |
| `built` | 已建置,未部署 |
| `testing` | 測試中 |
| `production` | 正式環境使用中 |
| `deprecated` | 已棄用 |
| `archived` | 已封存 |
---
## 版本流程圖
```
develop (git branch)
feature/bugfix commit
develop ──────────────────┐
│ │
│ (merge to main) │
▼ │
main (git) │
│ │
▼ │
Build v0.1.20260325_143000
│ │
├──► testing (3003) │
│ │
│ (approve) │
▼ ▼
v0.1 ───────────────────┘
├──► releases/v0.1/v0.1.20260325_143000/
├──► current/ (production)
changelog.json (update)
```
---
## Release Note (版本發布說明)
### Release Note 存放位置
```
/Users/accusys/momentry/versions/releases/{主版本}/{建置版本}/
├── binary
├── version.json
└── RELEASE_NOTE.md # 發布說明 (Markdown)
```
### Release Note 範本
```markdown
# Momentry Core v0.1.20260325_143000 Release Note
## 版本資訊
- **Build Version**: v0.1.20260325_143000
- **Main Version**: v0.1
- **Build Date**: 2026-03-25 14:30:00
- **Git Commit**: 83ae050
## 新功能 (Features)
### API Key 認證系統
- 添加 API Key 認證中介層
- 所有 `/api/v1/*` 端點需要 `X-API-Key` header
- 支援 API Key 使用追蹤和審計日誌
### Job Worker 系統
- 新增 Job Worker 二進位檔
- 支援最多 2 個並發處理器
- 新增 `/api/v1/jobs/:uuid` 端點查詢任務詳情
## 錯誤修復 (Bug Fixes)
| Issue | 描述 |
|-------|------|
| #001 | 修復 `get_processor_results_by_job` 欄位映射錯誤 |
| #002 | 修復 API Key 驗證時區處理問題 |
## API 變更 (API Changes)
### 新增端點
| Method | Endpoint | 說明 |
|--------|----------|------|
| GET | `/api/v1/jobs` | 取得所有任務列表 |
| GET | `/api/v1/jobs/:uuid` | 取得特定任務詳情 |
### 認證變更
| 端點 | 舊版 | 新版 |
|------|------|------|
| `/api/v1/*` | 公開 | 需要 API Key |
## 升級指南
### 從舊版升級
1. 備份當前版本
2. 停止服務
3. 替換 binary
4. 重啟服務
5. 更新 API Key 配置
### API Key 配置
```bash
# 請求範例
curl -H "X-API-Key: your_api_key" \
"http://localhost:3002/api/v1/videos"
```
## 已知問題 (Known Issues)
- 暫無
## 相關文檔
- [API 文檔](../docs/API_INDEX.md)
- [版本管理規範](../docs/VERSION_MANAGEMENT.md)
---
## Release Note 自動生成 Script
### /Users/accusys/momentry/scripts/generate_release_note.sh
```bash
#!/bin/bash
set -e
BUILD_VERSION=$1
MAIN_VERSION=$2
BUILD_DIR="/Users/accusys/momentry/versions/releases/${MAIN_VERSION}/${BUILD_VERSION}"
# 取得 Git 資訊
GIT_COMMITS=$(git log --oneline -10)
GIT_CHANGES=$(git diff --stat HEAD~5..HEAD)
cat > "${BUILD_DIR}/RELEASE_NOTE.md" << EOF
# Momentry Core ${BUILD_VERSION} Release Note
## 版本資訊
- **Build Version**: ${BUILD_VERSION}
- **Main Version**: ${MAIN_VERSION}
- **Build Date**: $(date '+%Y-%m-%d %H:%M:%S')
- **Git Commit**: $(git rev-parse --short HEAD)
## 變更內容
### Commit 記錄
\`\`\`
${GIT_COMMITS}
\`\`\`
### 變更統計
\`\`\`
${GIT_CHANGES}
\`\`\`
## 新功能
## 錯誤修復
## API 變更
## 升級指南
## 已知問題
EOF
echo "Release Note 生成完成: ${BUILD_DIR}/RELEASE_NOTE.md"
```
---
## Release Note 查詢
### 查詢所有 Release Note
```bash
find /Users/accusys/momentry/versions/releases -name "RELEASE_NOTE.md"
```
### 查看特定版本 Release Note
```bash
cat /Users/accusys/momentry/versions/releases/v0.1/v0.1.20260325_143000/RELEASE_NOTE.md
```
### 查詢最新版本 Release Note
```bash
cat /Users/accusys/momentry/versions/current/RELEASE_NOTE.md
```
---
## Release Note 範例
### 完整 Release Note 範例
\`\`\`markdown
# Momentry Core v0.1.20260325_143000 Release Note
## 版本資訊
| 項目 | 內容 |
|------|------|
| Build Version | v0.1.20260325_143000 |
| Main Version | v0.1 |
| Build Date | 2026-03-25 14:30:00 |
| Git Commit | 83ae050 |
| Status | ✅ Production |
## 新功能 (Features)
### 1. API Key 認證系統
添加完整的 API Key 認證系統,保護所有 API 端點。
**功能:**
- SHA256 key hash 驗證
- 使用統計追蹤
- 審計日誌記錄
- 異常檢測
**API 使用方式:**
\`\`\`bash
curl -H "X-API-Key: your_key" \\
"http://localhost:3002/api/v1/videos"
\`\`\`
### 2. Job Worker 系統
新增獨立的 Job Worker 處理影片處理任務。
**特性:**
- 最多 2 個並發處理器
- Polling-based 任務獲取
- 自動進度追蹤
## 錯誤修復 (Bug Fixes)
| Issue | 描述 | 嚴重性 |
|-------|------|--------|
| #001 | 修復 `get_processor_results_by_job` TIMESTAMP 欄位映射 | 🔴 高 |
| #002 | 修復 3002 port 衝突問題 | 🟡 中 |
## API 變更
### 新增端點
| Method | Endpoint | 說明 |
|--------|----------|------|
| GET | `/api/v1/jobs` | 取得任務列表 |
| GET | `/api/v1/jobs/:uuid` | 取得任務詳情 |
### 端點認證狀態
| 端點 | 認證需求 |
|------|----------|
| `/health` | ❌ 不需要 |
| `/api/v1/*` | ✅ 需要 `X-API-Key` |
## 升級指南
### 前置需求
- PostgreSQL 資料庫
- Redis 伺服器
- MongoDB 快取
### 升級步驟
1. **備份當前版本**
\`\`\`bash
cp /Users/accusys/momentry/bin/momentry \\
/Users/accusys/momentry/backup/bin/momentry_$(date +%Y%m%d)
\`\`\`
2. **停止服務**
\`\`\`bash
sudo launchctl unload /Library/LaunchDaemons/com.momentry.api.plist
\`\`\`
3. **替換 Binary**
\`\`\`bash
cp v0.1.20260325_143000/binary /Users/accusys/momentry/bin/momentry
\`\`\`
4. **重啟服務**
\`\`\`bash
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
\`\`\`
5. **驗證**
\`\`\`bash
curl http://localhost:3002/health
\`\`\`
## 已知問題 (Known Issues)
- 暫無
## 技術細節
### 認證流程
\`\`\`
Client Request
[X-API-Key Header] ──► Middleware
│ │
│ ▼
│ Hash Key (SHA256)
│ │
│ ▼
│ DB Lookup
│ │
│ ▼
│ Validate Status
│ │
▼ ▼
Handler ◄────────────────────┘
\`\`\`
### 資料庫變更
\`\`\`sql
-- 新增 duration_secs 欄位
ALTER TABLE processor_results
ADD COLUMN IF NOT EXISTS duration_secs DOUBLE PRECISION;
\`\`\`
## 回滾指南
如需回滾到上一版本:
\`\`\`bash
# 1. 停止服務
sudo launchctl unload /Library/LaunchDaemons/com.momentry.api.plist
# 2. 恢復舊版
cp /Users/accusys/momentry/backup/bin/momentry_v0.1.20260324_100000 \\
/Users/accusys/momentry/bin/momentry
# 3. 重啟服務
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
\`\`\`
## 聯繫與支援
- **Issue Tracker**: https://gitea.momentry.ddns.net/momentry/momentry_core/issues
- **文檔**: https://docs.momentry.ddns.net
---
*Generated: $(date '+%Y-%m-%d %H:%M:%S')*
\`\`\`
```

File diff suppressed because it is too large Load Diff

View File

@@ -1,379 +0,0 @@
# Momentry Chunk 資料結構說明
> **對象**: marcom 團隊
> **版本**: V1.0 | **日期**: 2026-03-25
---
## 1. 什麼是 Chunk
Chunk片段是影片處理後的最小單位。當影片上傳後系統會自動
1. **分析** - 偵測場景、人臉、姿態
2. **轉換** - 語音轉文字ASR
3. **分段** - 將內容切割成可搜尋的片段
4. **向量化** - 產生可搜尋的特徵向量
每個 Chunk 就是一個**可獨立搜尋的內容單位**。
---
## 2. Chunk 資料結構
### 2.1 主要欄位
| 欄位名 | 類型 | 說明 | 範例 |
|--------|------|------|------|
| `uuid` | 字串 (32) | 影片唯一識別碼 | `952f5854b9febad1` |
| `chunk_id` | 字串 (64) | Chunk 唯一識別碼 | `asr_00001` |
| `chunk_index` | 整數 | Chunk 順序號碼 | `1` |
| `chunk_type` | 字串 (32) | Chunk 類型 | `sentence` |
| `start_time` | 浮點數 | 開始時間(秒) | `12.5` |
| `end_time` | 浮點數 | 結束時間(秒) | `18.3` |
| `content` | JSONB | 詳細內容 | 見下方 |
| `vector_id` | 字串 (64) | 向量 ID | `vec_12345` |
| `text_content` | 文字 | 純文字內容 | `這是一段話` |
| `fps` | 浮點數 | 影片幀率 | `24.0` |
| `start_frame` | 整數 | 開始幀數 | `300` |
| `end_frame` | 整數 | 結束幀數 | `439` |
| `frame_count` | 整數 | 總幀數 | `139` |
### 2.2 Chunk 類型說明
| 類型 | ID | 說明 | 來源處理器 |
|------|-----|------|-----------|
| `sentence` | `sentence` | 語音轉文字片段 | ASR 處理 |
| `time` | `time_based` | 固定時間分段 | 系統自動切割 |
| `cut` | `cut` | 場景變化片段 | CUT 處理 |
| `trace` | `trace` | 軌跡追蹤片段 | YOLO 追蹤處理 |
| `story` | `story` | 故事線片段(父子區塊) | Story 分析處理 |
**父子區塊關係**
- `story` 是**父區塊**,可包含多個 `sentence``cut``trace` 子區塊
- 透過 `parent_chunk_id``child_chunk_ids` 建立階層關係
---
## 3. Content JSON 結構
每個 Chunk 的 `content` 欄位包含詳細的處理結果:
### 3.1 ASR Chunk語音轉文字
```json
{
"text": "今天天氣非常好,我們去郊外踏青吧",
"words": [
{
"word": "今天",
"start": 12.5,
"end": 12.8,
"confidence": 0.95
},
{
"word": "天氣",
"start": 12.8,
"end": 13.1,
"confidence": 0.92
}
],
"language": "zh-TW",
"speaker": null
}
```
### 3.2 Cut Chunk場景偵測
```json
{
"scenes": [
{
"scene_id": "cut_001",
"start_time": 12.5,
"end_time": 45.2,
"transition": "cut",
"confidence": 0.98
}
]
}
```
### 3.3 Trace Chunk軌跡追蹤
```json
{
"track_id": "track_001",
"object_class": "person",
"frames": [
{
"frame": 300,
"bbox": [120, 80, 200, 300],
"confidence": 0.95
},
{
"frame": 301,
"bbox": [122, 82, 202, 302],
"confidence": 0.94
}
],
"total_frames": 180
}
```
### 3.4 Story Chunk故事線
```json
{
"story_id": "story_001",
"title": "開場介紹",
"summary": "主持人介紹節目主題",
"child_chunk_ids": ["sentence_00001", "sentence_00002", "cut_00001"],
"tags": ["intro", "host"]
}
```
### 3.5 Metadata其他偵測資訊
人臉Face、文字辨識OCR、姿態Pose等偵測結果會附加在 `metadata` 欄位:
```json
{
"metadata": {
"faces": [
{
"bbox": [120, 80, 200, 180],
"confidence": 0.87,
"emotion": "neutral"
}
],
"ocr": {
"text": "MOMENTRY",
"confidence": 0.96
},
"pose": {
"keypoints": [
{"name": "nose", "x": 192, "y": 85, "confidence": 0.95}
]
}
}
}
```
---
## 4. 時間格式說明
### 4.1 秒數格式(常用)
```
格式: 秒.幀數
範例: 1234.60 = 第 1234 秒 + 第 60 幀
```
### 4.2 時間軸格式
```
格式: HH:MM:SS.FF
範例: 00:20:34.12 = 20分34秒12幀
```
### 4.3 幀數計算
```
幀數 = 秒數 × fps
例如: 12.5秒 × 24fps = 300幀
```
---
## 5. 實際資料範例
假設有一個影片,包含以下處理結果:
### 5.1 語音片段
```json
{
"uuid": "952f5854b9febad1",
"chunk_id": "asr_00001",
"chunk_type": "sentence",
"start_time": 12.5,
"end_time": 18.3,
"content": {
"text": "今天天氣非常好,我們去郊外踏青吧",
"language": "zh-TW"
},
"text_content": "今天天氣非常好,我們去郊外踏青吧",
"start_frame": 300,
"end_frame": 439,
"fps": 24.0
}
```
### 5.2 場景片段
```json
{
"uuid": "952f5854b9febad1",
"chunk_id": "cut_00001",
"chunk_type": "cut",
"start_time": 45.0,
"end_time": 120.5,
"content": {
"scenes": [{
"scene_id": "cut_001",
"transition": "cut",
"confidence": 0.98
}]
},
"start_frame": 1080,
"end_frame": 2892,
"fps": 24.0
}
```
---
## 6. 如何使用 Chunk
### 6.1 API 取得 Chunk
使用搜尋 API 取得 Chunk
```bash
curl -X POST "https://api.momentry.ddns.net/api/v1/search" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "關鍵字",
"limit": 10
}'
```
**指定影片搜尋**
```bash
curl -X POST "https://api.momentry.ddns.net/api/v1/search" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "關鍵字",
"uuid": "39567a0eb16f39fd",
"limit": 5
}'
```
### 6.2 搜尋相關片段
當使用者搜尋「天氣」時,系統會:
1. 將「天氣」轉換為向量
2. 在向量資料庫中搜尋相似向量
3. 找到相關的 Chunk
4. 返回時間軸和內容
### 6.3 播放指定片段
取得 Chunk 後可播放:
```
開始時間: 12.5 秒
結束時間: 18.3 秒
影片 UUID: 39567a0eb16f39fd
```
**播放器連結格式**
```
/player?uuid={uuid}&start={start_time}&end={end_time}
```
### 6.4 組合多個 Chunk
多個相關 Chunk 可以組合成一個章節或故事線。
### 6.5 Story Chunk父子關係
Story Chunk 可包含多個子 Chunk
```json
{
"chunk_id": "story_001",
"chunk_type": "story",
"content": {
"story_id": "story_001",
"title": "開場介紹",
"child_chunk_ids": ["sentence_00001", "sentence_00002", "cut_00001"]
}
}
```
---
## 7. API 回應格式
### /search 回應
```json
{
"results": [
{
"uuid": "39567a0eb16f39fd",
"chunk_id": "sentence_1471",
"chunk_type": "sentence",
"start_time": 5309.08,
"end_time": 5311.08,
"text": "influenced by a vital way,",
"score": 0.68
}
],
"query": "關鍵字"
}
```
### /n8n/search 回應
```json
{
"query": "關鍵字",
"count": 1,
"hits": [
{
"id": "sentence_1471",
"vid": "39567a0eb16f39fd",
"start": 5309.08,
"end": 5311.08,
"title": "Chunk sentence_1471",
"text": "influenced by a vital way,",
"score": 0.68,
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
}
]
}
```
> **注意**: `file_path` 是影片的實際路徑,可用於本地播放。
---
## 8. 快速參考
| 項目 | 說明 |
|------|------|
| UUID | 影片唯一識別碼16位 hex |
| Chunk ID | 片段識別碼(如 `sentence_00001` |
| chunk_type | 片段類型sentence/time/cut/trace/story |
| start_time | 開始時間(秒) |
| end_time | 結束時間(秒) |
| text_content | 純文字內容 |
| content | 詳細 JSON 結構 |
| metadata | 人臉、OCR、姿態等偵測結果 |
| parent_chunk_id | 父區塊 ID用於 story 區塊) |
| child_chunk_ids | 子區塊 ID 列表story 區塊專用) | |
---
## 附錄:版本歷史
| 版本 | 日期 | 內容 | 操作人 |
|------|------|------|--------|
| V1.0 | 2026-03-25 | 初版建立 | OpenCode |
| V1.1 | 2026-03-25 | 新增 API 取得 Chunk 方式、播放連結格式 | OpenCode |

View File

@@ -1,534 +0,0 @@
# Momentry Core 數據管理設計文檔 (v4)
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-17 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-17 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
---
## 0. 核心概念:雙 UUID 系統
為減少資料庫大小,在現有的 videos 表中增加內部 ID 映射:
### 0.1 設計原則
- **external_uuid**: 用戶可見的識別碼(如 UUID
- **id**: 資料庫自動產生的內部 ID (SERIAL),節省空間
- **映射關係**: 透過 videos 表的 `id` 欄位關聯
### 0.2 videos 表 (檔案映射表)
現有結構,增加 `id` 作為內部 ID
```sql
-- 現有 videos 表結構
CREATE TABLE videos (
id SERIAL PRIMARY KEY, -- 內部 ID (自動產生)
uuid VARCHAR(32) UNIQUE NOT NULL, -- 外部 UUID (用戶可見)
file_name VARCHAR(255) NOT NULL,
file_path TEXT,
duration DOUBLE PRECISION,
width INTEGER,
height INTEGER,
fps DOUBLE PRECISION,
probe_json JSONB,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_videos_uuid ON videos(uuid);
```
### 0.3 對照的好處
| 方式 | 儲存空間 (1000個視頻每個1000個chunk) |
|------|---------------------------------------|
| 直接用 uuid (32字元) | ~32MB |
| 使用 id (4字元) | ~4MB |
## 1. 數據流架構
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ 輸入階段 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 視頻文件 │→ │ ffprobe │ │ ASR │ │ YOLO │ │
│ │ (.mp4) │→ │ (probe) │→ │ (asr) │→ │ (yolo) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ ASRX │ │ CUT │ │ OCR │ │ FACE │ │
│ │ (asrx) │→ │ (cut) │→ │ (ocr) │→ │ (face) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ Pre-Chunk / Frame 階段 │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ pre_chunks 表 │ │
│ │ file_id → videos.id (FK) │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ type=sentence │ from asr, asrx │ 句子邊界範圍 │ │ │
│ │ │ type=cut │ from cut detection │ 場景切換範圍 │ │ │
│ │ │ type=time │ from time split │ 固定時間範圍 (10s) │ │ │
│ │ │ type=trace │ from yolo trace │ 物件追蹤範圍 │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ frames 表 │ │
│ │ file_id → videos.id (FK) │ │
│ │ - yolo 每幀識別結果 │ │
│ │ - ocr 每幀識別結果 │ │
│ │ - face 每幀識別結果 (如需要) │ │
│ │ - 單一圖像識別結果 → 直接入 frame │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ Chunk 階段 │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ chunks 表 │ │
│ │ file_id → videos.id (FK) │ │
│ │ │ │
│ │ 組合規則1: pre_chunk → chunk (直接轉換) │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ sentence_pre_chunk → sentence_chunk │ │ │
│ │ │ cut_pre_chunk → cut_chunk │ │ │
│ │ │ time_pre_chunk → time_chunk │ │ │
│ │ │ trace_pre_chunk → trace_chunk │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ 組合規則2: pre_chunk + frame 內容 → chunk (集合內容) │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ sentence_pre_chunk + 涵蓋範圍內的 frames → 豐富的 sentence_chunk │ │ │
│ │ │ time_pre_chunk + 涵蓋範圍內的 frames → 豐富的 time_chunk │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ Vector 階段 │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ PostgreSQL vectors │ │ Qdrant vectors │ │
│ │ (chunk_vectors) │ │ (chunk_v3) │ │
│ └──────────────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
```
## 2. Pre-Chunk 類型定義
### 2.1 Pre-Chunk 來源與類型對照表
| 來源類型 | source_type | 產出 Pre-Chunk Type | 說明 |
|---------|-------------|---------------------|------|
| ASR ( Whisper ) | asr | sentence | 句子邊界 |
| ASRX ( with timestamps ) | asrx | sentence | 帶時間戳的句子 |
| CUT (場景檢測) | cut | cut | 場景切換點 |
| TIME (固定時間) | time | time | 每 10 秒 |
| YOLO Trace | yolo_trace | trace | 物件追蹤軌跡 |
| YOLO (單幀) | yolo | **→ frame** | 不入 pre_chunk |
| OCR (單幀) | ocr | **→ frame** | 不入 pre_chunk |
| FACE (單幀) | face | **→ frame** | 不入 pre_chunk |
| PROBE | probe | metadata | 視頻元數據 |
### 2.2 Pre-Chunk Schema
```sql
CREATE TABLE pre_chunks (
id SERIAL PRIMARY KEY,
-- 檔案識別 (使用 videos 表的內部 ID 以節省空間)
file_id INTEGER NOT NULL REFERENCES videos(id),
-- 來源識別
source_type VARCHAR(32) NOT NULL, -- 'asr', 'asrx', 'cut', 'time', 'yolo_trace', 'probe'
source_file TEXT, -- 原始 JSON 文件路徑
-- Chunk 類型
chunk_type VARCHAR(32) NOT NULL, -- 'sentence', 'cut', 'time', 'trace'
-- 時間範圍
start_time DOUBLE PRECISION NOT NULL,
end_time DOUBLE PRECISION NOT NULL,
-- Frame 範圍 (精確到 frame)
start_frame INTEGER NOT NULL,
end_frame INTEGER NOT NULL,
-- FPS (用於 frame 計算)
fps DOUBLE PRECISION NOT NULL,
-- 原始 JSON 內容
raw_json JSONB NOT NULL,
-- 解析後的文字內容 (如有)
text_content TEXT,
-- 處理狀態
processed BOOLEAN DEFAULT FALSE,
chunk_id VARCHAR(64), -- 轉換後的 chunk_id
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(file_id, source_type, start_frame, end_frame)
);
CREATE INDEX idx_pre_chunks_file_id ON pre_chunks(file_id);
CREATE INDEX idx_pre_chunks_type ON pre_chunks(file_id, chunk_type);
CREATE INDEX idx_pre_chunks_time ON pre_chunks(file_id, start_time, end_time);
CREATE INDEX idx_pre_chunks_frame ON pre_chunks(file_id, start_frame, end_frame);
CREATE INDEX idx_pre_chunks_processed ON pre_chunks(file_id, processed);
```
## 3. Frame 管理原則
### 3.1 哪些數據進入 Frame
只儲存**單一圖像識別**的結果:
- YOLO 每幀檢測結果
- OCR 每幀識別結果
- FACE 每幀檢測結果
### 3.2 Frame Schema
```sql
CREATE TABLE frames (
id SERIAL PRIMARY KEY,
-- 檔案識別 (使用 videos 表的內部 ID 以節省空間)
file_id INTEGER NOT NULL REFERENCES videos(id),
frame_number INTEGER NOT NULL,
timestamp DOUBLE PRECISION NOT NULL,
fps DOUBLE PRECISION NOT NULL,
-- YOLO 結果 (JSONB 陣列)
yolo_objects JSONB,
-- OCR 結果 (JSONB 陣列)
ocr_results JSONB,
-- Face 結果 (JSONB 陣列)
face_results JSONB,
-- 原始幀圖像路徑 (可選)
frame_path TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(file_id, frame_number)
);
CREATE INDEX idx_frames_file_id ON frames(file_id);
CREATE INDEX idx_frames_frame ON frames(file_id, frame_number);
CREATE INDEX idx_frames_timestamp ON frames(file_id, timestamp);
```
## 4. Chunk 組合規則
### 4.1 組合規則 1: 直接轉換 (rule_1)
將 pre_chunk 直接轉換為 chunk
```
sentence_pre_chunk → sentence_chunk (rule: "rule_1")
cut_pre_chunk → cut_chunk (rule: "rule_1")
time_pre_chunk → time_chunk (rule: "rule_1")
trace_pre_chunk → trace_chunk (rule: "rule_1")
```
### 4.2 組合規則 2: 集合內容 (rule_2)
將 pre_chunk 與其時間區間內的所有 frame 識別結果集合:
```
sentence_pre_chunk + frames[在 start_time~end_time 範圍內] → 豐富的 sentence_chunk (rule: "rule_2")
time_pre_chunk + frames[在 start_time~end_time 範圍內] → 豐富的 time_chunk (rule: "rule_2")
```
### 4.3 Chunk Schema
```sql
CREATE TABLE chunks (
id SERIAL PRIMARY KEY,
-- 檔案識別 (使用 videos 表的內部 ID 以節省空間)
file_id INTEGER NOT NULL REFERENCES videos(id),
chunk_id VARCHAR(64) NOT NULL,
chunk_index INTEGER NOT NULL,
chunk_type VARCHAR(32) NOT NULL, -- 'sentence', 'cut', 'time', 'trace'
-- 組合規則 (payload 中記錄)
-- rule: 'rule_1' = 直接轉換, 'rule_2' = 集合內容
-- 時間範圍
start_time DOUBLE PRECISION NOT NULL,
end_time DOUBLE PRECISION NOT NULL,
-- Frame 範圍 (精確到 frame)
start_frame INTEGER NOT NULL,
end_frame INTEGER NOT NULL,
-- FPS
fps DOUBLE PRECISION NOT NULL,
-- 主要內容
text_content TEXT,
-- 完整內容 (JSONB) - 包含 rule 欄位
content JSONB NOT NULL,
-- 來源的 pre_chunk IDs
pre_chunk_ids INTEGER[],
-- 包含的 frame 數量
frame_count INTEGER DEFAULT 0,
-- 向量 ID
vector_id VARCHAR(64),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(file_id, chunk_id)
);
CREATE INDEX idx_chunks_file_id ON chunks(file_id);
CREATE INDEX idx_chunks_type ON chunks(file_id, chunk_type);
CREATE INDEX idx_chunks_time ON chunks(file_id, start_time, end_time);
CREATE INDEX idx_chunks_frame ON chunks(file_id, start_frame, end_frame);
CREATE INDEX idx_chunks_vector ON chunks(vector_id);
```
## 5. 處理流程範例
### 5.1 輸入數據
假設視頻長度 30 秒fps=30
| 來源 | 產出 |
|------|------|
| ASR | 3 個 sentence_pre_chunk (每句約 10s) |
| CUT | 2 個 cut_pre_chunk (場景 1, 場景 2) |
| TIME | 3 個 time_pre_chunk (0-10s, 10-20s, 20-30s) |
| YOLO | 900 個 frame 記錄 (每幀) |
| OCR | 依實際識別結果入 frame |
### 5.2 Chunk 產出
**使用規則 1 (直接轉換):**
- rule: "rule_1"
- 3 個 sentence_chunk
- 2 個 cut_chunk
- 3 個 time_chunk
**使用規則 2 (集合內容):**
- rule: "rule_2"
- 3 個 sentence_chunk (各含涵蓋時間範圍內的 yolo/ocr 結果)
- 3 個 time_chunk (各含涵蓋時間範圍內的 yolo/ocr 結果)
## 8. 數據示例
### 8.1 videos 表 (檔案映射)
```json
{
"id": 1,
"uuid": "abc123def456",
"file_name": "video_001.mp4",
"file_path": "/path/to/video_001.mp4",
"duration": 300.5,
"width": 1920,
"height": 1080,
"fps": 30.0
}
```
### 8.2 pre_chunks 表 (使用 file_id 關聯 videos)
```json
{
"file_id": 1,
"source_type": "asr",
"chunk_type": "sentence",
"start_time": 0.0,
"end_time": 5.5,
"start_frame": 0,
"end_frame": 165,
"fps": 30.0,
"raw_json": {...},
"text_content": "This is the first sentence"
}
```
### 8.3 frames 表 (使用 file_id 關聯 videos)
```json
{
"file_id": 1,
"frame_number": 300,
"timestamp": 10.0,
"fps": 30.0,
"yolo_objects": [
{"class": "person", "confidence": 0.9, "bbox": [100, 50, 200, 150]},
{"class": "car", "confidence": 0.85, "bbox": [50, 100, 150, 180]}
],
"ocr_results": [],
"face_results": []
}
```
### 8.4 chunks 表 (使用 file_id 關聯 videos)
```json
{
"file_id": 1,
"chunk_id": "sentence_0001",
"chunk_type": "sentence",
"rule": "rule_2",
"start_time": 10.0,
"end_time": 15.5,
"start_frame": 300,
"end_frame": 465,
"fps": 30.0,
"text_content": "The second sentence from the audio",
"content": {
"rule": "rule_2",
"asr_text": "The second sentence from the audio",
"objects": [
{"class": "person", "first_frame": 300, "last_frame": 450, "appears_in_frames": [300, 310, 320, ...]},
{"class": "car", "first_frame": 350, "last_frame": 465, "appears_in_frames": [350, 360, ...]}
],
"ocr": [...],
"faces": [...]
},
"pre_chunk_ids": [5],
"frame_count": 301
}
```
### 8.5 chunk_vectors 表 (使用 file_id 關聯 videos)
```json
{
"file_id": 1,
"chunk_id": "sentence_0001",
"chunk_type": "sentence",
"start_time": 10.0,
"end_time": 15.5,
"embedding": "[0.1, 0.2, ...]",
"metadata": {"text": "The second sentence..."}
}
```
### 8.6 Qdrant Payload
```json
{
"file_uuid": "abc123def456",
"chunk_id": "sentence_0001",
"chunk_type": "sentence",
"start_time": 10.0,
"end_time": 15.5,
"text": "The second sentence from the audio"
}
```
## 7. 向量管理原則
### 7.1 Vector Schema
```sql
-- Chunk 向量表 (PostgreSQL)
CREATE TABLE chunk_vectors (
id SERIAL PRIMARY KEY,
-- 檔案識別 (使用 videos 表的內部 ID 以節省空間)
file_id INTEGER NOT NULL REFERENCES videos(id),
chunk_id VARCHAR(64) NOT NULL,
chunk_type VARCHAR(32) NOT NULL,
-- 向量數據
embedding TEXT, -- JSON 格式的向量
embedding_vector VECTOR(768), -- pgvector 類型 (如可用)
-- 時間範圍 (用於時間查詢)
start_time DOUBLE PRECISION,
end_time DOUBLE PRECISION,
-- Metadata
metadata JSONB,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(chunk_id)
);
-- 索引
CREATE INDEX idx_chunk_vectors_file_id ON chunk_vectors(file_id);
```
### 7.2 Qdrant Collection
- Collection 名稱: `chunks_v3`
- Vector 維度: 768 (nomic-embed-text)
- Payload 包含: `file_uuid`, `chunk_id`, `chunk_type`, `start_time`, `end_time`, `text`
> **注意**: Qdrant 中仍使用 uuid (字串)因為需要可讀性和跨系統整合。PostgreSQL 內部使用 videos.id (整數) 以節省空間。
## 9. 設計原則總結
1. **單一圖像識別 → Frame**: yolo, ocr, face 等單幀識別結果直接入 frame 表
2. **時間序列識別 → Pre-Chunk**: asr, asrx, cut, time, trace 等有時間範圍的結果入 pre_chunk 表
3. **組合規則 1 (直接)**: pre_chunk → chunk (保持原有邊界)
4. **組合規則 2 (集合)**: pre_chunk + frames → chunk (加入識別內容)
5. **精確到 Frame**: 所有時間範圍都記錄 start_frame, end_frame
6. **雙向量存儲**: 同時支持 PostgreSQL 和 Qdrant
7. **跨視頻搜索**: 透過 videos 表的 uuid 進行搜索,內部使用 id 節省空間
8. **空間優化**: 內部表使用 videos.id (4 bytes) 而非 uuid (32 bytes)
## 10. 查詢範例
### 10.1 跨視頻搜索所有 chunk
```sql
-- 搜索所有視頻中包含 "hello" 的 chunk
SELECT c.*, v.uuid, v.file_name
FROM chunks c
JOIN videos v ON c.file_id = v.id
WHERE c.text_content ILIKE '%hello%';
```
### 10.2 查詢特定視頻的 chunk
```sql
-- 查詢 uuid 為 'abc123' 的視頻的所有 chunk
SELECT c.*
FROM chunks c
JOIN videos v ON c.file_id = v.id
WHERE v.uuid = 'abc123';
```
### 10.3 按時間範圍搜索
```sql
-- 搜索所有視頻在 10-20 秒範圍內的 chunk
SELECT c.*, v.uuid
FROM chunks c
JOIN videos v ON c.file_id = v.id
WHERE c.start_time >= 10.0 AND c.end_time <= 20.0;
```

File diff suppressed because it is too large Load Diff

View File

@@ -1,686 +0,0 @@
# Momentry Core API 示範手冊
| 項目 | 內容 |
|------|------|
| 建立者 | OpenCode |
| 建立時間 | 2026-03-25 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-25 | 創建示範手冊,包含 Demo API Key 與完整範例 | OpenCode | deepseek-reasoner |
---
**狀態**: 完成
---
## 快速開始
### Demo API Key
```
API Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69
Key ID: muser_68600856036340bcafc01930eb4bd839
過期日: 2027-03-25
```
### 測試連線
```bash
curl http://localhost:3002/health
```
```json
{"status":"ok","version":"0.1.0","uptime_ms":456464}
```
### 測試認證
```bash
curl -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
http://localhost:3002/api/v1/videos | jq '.videos | length'
```
```json
13
```
---
## 環境 URL
| 環境 | URL | 用途 |
|------|-----|------|
| **本地開發** | `http://localhost:3002` | 本機開發測試 |
| **外部訪問** | `https://api.momentry.ddns.net` | n8n/WordPress/curl 生產環境 |
---
## 端點總覽
| 方法 | 端點 | 說明 | 認證 |
|------|------|------|------|
| GET | `/health` | 健康檢查 | 公開 |
| GET | `/health/detailed` | 詳細健康檢查 | 公開 |
| POST | `/api/v1/register` | 註冊影片 | 需要 |
| POST | `/api/v1/probe` | 探測影片資訊 | 需要 |
| POST | `/api/v1/search` | 語意搜尋 | 需要 |
| POST | `/api/v1/n8n/search` | n8n 格式搜尋 | 需要 |
| POST | `/api/v1/search/hybrid` | 混合搜尋 | 需要 |
| GET | `/api/v1/videos` | 列出所有影片 | 需要 |
| GET | `/api/v1/lookup` | 查詢影片 UUID | 需要 |
| GET | `/api/v1/progress/:uuid` | 處理進度 | 需要 |
| GET | `/api/v1/jobs` | 任務列表 | 需要 |
| GET | `/api/v1/jobs/:uuid` | 任務詳情 | 需要 |
---
## 1. curl 範例
### 基本格式
```bash
curl -H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
URL
```
### 1.1 健康檢查(公開)
```bash
# 基本健康檢查
curl http://localhost:3002/health
# 詳細健康檢查(含服務狀態)
curl http://localhost:3002/health/detailed
```
### 1.2 列出影片
```bash
curl -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
http://localhost:3002/api/v1/videos | jq '.'
```
```json
{
"videos": [
{
"uuid": "952f5854b9febad1",
"file_name": "ExaSAN PCIe series - Director Ou Yu-Zhi Shares His Experience.mp4",
"duration": 159.637188,
"width": 640,
"height": 360
},
...
]
}
```
### 1.3 搜尋影片
```bash
curl -X POST \
-H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
-H "Content-Type: application/json" \
-d '{"query": "ExaSAN", "limit": 5}' \
http://localhost:3002/api/v1/search | jq '.'
```
```json
{
"results": [
{
"uuid": "952f5854b9febad1",
"chunk_id": "...",
"text": "...",
"score": 0.85,
"start_time": 0.0,
"end_time": 5.0
}
],
"total": 1,
"query": "ExaSAN",
"took_ms": 123
}
```
### 1.4 查詢進度
```bash
curl -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
http://localhost:3002/api/v1/progress/952f5854b9febad1 | jq '.'
```
```json
{
"uuid": "952f5854b9febad1",
"overall_progress": 67,
"current_processor": "yolo",
"processors": [
{"name": "asr", "status": "completed"},
{"name": "cut", "status": "completed"},
{"name": "yolo", "status": "running"}
]
}
```
---
## 2. n8n 範例
### 2.1 HTTP Request 節點設定
```
Method: POST
URL: https://api.momentry.ddns.net/api/v1/search
Authentication: None (使用 Header)
Headers:
┌─────────────────────┬──────────────────────────────────────────────────┐
│ Name │ Value │
├─────────────────────┼──────────────────────────────────────────────────┤
│ X-API-Key │ muser_68600856036340bcafc01930eb4bd839_... │
│ Content-Type │ application/json │
└─────────────────────┴──────────────────────────────────────────────────┘
Body Content (JSON):
{
"query": "{{ $json.search_term }}",
"limit": 5
}
```
### 2.2 n8n 搜尋 Workflow
```json
{
"nodes": [
{
"name": "Manual Trigger",
"type": "n8n-nodes-base.manualTrigger",
"position": [250, 300]
},
{
"name": "Set Search Term",
"type": "n8n-nodes-base.set",
"parameters": {
"values": {
"json": {
"search_term": "ExaSAN"
}
}
},
"position": [450, 300]
},
{
"name": "Search Videos",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"method": "POST",
"url": "https://api.momentry.ddns.net/api/v1/search",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "X-API-Key",
"value": "muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
}
]
},
"sendBody": true,
"bodyContentType": "json",
"specifyBody": "json",
"jsonBody": "={{ { \"query\": $json.search_term, \"limit\": 5 } }}"
},
"position": [650, 300]
},
{
"name": "Process Results",
"type": "n8n-nodes-base.code",
"parameters": {
"jsCode": "// Extract video results\nconst results = $input.first().json.results;\nreturn results.map(r => ({\n uuid: r.uuid,\n text: r.text,\n score: r.score,\n time: `${r.start_time}s - ${r.end_time}s`\n}));"
},
"position": [850, 300]
}
],
"connections": {
"Manual Trigger": {
"main": [[{"node": "Set Search Term"}]]
},
"Set Search Term": {
"main": [[{"node": "Search Videos"}]]
},
"Search Videos": {
"main": [[{"node": "Process Results"}]]
}
}
}
```
### 2.3 n8n 列出影片 Workflow
```json
{
"nodes": [
{
"name": "Get Videos",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"method": "GET",
"url": "https://api.momentry.ddns.net/api/v1/videos",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "X-API-Key",
"value": "muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
}
]
}
},
"position": [450, 300]
},
{
"name": "Extract Video List",
"type": "n8n-nodes-base.code",
"parameters": {
"jsCode": "const videos = $input.first().json.videos;\nreturn videos.map(v => ({\n json: {\n uuid: v.uuid,\n name: v.file_name,\n duration: Math.round(v.duration) + 's',\n resolution: `${v.width}x${v.height}`\n }\n}));"
},
"position": [650, 300]
},
{
"name": "Slack Notification",
"type": "n8n-nodes-base.slack",
"parameters": {
"channel": "#momentry",
"text": "=Found {{ $json.length }} videos:\n{{ $json.map(v => `• ${v.name} (${v.duration})`).join(`\n`) }}"
},
"position": [850, 300]
}
]
}
```
### 2.4 n8n 定時同步 Workflow
```json
{
"nodes": [
{
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"parameters": {
"rule": {
"interval": [{"field": "hours", "hours": 1}]
}
},
"position": [250, 300]
},
{
"name": "Get Pending Videos",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"method": "GET",
"url": "https://api.momentry.ddns.net/api/v1/videos"
},
"position": [450, 300]
},
{
"name": "Filter Processing",
"type": "n8n-nodes-base.filter",
"parameters": {
"conditions": {
"options": {"caseSensitive": true},
"conditions": [
{"id": "status", "leftValue": "{{ $json.status }}", "rightValue": "processing"}
]
}
},
"position": [650, 300]
}
]
}
```
---
## 3. WordPress 範例
### 3.1 PHP 函數庫
```php
<?php
/**
* Momentry API Client
*/
class Momentry_API {
private const API_URL = 'https://api.momentry.ddns.net';
private const API_KEY = 'muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69';
/**
* 發送 API 請求
*/
private function request(string $endpoint, array $data = [], string $method = 'GET'): array {
$url = self::API_URL . $endpoint;
$args = [
'headers' => [
'X-API-Key' => self::API_KEY,
'Content-Type' => 'application/json',
],
'timeout' => 30,
];
if ($method === 'POST') {
$args['method'] = 'POST';
$args['body'] = json_encode($data);
}
$response = wp_remote_request($url, $args);
if (is_wp_error($response)) {
throw new Exception($response->get_error_message());
}
return json_decode(wp_remote_retrieve_body($response), true);
}
/**
* 列出所有影片
*/
public function list_videos(): array {
return $this->request('/api/v1/videos');
}
/**
* 搜尋影片內容
*/
public function search(string $query, int $limit = 10): array {
return $this->request('/api/v1/search', [
'query' => $query,
'limit' => $limit,
], 'POST');
}
/**
* 取得影片進度
*/
public function get_progress(string $uuid): array {
return $this->request("/api/v1/progress/{$uuid}");
}
/**
* 檢查健康狀態
*/
public function health_check(): array {
return $this->request('/health');
}
}
```
### 3.2 短代碼 (Shortcode)
```php
<?php
/**
* WordPress 短代碼範例
*/
// 註冊短代碼
add_shortcode('momentry_videos', function($atts) {
$atts = shortcode_atts([
'limit' => 10,
], $atts);
$api = new Momentry_API();
try {
$result = $api->list_videos();
$videos = array_slice($result['videos'], 0, $atts['limit']);
ob_start();
?>
<div class="momentry-videos">
<h3>影片列表</h3>
<ul>
<?php foreach ($videos as $video): ?>
<li>
<strong><?= esc_html($video['file_name']) ?></strong>
<br>
<small>
UUID: <?= esc_html($video['uuid']) ?>
| 時長: <?= gmdate("H:i:s", $video['duration']) ?>
</small>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php
return ob_get_clean();
} catch (Exception $e) {
return '<p class="error">載入失敗: ' . esc_html($e->getMessage()) . '</p>';
}
});
// 搜尋短代碼
add_shortcode('momentry_search', function($atts, $content = '') {
$query = sanitize_text_field($content);
if (empty($query)) {
return '<p>請提供搜尋關鍵字</p>';
}
$api = new Momentry_API();
try {
$result = $api->search($query);
ob_start();
?>
<div class="momentry-search-results">
<h3>「<?= esc_html($query) ?>」搜尋結果</h3>
<?php if (empty($result['results'])): ?>
<p>沒有找到相關結果</p>
<?php else: ?>
<ul>
<?php foreach ($result['results'] as $item): ?>
<li>
<a href="/video/<?= esc_attr($item['uuid']) ?>?t=<?= (int)$item['start_time'] ?>">
<?= esc_html($item['text']) ?>
</a>
<br>
<small>相似度: <?= round($item['score'] * 100) ?>%</small>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
<?php
return ob_get_clean();
} catch (Exception $e) {
return '<p class="error">搜尋失敗: ' . esc_html($e->getMessage()) . '</p>';
}
});
```
### 3.3 使用方式
在 WordPress 頁面或文章中:
```
[momentry_videos limit="5"]
[momentry_search]ExaSAN[/momentry_search]
```
### 3.4 REST API 整合
```php
<?php
/**
* 註冊 WordPress REST API 端點
*/
add_action('rest_api_init', function() {
register_rest_route('momentry/v1', '/search', [
'methods' => 'GET',
'callback' => function(WP_REST_Request $request) {
$query = sanitize_text_field($request->get_param('q'));
if (empty($query)) {
return new WP_Error('missing_query', '需要搜尋關鍵字', ['status' => 400]);
}
$api = new Momentry_API();
$result = $api->search($query);
return new WP_REST_Response($result, 200);
},
'permission_callback' => '__return_true',
]);
});
// 使用方式: GET /wp-json/momentry/v1/search?q=ExaSAN
```
---
## 4. 疑難排解
### 4.1 常見錯誤
| 錯誤 | 原因 | 解決方案 |
|------|------|----------|
| `401 Unauthorized` | API Key 無效或過期 | 檢查 API Key 是否正確 |
| `500 Internal Server Error` | 伺服器錯誤 | 檢查 `/health/detailed` 服務狀態 |
| `Connection Timeout` | 網路問題 | 確認 `api.momentry.ddns.net` 可達 |
### 4.2 測試腳本
```bash
#!/bin/bash
# test_api.sh - Momentry API 測試腳本
API_KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
BASE_URL="http://localhost:3002"
echo "=== 1. 健康檢查 ==="
curl -s "$BASE_URL/health" | jq .
echo ""
echo "=== 2. 列出影片 ==="
curl -s -H "X-API-Key: $API_KEY" "$BASE_URL/api/v1/videos" | jq '.videos | length'
echo ""
echo "=== 3. 搜尋測試 ==="
curl -s -X POST -H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "test", "limit": 3}' \
"$BASE_URL/api/v1/search" | jq '.results | length'
echo ""
echo "=== 完成 ==="
```
### 4.3 驗證腳本
```bash
#!/bin/bash
# verify_auth.sh - 驗證 API Key
API_KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
BASE_URL="http://localhost:3002"
# 測試 1: 無 API Key
echo "測試 1: 無 API Key"
RESULT=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/api/v1/videos")
[ "$RESULT" = "401" ] && echo "✅ 正確拒絕 (401)" || echo "❌ 預期 401實際 $RESULT"
# 測試 2: 有 API Key
echo "測試 2: 有 API Key"
RESULT=$(curl -s -H "X-API-Key: $API_KEY" "$BASE_URL/api/v1/videos")
echo "$RESULT" | jq -e '.videos' > /dev/null && echo "✅ 成功取得資料" || echo "❌ 取得資料失敗"
# 測試 3: 無效 API Key
echo "測試 3: 無效 API Key"
RESULT=$(curl -s -o /dev/null -w "%{http_code}" -H "X-API-Key: invalid_key" "$BASE_URL/api/v1/videos")
[ "$RESULT" = "401" ] && echo "✅ 正確拒絕 (401)" || echo "❌ 預期 401實際 $RESULT"
```
---
## 5. API Key 管理
### 5.1 建立新 API Key
```bash
# 本地建立
./target/release/momentry api-key create "My App" --key-type user --ttl 90
```
### 5.2 列出 API Keys
```bash
./target/release/momentry api-key list
```
### 5.3 驗證 API Key
```bash
./target/release/momentry api-key validate --key "YOUR_API_KEY"
```
### 5.4 撤銷 API Key
```bash
./target/release/momentry api-key revoke --key "YOUR_API_KEY"
```
---
## 附錄
### A. 影片 UUID 說明
UUID 是基於檔案路徑的 SHA256 哈希前 16 位:
```
/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4
SHA256 Hash
9760d0820f0cf9a7
```
### B. 處理器狀態
| 狀態 | 說明 |
|------|------|
| `pending` | 等待處理 |
| `running` | 處理中 |
| `completed` | 已完成 |
| `failed` | 失敗 |
### C. 支援的處理器
- **ASR**: 語音識別
- **CUT**: 場景剪切
- **YOLO**: 物件偵測
### D. 聯絡支援
- Email: support@momentry.ddns.net
- 文件: https://docs.momentry.ddns.net
- GitHub: https://github.com/anomalyco/momentry

View File

@@ -1,540 +0,0 @@
# Momentry Core 開發日誌
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-18 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-18 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
---
> **文檔維護開始**2026-03-18
> **⚠️ 補充說明**事後補記2026-03-18 以前),僅供參考。未來紀錄將即時記錄,參考價值較高。
---
## 開發工具
### Coding LLM 模型
| 階段 | 工具 | 模型 | ID | 說明 |
|------|------|------|-----|------|
| **初期** | Claude CLI | - | - | 初始專案架構建立 |
| **中期** | OpenCode | big-pickle | opencode/big-pickle | 主要開發協作者 |
**切換記錄**
- 初期使用 Claude CLI 建立專案基本架構
- 中期切換至 OpenCode (big-pickle) 進行主要功能開發
---
## 2026-03-17
### ML 模型選用
| Processor | 模型 | 版本/大小 | 說明 |
|----------|------|-----------|------|
| **ASR** | WhisperX (faster-whisper) | base, int8 | 語音識別 + 對話分段 |
| **CUT** | PySceneDetect | 0.6.7.1 | ContentDetector 場景檢測 |
| **YOLO** | YOLOv8n | yolov8n.pt (6.2MB) | 物體檢測nano 版本最快) |
| **OCR** | EasyOCR | 1.7.2 | 文字識別 |
| **Face** | OpenCV Haar Cascade | built-in | 人臉檢測(無需額外下載) |
| **Pose** | YOLOv8n-Pose | yolov8n-pose.pt (6.5MB) | 姿態估計nano 版本) |
**模型下載**
- YOLOv8n: `yolov8n.pt` (6.2MB)
- YOLOv8n-Pose: `yolov8n-pose.pt` (6.5MB)
**Python 依賴**
```
torch==2.8.0
whisperx==3.8.2
ultralytics==8.4.23
scenedetect==0.6.7.1
easyocr==1.7.2
opencv-python==4.13.0.92
```
---
### ASR 實作完成
- 完成 Python ML processor scripts使用本地模型
- `asrx_processor.py` - whisperx for speaker diarization
- `cut_processor.py` - PySceneDetect for scene detection
- `yolo_processor.py` - YOLOv8 for object detection
- `ocr_processor.py` - EasyOCR for text recognition
- `face_processor.py` - OpenCV Haar Cascade for face detection
- `pose_processor.py` - YOLOv8 Pose for pose estimation
- 更新 `requirements.txt` with all dependencies
- 安裝完成torch 2.8.0, whisperx 3.8.2, ultralytics 8.4.23, scenedetect 0.6.7.1, easyocr 1.7.2, opencv-python 4.13.0.92
- 下載模型YOLOv8n.pt (6.2MB), YOLOv8n-Pose.pt (6.5MB)
### Async Streaming 實作
- 更新 Rust processor modules 使用 async streaming 進行 real-time progress
- `src/core/processor/asr.rs`
- `src/core/processor/cut.rs`
- `src/core/processor/yolo.rs`
- `src/core/processor/ocr.rs`
- `src/core/processor/face.rs`
- `src/core/processor/pose.rs`
### 測試結果
- 測試影片BigBuckBunny_320x180.mp4
- ASR: 4 segments
- CUT: 134 scenes
- YOLO: 14315 frames每幀處理耗時
- OCR: 40 frames with text
- Face: 44 frames with faces
- Pose: Timeout
---
### Warning 清理
修復 clippy warnings
- 移除未使用的 imports (HashMap in mongodb_db.rs, postgres_db.rs)
- 新增 `#[allow(dead_code)]` 標註未使用變數
- 新增 `Default` implementation for MongoDb, QdrantDb
-`probe` module 重新命名為 `ffprobe`
- 新增 `player` feature in Cargo.toml
- 修復 `format_in_format_args` 警告
---
### TUI Progress Window 實作
建立新的 UI module
- 建立 `src/ui/mod.rs`
- 建立 `src/ui/progress/mod.rs`
實作功能:
- ProcessorProgress 結構(追蹤每個 processor 狀態)
- ProgressState 結構(管理所有 processors
- ProgressUi 結構ratatui TUI 渲染)
- 整合到 `src/main.rs` 的 process 命令
TUI 顯示:
```
┌ Processing: BigBuckBunny_320x180.mp4 ────────────────────────────────────────┐
│ ASR [████████████] 100% (4 segs) │
│ CUT [████████████] 100% (134 scenes) │
│ ASRX [████████████] 100% (0 segs) │
│ YOLO [██░░░░░░░░░░░] 30% (4200/14315) ETA 2:30 │
│ OCR [---------] 0% │
│ Face [---------] 0% │
│ Pose [---------] 0% │
└──────────────────────────────────────────────────────────────────────────────┘
```
---
### 輸出位置討論
討論 stdout vs stderr vs TUI 的輸出配置:
- 最終結果 → stdout
- Python progress → 需改用 Redis Pub/Sub
- TUI Progress → stderr (ratatui)
---
## 2026-03-18
### Redis Message Bus 設計
討論使用 Redis 作為消息總線,分離 Python 輸出與 Rust TUI 顯示。
設計重點:
1. 頻道命名:`momentry:progress:{uuid}`
2. 本地 Redis`localhost:6379`
3. 失敗策略:完全失效(因 stdout 問題未解決)
### UUID 使用時機分析
分析 Redis Key 上使用 UUID 的時機:
**全局 Keys無 UUID**
- health, stats, jobs 管理
**Per-Video KeysUUID 必要)**
- job:{uuid}, progress:{uuid}, metrics:{uuid}
**Per-Processor KeysUUID + Processor 必要)**
- job:{uuid}:processor:{name}
### 備份系統整合
參考 `docs/SERVICE_ADDITION_GUIDE.md` 設計規範,規劃 OutputDir 模組:
1. **環境變數**
- `MOMENTRY_OUTPUT_DIR` - JSON 輸出目錄
- `MOMENTRY_BACKUP_DIR` - 備份目錄(預設:`/Users/accusys/momentry/backup/momentry`
- `MOMENTRY_BACKUP_ENABLED` - 啟用備份
2. **命名格式**
- 備份格式:`momentry_data_{YYYYMMDD}_{HHMMSS}_{uuid}.{ext}`
- 校驗和:`{filename}.sha256`
3. **CLI 命令**
- `cargo run -- backup list` - 列出備份
- `cargo run -- backup cleanup` - 清理舊備份
- `cargo run -- backup verify` - 驗證備份
---
### 監控系統整合
討論將 momentry_core 納入監控系統:
1. **Layer 2: Service 監控**
- 新增 momentry_core CLI 檢查
2. **Layer 7: Backup 監控**
- 新增 momentry 備份配置
3. **Redis 監控**
- 健康檢查
- Job 狀態監控
- 即時進度監控
---
## 實作完成項目
### 程式碼變更
| 日期 | 檔案 | 變更 |
|------|------|------|
| 2026-03-17 | `src/core/processor/*.rs` | Async streaming 更新 |
| 2026-03-17 | `src/ui/mod.rs` | 新增 UI module |
| 2026-03-17 | `src/ui/progress/mod.rs` | 新增 Progress TUI |
| 2026-03-17 | `src/main.rs` | 整合 Progress UI |
| 2026-03-18 | `src/core/storage/output_dir.rs` | 新增 OutputDir 模組 |
| 2026-03-18 | `src/core/storage/mod.rs` | 新增 output_dir export |
| 2026-03-18 | `src/core/db/redis_client.rs` | 新增 Redis 客戶端Hash + Pub/Sub |
| 2026-03-18 | `src/core/db/mod.rs` | 新增 redis_client export |
### 新增檔案
| 日期 | 檔案 | 說明 |
|------|------|------|
| 2026-03-18 | `docs/MOMENTRY_CORE_REDIS_KEYS.md` | Redis Key 設計規範 |
| 2026-03-18 | `docs/MOMENTRY_CORE_MONITORING.md` | 監控規範(暫定) |
| 2026-03-18 | `scripts/redis_publisher.py` | Redis 訊息發布模組 |
### 更新檔案
| 日期 | 檔案 | 說明 |
|------|------|------|
| 2026-03-17 | `Cargo.toml` | 新增 player feature |
| 2026-03-17 | `src/lib.rs` | 新增 ui module exports |
| 2026-03-18 | `docs/PENDING_ISSUES.md` | 新增問題 #2, #3 |
| 2026-03-18 | `src/core/storage/output_dir.rs` | 預設改為 `./output` |
| 2026-03-18 | `scripts/yolo_processor.py` | 新增 --uuid 參數 + Redis |
| 2026-03-18 | `scripts/cut_processor.py` | 新增 Redis |
| 2026-03-18 | `scripts/ocr_processor.py` | 新增 Redis |
| 2026-03-18 | `scripts/face_processor.py` | 新增 --uuid 參數 + Redis |
| 2026-03-18 | `scripts/pose_processor.py` | 新增 --uuid 參數 + Redis |
| 2026-03-18 | `scripts/asr_processor.py` | 新增 --uuid 參數 + Redis |
| 2026-03-18 | `scripts/asrx_processor.py` | 新增 --uuid 參數 + Redis |
| 2026-03-18 | `requirements.txt` | 新增 redis>=5.0.0 |
| 2026-03-18 | `src/core/processor/yolo.rs` | 新增 uuid 參數 |
| 2026-03-18 | `src/core/processor/cut.rs` | 新增 uuid 參數 |
| 2026-03-18 | `src/core/processor/ocr.rs` | 新增 uuid 參數 |
| 2026-03-18 | `src/core/processor/face.rs` | 新增 uuid 參數 |
| 2026-03-18 | `src/core/processor/pose.rs` | 新增 uuid 參數 |
| 2026-03-18 | `src/core/processor/asr.rs` | 新增 uuid 參數 |
| 2026-03-18 | `src/core/processor/asrx.rs` | 新增 uuid 參數 |
| 2026-03-18 | `src/main.rs` | 更新所有 processor 調用傳入 uuid |
| 2026-03-18 | `Cargo.toml` | 新增 futures-util 依賴 |
| 2026-03-18 | `src/core/db/redis_client.rs` | 新增 subscribe_and_callback 方法,密碼認證 |
| 2026-03-18 | `src/ui/progress/mod.rs` | 新增 update_from_redis 方法 |
| 2026-03-18 | `scripts/redis_publisher.py` | 新增密碼認證支援 |
| 2026-03-18 | 測試 | Redis Pub/Sub 成功運作 |
---
## 待解決問題
### 問題 #1: sqlx async INSERT 不會實際寫入數據庫
- 狀態:待解決
- 影響:`store_vector` 函數PVector 存儲
### 問題 #2: TUI 與 stdout 輸出混合
- 狀態:已解決
- 解決方案:使用 Redis Message Bus
- 進度:
- ✅ Redis 客戶端 (`src/core/db/redis_client.rs`)
- ✅ Python redis_publisher.py
- ✅ 所有 Python processors 更新完成
- ✅ 所有 Rust processor 函數更新完成
- ✅ main.rs 調用更新完成
- ✅ Rust TUI Redis 訂閱已完成
### 問題 #3: Redis Message Bus 尚未實作
- 狀態:已解決
- 詳細設計:參考 `docs/MOMENTRY_CORE_REDIS_KEYS.md`
- 進度Python 端 + Rust 端均已完成
---
## 環境變數
```bash
# 輸出目錄
MOMENTRY_OUTPUT_DIR=./output # 預設
# 備份
MOMENTRY_BACKUP_ENABLED=false # 預設
MOMENTRY_BACKUP_DIR=/Users/accusys/momentry/backup/momentry
# Redis未來實作
REDIS_URL=redis://localhost:6379
REDIS_PASSWORD=accusys
```
---
## 數據庫
- PostgreSQL: `postgres://accusys@localhost:5432/momentry`
- Redis: `localhost:6379`(待實作)
- Qdrant: `localhost:6333`
---
## 指令範例
```bash
# 註冊視頻
cargo run -- register /path/to/video.mp4
# 處理視頻
cargo run -- process <uuid>
# 列出備份
cargo run -- backup list
# 清理備份
cargo run -- backup cleanup
# 驗證備份
cargo run -- backup verify
# 查看狀態
cargo run -- status
# API Server
cargo run -- server --host 0.0.0.0 --port 3000
```
---
## 2026-03-18 (進行中)
### Redis Message Bus 實作
**問題**TUI 與 Python stdout 輸出混合,導致 TUI 顯示混亂
**解決方案**:使用 Redis Pub/Sub 作為訊息匯流排
**實作內容**
| 元件 | 檔案 | 狀態 |
|------|------|------|
| Redis 客戶端 | `src/core/db/redis_client.rs` | ✅ |
| Progress 訂閱 | `src/main.rs` | ✅ |
| UI 更新 | `src/ui/progress/mod.rs` | ✅ |
| Python Publisher | `scripts/redis_publisher.py` | ✅ |
| Python Processors | 7 個 `scripts/*_processor.py` | ✅ |
| Rust 函數 | `src/core/processor/*.rs` | ✅ |
**流程**
```
Python Processor ──(Redis Pub)──> Redis ──(Subscribe)──> Rust TUI
```
**測試結果**
- Redis 連線 ✅
- 密碼認證 ✅
- 即時進度發布 ✅
- TUI 即時更新 ✅
**新增依賴**
- `futures-util = "0.3"` (Cargo.toml)
- `redis >= 5.0.0` (requirements.txt)
---
## 2026-03-18 (HTTP API)
### HTTP API 實作
**問題**TUI 運作正常但使用者偏好 HTTP API 來查詢進度
**解決方案**:建立 HTTP 端點 + Redis Hash 儲存
**實作內容**
| 元件 | 檔案 | 變更 |
|------|------|------|
| HTTP 端點 | `src/api/server.rs` | 新增 `/api/v1/progress/:uuid` |
| Redis Hash 查詢 | `src/core/db/redis_client.rs` | 新增 `get_processor_status` 方法 |
| Progress 儲存 | `src/main.rs` | 新增 Redis HSET 儲存進度 |
**API 端點**
```
GET /api/v1/progress/:uuid
Response:
{
"uuid": "5dea6618a606e7c7",
"processors": [
{"name": "asr", "status": "complete", "current": 0, "total": 0, "message": "7 segments"},
{"name": "cut", "status": "complete", "current": 134, "total": 134, "message": "134 scenes"},
{"name": "yolo", "status": "complete", "current": 14300, "total": 14315, "message": "..."},
...
]
}
```
**流程**
```
Python Processor ──(Redis Pub)──> Redis ──(Subscribe)──> Rust TUI
└──(HSET)──> Redis Hash
HTTP Client ──(GET /progress/:uuid)──> Rust API ─(HGETALL)──> Redis Hash
```
**測試結果**
- ✅ 編譯成功
- ✅ API 伺服器啟動 (port 3002)
- ✅ 即時進度查詢
- ✅ 完整流程測試 (BigBuckBunny_320x180.mp4)
**除錯記錄**
1. 語法錯誤main.rs 有重複程式碼區塊 (lines 297-322),已移除
2. DB 連線池:從 5 增加到 10 個連線
3. PostgreSQL 狀態:處理 shutdown 狀態,殺掉 stale 連線
**新增變更**
- `src/api/server.rs` - 新增進度端點
- `src/core/db/redis_client.rs` - 新增 `get_processor_status` 方法
- `src/core/db/postgres_db.rs` - 連線池 5→10
- `src/main.rs` - Redis Hash 儲存 + 語法修復
**使用方式**
```bash
# 啟動 API 伺服器
cargo run --bin momentry -- server --host 127.0.0.1 --port 3002
# 註冊影片
cargo run --bin momentry -- register ~/test_video/BigBuckBunny_320x180.mp4
# 處理影片
cargo run --bin momentry -- process <uuid>
# 查詢進度
curl http://127.0.0.1:3002/api/v1/progress/<uuid>
```
---
## 2026-03-18 (Dashboard)
### Web Dashboard 實作
**目標**:建立 Web 介面監控 momentry_core 處理進度
**技術選擇**Static HTML + JavaScript (非 WASM)
**實作內容**
| 元件 | 檔案 | 說明 |
|------|------|------|
| Dashboard | `momentry_dashboard/dist/index.html` | 靜態 HTML 頁面 |
| API 代理 | Caddyfile port 3200 | 反向代理到 API server |
**功能**
- 影片列表顯示
- 即時進度條 (每 5 秒自動刷新)
- 搜尋功能
- 處理器狀態 (ASR/CUT/YOLO/OCR/Face/Pose)
**訪問**
- Dashboard: http://localhost:3200
- API: http://localhost:3200/api/v1/*
---
## 發生問題記錄
### HTTP API 問題
1. **語法錯誤** (main.rs)
- 位置lines 297-322
- 原因:重複的程式碼區塊
- 解決:移除重複區塊
2. **DB 連線池耗盡**
- 原因:預設 5 個連線不足
- 解決:增加到 10 個連線
3. **PostgreSQL shutdown 狀態**
- 原因:共享記憶體未釋放
- 解決:殺掉 stale 連線
### WASM Dashboard 問題
1. **Yew 版本問題**
- 嘗試yew 0.21 → 0.23
- 問題feature 名稱變更 (`web-sys``web_sys``csr`)
- 解決:放棄 WASM改用靜態 HTML
2. **編譯錯誤**
- `wasm32-unknown-unknown` target 未安裝
- 解決:`rustup target add wasm32-unknown-unknown`
3. **Yew 0.23 API 變更**
- Properties 需要 PartialEq derive
- 多處 API 語法變更
- 放棄 WASM 方案
### Gitea Push 問題
1. **Remote URL 錯誤**
- 原因:使用 localhost:3000 而非 gitea.momentry.ddns.net
- 解決:建立新 repo `momentry_core_0_1`
2. **認證問題**
- SSH key 未授權
- 密碼認證成功推送
### Caddy 設定問題
1. **API 代理順序**
- 問題try_files 在 reverse_proxy 之前導致 API 回傳 HTML
- 解決:使用 `handle` 區塊明確定義順序
```caddyfile
:3200 {
handle /api/* {
reverse_proxy localhost:3002
}
handle {
root * /Users/accusys/momentry_dashboard/dist
try_files {path} /index.html
file_server
}
}
```
---
## 未來工作
- [ ] 修復 WASM Dashboard (Yew 0.23 相容性)
- [ ] 新增影片播放器整合
- [ ] WebSocket 實時推送
- [ ] 移動端響應式設計

View File

@@ -1,474 +0,0 @@
# 文件創建規範
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-18 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-18 | 創建文件規範 | Warren | OpenCode / MiniMax M2.5 |
---
本文檔定義 Momentry Core 專案中文件的命名規範、格式標準和結構要求。
---
## 1. 檔案命名規範
### 命名模式
所有文件必須使用以下命名模式:
| 文件類型 | 模式 | 範例 |
|----------|------|------|
| 安裝指南 | `INSTALL_<NAME>.md` | `INSTALL_POSTGRESQL.md` |
| 開發指南 | `DEVELOP_<NAME>.md` | `DEVELOP_API.md` |
| API 參考 | `API_REFERENCE.md` | `API_REFERENCE.md` |
| 規格文件 | `<NAME>_SPEC.md` | `CHUNK_SPEC.md` |
| 設計文件 | `<NAME>_DESIGN.md` | `CHUNK_DESIGN.md` |
| 服務總覽 | `SERVICES.md` | `SERVICES.md` |
| 其他文件 | `<NAME>.md` | `README.md` |
### 命名規則
- 使用 **大駝峰** (PascalCase) 命名法
- 服務名稱使用 **全大寫** (e.g., `POSTGRESQL`, `SFTPGO`)
- 英文優先,縮寫保持大寫
- 使用底線 `_` 作為單詞分隔符
- 副檔名統一使用 `.md` (Markdown)
### 禁止事項
- 不允許使用中文檔名
- 不允許空格
- 不允許混合大小寫 (如 `Install_PostgreSQL.md`)
---
## 2. 文件結構模板
### 安裝指南結構
```markdown
# <服務名稱> 安裝指南 (部署類型)
## 概述
本文檔說明如何...
---
## 當前狀態
| 項目 | 狀態 |
|------|------|
| <服務名> | ✅ 已安裝 v<版本號> |
| Port | <端口號> |
| ... | ... |
---
## 安裝步驟
### Step 1: <步驟名稱>
<說明內容>
```bash
# 代碼範例
command --option value
```
### Step 2: <步驟名稱>
...
---
## 卸載步驟
### Step 1: <步驟名稱>
...
---
## 故障排除
### <問題名稱>
<解決方案>
---
## 檔案位置
| 類型 | 路徑 | 說明 |
|------|------|------|
| 安裝 | /path/to/install | 說明 |
...
---
## 常用指令
```bash
# 驗證
command verify
# 查看版本
command --version
```
---
## 版本資訊
- 版本: <版本號>
- 安裝日期: <日期>
```
---
### 規格文件結構
```markdown
# <名稱> 規格文件
## 概述
<簡短描述>
---
## 詳細規格
### 1. <功能模組>
#### 欄位定義
| 欄位 | 類型 | 必填 | 說明 |
|------|------|------|------|
| field1 | string | Yes | 說明 |
#### 資料結構
```json
{
"example": "data"
}
```
---
## 限制條件
- <限制1>
- <限制2>
---
## 相關文件
- `RELATED_FILE.md` - 相關說明
```
---
## 3. 格式標準
### Markdown 格式
| 項目 | 標準 |
|------|------|
| 標題層級 | H1 (`#`) → H2 (`##`) → H3 (`###`) |
| 水平線 | 使用 `---` 分隔主要章節 |
| 程式碼區塊 | 使用三個反引號 ``` 並標註語言 |
| 表格 | 使用 `|` 和 `-` 對齊 |
| 強調 | 使用 `**粗體**` 和 `*斜體*` |
### 程式碼區塊語言標註
```bash
# Bash
```bash
command
```
```json
# JSON
```json
{"key": "value"}
```
```rust
# Rust
```rust
fn main() {}
```
```yaml
# YAML
key: value
```
### 表格格式
```markdown
| Header 1 | Header 2 | Header 3 |
|----------|----------|----------|
| Cell 1 | Cell 2 | Cell 3 |
| Cell 4 | Cell 5 | Cell 6 |
```
### 列表格式
- 使用 `-` 作為無序列表標記
- 使用數字 `1.` 作為有序列表標記
- 縮進使用 2 個空格
---
## 4. 語言規範
### 標題語言
| 區域 | 語言 |
|------|------|
| 主要內容 | 繁體中文 |
| 技術術語 | 英文保留 |
| 命令和代碼 | 英文 |
| 文件標題 | 繁體中文 |
### 常用術語對照
| 英文 | 中文 |
|------|------|
| Install | 安裝 |
| Configure/Config | 配置/設定 |
| Uninstall | 卸載 |
| Troubleshooting | 故障排除 |
| Status | 狀態 |
| Documentation | 文件 |
| Guide | 指南 |
| Overview | 概述 |
| Specification | 規格 |
| Current Status | 當前狀態 |
| Default | 預設 |
| Required | 必填 |
| Optional | 選填 |
| Example | 範例 |
### 標點符號
- 中文內容使用全形標點:```。```````
- 英文/程式內容使用半形標點:`:``(``)`
- 命令行使用 `` `command` `` 格式
---
## 5. 內容要求
### 必需章節
每份文件必須包含:
1. **標題** - 文件名稱
2. **概述** - 檔案用途說明
3. **版本/狀態資訊** - 當前狀態
4. **檔案位置** - 重要路徑列表
5. **常用指令** - 基本操作命令
### 版本資訊格式
每份文件頂部必須包含以下資訊:
```markdown
| 項目 | 內容 |
|------|------|
| 建立者 | <姓名> |
| 建立時間 | <YYYY-MM-DD> |
| 文件版本 | V1.0 |
```
版本歷史表:
```markdown
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-18 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
```
---
### 版本資訊章節格式
```markdown
---
## 版本資訊
- 版本: <版本號>
- 安裝日期: <YYYY-MM-DD>
- 文件更新: <YYYY-MM-DD>
```
### 狀態標記
| 狀態 | 標記 |
|------|------|
| 已安裝 | ✅ 已安裝 v<x.x.x> |
| 未安裝 | ❌ 未安裝 |
| 可選 | ⚙️ 可選 |
| 進行中 | 🔄 進行中 |
---
## 6. 示例文件
### 正確範例
```markdown
# PostgreSQL 安裝指南 (本地部署)
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-18 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-18 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
---
## 概述
本文檔說明如何在 macOS 上安裝 PostgreSQL...
---
## 當前狀態
| 項目 | 狀態 |
|------|------|
| PostgreSQL | ✅ 已安裝 v16.2 |
| Port | 5432 |
---
## 安裝步驟
### Step 1: 安裝 PostgreSQL
```bash
brew install postgresql@16
```
### Step 2: 啟動服務
```bash
brew services start postgresql@16
```
---
## 檔案位置
| 類型 | 路徑 |
|------|------|
| 配置文件 | /path/to/config |
| 數據目錄 | /path/to/data |
---
## 版本資訊
- 版本: 16.2
- 安裝日期: 2026-03-01
```
### 錯誤範例
```
❌ PostgreSQL安裝.md # 中文檔名
❌ install-postgresql.md # 全部小寫
❌ Install PostgreSQL.md # 空格
❌ postgresql_install.md # 非標準命名
```
---
## 7. 文件審查清單
創建新文件時,請確認:
- [ ] 檔案命名符合 `INSTALL_*.md` 或其他標準模式
- [ ] 文件包含頂部資訊表(建立者、建立時間、版本)
- [ ] 文件包含版本歷史表
- [ ] 文件包含概述章節
- [ ] 文件包含當前狀態/版本資訊
- [ ] 文件包含檔案位置章節
- [ ] 文件包含常用指令章節
- [ ] 使用統一的 Markdown 格式
- [ ] 使用繁體中文作為主要語言
- [ ] 程式碼區塊標註語言類型
- [ ] 表格格式正確
- [ ] 章節使用 `---` 分隔
### 頂部資訊表範本
```markdown
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-18 |
| 文件版本 | V1.0 |
```
### 版本歷史表範本
```markdown
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-18 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
```
---
## 8. 更新現有文件
當更新現有文件時:
1. 更新 **版本資訊** 中的日期
2. 如有必要,更新版本號
3. 記錄重大變更於 `CHANGELOG.md``DEVELOPMENT_LOG.md`
---
## 附錄:文件類型參考
| 前綴 | 用途 | 位置 |
|------|------|------|
| `INSTALL_` | 服務安裝指南 | `/docs/` |
| `DEVELOP_` | 開發指南 | `/docs/` |
| `*_SPEC.md` | 規格定義 | `/docs/` |
| `*_DESIGN.md` | 設計文件 | `/docs/` |
| `API_REFERENCE.md` | API 參考文件 | `/docs/` |
| `README.md` | 專案總覽 | `/` |
| `AGENTS.md` | AI 代理指令 | `/` |
| `CHANGELOG.md` | 變更日誌 | `/` |

View File

@@ -1,167 +0,0 @@
# Document Embedding Strategy - Parent-Child Chunks
| Item | Content |
|------|---------|
| Author | Warren |
| Created | 2026-03-23 |
| Document Version | V1.0 |
---
## Version History
| Version | Date | Purpose | Operator | Tool/Model |
|---------|------|---------|----------|------------|
| V1.0 | 2026-03-23 | Create document embedding strategy | Warren | OpenCode |
---
## Overview
Momentry uses a **parent-child chunk hierarchy** for improved RAG retrieval. This document describes the embedding strategy for this hierarchy.
## Chunk Structure
### Parent Chunk
- **Purpose**: Summarize multiple child chunks with narrative description
- **Content**: High-level description of multiple scenes/segments
- **Example**:
```json
{
"chunk_id": "story_asr_0000",
"chunk_type": "story",
"text_content": "[0s-125s] A man enters a building. He walks down a hallway.",
"child_chunk_ids": ["asr_0001", "asr_0002", "asr_0003", "asr_0004", "asr_0005"]
}
```
### Child Chunk
- **Purpose**: Individual segments from ASR, scenes from CUT, etc.
- **Content**: Raw transcription or detection results
- **Example**:
```json
{
"chunk_id": "asr_0001",
"chunk_type": "sentence",
"text_content": "Hello world",
"parent_chunk_id": "story_asr_0000"
}
```
## Embedding Strategy
### For Vector Search
When embedding chunks for vector search, we combine **parent description + child content** to provide both context and detail.
#### Parent Chunk Embedding
```
embedding_text = f"Summary: {parent.text_content}
Children: {child_text_1}. {child_text_2}. {child_text_3}..."
```
**Prefix**: `search_document:` (for documents in Qdrant)
**Example**:
```
search_document: Summary: A man enters a building. He walks down a hallway.
Children: Hello, how are you? I'm fine thank you. The weather is nice today.
```
#### Child Chunk Embedding
```
embedding_text = f"[{child.chunk_type}] {child.text_content}
Parent: {parent.description}"
```
**Prefix**: `search_document:`
**Example**:
```
search_document: [sentence] Hello, how are you?
Parent: A man enters a building. He walks down a hallway.
```
### For BM25 Text Search
BM25 operates on raw text with PostgreSQL full-text search.
- **Index**: `search_vector` (TSVECTOR) on `chunks.text_content`
- **Search**: Uses `ts_rank_cd()` for ranking
## Hybrid Search Ranking
Combined score = `(vector_score * 0.7) + (bm25_score * 0.3)`
### Why 0.7/0.3?
| Weight | Vector | BM25 |
|--------|--------|------|
| Pros | Semantic similarity | Exact keyword match |
| Cons | May miss specific terms | No semantic understanding |
| Best for | Thematic queries | Fact lookup |
## Query Patterns
### Thematic Query ("What are the main themes?")
- Use higher `vector_weight` (0.8-0.9)
- Vector search finds semantically similar content
### Fact Lookup ("Who said X?")
- Use higher `bm25_weight` (0.5-0.7)
- BM25 finds exact matches
### Balanced ("Tell me about scene 5")
- Use default 0.7/0.3
## Implementation
### Embedding Generation
```rust
fn build_embedding_text(chunk: &Chunk, parent_text: Option<&str>) -> String {
match chunk.chunk_type {
ChunkType::Story => {
format!(
"Summary: {}\nChildren: {}",
chunk.text_content,
get_children_text(chunk)
)
}
_ => {
format!(
"[{}] {}\nParent: {}",
chunk.chunk_type.as_str(),
chunk.text_content,
parent_text.unwrap_or("N/A")
)
}
}
}
```
### Storage
- Parent chunks stored with their `child_chunk_ids`
- Child chunks reference `parent_chunk_id`
- Both stored in PostgreSQL with full-text index
- Vectors stored in Qdrant
## Example Flow
1. **Story Processing** generates parent-child hierarchy
2. **Embedding** creates vector for each chunk
3. **Storage** saves to PostgreSQL + Qdrant
4. **Search** retrieves using hybrid search
5. **Results** include both parent context and child details
## Best Practices
1. **Chunk Size**: 5 child chunks per parent (configurable)
2. **Text Length**: Keep embeddings under 512 tokens
3. **Parent Description**: Include temporal markers (timestamps)
4. **Child Content**: Preserve original transcription
## Future Enhancements
- [ ] GraphRAG integration for relationship traversal
- [ ] Cross-chunk entity linking
- [ ] Temporal graph building

View File

@@ -1,323 +0,0 @@
# 文件修改管理規範 v1.0
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-22 |
| 文件版本 | V1.0 |
---
## 1. 概述
本文檔定義 Momentry 專案的文件修改流程,確保不同工具/模型對文件的一致性理解,防止誤修改並保留完整的修改紀錄。
### 1.1 適用範圍
- 所有 `.md` 文件技術文檔、安裝指南、API 文件等)
- 所有 `.rs` 文件Rust 源代碼)
- 所有 `.sh` 文件Shell 腳本)
- 所有 `.yaml` / `.yml` 文件(配置文件)
- 所有 `.json` 文件(配置及數據文件)
### 1.2 核心原則
1. **先讀後改**:修改前必須完整閱讀相關文件
2. **預檢清單**:修改前執行預檢查步驟
3. **變更對照**:修改後必須比對差異
4. **驗證確認**:變更後執行驗證測試
5. **完整紀錄**:所有修改必須記錄於版本歷史
---
## 2. 修改前預檢清單
### 2.1 文件閱讀要求
修改文件前,必須完成以下閱讀:
| 步驟 | 項目 | 說明 |
|------|------|------|
| 1 | 閱讀完整文件 | 不可僅閱讀部分章節 |
| 2 | 理解文件用途 | 確認文件的目標讀者 |
| 3 | 確認現有術語 | 使用一致的術語和命名 |
| 4 | 查閱相關文件 | 確認相關聯的文件 |
### 2.2 預檢問題清單
在修改前回答以下問題:
```
□ 1. 此修改是否影響其他文件?
□ 2. 此修改是否與現有規範衝突?
□ 3. 此修改是否需要更新版本歷史?
□ 4. 此修改是否需要新增測試?
□ 5. 此修改是否需要通知相關人員?
□ 6. 此修改是否有破壞性變更Breaking Change
```
### 2.3 預檢命令
修改前執行以下命令確認現有狀態:
```bash
# 1. 確認 git 狀態
git status
# 2. 檢查相關文件的最新版本
git log -3 --oneline <file_path>
# 3. 查看現有版本歷史
cat docs/<file>.md | grep -A 20 "版本歷史"
```
---
## 3. 文件修改流程
### 3.1 標準修改流程
```
┌─────────────────────────────────────────────────────────────┐
│ Step 1: 閱讀 │
│ ├─ 完整閱讀目標文件 │
│ └─ 閱讀相關聯文件 │
├─────────────────────────────────────────────────────────────┤
│ Step 2: 預檢 │
│ ├─ 回答預檢問題清單 │
│ └─ 執行預檢命令 │
├─────────────────────────────────────────────────────────────┤
│ Step 3: 規劃 │
│ ├─ 說明修改內容 │
│ └─ 列出變更差異 │
├─────────────────────────────────────────────────────────────┤
│ Step 4: 修改 │
│ ├─ 執行修改 │
│ └─ 更新版本歷史 │
├─────────────────────────────────────────────────────────────┤
│ Step 5: 驗證 │
│ ├─ 執行 lint/format 檢查 │
│ └─ 執行相關測試 │
├─────────────────────────────────────────────────────────────┤
│ Step 6: 提交 │
│ └─ 撰寫清晰的 commit message │
└─────────────────────────────────────────────────────────────┘
```
### 3.2 預修改彙報格式
在執行修改前,必須先彙報以下內容:
```markdown
## 檔案
`<file_path>`
## 修改原因
<說明修改的目的>
## 變更內容
```diff
- <刪除的內容>
+ <新增的內容>
```
## 版本歷史更新
| 版本 | 日期 | 內容 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| Vx.x | YYYY-MM-DD | <修改說明> | <操作者> | <使用的工具> |
```
### 3.3 版本歷史格式
每個文件頂部必須包含版本歷史表:
```markdown
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-15 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
| V1.1 | 2026-03-22 | 更新內容 | Warren | OpenCode / big-pickle |
```
---
## 4. 變更對照
### 4.1 diff 對照
修改後必須提供 diff 對照:
```bash
git diff <file_path>
```
### 4.2 變更類型分類
| 類型 | 標記 | 說明 |
|------|------|------|
| 新增 | `+` | 新增內容 |
| 刪除 | `-` | 刪除內容 |
| 修改 | `~` | 修改內容 |
| 移動 | `↕` | 移動位置 |
| 格式 | `@` | 格式變更 |
### 4.3 變更確認清單
```
□ 1. diff 輸出已確認
□ 2. 變更符合預期
□ 3. 無意外變更
□ 4. 版本歷史已更新
□ 5. 其他關聯文件已檢查
```
---
## 5. 驗證流程
### 5.1 自動化驗證
修改後執行以下自動化檢查:
```bash
# Rust 文件
cargo fmt -- --check
cargo clippy --lib
cargo test --lib
# Python 文件
ruff check <files>
ruff format --check <files>
# Markdown 文件
markdownlint <files>
# Shell 文件
shellcheck -S error <files>
```
### 5.2 手動驗證清單
```
□ 1. 文件語法正確
□ 2. 連結有效
□ 3. 格式一致
□ 4. 術語一致
□ 5. 版本歷史完整
□ 6. 變更記錄清晰
```
---
## 6. 提交規範
### 6.1 Commit Message 格式
```
<type>: <subject>
<body>
<footer>
```
### 6.2 Type 類型
| Type | 說明 |
|------|------|
| `feat` | 新功能 |
| `fix` | 錯誤修復 |
| `refactor` | 重構 |
| `docs` | 文檔更新 |
| `style` | 格式變更 |
| `test` | 測試相關 |
| `chore` | 維護工作 |
### 6.3 Commit Message 範例
```bash
# 文檔更新
git commit -m "docs: update INSTALL_MONGODB.md with LaunchAgent instructions
- Add LaunchDaemon plist installation steps
- Update management commands section
- Add version history entry
Closes: #xxx"
# 配置文件更新
git commit -m "fix: remove duplicate mongodb entry in monitor_config.yaml
The backup section had two mongodb entries which caused confusion.
Removed the duplicate config entry at line 408-414."
```
---
## 7. AI 工具修改額外規範
### 7.1 修改前確認
AI 工具修改文件前,必須:
1. **完整閱讀**:閱讀完整文件(不可只讀取部分)
2. **理解語境**:理解文件的用途和目標讀者
3. **查閱相關**:查閱相關聯文件確保一致性
4. **提出計畫**:修改前先提出變更計畫供確認
### 7.2 修改後確認
AI 工具修改文件後,必須:
1. **展示差異**:顯示修改的 diff 內容
2. **說明變更**:解釋每項變更的目的
3. **執行驗證**:執行相關的 lint/test 命令
4. **更新歷史**:更新版本歷史表
### 7.3 禁止事項
AI 工具**禁止**以下行為:
```
□ 未閱讀完整文件即進行修改
□ 未經確認直接執行大規模修改
□ 未提供 diff 即提交變更
□ 未更新版本歷史
□ 未執行驗證即聲稱完成
□ 擅自刪除其他工具/模型添加的內容
□ 無視現有文件規範
```
---
## 8. 審查清單
### 8.1 提交前審查
```
□ 1. 修改內容已完整閱讀
□ 2. 預檢問題清單已回答
□ 3. diff 對照已確認
□ 4. 版本歷史已更新
□ 5. 自動化驗證已通過
□ 6. 手動驗證清單已完成
□ 7. Commit message 符合規範
□ 8. 無敏感資訊泄露
```
### 8.2 Code Review 關注點
- [ ] 修改是否影響現有功能?
- [ ] 修改是否與專案規範一致?
- [ ] 是否有遺漏的關聯修改?
- [ ] 測試是否足夠?
- [ ] 文檔是否同步更新?
---
## 9. 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-22 | 創建文件 | Warren | OpenCode / big-pickle |

View File

@@ -1,707 +0,0 @@
# Momentry 系統全新 Mac 安裝指南
| 項目 | 內容 |
|------|------|
| 建立時間 | 2026-03-23 |
| 文件版本 | V1.0 |
| 適用對象 | 全新 Mac (Intel 或 Apple Silicon) |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 |
|------|------|------|--------|
| V1.0 | 2026-03-23 | 創建文件 | OpenCode |
---
## 快速概覽
### 安裝時間估算
| 項目 | 時間 |
|------|------|
| 系統準備 | 30 分鐘 |
| Homebrew 安裝 | 15 分鐘 |
| 資料庫服務 | 30 分鐘 |
| 應用服務 | 60 分鐘 |
| Momentry Core | 20 分鐘 |
| **總計** | **~2.5 小時** |
### 系統需求
| 項目 | 最低需求 | 推薦需求 |
|------|----------|----------|
| macOS | 13.0 Ventura | 14.0+ Sonoma |
| 記憶體 | 8GB | 16GB+ |
| 儲存空間 | 100GB | 500GB+ |
| CPU | Apple Silicon M1 或 Intel i5 | M2/M3 或 Intel i7+ |
---
## 第一部分:系統準備
### 1.1 macOS 初始設定
首次開機後,執行以下設定:
```bash
# 1. 設定電腦名稱
sudo scutil --set ComputerName "Momentry"
sudo scutil --set LocalHostName "momentry"
# 2. 啟用 SSH 遠端登入
sudo systemsetup -setremotelogin on
# 3. 關閉休眠 (防止遠端斷線)
sudo pmset -a sleep 0
sudo pmset -a hibernatemode 0
# 4. 允許任何來源 App (安裝開發工具用)
sudo spctl --master-disable
# 5. 設定時區
sudo systemsetup -settimezone "Asia/Taipei"
```
### 1.2 建立管理員帳戶
```bash
# 建立 Momentry 管理員帳戶 (可選)
sudo dscl . -create /Users/momentry
sudo dscl . -create /Users/momentry UserShell /bin/zsh
sudo dscl . -create /Users/momentry RealName "Momentry Admin"
sudo dscl . -create /Users/momentry PrimaryGroupID 80
sudo dscl . -create /Users/momentry UniqueID 1001
sudo dscl . -passwd /Users/momentry "momentry_password"
sudo dscl . -append /Groups/admin GroupMembership momentry
```
---
## 第二部分Homebrew 安裝
### 2.1 安裝 Homebrew
```bash
# 安裝 Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Apple Silicon 設定
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"
# Intel Mac 設定
# echo 'eval "$(/usr/local/bin/brew shellenv)"' >> ~/.zprofile
# eval "$(/usr/local/bin/brew shellenv)"
# 驗證
brew --version
```
### 2.2 安裝基礎工具
```bash
# 開發工具
brew install \
git \
curl \
wget \
jq \
yq \
tree \
htop \
tmux \
zsh \
zsh-completions \
ncdu \
httpie \
openssl \
readline \
sqlite \
python@3.11 \
rustup-init
# Rust 初始化
rustup-init -y
source "$HOME/.cargo/env"
# Python 虛擬環境
python3.11 -m venv ~/.venv
source ~/.venv/bin/activate
pip install --upgrade pip
```
---
## 第三部分:建立目錄結構
```bash
# 建立目錄結構
mkdir -p /Users/accusys/momentry/{var,etc,log,scripts,backup}
mkdir -p /Users/accusys/momentry/var/{postgresql,mongodb,mariadb,redis,qdrant,n8n,ollama,sftpgo}
mkdir -p /Users/accusys/momentry/etc/{sftpgo,caddy,gitea,php}
mkdir -p /Users/accusys/momentry/scripts
mkdir -p /Users/accusys/momentry/backup/{daily,weekly,monthly}
mkdir -p /Users/accusys/workspace/sftpgo
mkdir -p /Users/accusys/sftpgo_test/{demo,uploads}
# 設定權限
chown -R accusys:staff /Users/accusys/momentry
chown -R accusys:staff /Users/accusys/workspace
chown -R accusys:staff /Users/accusys/sftpgo_test
```
---
## 第四部分:資料庫服務安裝
### 4.1 PostgreSQL
```bash
# 安裝
brew install postgresql@18
# 啟動服務
brew services start postgresql@18
# 建立資料庫
createdb momentry
createdb video_register
createdb n8n
createdb sftpgo
# 建立用戶
psql -U accusys -d postgres << 'EOF'
CREATE USER sftpgo WITH PASSWORD 'sftpgo_pass_2026';
GRANT ALL PRIVILEGES ON DATABASE sftpgo TO sftpgo;
GRANT ALL PRIVILEGES ON DATABASE momentry TO accusys;
GRANT ALL PRIVILEGES ON DATABASE video_register TO accusys;
GRANT ALL PRIVILEGES ON DATABASE n8n TO accusys;
EOF
# 驗證
pg_isready -h 127.0.0.1 -p 5432
```
### 4.2 MongoDB
```bash
# 安裝
brew tap mongodb/brew
brew install mongodb-community
# 建立資料目錄
mkdir -p /Users/accusys/momentry/var/mongodb
chown -R accusys:staff /Users/accusys/momentry/var/mongodb
# 啟動服務
brew services start mongodb-community
# 建立用戶
mongosh admin --eval "
db.createUser({
user: 'accusys',
pwd: 'Test3200Test3200',
roles: [
{ role: 'readWriteAnyDatabase', db: 'admin' },
{ role: 'dbAdminAnyDatabase', db: 'admin' }
]
});
"
# 驗證
mongosh --quiet --eval "db.adminCommand('ping')"
```
### 4.3 Redis
```bash
# 安裝
brew install redis
# 建立配置目錄
mkdir -p /Users/accusys/momentry/etc/redis
# 建立配置文件
cat > /Users/accusys/momentry/etc/redis/redis.conf << 'EOF'
requirepass accusys
dir /Users/accusys/momentry/var/redis
logfile /Users/accusys/momentry/log/redis.log
daemonize no
port 6379
bind 127.0.0.1
EOF
# 啟動服務
redis-server /Users/accusys/momentry/etc/redis/redis.conf &
sleep 2
# 驗證
redis-cli -a accusys ping
```
### 4.4 MariaDB (WordPress)
```bash
# 安裝
brew install mariadb
# 啟動服務
brew services start mariadb
# 安全設定
mysql_secure_installation
# 建立資料庫
mysql -u root << 'EOF'
CREATE DATABASE wordpress CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wordpress'@'localhost' IDENTIFIED BY 'wordpress_password';
GRANT ALL PRIVILEGES ON wordpress.* TO 'wordpress'@'localhost';
FLUSH PRIVILEGES;
EOF
# 驗證
mysql -u root -e "SHOW DATABASES;"
```
---
## 第五部分:應用服務安裝
### 5.1 Ollama (LLM)
```bash
# 安裝
brew install ollama
# 建立資料目錄
mkdir -p /Users/accusys/momentry/var/ollama
mkdir -p ~/.ollama/models
# 啟動服務
brew services start ollama
# 下載模型
ollama pull llama3.2
ollama pull nomic-embed-text
# 驗證
curl -s http://localhost:11434/api/tags | jq '.models[].name'
```
### 5.2 Caddy (反向代理)
```bash
# 安裝
brew install caddy
# 建立配置目錄
mkdir -p /Users/accusys/momentry/etc/caddy
# 建立 Caddyfile
cat > /Users/accusys/momentry/etc/caddy/Caddyfile << 'EOF'
{
admin off
auto_https off
}
# 範例:本地開發
:8080 {
respond "Momentry Server" 200
}
EOF
# 啟動服務
brew services start caddy
# 驗證
curl -s http://localhost:8080
```
### 5.3 Gitea (Git 服務)
```bash
# 安裝
brew install gitea
# 建立資料目錄
mkdir -p /Users/accusys/momentry/var/gitea
mkdir -p /Users/accusys/momentry/etc/gitea
# 複製設定檔
cp /opt/homebrew/etc/gitea/conf/app.ini /Users/accusys/momentry/etc/gitea/
# 啟動服務
brew services start gitea
# 驗證
curl -s http://localhost:3000
```
### 5.4 n8n (工作流程自動化)
```bash
# 安裝 Node.js (如果尚未安裝)
brew install node@22
# 全域安裝 n8n
npm install -g n8n
# 建立資料目錄
mkdir -p /Users/accusys/momentry/var/n8n
# 設定環境變數
export N8N_DATA_DIR=/Users/accusys/momentry/var/n8n
export DB_TYPE=postgresdb
export DB_POSTGRES_HOST=127.0.0.1
export DB_POSTGRES_PORT=5432
export DB_POSTGRES_DATABASE=n8n
export DB_POSTGRES_USER=accusys
export DB_POSTGRES_PASSWORD=accusys
export N8N_ENCRYPTION_KEY=Test3200Test3200Test3200
# 啟動服務
n8n start &
# 驗證
curl -s http://localhost:5678
```
### 5.5 SFTPGo (SFTP 服務)
```bash
# 安裝
brew install sftpgo
# 建立配置目錄
mkdir -p /Users/accusys/momentry/etc/sftpgo
# 建立配置文件
cat > /Users/accusys/momentry/etc/sftpgo/sftpgo.json << 'EOF'
{
"data_provider": {
"driver": "postgresql",
"host": "127.0.0.1",
"port": 5432,
"username": "sftpgo",
"password": "sftpgo_pass_2026",
"name": "sftpgo",
"create_default_admin": true
},
"httpd": {
"bind_port": 8080,
"bind_address": "127.0.0.1"
},
"sftpd": {
"bindings": [
{"port": 2022}
]
},
"ftpd": {
"bindings": [
{"port": 0}
]
}
}
EOF
# 建立 launchd plist
cat > /Library/LaunchDaemons/com.momentry.sftpgo.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.momentry.sftpgo</string>
<key>UserName</key>
<string>accusys</string>
<key>WorkingDirectory</key>
<string>/Users/accusys/workspace/sftpgo</string>
<key>ProgramArguments</key>
<array>
<string>/opt/homebrew/bin/sftpgo</string>
<string>serve</string>
<string>--config-file</string>
<string>/Users/accusys/momentry/etc/sftpgo/sftpgo.json</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/opt/homebrew/bin:/opt/homebrew/sbin:/usr/bin:/bin</string>
<key>HOME</key>
<string>/Users/accusys</string>
<key>SFTPGO_DEFAULT_ADMIN_USERNAME</key>
<string>admin</string>
<key>SFTPGO_DEFAULT_ADMIN_PASSWORD</key>
<string>Test3200Test3200</string>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/accusys/momentry/log/sftpgo.log</string>
<key>StandardErrorPath</key>
<string>/Users/accusys/momentry/log/sftpgo.error.log</string>
</dict>
</plist>
EOF
# 載入服務
sudo launchctl load /Library/LaunchDaemons/com.momentry.sftpgo.plist
# 驗證
sleep 3
curl -s -X POST http://localhost:8080/api/v2/token -u "admin:Test3200Test3200"
```
### 5.6 Qdrant (向量資料庫)
```bash
# 安裝 Rust (如果尚未安裝)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
# 安裝 Qdrant
cargo install qdrant
# 建立資料目錄
mkdir -p /Users/accusys/momentry/var/qdrant/storage
# 啟動服務
qdrant --data-path /Users/accusys/momentry/var/qdrant/storage --api-key Test3200Test3200Test3200 &
# 驗證
sleep 3
curl -s http://localhost:6333/collections -H "api-key: Test3200Test3200Test3200"
```
---
## 第六部分Momentry Core 安裝
### 6.1 安裝依賴
```bash
# 安裝 Python 依賴
pip install \
openai-whisper \
ultralytics \
opencv-python \
pillow \
python-dotenv \
requests \
httpx
# 安裝 FFmpeg
brew install ffmpeg
```
### 6.2 安裝 Momentry Core
```bash
# 克隆或進入專案目錄
cd /Users/accusys/momentry_core_0.1
# 編譯專案
cargo build --release
# 複製執行檔
cp target/release/momentry /usr/local/bin/
# 建立 launchd plist
cat > /Library/LaunchDaemons/com.momentry.api.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.momentry.api</string>
<key>UserName</key>
<string>accusys</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/momentry</string>
<string>server</string>
<string>--host</string>
<string>0.0.0.0</string>
<string>--port</string>
<string>3002</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>DATABASE_URL</key>
<string>postgres://accusys:accusys@127.0.0.1:5432/momentry</string>
<key>RUST_LOG</key>
<string>info</string>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/accusys/momentry/log/momentry.log</string>
<key>StandardErrorPath</key>
<string>/Users/accusys/momentry/log/momentry.error.log</string>
</dict>
</plist>
EOF
# 載入服務
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
# 驗證
sleep 3
curl -s http://localhost:3002/health
```
---
## 第七部分:服務驗證
### 7.1 健康檢查腳本
```bash
#!/bin/bash
# health_check.sh
echo "=========================================="
echo "Momentry System Health Check"
echo "=========================================="
echo ""
# PostgreSQL
pg_isready -h 127.0.0.1 -p 5432 > /dev/null 2>&1
[ $? -eq 0 ] && echo "✅ PostgreSQL" || echo "❌ PostgreSQL"
# MongoDB
mongosh --quiet --eval "db.adminCommand('ping')" > /dev/null 2>&1
[ $? -eq 0 ] && echo "✅ MongoDB" || echo "❌ MongoDB"
# Redis
redis-cli -a accusys ping > /dev/null 2>&1
[ $? -eq 0 ] && echo "✅ Redis" || echo "❌ Redis"
# Ollama
curl -s http://localhost:11434/api/tags > /dev/null 2>&1
[ $? -eq 0 ] && echo "✅ Ollama" || echo "❌ Ollama"
# n8n
curl -s http://localhost:5678 > /dev/null 2>&1
[ $? -eq 0 ] && echo "✅ n8n" || echo "❌ n8n"
# SFTPGo
curl -s http://localhost:8080 > /dev/null 2>&1
[ $? -eq 0 ] && echo "✅ SFTPGo" || echo "❌ SFTPGo"
# Qdrant
curl -s http://localhost:6333/ > /dev/null 2>&1
[ $? -eq 0 ] && echo "✅ Qdrant" || echo "❌ Qdrant"
# Momentry API
curl -s http://localhost:3002/health > /dev/null 2>&1
[ $? -eq 0 ] && echo "✅ Momentry API" || echo "❌ Momentry API"
# Caddy
curl -sI http://localhost:8080 > /dev/null 2>&1
[ $? -eq 0 ] && echo "✅ Caddy" || echo "❌ Caddy"
echo ""
echo "=========================================="
```
### 7.2 執行驗證
```bash
# 儲存並執行
chmod +x health_check.sh
./health_check.sh
```
---
## 第八部分:重要憑證總覽
### 8.1 服務密碼
| 服務 | 用戶名 | 密碼 | 用途 |
|------|--------|------|------|
| **PostgreSQL** | accusys | `accusys` | 主要資料庫 |
| **PostgreSQL** | sftpgo | `sftpgo_pass_2026` | SFTPGo 資料庫 |
| **MongoDB** | accusys | `Test3200Test3200` | 文件資料庫 |
| **Redis** | - | `accusys` | 快取服務 |
| **n8n** | - | `Test3200Test3200Test3200` | n8n 加密金鑰 |
| **SFTPGo** | admin | `Test3200Test3200` | SFTPGo 管理員 |
| **Qdrant** | - | `Test3200Test3200Test3200` | Qdrant API Key |
| **MariaDB** | root | (設定時指定) | WordPress 資料庫 |
| **MariaDB** | wordpress | `wordpress_password` | WordPress 資料庫用戶 |
### 8.2 服務連接埠
| 服務 | 連接埠 | 協定 |
|------|--------|------|
| PostgreSQL | 5432 | TCP |
| MongoDB | 27017 | TCP |
| Redis | 6379 | TCP |
| Ollama | 11434 | HTTP |
| n8n | 5678 | HTTP |
| SFTPGo | 8080 | HTTP |
| SFTPGo | 2022 | SFTP |
| Qdrant | 6333 | HTTP |
| Momentry API | 3002 | HTTP |
| Caddy | 80/443 | HTTP/HTTPS |
| Gitea | 3000 | HTTP |
| PHP-FPM | 9000 | TCP |
---
## 第九部分:相關文檔
| 文檔 | 說明 |
|------|------|
| `SERVICES.md` | 服務詳細說明 |
| `INSTALL_POSTGRESQL.md` | PostgreSQL 安裝指南 |
| `INSTALL_MONGODB.md` | MongoDB 安裝指南 |
| `INSTALL_REDIS.md` | Redis 安裝指南 |
| `INSTALL_QDRANT.md` | Qdrant 安裝指南 |
| `INSTALL_N8N.md` | n8n 安裝指南 |
| `INSTALL_SFTPGO.md` | SFTPGo 安裝指南 |
| `SFTPGO_DEMO_USER.md` | SFTPGo Demo 用戶指南 |
| `MOMENTRY_CORE_MONITORING.md` | 監控規範 |
| `API_INDEX.md` | API 文件索引 |
---
## 附錄:常見問題
### Q1: n8n 無法連接 PostgreSQL
確保資料庫用戶有登入權限:
```bash
psql -U accusys -d postgres -c "ALTER USER accusys WITH LOGIN;"
```
### Q2: SFTPGo 無法啟動
檢查 PostgreSQL 是否運行:
```bash
pg_isready -h 127.0.0.1 -p 5432
```
### Q3: Qdrant API Key 無效
使用正確的 API Key
```bash
curl -H "api-key: Test3200Test3200Test3200" http://localhost:6333/collections
```
### Q4: Momentry API 無法啟動
檢查環境變數:
```bash
export DATABASE_URL="postgres://accusys:accusys@127.0.0.1:5432/momentry"
export RUST_LOG=debug
momentry server --host 0.0.0.0 --port 3002
```

View File

@@ -1,467 +0,0 @@
# Caddy 安裝指南 (本地部署)
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-16 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
---
## 概述
本文檔說明如何在 macOS 上安裝 Caddy Web Server配置為本地部署作為反向代理伺服器。
---
## 當前狀態
| 項目 | 狀態 |
|------|------|
| Caddy | ✅ 已安裝 v2.10.2 |
| 設定檔 | /Users/accusys/momentry/etc/Caddyfile |
| 日誌目錄 | /Users/accusys/momentry/log/ |
| Plist | /Library/LaunchDaemons/com.momentry.caddy.plist |
---
## 安裝步驟
### Step 1: 安裝 Caddy (使用 brew)
```bash
# 安裝 Caddy
brew install caddy
```
**驗證**:
```bash
caddy --version
# v2.10.2
```
---
### Step 2: 建立目錄
```bash
# 建立配置目錄
mkdir -p /Users/accusys/momentry/etc/caddy
# 建立日誌目錄
mkdir -p /Users/accusys/momentry/log
# 建立數據目錄
mkdir -p /Users/accusys/momentry/var/caddy
# 建立日誌文件
touch /Users/accusys/momentry/log/caddy.log
touch /Users/accusys/momentry/log/caddy.error.log
# 設定權限
# 注意: Caddy 使用 ports 80/443必須以 root 身份運行
# 因此 var/caddy 目錄需要 root:admin 權限
chown -R accusys:staff /Users/accusys/momentry/etc/caddy
chown -R accusys:staff /Users/accusys/momentry/log
sudo chown -R root:admin /Users/accusys/momentry/var/caddy
```
---
### Step 3: 建立設定檔
建立 `/Users/accusys/momentry/etc/Caddyfile`:
```Caddyfile
{
email admin@accusys.com.tw
metrics
}
# 定義日誌 Snippet
(common_log) {
log {
output file /Users/accusys/momentry/log/{args[0]}.log {
roll_size 100mb
roll_keep 5
roll_keep_for 720h
}
format json
}
}
# Example: 反向代理到本地服務
example.momentry.ddns.net {
reverse_proxy localhost:8080 {
header_up Host {upstream_hostport}
}
import common_log example_access
}
```
---
### Step 4: 使用 plist 開機自動啟動
**注意**: Caddy 需要使用 ports 80 和 443必須以 root 身份運行。
```bash
# 複製 plist 到 LaunchDaemons 目錄
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.caddy.plist /Library/LaunchDaemons/
# 載入並啟動
sudo launchctl load /Library/LaunchDaemons/com.momentry.caddy.plist
```
---
## 監控配置
### 添加到監控配置
`monitor/config/monitor_config.yaml` 中添加:
```yaml
service:
services:
- name: "caddy"
type: "http"
port: 80
host: "localhost"
check_url: "http://localhost:2019/config/"
timeout: 5
enabled: true
```
---
## 卸載步驟
### 重要: 路徑說明
| 路徑 | 類型 | 說明 |
|------|------|------|
| `/Users/accusys/momentry/etc/caddy/` | 配置 | **不要刪除** - Caddy 配置 |
| `/Users/accusys/momentry/log/` | 日誌 | **不要刪除** - 日誌目錄 |
| `/Users/accusys/momentry/var/caddy/` | 數據 | **不要刪除** - Caddy 數據 |
| `/opt/homebrew/opt/caddy/` | 安裝 | **刪除** - Caddy 安裝目錄 |
| `/opt/homebrew/opt/caddy/` | 安裝 | **刪除** - Caddy 安裝目錄 |
### Step 1: 停止 Caddy
```bash
# 找到 Caddy 進程
ps aux | grep caddy | grep -v grep
# 停止 Caddy
pkill caddy
# 確認停止
ps aux | grep caddy | grep -v grep || echo "Caddy 已停止"
```
---
### Step 2: 卸載 Caddy
```bash
# 卸載 Caddy
brew uninstall caddy
# 移除 plist
sudo launchctl unload /Library/LaunchDaemons/com.momentry.caddy.plist
sudo rm /Library/LaunchDaemons/com.momentry.caddy.plist
```
---
### Step 3: 刪除專屬檔案
```bash
# 刪除配置目錄 (可選)
rm -rf /Users/accusys/momentry/etc/Caddyfile
# 刪除日誌 (可選)
rm -f /Users/accusys/momentry/log/caddy.log
rm -f /Users/accusys/momentry/log/caddy.error.log
# 刪除數據目錄 (可選)
rm -rf /Users/accusys/momentry/var/caddy
```
**注意: 不要刪除以下共用目錄**:
```bash
# 這些是共用的,不要刪除!
# /Users/accusys/momentry/etc
# /Users/accusys/momentry/log
# /Users/accusys/momentry/var
```
---
### Step 4: 卸載後檢查清單
```bash
echo "=== Caddy 卸載後檢查 ==="
# 1. 檢查 Caddy 進程
echo "1. Caddy 進程:"
ps aux | grep caddy | grep -v grep && echo " ✗ 仍在運行" || echo " ✓ 已停止"
# 2. Port 80/443
echo "2. Port 80/443:"
(lsof -i :80 > /dev/null 2>&1 || lsof -i :443 > /dev/null 2>&1) && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
# 3. caddy 命令
echo "3. caddy 命令:"
which caddy > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 4. brew 安裝
echo "4. brew 安裝:"
brew list caddy > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 5. launchctl 服務
echo "5. launchctl 服務:"
sudo launchctl list | grep caddy > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 6. 配置目錄 (可選刪除)
echo "6. 配置目錄:"
[ -d "/Users/accusys/momentry/etc" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
# 7. 日誌目錄 (可選刪除)
echo "7. 日誌目錄:"
[ -d "/Users/accusys/momentry/log" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
```
**預期結果**:
```
=== Caddy 卸載後檢查 ===
1. Caddy 進程:
✓ 已停止
2. Port 80/443:
✓ 已釋放
3. caddy 命令:
✓ 已移除
4. brew 安裝:
✓ 已移除
5. launchctl 服務:
✓ 已移除
6. 配置目錄:
✓ 保留 (或 ✗ 已刪除)
7. 日誌目錄:
✓ 保留 (或 ✗ 已刪除)
```
---
## 手動檢查命令
```bash
# 1. 檢查進程
ps aux | grep caddy | grep -v grep
# 2. 檢查 Port
lsof -i :80
lsof -i :443
lsof -i :2019
# 3. 測試配置語法
caddy validate --config /Users/accusys/momentry/etc/Caddyfile
# 4. 查看 Caddy 版本
caddy version
# 5. 重新載入配置
caddy reload --config /Users/accusys/momentry/etc/Caddyfile
# 6. 查看日誌
tail -20 /Users/accusys/momentry/log/caddy.log
# 7. 查看 Caddy 適配的網站
curl -I http://localhost:2019/config/
```
---
## Caddyfile 範例
### 基本反向代理
```Caddyfile
{
email admin@accusys.com.tw
}
# 反向代理到本地服務
example.local {
reverse_proxy localhost:8080
}
```
### 帶 SSL 的反向代理
```Caddyfile
{
email admin@accusys.com.tw
}
# 使用 Let's Encrypt 自動 SSL
example.momentry.ddns.net {
reverse_proxy localhost:8080 {
header_up Host {upstream_hostport}
}
}
```
### 多站點配置
```Caddyfile
{
email admin@accusys.com.tw
}
# 站點 1
site1.example.com {
reverse_proxy localhost:8080
}
# 站點 2
site2.example.com {
reverse_proxy localhost:8081
}
```
---
## 環境變數
`.env` 中:
```env
CADDY_CONFIG=/Users/accusys/momentry/etc/Caddyfile
CADDY_HOME=/Users/accusys/.local/share/caddy
```
---
## 故障排除
### Caddy 無法啟動
```bash
# 檢查日誌
tail -f /Users/accusys/momentry/log/caddy.log
# 檢查配置語法
caddy validate --config /Users/accusys/momentry/etc/Caddyfile
# 檢查目錄權限
ls -la /Users/accusys/momentry/etc/
# 重新設定權限
chown -R $(whoami):staff /Users/accusys/momentry/etc
```
### Port 被佔用
```bash
# 檢查哪個程序佔用 port 80
lsof -i :80
# 終止佔用程序
kill <PID>
```
### 需要重新載入配置
```bash
# 重新載入配置 (熱重載)
caddy reload --config /Users/accusys/momentry/etc/Caddyfile
# 或者停止後重新啟動
sudo launchctl unload /Library/LaunchDaemons/com.momentry.caddy.plist
sudo launchctl load /Library/LaunchDaemons/com.momentry.caddy.plist
```
---
## 檔案位置
| 類型 | 路徑 | 說明 |
|------|------|------|
| 安裝 | `/opt/homebrew/opt/caddy/` | Caddy 安裝目錄 |
| 執行檔 | `/opt/homebrew/opt/caddy/bin/caddy` | Caddy 執行檔 |
| 配置 | `/Users/accusys/momentry/etc/Caddyfile` | 設定檔 |
| 日誌 | `/Users/accusys/momentry/log/caddy.log` | 執行日誌 |
| 錯誤日誌 | `/Users/accusys/momentry/log/caddy.error.log` | 錯誤日誌 |
| 數據 | `/Users/accusys/momentry/var/caddy/` | Caddy 數據目錄 |
| plist | `/Library/LaunchDaemons/com.momentry.caddy.plist` | 開機啟動 |
| 備份 | `/Users/accusys/momentry/var/caddy_backup/Caddyfile` | 配置備份 |
---
## 常用指令
```bash
# 驗證配置
caddy validate --config /Users/accusys/momentry/etc/Caddyfile
# 熱重載配置
caddy reload --config /Users/accusys/momentry/etc/Caddyfile
# 停止 Caddy
caddy stop
# 啟動 Caddy
caddy start --config /Users/accusys/momentry/etc/Caddyfile
# 適配所有網站
caddy adapt --config /Users/accusys/momentry/etc/Caddyfile --adapter caddyfile
```
---
## 備份與恢復
### 備份
```bash
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/Users/accusys/momentry/backup/daily/caddy"
mkdir -p "$BACKUP_DIR"
# 備份配置
tar -czf "$BACKUP_DIR/caddy_cfg_${TIMESTAMP}.tar.gz" \
/Users/accusys/momentry/etc/Caddyfile
# 驗證
sha256sum "$BACKUP_DIR/caddy_cfg_${TIMESTAMP}.tar.gz" > "$BACKUP_DIR/caddy_${TIMESTAMP}.sha256"
```
### 恢復
```bash
# 解壓配置
tar -xzf /Users/accusys/momentry/backup/daily/caddy/caddy_cfg_20260316_102416.tar.gz -C /
# 驗證並重載
caddy validate --config /Users/accusys/momentry/etc/Caddyfile
caddy reload --config /Users/accusys/momentry/etc/Caddyfile
```
---
## 版本資訊
- 版本: 2.10.2
- 配置: /Users/accusys/momentry/etc/Caddyfile
- 日誌目錄: /Users/accusys/momentry/log/

View File

@@ -1,410 +0,0 @@
# Gitea 安裝指南 (本地部署)
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-15 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-15 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
---
## 概述
本文檔說明如何在 macOS 上安裝 Gitea Git 服務,配置為本地部署。
---
## 當前狀態
| 項目 | 狀態 |
|------|------|
| Gitea | ✅ 已安裝 v1.25.3 |
| 數據目錄 | /Users/accusys/momentry/var/gitea/ |
| 配置目錄 | /Users/accusys/momentry/etc/gitea/ |
| 日誌目錄 | /Users/accusys/momentry/log/ |
| Plist | /Library/LaunchDaemons/com.momentry.gitea.plist |
---
## 安裝步驟
### Step 1: 安裝 Gitea (使用 brew)
```bash
# 安裝 Gitea
brew install gitea
```
**驗證**:
```bash
gitea --version
# gitea version 1.25.3
```
---
### Step 2: 建立目錄
```bash
# 建立數據目錄
mkdir -p /Users/accusys/momentry/var/gitea/data
mkdir -p /Users/accusys/momentry/var/gitea/log
# 建立配置目錄
mkdir -p /Users/accusys/momentry/etc/gitea
# 建立日誌目錄
mkdir -p /Users/accusys/momentry/log
# 建立日誌文件
touch /Users/accusys/momentry/log/gitea.log
touch /Users/accusys/momentry/log/gitea.error.log
# 設定權限
chown -R accusys:staff /Users/accusys/momentry/var/gitea
chown -R accusys:staff /Users/accusys/momentry/etc/gitea
chown -R accusys:staff /Users/accusys/momentry/log
```
---
### Step 3: 建立設定檔
建立 `/Users/accusys/momentry/etc/gitea/app.ini`:
```ini
APP_NAME = Gitea: Git with a cup of tea
RUN_USER = accusys
WORK_PATH = /Users/accusys/momentry/var/gitea
RUN_MODE = prod
[database]
DB_TYPE = postgres
HOST = 127.0.0.1:5432
NAME = gitea
USER = gitea
PASSWD = gitea_pass
SSL_MODE = disable
[repository]
ROOT = /Users/accusys/momentry/var/gitea/data/gitea-repositories
[server]
SSH_DOMAIN = gitea.momentry.ddns.net
DOMAIN = gitea.momentry.ddns.net
HTTP_PORT = 3000
ROOT_URL = http://gitea.momentry.ddns.net:3000/
APP_DATA_PATH = /Users/accusys/momentry/var/gitea/data
DISABLE_SSH = false
SSH_PORT = 2222
LFS_START_SERVER = true
OFFLINE_MODE = true
[lfs]
PATH = /Users/accusys/momentry/var/gitea/data/lfs
[log]
MODE = console, file
ROOT_PATH = /Users/accusys/momentry/log
```
---
### Step 4: 使用 plist 開機自動啟動
```bash
# 複製 plist 到 LaunchDaemons 目錄
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.gitea.plist /Library/LaunchDaemons/
# 載入並啟動
sudo launchctl load /Library/LaunchDaemons/com.momentry.gitea.plist
```
---
## 監控配置
### 添加到監控配置
`monitor/config/monitor_config.yaml` 中添加:
```yaml
service:
services:
- name: "gitea"
type: "http"
port: 3000
host: "localhost"
check_url: "http://localhost:3000/"
timeout: 5
enabled: true
```
---
## 卸載步驟
### 重要: 路徑說明
| 路徑 | 類型 | 說明 |
|------|------|------|
| `/Users/accusys/momentry/var/gitea/` | 數據 | **不要刪除** - Gitea 數據 |
| `/Users/accusys/momentry/etc/gitea/` | 配置 | **不要刪除** - Gitea 配置 |
| `/Users/accusys/momentry/log/` | 日誌 | **不要刪除** - 日誌目錄 |
| `/opt/homebrew/opt/gitea/` | 安裝 | **刪除** - Gitea 安裝目錄 |
### Step 1: 停止 Gitea
```bash
# 找到 Gitea 進程
ps aux | grep gitea | grep -v grep
# 停止 Gitea
pkill gitea
# 確認停止
ps aux | grep gitea | grep -v grep || echo "Gitea 已停止"
```
---
### Step 2: 卸載 Gitea
```bash
# 卸載 Gitea
brew uninstall gitea
# 移除 plist
sudo launchctl unload /Library/LaunchDaemons/com.momentry.gitea.plist
sudo rm /Library/LaunchDaemons/com.momentry.gitea.plist
```
---
### Step 3: 刪除專屬檔案
```bash
# 刪除數據目錄 (可選)
rm -rf /Users/accusys/momentry/var/gitea
# 刪除配置目錄 (可選)
rm -rf /Users/accusys/momentry/etc/gitea
# 刪除日誌 (可選)
rm -f /Users/accusys/momentry/log/gitea.log
rm -f /Users/accusys/momentry/log/gitea.error.log
```
**注意: 不要刪除以下共用目錄**:
```bash
# 這些是共用的,不要刪除!
# /Users/accusys/momentry/var
# /Users/accusys/momentry/etc
# /Users/accusys/momentry/log
```
---
### Step 4: 卸載後檢查清單
```bash
echo "=== Gitea 卸載後檢查 ==="
# 1. 檢查 Gitea 進程
echo "1. Gitea 進程:"
ps aux | grep gitea | grep -v grep && echo " ✗ 仍在運行" || echo " ✓ 已停止"
# 2. Port 3000
echo "2. Port 3000:"
lsof -i :3000 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
# 3. gitea 命令
echo "3. gitea 命令:"
which gitea > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 4. brew 安裝
echo "4. brew 安裝:"
brew list gitea > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 5. launchctl 服務
echo "5. launchctl 服務:"
sudo launchctl list | grep gitea > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 6. 數據目錄 (可選刪除)
echo "6. 數據目錄:"
[ -d "/Users/accusys/momentry/var/gitea" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
# 7. 配置目錄 (可選刪除)
echo "7. 配置目錄:"
[ -d "/Users/accusys/momentry/etc/gitea" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
```
**預期結果**:
```
=== Gitea 卸載後檢查 ===
1. Gitea 進程:
✓ 已停止
2. Port 3000:
✓ 已釋放
3. gitea 命令:
✓ 已移除
4. brew 安裝:
✓ 已移除
5. launchctl 服務:
✓ 已移除
6. 數據目錄:
✓ 保留 (或 ✗ 已刪除)
7. 配置目錄:
✓ 保留 (或 ✗ 已刪除)
```
---
## 手動檢查命令
```bash
# 1. 檢查進程
ps aux | grep gitea | grep -v grep
# 2. 檢查 Port
lsof -i :3000
# 3. 測試連線
curl http://localhost:3000/
# 4. 查看版本
gitea --version
# 5. 驗證配置
gitea doctor --config /Users/accusys/momentry/etc/gitea/app.ini
# 6. 查看日誌
tail -20 /Users/accusys/momentry/log/gitea.log
```
---
## 連線資訊
| 項目 | 值 |
|------|-----|
| URL | http://localhost:3000 |
| Domain | gitea.momentry.ddns.net |
| SSH Port | 2222 |
| Database | PostgreSQL (gitea) |
---
## 環境變數
`.env` 中:
```env
GITEA_URL=http://localhost:3000
GITEA_ROOT=/Users/accusys/momentry/var/gitea/data/gitea-repositories
```
---
## 故障排除
### Gitea 無法啟動
```bash
# 檢查日誌
tail -f /Users/accusys/momentry/log/gitea.log
# 檢查配置語法
gitea doctor --config /Users/accusys/momentry/etc/gitea/app.ini
# 檢查目錄權限
ls -la /Users/accusys/momentry/var/gitea/
# 重新設定權限
chown -R $(whoami):staff /Users/accusys/momentry/var/gitea
```
### Port 被佔用
```bash
# 檢查哪個程序佔用 port 3000
lsof -i :3000
# 終止佔用程序
kill <PID>
```
### 需要重新載入 plist
```bash
# 卸載舊服務 (如果存在)
sudo launchctl unload /Library/LaunchDaemons/com.momentry.gitea.plist 2>/dev/null
# 載入新服務
sudo launchctl load /Library/LaunchDaemons/com.momentry.gitea.plist
```
---
## 檔案位置
| 類型 | 路徑 | 說明 |
|------|------|------|
| 安裝 | `/opt/homebrew/opt/gitea/` | Gitea 安裝目錄 |
| 執行檔 | `/opt/homebrew/opt/gitea/bin/gitea` | Gitea 執行檔 |
| 數據目錄 | `/Users/accusys/momentry/var/gitea/data/` | 數據儲存 |
| 配置 | `/Users/accusys/momentry/etc/gitea/app.ini` | 設定檔 |
| 日誌 | `/Users/accusys/momentry/log/gitea.log` | 執行日誌 |
| 錯誤日誌 | `/Users/accusys/momentry/log/gitea.error.log` | 錯誤日誌 |
| plist | `/Library/LaunchDaemons/com.momentry.gitea.plist` | 開機啟動 |
| 備份 | `/Users/accusys/momentry/var/gitea_backup/app.ini` | 配置備份 |
---
## 資料庫資訊
Gitea 使用 PostgreSQL 作為資料庫:
| 項目 | 值 |
|------|-----|
| Database | gitea |
| User | gitea |
| Host | 127.0.0.1:5432 |
| Password | gitea_pass |
---
## 常用指令
```bash
# 啟動 Gitea
gitea web --config /Users/accusys/momentry/etc/gitea/app.ini
# 驗證配置
gitea doctor --config /Users/accusys/momentry/etc/gitea/app.ini
# 查看版本
gitea --version
# 備份數據
gitea dump --config /Users/accusys/momentry/etc/gitea/app.ini --zipfile /Users/accusys/momentry/var/gitea_backup.zip
```
---
## 版本資訊
- 版本: 1.25.3
- HTTP Port: 3000
- SSH Port: 2222
- 數據目錄: /Users/accusys/momentry/var/gitea/
- 配置: /Users/accusys/momentry/etc/gitea/app.ini
- 日誌目錄: /Users/accusys/momentry/log/

View File

@@ -1,393 +0,0 @@
# Gitea MCP Server 安裝指南 (本地部署)
| 項目 | 內容 |
|------|------|
| 建立者 | OpenCode |
| 建立時間 | 2026-03-24 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-24 | 創建文件 | OpenCode | OpenCode / big-pickle |
---
## 概述
本文檔說明如何在 macOS 上安裝 Gitea MCP Server配置為透過 OpenCode MCP 整合存取 Gitea API。
Gitea MCP Server 是一個 MCP (Model Context Protocol) 伺服器,提供對 Gitea API 的完整存取能力,包括 repos、issues、pull requests、workflows 等資源管理。
---
## 當前狀態
| 項目 | 狀態 |
|------|------|
| Gitea MCP Server | ✅ 已安裝 |
| 安裝方式 | Homebrew (`gitea-mcp-server`) |
| Plist | /Library/LaunchDaemons/com.momentry.gitea-mcp-server.plist |
| 執行身份 | accusys |
| 監聽端口 | 8787 |
| Gitea 主機 | http://localhost:3000 |
| Launchd 狀態 | ✅ 已註冊 |
| RunAtLoad | ✅ 已設定 |
| KeepAlive | ✅ 已設定 |
---
## 前置條件
- Gitea 服務已運行(端口 3000
- Homebrew 已安裝
- 管理員權限
---
## 安裝步驟
### Step 1: 安裝 Gitea MCP Server
```bash
brew install gitea-mcp-server
```
**驗證**:
```bash
which gitea-mcp-server
# /opt/homebrew/bin/gitea-mcp-server
/opt/homebrew/bin/gitea-mcp-server --help
```
---
### Step 2: 創建日誌目錄
```bash
mkdir -p /Users/accusys/momentry/log
touch /Users/accusys/momentry/log/gitea-mcp-server.log
touch /Users/accusys/momentry/log/gitea-mcp-server.error.log
```
---
### Step 3: 獲取 Gitea API Token
#### 步驟 3.1: 登入 Gitea
1. 開啟瀏覽器,訪問 http://localhost:3000
2. 使用管理員帳號登入
#### 步驟 3.2: 生成 Personal Access Token
1. 點擊右上角頭像 → **Settings**
2. 左側選單選擇 **Applications**
3.**Access Tokens** 區塊:
- **Token Name**: `opencode-mcp`
- **Expiration**: 選擇過期時間(如 365 days
- **Scopes**: 勾選需要的權限
- `repo` - 倉庫操作
- `read:user` - 讀取用戶資訊
- `read:org` - 讀取組織資訊
4. 點擊 **Generate Token**
5. **立即複製** 生成的 Token
#### 步驟 3.3: 驗證 Token
```bash
curl -H "Authorization: token <YOUR_TOKEN>" http://localhost:3000/api/v1/user
```
---
### Step 4: 創建 Plist 檔案
```bash
sudo tee /Library/LaunchDaemons/com.momentry.gitea-mcp-server.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.momentry.gitea-mcp-server</string>
<key>UserName</key>
<string>accusys</string>
<key>ProgramArguments</key>
<array>
<string>/opt/homebrew/bin/gitea-mcp-server</string>
<string>-transport</string>
<string>http</string>
<string>-port</string>
<string>8787</string>
<string>-host</string>
<string>http://localhost:3000</string>
<string>-token</string>
<string><GITEA_TOKEN></string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/accusys/momentry/log/gitea-mcp-server.log</string>
<key>StandardErrorPath</key>
<string>/Users/accusys/momentry/log/gitea-mcp-server.error.log</string>
</dict>
</plist>
EOF
```
**注意**: 將 `<GITEA_TOKEN>` 替換為實際的 Token 值。
---
### Step 5: 設定權限
```bash
sudo chown root:wheel /Library/LaunchDaemons/com.momentry.gitea-mcp-server.plist
sudo chmod 644 /Library/LaunchDaemons/com.momentry.gitea-mcp-server.plist
```
---
### Step 6: 載入服務
```bash
sudo launchctl bootstrap system /Library/LaunchDaemons/com.momentry.gitea-mcp-server.plist
```
---
### Step 7: 驗證服務
```bash
# 檢查服務狀態
sudo launchctl list | grep gitea-mcp-server
# 檢查進程
ps aux | grep gitea-mcp-server | grep -v grep
# 測試端點
curl -s http://localhost:8787/
```
---
### Step 8: 整合 OpenCode MCP
#### 步驟 8.1: 更新 OpenCode 配置
編輯 `~/.config/opencode/opencode.json`
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"gitea": {
"type": "local",
"enabled": true,
"command": [
"/opt/homebrew/bin/gitea-mcp-server",
"-token", "<GITEA_TOKEN>",
"-host", "http://localhost:3000"
]
},
"n8n": {
"type": "local",
"enabled": true,
"command": ["/opt/homebrew/bin/mcp-n8n"],
"environment": {
"N8N_BASE_URL": "http://localhost:5678",
"N8N_API_KEY": "<N8N_API_KEY>"
}
}
}
}
```
#### 步驟 8.2: 重啟 OpenCode
```bash
# 停止現有 OpenCode
pkill -f opencode
# 重新啟動
opencode
```
#### 步驟 8.3: 驗證 MCP 連接
在 OpenCode 中執行:
```
/mcps
```
確認 gitea 顯示為 `connected`
---
## Plist 參數說明
| 參數 | 說明 | 值 |
|------|------|-----|
| `-transport` | 傳輸類型 | `http` |
| `-port` | HTTP 監聽端口 | `8787` |
| `-host` | Gitea 主機 URL | `http://localhost:3000` |
| `-token` | Gitea API Token | 見 Step 3 |
---
## 管理指令
### 啟動服務
```bash
sudo launchctl bootstrap system /Library/LaunchDaemons/com.momentry.gitea-mcp-server.plist
```
---
### 停止服務
```bash
sudo launchctl bootout system/com.momentry.gitea-mcp-server
```
---
### 重啟服務
```bash
sudo launchctl bootout system/com.momentry.gitea-mcp-server
sudo launchctl bootstrap system /Library/LaunchDaemons/com.momentry.gitea-mcp-server.plist
```
---
### 查看日誌
```bash
# 即時查看日誌
tail -f /Users/accusys/momentry/log/gitea-mcp-server.log
# 錯誤日誌
tail -f /Users/accusys/momentry/log/gitea-mcp-server.error.log
```
---
## 卸載步驟
### Step 1: 停止服務
```bash
sudo launchctl bootout system/com.momentry.gitea-mcp-server
```
---
### Step 2: 移除 Plist
```bash
sudo rm /Library/LaunchDaemons/com.momentry.gitea-mcp-server.plist
```
---
### Step 3: 從 OpenCode 配置移除
編輯 `~/.config/opencode/opencode.json`,移除 `mcp.gitea` 區塊。
---
## 故障排除
### 服務無法啟動
1. 檢查 Token 是否正確
2. 檢查 Gitea 是否運行:`curl -s http://localhost:3000/`
3. 檢查日誌:`/Users/accusys/momentry/log/gitea-mcp-server.error.log`
---
### Token 無效
1. 確認 Token 未過期
2. 確認 Token 有足夠的權限
3. 重新生成 Token
---
### 端口被佔用
```bash
# 檢查端口占用
lsof -i :8787
# 修改 plist 中的端口後重新載入
```
---
### OpenCode MCP 未顯示
1. 確認 OpenCode 已重啟
2. 檢查 `~/.config/opencode/opencode.json` 格式正確
3. 確認 gitea-mcp-server 程序正在運行
---
## 檔案位置
| 類型 | 路徑 | 說明 |
|------|------|------|
| Plist | /Library/LaunchDaemons/com.momentry.gitea-mcp-server.plist | Launchd 服務配置 |
| 日誌 | /Users/accusys/momentry/log/gitea-mcp-server.log | 標準輸出日誌 |
| 錯誤日誌 | /Users/accusys/momentry/log/gitea-mcp-server.error.log | 錯誤日誌 |
| OpenCode 配置 | ~/.config/opencode/opencode.json | MCP 設定檔 |
| Gitea | http://localhost:3000 | Gitea Web UI |
---
## 常用指令
```bash
# 驗證服務狀態
sudo launchctl list | grep gitea-mcp-server
# 查看服務 PID
ps aux | grep gitea-mcp-server | grep -v grep
# 測試端點
curl -s http://localhost:8787/
# 驗證 Gitea 連接
curl -H "Authorization: token <TOKEN>" http://localhost:3000/api/v1/user
# 查看即時日誌
tail -f /Users/accusys/momentry/log/gitea-mcp-server.log
```
---
## 版本資訊
- 版本: 1.0
- 安裝日期: 2026-03-24
- 文件更新: 2026-03-24
---
## 相關文件
- [OpenCode MCP 整合](./N8N_MCP_SETUP.md) - n8n MCP 設定說明
- [服務總覽](./SERVICES.md) - 所有服務狀態總覽
- [待解決問題](./PENDING_ISSUES.md) - MCP 安裝狀態追蹤

View File

@@ -1,396 +0,0 @@
# MariaDB 安裝指南 (本地部署)
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-16 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
---
## 概述
本文檔說明如何在 macOS 上安裝 MariaDB配置為本地部署支援遠端訪問。
---
## 當前狀態
| 項目 | 狀態 |
|------|------|
| MariaDB | ✅ 已安裝 v12.1.2 |
| 數據目錄 | /Users/accusys/momentry/var/mariadb/ |
| 日誌目錄 | /Users/accusys/momentry/log/ |
| Plist | /Library/LaunchDaemons/com.momentry.mariadb.plist |
---
## 安裝步驟
### Step 1: 安裝 MariaDB (使用 brew)
```bash
# 安裝 MariaDB
brew install mariadb
```
**驗證**:
```bash
mariadb --version
# mariadb from 12.1.2-MariaDB
```
---
### Step 2: 建立目錄結構
```bash
# 建立數據目錄
mkdir -p /Users/accusys/momentry/var/mariadb
# 建立配置目錄
mkdir -p /Users/accusys/momentry/etc/mariadb
# 建立日誌目錄
mkdir -p /Users/accusys/momentry/log
# 建立日誌文件
touch /Users/accusys/momentry/log/mariadb.log
touch /Users/accusys/momentry/log/mariadb.error.log
# 設定權限
chown -R accusys:staff /Users/accusys/momentry/var/mariadb
chown -R accusys:staff /Users/accusys/momentry/etc/mariadb
chown -R accusys:staff /Users/accusys/momentry/log
```
**注意**: 如果需要從舊數據遷移,需要先初始化新目錄:
```bash
# 初始化新數據目錄
mysql_install_db --datadir=/Users/accusys/momentry/var/mariadb
```
---
### Step 3: 使用 plist 開機自動啟動
```bash
# 複製 plist 到 LaunchDaemons 目錄
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.mariadb.plist /Library/LaunchDaemons/
# 載入並啟動
sudo launchctl load /Library/LaunchDaemons/com.momentry.mariadb.plist
```
---
## 監控配置
### 添加到監控配置
`monitor/config/monitor_config.yaml` 中添加:
```yaml
database:
mariadb:
enabled: true
host: "localhost"
port: 3306
user: "root"
```
---
## 卸載步驟
### 重要: 路徑說明
| 路徑 | 類型 | 說明 |
|------|------|------|
| `/Users/accusys/momentry/var/mariadb/` | 數據 | **不要刪除** - 數據目錄 |
| `/Users/accusys/momentry/etc/mariadb/` | 配置 | **不要刪除** - 配置目錄 |
| `/Users/accusys/momentry/log/` | 日誌 | **不要刪除** - 日誌目錄 |
| `/opt/homebrew/opt/mariadb/` | 安裝 | **刪除** - MariaDB 安裝目錄 |
### Step 1: 停止 MariaDB
```bash
# 找到 MariaDB 進程
ps aux | grep mariadb | grep -v grep
# 停止 MariaDB
mysqladmin -u root -p shutdown
# 或
pkill mariadbd
# 確認停止
ps aux | grep mariadb | grep -v grep || echo "MariaDB 已停止"
```
---
### Step 2: 卸載 MariaDB
```bash
# 卸載 MariaDB
brew uninstall mariadb
# 移除 plist
sudo launchctl unload /Library/LaunchDaemons/com.momentry.mariadb.plist
sudo rm /Library/LaunchDaemons/com.momentry.mariadb.plist
```
---
### Step 3: 刪除專屬檔案
```bash
# 刪除數據目錄 (可選)
rm -rf /Users/accusys/momentry/var/mariadb
# 刪除日誌 (可選)
rm -f /Users/accusys/momentry/log/mariadb.log
rm -f /Users/accusys/momentry/log/mariadb.error.log
```
**注意: 不要刪除以下共用目錄**:
```bash
# 這些是共用的,不要刪除!
# /Users/accusys/momentry/var
# /Users/accusys/momentry/log
```
---
### Step 4: 卸載後檢查清單
```bash
echo "=== MariaDB 卸載後檢查 ==="
# 1. 檢查 MariaDB 進程
echo "1. MariaDB 進程:"
ps aux | grep mariadb | grep -v grep && echo " ✗ 仍在運行" || echo " ✓ 已停止"
# 2. Port 3306
echo "2. Port 3306:"
lsof -i :3306 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
# 3. mariadb 命令
echo "3. mariadb 命令:"
which mariadb > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 4. brew 安裝
echo "4. brew 安裝:"
brew list mariadb > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 5. launchctl 服務
echo "5. launchctl 服務:"
sudo launchctl list | grep mariadb > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 6. 數據目錄 (可選刪除)
echo "6. 數據目錄:"
[ -d "/Users/accusys/momentry/var/mariadb" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
# 7. 日誌目錄 (可選刪除)
echo "7. 日誌目錄:"
[ -d "/Users/accusys/momentry/log" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
```
**預期結果**:
```
=== MariaDB 卸載後檢查 ===
1. MariaDB 進程:
✓ 已停止
2. Port 3306:
✓ 已釋放
3. mariadb 命令:
✓ 已移除
4. brew 安裝:
✓ 已移除
5. launchctl 服務:
✓ 已移除
6. 數據目錄:
✓ 保留 (或 ✗ 已刪除)
7. 日誌目錄:
✓ 保留 (或 ✗ 已刪除)
```
---
## 手動檢查命令
```bash
# 1. 檢查進程
ps aux | grep mariadb | grep -v grep
# 2. 檢查 Port
lsof -i :3306
# 3. 測試連線
mariadb -u root -e "SELECT 1;"
# 4. 查看所有數據庫
mariadb -u root -e "SHOW DATABASES;"
# 5. 查看用戶
mariadb -u root -e "SELECT User, Host FROM mysql.user;"
# 6. 查看表
mariadb -u root -e "USE mysql; SHOW TABLES;"
# 7. 查看日誌
tail -20 /Users/accusys/momentry/log/mariadb.log
```
---
## 連線資訊
| 項目 | 值 |
|------|-----|
| Host | localhost |
| Port | 3306 |
| User | root |
---
## 環境變數
`.env` 中:
```env
MARIADB_URL=mariadb://root@localhost:3306
```
---
## 遠端訪問
- MariaDB 綁定到所有網路介面 (0.0.0.0)
- 本地網路其他機器可透過 IP 訪問
- 請設定用戶權限限制訪問
---
## 故障排除
### MariaDB 無法啟動
```bash
# 檢查日誌
tail -f /Users/accusys/momentry/log/mariadb.log
# 檢查目錄權限
ls -la /Users/accusys/momentry/var/mariadb/
# 重新設定權限
chown -R $(whoami):staff /Users/accusys/momentry/var/mariadb
```
### Port 被佔用
```bash
# 檢查哪個程序佔用 port 3306
lsof -i :3306
# 終止佔用程序
kill <PID>
```
### 需要重新載入 plist
```bash
# 卸載舊服務 (如果存在)
sudo launchctl unload /Library/LaunchDaemons/com.momentry.mariadb.plist 2>/dev/null
# 載入新服務
sudo launchctl load /Library/LaunchDaemons/com.momentry.mariadb.plist
```
---
## 檔案位置
| 類型 | 路徑 | 說明 |
|------|------|------|
| 安裝 | `/opt/homebrew/opt/mariadb/` | MariaDB 安裝目錄 |
| 執行檔 | `/opt/homebrew/opt/mariadb/bin/mariadbd` | MariaDB 執行檔 |
| 數據目錄 | `/Users/accusys/momentry/var/mariadb/` | 數據儲存 |
| 日誌 | `/Users/accusys/momentry/log/mariadb.log` | 執行日誌 |
| 錯誤日誌 | `/Users/accusys/momentry/log/mariadb.error.log` | 錯誤日誌 |
| plist | `/Library/LaunchDaemons/com.momentry.mariadb.plist` | 開機啟動 |
| 備份 | `/Users/accusys/momentry/var/mariadb_backup/` | 數據備份 |
---
## 備份與恢復
### 備份用戶配置
已創建專用備份用戶:
- 用戶名:`momentry_backup`
- 密碼:`momentry_backup_pwd_2026`
- 權限SELECT, LOCK TABLES, SHOW VIEW, EVENT, TRIGGER
### 備份 (mysqldump)
```bash
# 備份所有數據庫
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
mysqldump -u momentry_backup -pmomentry_backup_pwd_2026 --all-databases | gzip > \
/Users/accusys/momentry/backup/daily/mariadb/mariadb_db_all_${TIMESTAMP}.sql.gz
# 備份指定數據庫 (WordPress)
mysqldump -u momentry_backup -pmomentry_backup_pwd_2026 wordpress | gzip > \
/Users/accusys/momentry/backup/daily/mariadb/mariadb_db_wordpress_${TIMESTAMP}.sql.gz
# 驗證
sha256sum /Users/accusys/momentry/backup/daily/mariadb/mariadb_db_*.sql.gz > \
/Users/accusys/momentry/backup/daily/mariadb/mariadb_db_${TIMESTAMP}.sha256
```
### 恢復 (mysql)
```bash
# 恢復所有數據庫
gunzip < /Users/accusys/momentry/backup/daily/mariadb/mariadb_db_all_20260316_101802.sql.gz | \
mysql -u momentry_backup -pmomentry_backup_pwd_2026
# 恢復指定數據庫
gunzip < /Users/accusys/momentry/backup/daily/mariadb/mariadb_db_wordpress_20260316_101802.sql.gz | \
mysql -u momentry_backup -pmomentry_backup_pwd_2026 wordpress
```
### 數據目錄複製 (完整遷移) - 離線
```bash
# 1. 停止 MariaDB
mysqladmin -u momentry_backup -pmomentry_backup_pwd_2026 shutdown
# 2. 複製數據目錄
cp -r /opt/homebrew/var/mysql/* /Users/accusys/momentry/var/mariadb/
# 3. 設定權限
chown -R $(whoami):staff /Users/accusys/momentry/var/mariadb
# 4. 啟動 MariaDB
sudo launchctl load /Library/LaunchDaemons/com.momentry.mariadb.plist
```
---
## 版本資訊
- 版本: 12.1.2
- Port: 3306
- User: root
- 數據目錄: /Users/accusys/momentry/var/mariadb/
- 日誌目錄: /Users/accusys/momentry/log/

View File

@@ -1,464 +0,0 @@
# Momentry Core API 安裝指南 (本地部署)
| 項目 | 內容 |
|------|------|
| 建立者 | OpenCode |
| 建立時間 | 2026-03-23 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-23 | 創建文件 | OpenCode | - |
---
## 概述
本文檔說明如何在 macOS 上安裝 Momentry Core API 服務,配置為本地部署,並設定開機自動啟動。
Momentry Core API 是一個 Rust 編寫的數位資產管理 API 服務,提供:
- 影片搜尋 API (`/api/v1/search`)
- n8n 整合 API (`/api/v1/n8n/search`)
- 健康檢查端點 (`/health`)
- 影片註冊與處理功能
---
## 當前狀態
| 項目 | 狀態 |
|------|------|
| Momentry Core API | ✅ 已安裝 v0.1.0 |
| Binary | `/Users/accusys/momentry_core_0.1/target/release/momentry` |
| Port | 3002 |
| 反向代理 | Caddy (`api.momentry.ddns.net`) |
| 數據庫 | PostgreSQL (momentry) |
| 向量庫 | Qdrant |
| Cache | Redis |
| launchd plist | ✅ 已建立 (/Library/LaunchDaemons/com.momentry.api.plist) |
---
## 系統需求
### 必要服務
| 服務 | 版本 | 用途 |
|------|------|------|
| PostgreSQL | 16+ | 主數據庫 |
| Redis | 1.0+ | 快取與佇列 |
| Qdrant | 1.7+ | 向量搜尋 |
| Ollama | 最新 | LLM 與 Embedding |
### Rust 環境
```bash
# 檢查 Rust 版本
rustc --version
cargo --version
# 如需安裝 Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```
---
## 安裝步驟
### Step 1: 編譯 Momentry Core
```bash
# 進入專案目錄
cd /Users/accusys/momentry_core_0.1
# 編譯 release 版本
cargo build --release
# 驗證編譯結果
ls -la target/release/momentry
```
---
### Step 2: 設定環境變數
建立環境變數檔案:
```bash
# 建立執行目錄
mkdir -p /Users/accusys/momentry_core_0.1/momentry_runtime/env
# 建立環境變數檔案
cat > /Users/accusys/momentry_core_0.1/momentry_runtime/env/momentry.env << 'EOF'
# Database Configuration
DATABASE_URL=postgres://accusys@localhost:5432/momentry
# Redis Configuration
REDIS_URL=redis://:accusys@localhost:6379
REDIS_PASSWORD=accusys
# API Server
API_HOST=127.0.0.1
API_PORT=3002
# Ollama (LLM)
OLLAMA_HOST=http://localhost:11434
# Qdrant (Vector Database)
QDRANT_URL=http://localhost:6333
QDRANT_COLLECTION=momentry_chunks
EOF
```
---
### Step 3: 手動啟動服務
```bash
# 啟動 API 服務
cd /Users/accusys/momentry_core_0.1
./target/release/momentry server --port 3002
# 驗證服務
curl http://localhost:3002/health
# {"status":"ok","version":"0.1.0","uptime_ms":1234}
```
---
### Step 4: 設定 Caddy 反向代理
`/Users/accusys/momentry/etc/Caddyfile` 中新增:
```caddy
# Momentry Core API
api.momentry.ddns.net {
reverse_proxy localhost:3002
import common_log momentry_api_access
}
```
重新載入 Caddy
```bash
# 重新載入配置
caddy reload --config /Users/accusys/momentry/etc/Caddyfile
# 驗證
curl -sk https://api.momentry.ddns.net/health
```
---
### Step 5: 建立 launchd plist (開機自動啟動)
建立 plist 檔案:
```bash
sudo tee /Library/LaunchDaemons/com.momentry.api.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.momentry.api</string>
<key>UserName</key>
<string>accusys</string>
<key>GroupName</key>
<string>staff</string>
<key>WorkingDirectory</key>
<string>/Users/accusys/momentry_core_0.1</string>
<key>ProgramArguments</key>
<array>
<string>/Users/accusys/momentry_core_0.1/target/release/momentry</string>
<string>server</string>
<string>--port</string>
<string>3002</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
<key>DATABASE_URL</key>
<string>postgres://accusys@localhost:5432/momentry</string>
<key>REDIS_URL</key>
<string>redis://:accusys@localhost:6379</string>
<key>REDIS_PASSWORD</key>
<string>accusys</string>
<key>OLLAMA_HOST</key>
<string>http://localhost:11434</string>
<key>QDRANT_URL</key>
<string>http://localhost:6333</string>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/accusys/momentry/log/momentry_api.log</string>
<key>StandardErrorPath</key>
<string>/Users/accusys/momentry/log/momentry_api.error.log</string>
</dict>
</plist>
EOF
```
建立日誌檔案:
```bash
# 建立日誌目錄(如不存在)
mkdir -p /Users/accusys/momentry/log
# 建立日誌檔案
touch /Users/accusys/momentry/log/momentry_api.log
touch /Users/accusys/momentry/log/momentry_api.error.log
# 設定權限
chown accusys:staff /Users/accusys/momentry/log/momentry_api.log
chown accusys:staff /Users/accusys/momentry/log/momentry_api.error.log
```
載入服務:
```bash
# 載入服務
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
# 驗證服務
launchctl list | grep momentry.api
# 檢查服務狀態
curl http://localhost:3002/health
```
---
## 卸載步驟
### Step 1: 停止並移除服務
```bash
# 停止服務
sudo launchctl unload /Library/LaunchDaemons/com.momentry.api.plist
# 移除 plist
sudo rm /Library/LaunchDaemons/com.momentry.api.plist
```
### Step 2: 移除 Caddy 配置
`/Users/accusys/momentry/etc/Caddyfile` 中移除 `api.momentry.ddns.net` 區塊。
```bash
# 重新載入 Caddy
caddy reload --config /Users/accusys/momentry/etc/Caddyfile
```
---
## 故障排除
### API 返回 502 Bad Gateway
**問題**: `api.momentry.ddns.net` 返回 502 錯誤
**原因**: Momentry Core API 服務未啟動
**解決方案**:
```bash
# 檢查服務狀態
launchctl list | grep momentry.api
# 如服務未啟動,手動啟動
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
# 或手動啟動測試
cd /Users/accusys/momentry_core_0.1
./target/release/momentry server --port 3002
```
---
### API 返回 404 Not Found
**問題**: 端點返回 404
**原因**: Binary 過舊,缺少該端點
**解決方案**:
```bash
# 重新編譯
cd /Users/accusys/momentry_core_0.1
cargo build --release
# 重啟服務
sudo launchctl unload /Library/LaunchDaemons/com.momentry.api.plist
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
```
---
### 服務無法啟動
**問題**: launchd 無法啟動服務
**檢查步驟**:
```bash
# 檢查日誌
tail -50 /Users/accusys/momentry/log/momentry_api.error.log
# 檢查 plist 語法
plutil -lint /Library/LaunchDaemons/com.momentry.api.plist
# 檢查權限
ls -la /Users/accusys/momentry_core_0.1/target/release/momentry
# 手動測試
/Users/accusys/momentry_core_0.1/target/release/momentry server --port 3002
```
---
## 檔案位置
| 類型 | 路徑 | 說明 |
|------|------|------|
| Binary | `/Users/accusys/momentry_core_0.1/target/release/momentry` | 執行檔 |
| 環境變數 | `/Users/accusys/momentry_core_0.1/momentry_runtime/env/momentry.env` | 環境設定 |
| launchd plist | `/Library/LaunchDaemons/com.momentry.api.plist` | 開機啟動配置 |
| 日誌 | `/Users/accusys/momentry/log/momentry_api.log` | 標準輸出 |
| 錯誤日誌 | `/Users/accusys/momentry/log/momentry_api.error.log` | 錯誤輸出 |
| Caddy 配置 | `/Users/accusys/momentry/etc/Caddyfile` | 反向代理配置 |
---
## 常用指令
### 服務管理
```bash
# 啟動服務
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
# 停止服務
sudo launchctl unload /Library/LaunchDaemons/com.momentry.api.plist
# 重啟服務
sudo launchctl unload /Library/LaunchDaemons/com.momentry.api.plist
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
# 檢查服務狀態
launchctl list | grep momentry.api
```
### 健康檢查
```bash
# 本地健康檢查
curl http://localhost:3002/health
# 詳細健康檢查
curl http://localhost:3002/health/detailed
# 外部健康檢查
curl -sk https://api.momentry.ddns.net/health
```
### API 測試
```bash
# 搜尋 API
curl -X POST http://localhost:3002/api/v1/search \
-H "Content-Type: application/json" \
-d '{"query":"test"}'
# n8n 搜尋 API
curl -X POST http://localhost:3002/api/v1/n8n/search \
-H "Content-Type: application/json" \
-d '{"query":"test"}'
# 列出影片
curl http://localhost:3002/api/v1/videos
```
### 日誌查看
```bash
# 查看最近的日誌
tail -50 /Users/accusys/momentry/log/momentry_api.log
# 即時監控日誌
tail -f /Users/accusys/momentry/log/momentry_api.log
# 查看錯誤日誌
tail -50 /Users/accusys/momentry/log/momentry_api.error.log
```
### 重新編譯
```bash
# 編譯 release 版本
cd /Users/accusys/momentry_core_0.1
cargo build --release
# 編譯後重啟服務
sudo launchctl unload /Library/LaunchDaemons/com.momentry.api.plist
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
```
---
## API 端點
| 端點 | 方法 | 說明 |
|------|------|------|
| `/health` | GET | 健康檢查 |
| `/health/detailed` | GET | 詳細健康檢查 |
| `/api/v1/register` | POST | 註冊影片 |
| `/api/v1/search` | POST | 搜尋影片 |
| `/api/v1/n8n/search` | POST | n8n 格式搜尋 |
| `/api/v1/search/hybrid` | POST | 混合搜尋 |
| `/api/v1/lookup` | GET | 查詢 UUID |
| `/api/v1/videos` | GET | 列出所有影片 |
| `/api/v1/progress/:uuid` | GET | 查詢處理進度 |
---
## 版本資訊
- 版本: 0.1.0
- 安裝日期: 2026-03-23
- Rust 版本: 1.xx
- 文件更新: 2026-03-23
---
## 相關文件
- `docs/SERVICES.md` - 服務總覽
- `docs/API_REFERENCE.md` - API 參考
- `docs/INSTALL_POSTGRESQL.md` - PostgreSQL 安裝
- `docs/INSTALL_REDIS.md` - Redis 安裝
- `docs/INSTALL_QDRANT.md` - Qdrant 安裝
- `docs/PENDING_ISSUES.md` - 待解決問題

View File

@@ -1,392 +0,0 @@
# MongoDB 安裝指南 (本地部署)
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-15 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-15 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
---
## 概述
本文檔說明如何在 macOS 上安裝 MongoDB Community Edition配置為本地部署支援遠端訪問。
---
## 當前狀態
| 項目 | 狀態 |
|------|------|
| MongoDB (mongodb-community) | ✅ 已安裝 v8.2.6 |
| 數據目錄 | /opt/homebrew/var/mongodb |
| 日誌目錄 | /Users/accusys/momentry/log |
---
## 安裝步驟
### Step 1: 安裝 MongoDB Community
```bash
# 安裝 MongoDB Community
brew tap mongodb/brew
brew install mongodb-community
```
**驗證**:
```bash
mongod --version
# db version v8.x.x
mongosh --version
# 2.7.x
sudo launchctl list | grep mongo
# 確認 MongoDB 服務已載入
```
---
### Step 2: 數據目錄 (已存在 - 共用)
數據目錄使用 homebrew 預設位置:
- 數據目錄: `/opt/homebrew/var/mongodb`
- 配置目錄: `/opt/homebrew/etc/mongod.conf`
- 日誌目錄: `/Users/accusys/momentry/log`
**建立配置目錄和日誌文件**:
```bash
# 建立配置目錄
mkdir -p /Users/accusys/momentry/etc/mongodb
# 建立日誌文件
touch /Users/accusys/momentry/log/mongodb.log
touch /Users/accusys/momentry/log/mongodb.error.log
# 確認權限:
ls -la /Users/accusys/momentry/
chown -R accusys:staff /Users/accusys/momentry
```
---
### Step 3: 使用 LaunchAgent 啟動 (開機自動)
```bash
# 複製 plist 到 LaunchDaemons 目錄 (開機自動需要 root 權限)
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.mongodb.plist \
/Library/LaunchDaemons/
# 載入並啟動
sudo launchctl load /Library/LaunchDaemons/com.momentry.mongodb.plist
# 驗證
launchctl list | grep mongodb
pgrep -a mongod
```
---
### Step 4: 建立資料庫用戶
```bash
mongosh --eval '
use admin
db.createUser({
user: "accusys",
pwd: "Test3200Test3200",
roles: [
{ role: "readWrite", db: "momentry" },
{ role: "dbAdmin", db: "momentry" }
]
})
---
### Step 4: 驗證安裝
```bash
# 檢查進程
pgrep -a mongod
# 檢查端口
lsof -i :27017
# 測試連線
mongosh --eval "db.adminCommand('ping')"
# 檢查 LaunchAgent
launchctl list | grep mongodb
```
---
## 監控配置
### 添加到監控配置
`monitor/config/monitor_config.yaml` 中添加:
```yaml
database:
mongodb:
enabled: true
host: "localhost"
port: 27017
user: "accusys"
database: "momentry"
```
---
## 卸載步驟
### 重要: 路徑說明
| 路徑 | 類型 | 說明 |
|------|------|------|
| `/Users/accusys/momentry/` | 共用 | **不要刪除** - 多個系統共用 |
| `/Users/accusys/momentry/var` | 共用 | **不要刪除** - 數據目錄 |
| `/Users/accusys/momentry/etc/mongodb/` | 配置 | **不要刪除** - MongoDB 配置 |
| `/Users/accusys/momentry/log` | 共用 | **不要刪除** - 日誌目錄 |
| `~/.mongosh_history` | 專屬 | 可選刪除 - Mongo Shell 歷史 |
### Step 1: 停止 MongoDB
```bash
# 找到 MongoDB 進程
ps aux | grep mongod | grep -v grep
# 停止 MongoDB
pkill mongod
# 或
kill <PID>
# 確認停止
ps aux | grep mongod | grep -v grep || echo "MongoDB 已停止"
```
---
### Step 2: 卸載 MongoDB
```bash
# 完全卸載 (保留數據)
brew uninstall mongodb-community
```
---
### Step 3: 刪除專屬檔案
```bash
# 刪除 MongoDB 專屬配置 (如果有)
rm -f ~/.mongosh_history
# 刪除臨時文件 (可選)
rm -rf /tmp/mongodb-*
```
**注意: 不要刪除以下共用目錄**:
```bash
# 這些是共用的,不要刪除!
# /Users/accusys/momentry/var
# /Users/accusys/momentry/log
```
---
### Step 4: 卸載後檢查清單
```bash
echo "=== MongoDB 卸載後檢查 ==="
# 1. 檢查 MongoDB 進程
echo "1. MongoDB 進程:"
ps aux | grep mongod | grep -v grep && echo " ✗ 仍在運行" || echo " ✓ 已停止"
# 2. 檢查 Port 27017
echo "2. Port 27017:"
lsof -i :27017 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
# 3. 檢查 mongod 命令
echo "3. mongod 命令:"
which mongod > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 4. 檢查 launchctl
echo "4. launchctl 服務:"
sudo launchctl list | grep mongo > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 5. 檢查 Homebrew
echo "5. Homebrew 移除:"
brew list mongo > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 6. 檢查數據目錄 (應該存在)
echo "6. 數據目錄:"
[ -d "/Users/accusys/momentry/var" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
# 7. 檢查日誌目錄 (應該存在)
echo "7. 日誌目錄:"
[ -d "/Users/accusys/momentry/log" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
```
**預期結果**:
```
=== MongoDB 卸載後檢查 ===
1. MongoDB 進程:
✓ 已停止
2. Port 27017:
✓ 已釋放
3. mongod 命令:
✓ 已移除
4. launchctl 服務:
✓ 已移除
5. Homebrew 移除:
✓ 已移除
6. 數據目錄:
✓ 保留
7. 日誌目錄:
✓ 保留
```
---
## 手動檢查命令
```bash
# 1. 檢查 Process 是否運行
ps aux | grep mongo | grep -v grep
# 2. 檢查 Port 27017
lsof -i :27017
# 3. 測試連線 (無認證)
mongosh --eval "db.adminCommand('ping')"
# 4. 測試連線 (有認證)
mongosh "mongodb://accusys:Test3200Test3200@localhost:27017/admin" --eval "db.adminCommand('ping')"
# 5. 查看所有資料庫
mongosh "mongodb://accusys:Test3200Test3200@localhost:27017/admin" --quiet --eval "db.adminCommand({listDatabases:1}).databases"
# 6. 查看用戶
mongosh "mongodb://accusys:Test3200Test3200@localhost:27017/admin" --quiet --eval "db.getUser('accusys')"
# 7. 查看日誌
tail -20 /Users/accusys/momentry/log/mongodb.log
tail -20 /Users/accusys/momentry/log/mongodb.error.log
```
---
## 管理命令
### 啟動/停止
```bash
# 使用 LaunchAgent (開機自動 - LaunchDaemons 目錄)
sudo launchctl load /Library/LaunchDaemons/com.momentry.mongodb.plist # 啟動
sudo launchctl unload /Library/LaunchDaemons/com.momentry.mongodb.plist # 停止
# 手動啟動 (僅除錯用)
nohup /opt/homebrew/bin/mongod \
--dbpath /Users/accusys/momentry/var \
--logpath /Users/accusys/momentry/log/mongodb.log \
--port 27017 \
--bind_ip 0.0.0.0 \
> /Users/accusys/momentry/log/mongodb.log 2>&1 &
# 強制停止
pkill mongod
```
---
## 連線字串
```bash
# 無認證 (本地)
mongodb://localhost:27017
# 有認證 (admin 資料庫)
mongodb://accusys:Test3200Test3200@localhost:27017/admin
# 有認證 (momentry 資料庫)
mongodb://accusys:Test3200Test3200@localhost:27017/momentry?authSource=admin
```
---
## 環境變數
`.env` 中:
```env
MONGODB_URL=mongodb://accusys:Test3200Test3200@localhost:27017/admin
MONGODB_DATABASE=momentry
```
---
## 遠端訪問
- MongoDB 綁定到 `0.0.0.0` (所有網路介面)
- 本地網路其他機器可透過 IP 訪問
- 建議設定防火牆規則限制訪問 IP
---
## 故障排除
### MongoDB 無法啟動
```bash
# 檢查日誌
tail -f /Users/accusys/momentry/log/mongodb.log
# 檢查目錄權限
ls -la /Users/accusys/momentry/
# 重新設定權限
chown -R $(whoami):staff /Users/accusys/momentry
```
### Port 被佔用
```bash
# 檢查哪個程序佔用 port 27017
lsof -i :27017
# 終止佔用程序
kill <PID>
```
---
## 檔案位置
| 類型 | 路徑 | 說明 |
|------|------|------|
| 數據目錄 | `/Users/accusys/momentry/var` | **共用 - 不要刪除** |
| 日誌目錄 | `/Users/accusys/momentry/log` | **共用 - 不要刪除** |
| mongod | `/opt/homebrew/bin/mongod` | 安裝後存在 |
| Homebrew | `/opt/homebrew/Cellar/mongodb-community/` | 卸載時刪除 |
| Homebrew | `/opt/homebrew/Cellar/mongodb-database-tools/` | 卸載時刪除 |
| Homebrew | `/opt/homebrew/Cellar/mongosh/` | 卸載時刪除 |
| 配置檔 | `/opt/homebrew/etc/mongod.conf` | 卸載時刪除 |
---
## 版本資訊
- 用戶: accusys
- 密碼: Test3200Test3200
- 數據目錄: /Users/accusys/momentry/var (共用 - 不要刪除!)
- 日誌目錄: /Users/accusys/momentry/log (共用 - 不要刪除!)

View File

@@ -1,489 +0,0 @@
# n8n 安裝指南 (本地部署)
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-16 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
---
## 概述
本文檔說明如何在 macOS 上安裝 n8n 工作流自動化平台,配置為本地部署,使用 Queue 模式。
---
## 當前狀態
| 項目 | 狀態 |
|------|------|
| n8n | ✅ 已安裝 v2.12.3 |
| 數據目錄 | /Users/accusys/momentry/var/n8n/ |
| 日誌目錄 | /Users/accusys/momentry/log/ |
| Main Plist | /Library/LaunchDaemons/com.momentry.n8n.main.plist |
| Worker Plist | /Library/LaunchDaemons/com.momentry.n8n.worker.plist |
| 數據庫 | PostgreSQL (n8n) |
| 隊列 | Redis |
| Launchd 狀態 | ✅ Main + Worker 已註冊 |
| RunAtLoad | ✅ 已設定 |
| KeepAlive | ✅ 已設定 |
### 重要更新 (2026-03-24)
1. **n8n Main + Worker**: 兩個服務都使用自定義 plist
2. **Runner 禁用**: 為避免端口衝突Main 服務設定 `N8N_RUNNERS_ENABLED=false`
3. **Worker 端口**: Worker 使用 5681, 5682, 5690, 5691 端口
---
## 安裝步驟
### Step 1: 安裝 n8n (使用 brew)
```bash
# 安裝 n8n
brew install n8n
```
**驗證**:
```bash
n8n --version
# 2.12.3
```
---
### Step 2: 建立目錄
```bash
# 建立數據目錄
mkdir -p /Users/accusys/momentry/var/n8n
# 建立配置目錄
mkdir -p /Users/accusys/momentry/etc/n8n
# 建立日誌目錄
mkdir -p /Users/accusys/momentry/log
# 建立日誌文件
touch /Users/accusys/momentry/log/n8n.main.log
touch /Users/accusys/momentry/log/n8n.main.error.log
touch /Users/accusys/momentry/log/n8n.worker.log
touch /Users/accusys/momentry/log/n8n.worker.error.log
# 設定權限
chown -R accusys:staff /Users/accusys/momentry/var/n8n
chown -R accusys:staff /Users/accusys/momentry/etc/n8n
chown -R accusys:staff /Users/accusys/momentry/log
```
---
### Step 3: 數據遷移 (如果從舊位置遷移)
```bash
# 停止舊服務
sudo launchctl unload /Library/LaunchDaemons/com.n8n.main.plist
sudo launchctl unload /Library/LaunchDaemons/com.n8n.worker.plist
# 複製數據
cp -r /Users/accusys/.n8n/* /Users/accusys/momentry/var/n8n/
# 設定權限
chown -R accusys:staff /Users/accusys/momentry/var/n8n
```
---
### Step 4: 使用 plist 開機自動啟動
```bash
# 複製 plist 到 LaunchDaemons 目錄
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.n8n.main.plist /Library/LaunchDaemons/
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.n8n.worker.plist /Library/LaunchDaemons/
# 移除舊 plist (如果存在)
sudo launchctl unload /Library/LaunchDaemons/com.n8n.main.plist 2>/dev/null
sudo launchctl unload /Library/LaunchDaemons/com.n8n.worker.plist 2>/dev/null
sudo rm /Library/LaunchDaemons/com.n8n.main.plist 2>/dev/null
sudo rm /Library/LaunchDaemons/com.n8n.worker.plist 2>/dev/null
# 載入並啟動
sudo launchctl load /Library/LaunchDaemons/com.momentry.n8n.main.plist
sudo launchctl load /Library/LaunchDaemons/com.momentry.n8n.worker.plist
```
---
## 監控配置
### 添加到監控配置
`monitor/config/monitor_config.yaml` 中添加:
```yaml
service:
services:
- name: "n8n"
type: "http"
port: 5678
host: "localhost"
check_url: "http://localhost:5678/"
timeout: 5
enabled: true
```
### 添加健康檢查函數
`monitor/service/health_check.sh` 中添加:
```bash
check_n8n() {
local start=$(date +%s%N)
if curl -s http://localhost:5678/ > /dev/null 2>&1; then
local end=$(date +%s%N)
local ms=$(( (end - start) / 1000000 ))
echo -e "${GREEN}${NC} n8n (5678) - ${ms}ms"
record_service "n8n" "up" "$ms" ""
return 0
else
echo -e "${RED}${NC} n8n (5678) - Down"
record_service "n8n" "down" "0" "Connection failed"
return 1
fi
}
```
### n8n Workflow 監控
n8n 有專門的工作流監控腳本,請參考 `monitor/workflow/n8n_workflow_monitor.sh`
---
## 卸載步驟
### 重要: 路徑說明
| 路徑 | 類型 | 說明 |
|------|------|------|
| `/Users/accusys/momentry/var/n8n/` | 數據 | **不要刪除** - n8n 數據 |
| `/Users/accusys/momentry/etc/n8n/` | 配置 | **不要刪除** - n8n 配置 |
| `/Users/accusys/momentry/log/` | 日誌 | **不要刪除** - n8n 日誌 |
| `/opt/homebrew/lib/node_modules/n8n/` | 安裝 | **刪除** - n8n 安裝目錄 |
### Step 1: 停止 n8n
```bash
# 停止 n8n 服務
sudo launchctl unload /Library/LaunchDaemons/com.momentry.n8n.main.plist
sudo launchctl unload /Library/LaunchDaemons/com.momentry.n8n.worker.plist
# 確認停止
ps aux | grep n8n | grep -v grep || echo "n8n 已停止"
```
---
### Step 2: 卸載 n8n
```bash
# 卸載 n8n
brew uninstall n8n
# 移除 plist
sudo rm /Library/LaunchDaemons/com.momentry.n8n.main.plist
sudo rm /Library/LaunchDaemons/com.momentry.n8n.worker.plist
```
---
### Step 3: 刪除專屬檔案
```bash
# 刪除數據目錄 (可選)
rm -rf /Users/accusys/momentry/var/n8n
# 刪除日誌 (可選)
rm -f /Users/accusys/momentry/log/n8n-*.log
```
**注意: 不要刪除以下共用目錄**:
```bash
# 這些是共用的,不要刪除!
# /Users/accusys/momentry/var
# /Users/accusys/momentry/log
# PostgreSQL n8n 數據庫 (如需保留)
```
---
### Step 4: 卸載後檢查清單
```bash
echo "=== n8n 卸載後檢查 ==="
# 1. 檢查 n8n 進程
echo "1. n8n Main 進程:"
ps aux | grep "n8n.*start" | grep -v grep && echo " ✗ 仍在運行" || echo " ✓ 已停止"
echo "2. n8n Worker 進程:"
ps aux | grep "n8n.*worker" | grep -v grep && echo " ✗ 仍在運行" || echo " ✓ 已停止"
# 2. Port 8085
echo "3. Port 8085:"
lsof -i :8085 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
# 3. Port 5679 (Worker)
echo "4. Port 5679 (Worker):"
lsof -i :5679 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
# 4. n8n 命令
echo "5. n8n 命令:"
which n8n > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 5. brew 安裝
echo "6. brew 安裝:"
brew list n8n > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 6. launchctl 服務
echo "7. launchctl 服務:"
sudo launchctl list | grep n8n > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
```
---
## 備份步驟
### 備份 n8n 數據
```bash
# 建立備份目錄
mkdir -p /Users/accusys/momentry/var/n8n_backup
# 1. 備份 PostgreSQL 數據庫
PGPASSWORD=accusys pg_dump -U accusys -d n8n > /Users/accusys/momentry/var/n8n_backup/n8n_database_$(date +%Y%m%d).sql
# 2. 備份用戶數據夾
cp -r /Users/accusys/momentry/var/n8n /Users/accusys/momentry/var/n8n_backup/
# 3. 壓縮備份
cd /Users/accusys/momentry/var && tar -czvf n8n_backup_$(date +%Y%m%d).tar.gz n8n_backup/
# 4. 清理臨時備份
rm -rf /Users/accusys/momentry/var/n8n_backup
```
### 還原數據
```bash
# 1. 停止 n8n
sudo launchctl unload /Library/LaunchDaemons/com.momentry.n8n.main.plist
sudo launchctl unload /Library/LaunchDaemons/com.momentry.n8n.worker.plist
# 2. 還原 PostgreSQL 數據庫
PGPASSWORD=accusys psql -U accusys -d n8n < /Users/accusys/momentry/var/n8n_backup/n8n_database_20260314.sql
# 3. 還原用戶數據夾
rm -rf /Users/accusys/momentry/var/n8n
cp -r /Users/accusys/momentry/var/n8n_backup/n8n /Users/accusys/momentry/var/n8n
chown -R accusys:staff /Users/accusys/momentry/var/n8n
# 4. 啟動 n8n
sudo launchctl load /Library/LaunchDaemons/com.momentry.n8n.main.plist
sudo launchctl load /Library/LaunchDaemons/com.momentry.n8n.worker.plist
```
---
## 手動檢查命令
```bash
# 1. 檢查進程
ps aux | grep n8n | grep -v grep
# 2. 檢查 Port
lsof -i :5678
lsof -i :5679
# 3. 測試連線
curl http://localhost:5678/
# 4. 查看版本
n8n --version
# 5. 查看日誌
tail -20 /Users/accusys/momentry/log/n8n-main.log
tail -20 /Users/accusys/momentry/log/n8n-worker.log
# 6. 查看錯誤日誌
tail -20 /Users/accusys/momentry/log/n8n-main.error.log
tail -20 /Users/accusys/momentry/log/n8n-worker.error.log
# 7. 檢查 launchctl 狀態
sudo launchctl list | grep n8n
```
---
## 連線資訊
| 項目 | 值 |
|------|-----|
| URL | http://localhost:5678 |
| Domain | n8n.momentry.ddns.net |
| 數據庫 | PostgreSQL (n8n) |
| 隊列 | Redis |
| Encryption Key | Test3200Test3200Test3200 |
### Queue 模式端口
| 服務 | Port |
|------|------|
| Main | 5678 |
| Task Broker (Worker 連接) | 5679 |
---
## 環境變數
`.env` 中:
```env
N8N_URL=http://localhost:5678
N8N_USER_FOLDER=/Users/accusys/momentry/var/n8n
```
---
## 故障排除
### n8n 無法啟動
```bash
# 檢查日誌
tail -f /Users/accusys/momentry/log/n8n-main.log
tail -f /Users/accusys/momentry/log/n8n-worker.log
# 檢查環境變數
launchctl list | grep n8n
# 檢查數據庫連線
PGPASSWORD=accusys psql -U accusys -d n8n -c "SELECT 1"
# 檢查 Redis 連線
redis-cli -a accusys ping
```
### Port 被佔用
```bash
# 檢查哪個程序佔用 port
lsof -i :8085
# 終止佔用程序
kill <PID>
```
### 數據庫連線失敗
```bash
# 檢查 PostgreSQL
pg_isready -h 127.0.0.1 -p 5432 -U n8n
# 測試連線
PGPASSWORD=accusys psql -h 127.0.0.1 -U n8n -d n8n -c "SELECT version();"
```
### 需要重新載入 plist
```bash
# 卸載舊服務
sudo launchctl unload /Library/LaunchDaemons/com.momentry.n8n.main.plist
sudo launchctl unload /Library/LaunchDaemons/com.momentry.n8n.worker.plist
# 載入新服務
sudo launchctl load /Library/LaunchDaemons/com.momentry.n8n.main.plist
sudo launchctl load /Library/LaunchDaemons/com.momentry.n8n.worker.plist
```
---
## 檔案位置
| 類型 | 路徑 | 說明 |
|------|------|------|
| 安裝 | `/opt/homebrew/lib/node_modules/n8n/` | n8n 安裝目錄 |
| 執行檔 | `/opt/homebrew/bin/n8n` | n8n 執行檔 |
| 數據目錄 | `/Users/accusys/momentry/var/n8n/` | 數據儲存 |
| 配置目錄 | `/Users/accusys/momentry/etc/n8n/` | 配置儲存 |
| Main 日誌 | `/Users/accusys/momentry/log/n8n.main.log` | 主服務日誌 |
| Main 錯誤日誌 | `/Users/accusys/momentry/log/n8n.main.error.log` | 主服務錯誤日誌 |
| Worker 日誌 | `/Users/accusys/momentry/log/n8n.worker.log` | Worker 日誌 |
| Worker 錯誤日誌 | `/Users/accusys/momentry/log/n8n.worker.error.log` | Worker 錯誤日誌 |
| Main Plist | `/Library/LaunchDaemons/com.momentry.n8n.main.plist` | 主服務開機啟動 |
| Worker Plist | `/Library/LaunchDaemons/com.momentry.n8n.worker.plist` | Worker 開機啟動 |
| 備份 | `/Users/accusys/momentry/var/n8n_backup/` | 數據備份 |
---
## 數據庫資訊
n8n 使用 PostgreSQL 作為數據庫:
| 項目 | 值 |
|------|-----|
| Database | n8n |
| User | n8n |
| Host | 127.0.0.1:5432 |
| Password | accusys |
### Redis 隊列資訊
| 項目 | 值 |
|------|-----|
| Host | 127.0.0.1:6379 |
| Password | accusys |
| Mode | Queue (Bull) |
---
## 常用指令
```bash
# 啟動 n8n Main
n8n start
# 啟動 n8n Worker
n8n worker
# 查看版本
n8n --version
# 備份數據
PGPASSWORD=accusys pg_dump -U accusys -d n8n > n8n_backup.sql
# 重新載入服務
sudo launchctl unload /Library/LaunchDaemons/com.momentry.n8n.main.plist
sudo launchctl load /Library/LaunchDaemons/com.momentry.n8n.main.plist
```
---
## 版本資訊
- 版本: 2.3.5
- Main Port: 5678
- Task Broker (Worker): 5679
- 數據目錄: /Users/accusys/momentry/var/n8n/
- 日誌目錄: /Users/accusys/momentry/log/
- 數據庫: PostgreSQL n8n
- 隊列: Redis

View File

@@ -1,375 +0,0 @@
# Ollama 安裝指南 (本地部署)
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-15 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-15 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
---
## 概述
本文檔說明如何在 macOS 上安裝 Ollama配置為本地部署用於運行大型語言模型 (LLM)。
---
## 當前狀態
| 項目 | 狀態 |
|------|------|
| Ollama | ✅ 已安裝 v0.13.5 |
| Port | 11434 |
| Models 目錄 | /Users/accusys/momentry/var/ollama/models |
| 日誌目錄 | /Users/accusys/momentry/log/ |
| Plist | /Library/LaunchDaemons/com.momentry.ollama.plist |
---
## 安裝步驟
### Step 1: 安裝 Ollama (使用 brew)
```bash
# 安裝 Ollama
brew install ollama
```
**驗證**:
```bash
ollama --version
# ollama version is 0.13.5
```
---
### Step 2: 建立目錄
```bash
# 建立數據目錄
mkdir -p /Users/accusys/momentry/var/ollama
# 建立配置目錄
mkdir -p /Users/accusys/momentry/etc/ollama
# 建立日誌目錄
mkdir -p /Users/accusys/momentry/log
# 建立日誌文件
touch /Users/accusys/momentry/log/ollama.log
touch /Users/accusys/momentry/log/ollama.error.log
# 設定權限
chown -R accusys:staff /Users/accusys/momentry/var/ollama
chown -R accusys:staff /Users/accusys/momentry/etc/ollama
chown -R accusys:staff /Users/accusys/momentry/log
```
---
### Step 3: 使用 plist 開機自動啟動
```bash
# 複製 plist 到 LaunchDaemons 目錄
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.ollama.plist /Library/LaunchDaemons/
# 載入並啟動
sudo launchctl load /Library/LaunchDaemons/com.momentry.ollama.plist
```
---
## 監控配置
### 添加到監控配置
`monitor/config/monitor_config.yaml` 中添加:
```yaml
service:
services:
- name: "ollama"
type: "http"
port: 11434
host: "localhost"
check_url: "http://localhost:11434/api/tags"
timeout: 5
enabled: true
```
### 添加健康檢查函數
`monitor/service/health_check.sh` 中添加:
```bash
check_ollama() {
local start=$(date +%s%N)
if curl -s http://localhost:11434/api/tags > /dev/null 2>&1; then
local end=$(date +%s%N)
local ms=$(( (end - start) / 1000000 ))
echo -e "${GREEN}${NC} Ollama (11434) - ${ms}ms"
record_service "ollama" "up" "$ms" ""
return 0
else
echo -e "${RED}${NC} Ollama (11434) - Down"
record_service "ollama" "down" "0" "Connection failed"
return 1
fi
}
```
---
## 卸載步驟
### 重要: 路徑說明
| 路徑 | 類型 | 說明 |
|------|------|------|
| `/Users/accusys/momentry/var/ollama/` | 數據 | **不要刪除** - Ollama 數據 |
| `/Users/accusys/momentry/etc/ollama/` | 配置 | **不要刪除** - Ollama 配置 |
| `/Users/accusys/momentry/log/` | 日誌 | **不要刪除** - 日誌目錄 |
| `/opt/homebrew/opt/ollama/` | 安裝 | **刪除** - Ollama 安裝目錄 |
### Step 1: 停止 Ollama
```bash
# 找到 Ollama 進程
ps aux | grep ollama | grep -v grep
# 停止 Ollama
pkill ollama
# 確認停止
ps aux | grep ollama | grep -v grep || echo "Ollama 已停止"
```
---
### Step 2: 卸載 Ollama
```bash
# 卸載 Ollama
brew uninstall ollama
# 移除 plist
sudo launchctl unload /Library/LaunchDaemons/com.momentry.ollama.plist
sudo rm /Library/LaunchDaemons/com.momentry.ollama.plist
```
---
### Step 3: 刪除專屬檔案
```bash
# 刪除日誌 (可選)
rm -f /Users/accusys/momentry/log/ollama.log
rm -f /Users/accusys/momentry/log/ollama.error.log
```
**注意: 不要刪除以下目錄**:
```bash
# 這些是重要的,不要刪除!
# /Users/accusys/.ollama/models
# /Users/accusys/momentry/log
```
---
### Step 4: 卸載後檢查清單
```bash
echo "=== Ollama 卸載後檢查 ==="
# 1. 檢查 Ollama 進程
echo "1. Ollama 進程:"
ps aux | grep ollama | grep -v grep && echo " ✗ 仍在運行" || echo " ✓ 已停止"
# 2. Port 11434
echo "2. Port 11434:"
lsof -i :11434 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
# 3. ollama 命令
echo "3. ollama 命令:"
which ollama > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 4. brew 安裝
echo "4. brew 安裝:"
brew list ollama > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 5. launchctl 服務
echo "5. launchctl 服務:"
sudo launchctl list | grep ollama > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 6. 模型目錄 (應該保留)
echo "6. 模型目錄:"
[ -d "/Users/accusys/.ollama/models" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
```
---
## 手動檢查命令
```bash
# 1. 檢查進程
ps aux | grep ollama | grep -v grep
# 2. 檢查 Port
lsof -i :11434
# 3. 測試連線
curl http://localhost:11434/
# 4. 查看版本
ollama --version
# 5. 查看已安裝的模型
ollama list
# 6. 查看日誌
tail -20 /Users/accusys/momentry/log/ollama.log
```
---
## 連線資訊
| 項目 | 值 |
|------|-----|
| Host | localhost |
| Port | 11434 |
| Models | /Users/accusys/.ollama/models |
---
## 環境變數
`.env` 中:
```env
OLLAMA_HOST=0.0.0.0:11434
OLLAMA_MODELS=/Users/accusys/.ollama/models
OLLAMA_FLASH_ATTENTION=1
OLLAMA_KV_CACHE_TYPE=q8_0
```
### 環境變數說明
| 變數 | 說明 | 預設值 |
|------|------|---------|
| OLLAMA_HOST | 綁定主機和端口 | 127.0.0.1:11434 |
| OLLAMA_MODELS | 模型儲存目錄 | ~/.ollama/models |
| OLLAMA_FLASH_ATTENTION | 啟用 Flash Attention | 0 |
| OLLAMA_KV_CACHE_TYPE | KV 緩存類型 | f16 |
---
## 遠端訪問
- Ollama 綁定到 `0.0.0.0:11434` (所有網路介面)
- 本地網路其他機器可透過 IP 訪問
- 請注意安全風險
---
## 故障排除
### Ollama 無法啟動
```bash
# 檢查日誌
tail -f /Users/accusys/momentry/log/ollama.log
# 檢查模型目錄權限
ls -la /Users/accusys/.ollama/models/
# 重新設定權限
chown -R $(whoami):staff /Users/accusys/.ollama
```
### Port 被佔用
```bash
# 檢查哪個程序佔用 port 11434
lsof -i :11434
# 終止佔用程序
kill <PID>
```
### 需要重新載入 plist
```bash
# 卸載舊服務 (如果存在)
sudo launchctl unload /Library/LaunchDaemons/com.momentry.ollama.plist 2>/dev/null
# 載入新服務
sudo launchctl load /Library/LaunchDaemons/com.momentry.ollama.plist
```
---
## 檔案位置
| 類型 | 路徑 | 說明 |
|------|------|------|
| 安裝 | `/opt/homebrew/opt/ollama/` | Ollama 安裝目錄 |
| 執行檔 | `/opt/homebrew/opt/ollama/bin/ollama` | Ollama 執行檔 |
| 數據目錄 | `/Users/accusys/momentry/var/ollama/` | 數據儲存 |
| 配置目錄 | `/Users/accusys/momentry/etc/ollama/` | 配置儲存 |
| 模型目錄 | `/Users/accusys/.ollama/models/` | AI 模型儲存 |
| 日誌 | `/Users/accusys/momentry/log/ollama.log` | 執行日誌 |
| 錯誤日誌 | `/Users/accusys/momentry/log/ollama.error.log` | 錯誤日誌 |
| plist | `/Library/LaunchDaemons/com.momentry.ollama.plist` | 開機啟動 |
| 備份 | `/Users/accusys/momentry/var/ollama_backup/environment.txt` | 環境變數備份 |
---
## 常用指令
```bash
# 查看版本
ollama --version
# 查看已安裝的模型
ollama list
# 拉取模型
ollama pull mistral
ollama pull llama2
# 運行模型
ollama run mistral
# 刪除模型
ollama remove mistral
# 查看模型資訊
ollama show mistral
```
---
## 已安裝的模型
查看已安裝的模型:
```bash
ollama list
```
---
## 版本資訊
- 版本: 0.13.5
- Port: 11434
- Models: /Users/accusys/.ollama/models/
- 日誌目錄: /Users/accusys/momentry/log/

View File

@@ -1,395 +0,0 @@
# PHP 安裝指南 (本地部署)
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-16 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
---
## 概述
本文檔說明如何在 macOS 上安裝 PHP 及 PHP-FPM配置為本地部署。
---
## 當前狀態
| 項目 | 狀態 |
|------|------|
| PHP | ✅ 已安裝 v8.5.2 |
| PHP-FPM | ✅ 執行中 |
| 配置目錄 | /Users/accusys/momentry/etc/php/ |
| 日誌目錄 | /Users/accusys/momentry/log/ |
| Plist | /Library/LaunchDaemons/com.momentry.php.plist |
---
## 安裝步驟
### Step 1: 安裝 PHP (使用 brew)
```bash
# 安裝 PHP
brew install php
# 安裝 PHP-FPM (通常包含在 php 中)
brew install php --fpm
```
**驗證**:
```bash
php --version
# PHP 8.5.2 (cli)
```
---
### Step 2: 建立目錄
```bash
# 建立配置目錄
mkdir -p /Users/accusys/momentry/etc/php
# 建立日誌目錄
mkdir -p /Users/accusys/momentry/log
# 建立日誌文件
touch /Users/accusys/momentry/log/php.log
touch /Users/accusys/momentry/log/php.error.log
# 設定權限
chown -R accusys:staff /Users/accusys/momentry/etc/php
chown -R accusys:staff /Users/accusys/momentry/log
```
---
### Step 3: 建立設定檔
建立 `/Users/accusys/momentry/etc/php/php-fpm.conf`:
```ini
[global]
pid = /Users/accusys/momentry/var/php-fpm.pid
error_log = /Users/accusys/momentry/log/php.error.log
log_level = notice
[www]
user = accusys
group = staff
listen = 127.0.0.1:9000
listen.owner = accusys
listen.group = staff
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
```
複製 php.ini:
```bash
cp /opt/homebrew/etc/php/8.5/php.ini /Users/accusys/momentry/etc/php/
```
---
### Step 4: 使用 plist 開機自動啟動
```bash
# 複製 plist 到 LaunchDaemons 目錄
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.php.plist /Library/LaunchDaemons/
# 載入並啟動
sudo launchctl load /Library/LaunchDaemons/com.momentry.php.plist
```
---
## 監控配置
### 添加到監控配置
`monitor/config/monitor_config.yaml` 中添加:
```yaml
service:
services:
- name: "php-fpm"
type: "tcp"
port: 9000
host: "localhost"
timeout: 5
enabled: true
```
---
## 卸載步驟
### 重要: 路徑說明
| 路徑 | 類型 | 說明 |
|------|------|------|
| `/Users/accusys/momentry/etc/php/` | 配置 | **不要刪除** - PHP 配置 |
| `/Users/accusys/momentry/log/` | 日誌 | **不要刪除** - 日誌目錄 |
| `/opt/homebrew/opt/php/` | 安裝 | **刪除** - PHP 安裝目錄 |
### Step 1: 停止 PHP-FPM
```bash
# 找到 PHP-FPM 進程
ps aux | grep php-fpm | grep -v grep
# 停止 PHP-FPM
pkill php-fpm
# 確認停止
ps aux | grep php-fpm | grep -v grep || echo "PHP-FPM 已停止"
```
---
### Step 2: 卸載 PHP
```bash
# 卸載 PHP
brew uninstall php
# 移除 plist
sudo launchctl unload /Library/LaunchDaemons/com.momentry.php.plist
sudo rm /Library/LaunchDaemons/com.momentry.php.plist
```
---
### Step 3: 刪除專屬檔案
```bash
# 刪除配置目錄 (可選)
rm -rf /Users/accusys/momentry/etc/php
# 刪除日誌 (可選)
rm -f /Users/accusys/momentry/log/php.log
rm -f /Users/accusys/momentry/log/php.error.log
```
**注意: 不要刪除以下共用目錄**:
```bash
# 這些是共用的,不要刪除!
# /Users/accusys/momentry/etc
# /Users/accusys/momentry/log
```
---
### Step 4: 卸載後檢查清單
```bash
echo "=== PHP 卸載後檢查 ==="
# 1. 檢查 PHP-FPM 進程
echo "1. PHP-FPM 進程:"
ps aux | grep php-fpm | grep -v grep && echo " ✗ 仍在運行" || echo " ✓ 已停止"
# 2. Port 9000
echo "2. Port 9000:"
lsof -i :9000 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
# 3. php 命令
echo "3. php 命令:"
which php > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 4. brew 安裝
echo "4. brew 安裝:"
brew list php > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 5. launchctl 服務
echo "5. launchctl 服務:"
sudo launchctl list | grep php > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 6. 配置目錄 (可選刪除)
echo "6. 配置目錄:"
[ -d "/Users/accusys/momentry/etc/php" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
# 7. 日誌目錄 (可選刪除)
echo "7. 日誌目錄:"
[ -d "/Users/accusys/momentry/log" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
```
---
## 手動檢查命令
```bash
# 1. 檢查 PHP 版本
php --version
# 2. 檢查 PHP-FPM 進程
ps aux | grep php-fpm | grep -v grep
# 3. 檢查 Port
lsof -i :9000
# 4. 測試 PHP
php -r "echo 'PHP OK' . PHP_EOL;"
# 5. 查看 PHP 模組
php -m
# 6. 查看 PHP 配置
php -i | grep "Loaded Configuration File"
# 7. 查看日誌
tail -20 /Users/accusys/momentry/log/php.log
```
---
## 連線資訊
| 項目 | 值 |
|------|-----|
| PHP-FPM Port | 9000 |
| PHP Version | 8.5.2 |
| Config | /Users/accusys/momentry/etc/php/php-fpm.conf |
| php.ini | /Users/accusys/momentry/etc/php/php.ini |
---
## 環境變數
`.env` 中:
```env
PHP_INI_SCAN_DIR=/Users/accusys/momentry/etc/php/conf.d
```
---
## 故障排除
### PHP-FPM 無法啟動
```bash
# 檢查日誌
tail -f /Users/accusys/momentry/log/php.error.log
# 檢查配置語法
/opt/homebrew/opt/php/sbin/php-fpm --test --fpm-config /Users/accusys/momentry/etc/php/php-fpm.conf
# 檢查目錄權限
ls -la /Users/accusys/momentry/etc/php/
# 重新設定權限
chown -R $(whoami):staff /Users/accusys/momentry/etc/php
```
### Port 被佔用
```bash
# 檢查哪個程序佔用 port 9000
lsof -i :9000
# 終止佔用程序
kill <PID>
```
### 需要重新載入 plist
```bash
# 卸載舊服務 (如果存在)
sudo launchctl unload /Library/LaunchDaemons/com.momentry.php.plist 2>/dev/null
# 載入新服務
sudo launchctl load /Library/LaunchDaemons/com.momentry.php.plist
```
---
## 檔案位置
| 類型 | 路徑 | 說明 |
|------|------|------|
| 安裝 | `/opt/homebrew/opt/php/` | PHP 安裝目錄 |
| 執行檔 | `/opt/homebrew/bin/php` | PHP 執行檔 |
| PHP-FPM | `/opt/homebrew/opt/php/sbin/php-fpm` | PHP-FPM 執行檔 |
| php.ini | `/Users/accusys/momentry/etc/php/8.5/php.ini` | PHP 配置 |
| PHP-FPM 配置 | `/Users/accusys/momentry/etc/php/8.5/php-fpm.conf` | PHP-FPM 主配置 |
| PHP-FPM pool | `/Users/accusys/momentry/etc/php/8.5/php-fpm.d/` | Pool 配置 |
| 日誌 | `/Users/accusys/momentry/log/php.log` | 執行日誌 |
| 錯誤日誌 | `/opt/homebrew/var/log/php-fpm.log` | PHP-FPM 錯誤日誌 |
| plist | `/Library/LaunchDaemons/com.momentry.php.plist` | 開機啟動 |
| 備份 | `/Users/accusys/momentry/backup/daily/php/` | 配置備份 |
---
## 常用指令
```bash
# 測試 PHP-FPM 配置
/opt/homebrew/opt/php/sbin/php-fpm --test --fpm-config /Users/accusys/momentry/etc/php/php-fpm.conf
# 查看 PHP 模組
php -m
# 查看已載入的配置
php -i
# 測試 PHP 腳本
php -r "echo 'Hello World' . PHP_EOL;"
# 查看 PHP-FPM 狀態
curl http://127.0.0.1:9000/status
```
---
## 備份與恢復
### 備份
```bash
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/Users/accusys/momentry/backup/daily/php"
mkdir -p "$BACKUP_DIR"
# 備份配置 (注意PHP-FPM 實際使用 /Users/accusys/momentry/etc/php/8.5/ 配置)
tar -czf "$BACKUP_DIR/php_cfg_${TIMESTAMP}.tar.gz" \
/Users/accusys/momentry/etc/php/8.5/php.ini \
/Users/accusys/momentry/etc/php/8.5/php-fpm.conf \
/Users/accusys/momentry/etc/php/8.5/php-fpm.d/
# 驗證
sha256sum "$BACKUP_DIR/php_cfg_${TIMESTAMP}.tar.gz" > "$BACKUP_DIR/php_${TIMESTAMP}.sha256"
```
### 恢復
```bash
# 解壓配置
tar -xzf /Users/accusys/momentry/backup/daily/php/php_cfg_20260316_102727.tar.gz -C /
# 測試配置
/opt/homebrew/opt/php/sbin/php-fpm --test --fpm-config /Users/accusys/momentry/etc/php/8.5/php-fpm.conf
```
---
## 版本資訊
- PHP Version: 8.5.2
- PHP-FPM Port: 9000
- 配置目錄: /Users/accusys/momentry/etc/php/
- 日誌目錄: /Users/accusys/momentry/log/

View File

@@ -1,397 +0,0 @@
# PostgreSQL 安裝指南 (本地部署)
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-15 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-15 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
---
## 概述
本文檔說明如何在 macOS 上安裝 PostgreSQL配置為本地部署支援遠端訪問。
---
## 當前狀態
| 項目 | 狀態 |
|------|------|
| PostgreSQL | ✅ 已安裝 v18.1 |
| 數據目錄 | /Users/accusys/momentry/var/postgresql |
| 日誌目錄 | /Users/accusys/momentry/log/ |
| Plist | /Library/LaunchDaemons/com.momentry.postgresql.plist |
| Launchd 狀態 | ✅ 已註冊 |
| RunAtLoad | ✅ 已設定 |
| KeepAlive | ✅ 已設定 |
### 重要更新 (2026-03-24)
1. **資料目錄已變更**: 從 `/opt/homebrew/var/postgresql@18` 遷移到 `/Users/accusys/momentry/var/postgresql`
2. **統一管理**: 所有 Momentry 服務現在都使用 `/Library/LaunchDaemons/` 下的自定義 plist
3. **避免衝突**: 刪除了 homebrew plist避免 reboot 後使用舊資料目錄
---
## 安裝步驟
### Step 1: 安裝 PostgreSQL (使用 brew)
```bash
# 安裝 PostgreSQL
brew install postgresql@18
```
**驗證**:
```bash
postgres --version
# postgres (PostgreSQL) 18.1
```
---
### Step 2: 建立目錄結構
```bash
# 建立數據目錄
mkdir -p /Users/accusys/momentry/var/postgresql
# 建立配置目錄
mkdir -p /Users/accusys/momentry/etc/postgresql
# 建立日誌目錄
mkdir -p /Users/accusys/momentry/log
# 建立日誌文件
touch /Users/accusys/momentry/log/postgresql.log
touch /Users/accusys/momentry/log/postgresql.error.log
# 設定權限
chown -R accusys:staff /Users/accusys/momentry/var/postgresql
chown -R accusys:staff /Users/accusys/momentry/etc/postgresql
chown -R accusys:staff /Users/accusys/momentry/log
```
**注意**: 如果需要從舊數據遷移,需要先初始化新目錄:
```bash
# 初始化新數據目錄 (會創建默認數據庫)
initdb -D /Users/accusys/momentry/var/postgresql -U accusys
```
---
### Step 3: 使用 plist 開機自動啟動
```bash
# 複製 plist 到 LaunchDaemons 目錄
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.postgresql.plist /Library/LaunchDaemons/
# 載入並啟動
sudo launchctl load /Library/LaunchDaemons/com.momentry.postgresql.plist
```
---
## 監控配置
### 添加到監控配置
`monitor/config/monitor_config.yaml` 中添加:
```yaml
database:
postgresql:
enabled: true
host: "localhost"
port: 5432
user: "accusys"
database: "momentry"
```
### 添加健康檢查函數
`monitor/database/postgres_monitor.sh` 中已包含 PostgreSQL 監控。
---
## 卸載步驟
### 重要: 路徑說明
| 路徑 | 類型 | 說明 |
|------|------|------|
| `/Users/accusys/momentry/var/postgresql/` | 數據 | **不要刪除** - 數據目錄 |
| `/Users/accusys/momentry/etc/postgresql/` | 配置 | **不要刪除** - 配置目錄 |
| `/Users/accusys/momentry/log/` | 日誌 | **不要刪除** - 日誌目錄 |
| `/opt/homebrew/opt/postgresql@18/` | 安裝 | **刪除** - PostgreSQL 安裝目錄 |
### Step 1: 停止 PostgreSQL
```bash
# 找到 PostgreSQL 進程
ps aux | grep postgres | grep -v grep
# 停止 PostgreSQL
pg_ctl -D /opt/homebrew/var/postgresql@18 stop
# 或
pkill -f postgresql
# 確認停止
ps aux | grep postgres | grep -v grep || echo "PostgreSQL 已停止"
```
---
### Step 2: 卸載 PostgreSQL
```bash
# 卸載 PostgreSQL
brew uninstall postgresql@18
# 移除 plist
sudo launchctl unload /Library/LaunchDaemons/com.momentry.postgresql.plist
sudo rm /Library/LaunchDaemons/com.momentry.postgresql.plist
```
---
### Step 3: 刪除專屬檔案
```bash
# 刪除數據目錄 (可選)
rm -rf /Users/accusys/momentry/var/postgresql
# 刪除日誌 (可選)
rm -f /Users/accusys/momentry/log/postgresql.log
rm -f /Users/accusys/momentry/log/postgresql.error.log
```
**注意: 不要刪除以下共用目錄**:
```bash
# 這些是共用的,不要刪除!
# /Users/accusys/momentry/var
# /Users/accusys/momentry/log
```
---
### Step 4: 卸載後檢查清單
```bash
echo "=== PostgreSQL 卸載後檢查 ==="
# 1. 檢查 PostgreSQL 進程
echo "1. PostgreSQL 進程:"
ps aux | grep postgres | grep -v grep && echo " ✗ 仍在運行" || echo " ✓ 已停止"
# 2. Port 5432
echo "2. Port 5432:"
lsof -i :5432 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
# 3. postgres 命令
echo "3. postgres 命令:"
which postgres > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 4. brew 安裝
echo "4. brew 安裝:"
brew list postgresql@18 > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 5. launchctl 服務
echo "5. launchctl 服務:"
sudo launchctl list | grep postgresql > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 6. 數據目錄 (可選刪除)
echo "6. 數據目錄:"
[ -d "/Users/accusys/momentry/var/postgresql" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
# 7. 日誌目錄 (可選刪除)
echo "7. 日誌目錄:"
[ -d "/Users/accusys/momentry/log" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
```
**預期結果**:
```
=== PostgreSQL 卸載後檢查 ===
1. PostgreSQL 進程:
✓ 已停止
2. Port 5432:
✓ 已釋放
3. postgres 命令:
✓ 已移除
4. brew 安裝:
✓ 已移除
5. launchctl 服務:
✓ 已移除
6. 數據目錄:
✓ 保留 (或 ✗ 已刪除)
7. 日誌目錄:
✓ 保留 (或 ✗ 已刪除)
```
---
## 手動檢查命令
```bash
# 1. 檢查進程
ps aux | grep postgres | grep -v grep
# 2. 檢查 Port
lsof -i :5432
# 3. 測試連線
psql -U accusys -l
# 4. 查看所有數據庫
psql -U accusys -c "\l"
# 5. 查看連接
psql -U accusys -c "\conninfo"
# 6. 查看表
psql -U accusys -d momentry -c "\dt"
# 7. 查看日誌
tail -20 /Users/accusys/momentry/log/postgresql.log
```
---
## 連線資訊
| 項目 | 值 |
|------|-----|
| Host | localhost |
| Port | 5432 |
| User | accusys |
| Database | momentry, video_register, gitea, n8n |
---
## 環境變數
`.env` 中:
```env
POSTGRES_URL=postgresql://accusys@localhost:5432
POSTGRES_DB=momentry
```
---
## 遠端訪問
- PostgreSQL 綁定到所有網路介面 (0.0.0.0)
- 本地網路其他機器可透過 IP 訪問
- 請設定 `pg_hba.conf` 限制訪問 IP
---
## 故障排除
### PostgreSQL 無法啟動
```bash
# 檢查日誌
tail -f /Users/accusys/momentry/log/postgresql.log
# 檢查目錄權限
ls -la /Users/accusys/momentry/var/postgresql/
# 重新設定權限
chown -R $(whoami):staff /Users/accusys/momentry/var/postgresql
```
### Port 被佔用
```bash
# 檢查哪個程序佔用 port 5432
lsof -i :5432
# 終止佔用程序
kill <PID>
```
### 需要重新載入 plist
```bash
# 卸載舊服務 (如果存在)
sudo launchctl unload /Library/LaunchDaemons/com.momentry.postgresql.plist 2>/dev/null
# 載入新服務
sudo launchctl load /Library/LaunchDaemons/com.momentry.postgresql.plist
```
---
## 檔案位置
| 類型 | 路徑 | 說明 |
|------|------|------|
| 安裝 | `/opt/homebrew/opt/postgresql@18/` | PostgreSQL 安裝目錄 |
| 執行檔 | `/opt/homebrew/opt/postgresql@18/bin/postgres` | PostgreSQL 執行檔 |
| 數據目錄 | `/Users/accusys/momentry/var/postgresql/` | 數據儲存 |
| 日誌 | `/Users/accusys/momentry/log/postgresql.log` | 執行日誌 |
| 錯誤日誌 | `/Users/accusys/momentry/log/postgresql.error.log` | 錯誤日誌 |
| plist | `/Library/LaunchDaemons/com.momentry.postgresql.plist` | 開機啟動 |
| 備份 | `/Users/accusys/momentry/var/momentry_db_backup_latest.sql` | momentry 數據庫備份 |
| 備份 | `/Users/accusys/momentry/var/video_register_db_backup_latest.sql` | video_register 數據庫備份 |
---
## 備份與恢復
### 備份 (pg_dump)
```bash
# 備份 momentry 數據庫
pg_dump -U accusys momentry > /Users/accusys/momentry/var/momentry_db_backup_latest.sql
# 備份 video_register 數據庫
pg_dump -U accusys video_register > /Users/accusys/momentry/var/video_register_db_backup_latest.sql
```
### 恢復 (psql)
```bash
# 恢復 momentry 數據庫
psql -U accusys -d momentry < /Users/accusys/momentry/var/momentry_db_backup_latest.sql
# 恢復 video_register 數據庫
psql -U accusys -d video_register < /Users/accusys/momentry/var/video_register_db_backup_latest.sql
```
### 數據目錄複製 (完整遷移)
```bash
# 1. 停止 PostgreSQL
pg_ctl -D /Users/accusys/momentry/var/postgresql stop
# 2. 複製數據目錄
cp -r /opt/homebrew/var/postgresql@18/* /Users/accusys/momentry/var/postgresql/
# 3. 設定權限
chown -R $(whoami):staff /Users/accusys/momentry/var/postgresql
# 4. 啟動 PostgreSQL
sudo launchctl load /Library/LaunchDaemons/com.momentry.postgresql.plist
```
---
## 版本資訊
- 版本: 18.1
- Port: 5432
- User: accusys
- 數據目錄: /Users/accusys/momentry/var/postgresql/
- 日誌目錄: /Users/accusys/momentry/log/

View File

@@ -1,472 +0,0 @@
# Qdrant 安裝指南 (本地部署)
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-16 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
---
## 概述
本文檔說明如何在 macOS 上安裝 Qdrant Vector Database配置為本地部署支援遠端訪問。
---
## 當前狀態
| 項目 | 狀態 |
|------|------|
| Qdrant | ✅ 已安裝 v1.17.0 |
| 數據目錄 | /Users/accusys/momentry/var/qdrant/ |
| 日誌目錄 | /Users/accusys/momentry/log/ |
| Plist | /Library/LaunchDaemons/com.momentry.qdrant.plist |
---
## 安裝步驟
### Step 1: 安裝 Qdrant (使用 cargo)
```bash
# 安裝 Qdrant 從 GitHub
cargo install --git https://github.com/qdrant/qdrant.git --locked
```
**驗證**:
```bash
qdrant --version
# qdrant 1.17.0
```
---
### Step 2: 驗證 Qdrant 安裝
```bash
# 驗證 Qdrant 安裝
~/.cargo/bin/qdrant --version
# qdrant 1.17.0
```
---
### Step 3: 建立目錄結構
```bash
# 建立數據目錄
mkdir -p /Users/accusys/momentry/var/qdrant
# 建立配置目錄
mkdir -p /Users/accusys/momentry/etc/qdrant
# 建立日誌目錄
mkdir -p /Users/accusys/momentry/log
# 建立日誌文件
touch /Users/accusys/momentry/log/qdrant.log
touch /Users/accusys/momentry/log/qdrant.error.log
# 設定權限
chown -R accusys:staff /Users/accusys/momentry/var/qdrant
chown -R accusys:staff /Users/accusys/momentry/etc/qdrant
chown -R accusys:staff /Users/accusys/momentry/log
```
---
### Step 4: 使用 plist 開機自動啟動
```bash
# 複製 plist 到 LaunchDaemons 目錄
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.qdrant.plist /Library/LaunchDaemons/
# 載入並啟動
sudo launchctl load /Library/LaunchDaemons/com.momentry.qdrant.plist
```
---
## 監控配置
### 添加到監控配置
`monitor/config/monitor_config.yaml` 中添加:
```yaml
database:
qdrant:
enabled: true
host: "localhost"
port: 6333
```
### 添加健康檢查函數
`monitor/database/qdrant_monitor.sh` 中已包含 Qdrant 監控。
---
## 卸載步驟
### 重要: 路徑說明
| 路徑 | 類型 | 說明 |
|------|------|------|
| `/Users/accusys/momentry/var/qdrant/` | 數據 | **不要刪除** - Qdrant 數據 |
| `/Users/accusys/momentry/etc/qdrant/` | 配置 | **不要刪除** - Qdrant 配置 |
| `/Users/accusys/momentry/log/` | 日誌 | **不要刪除** - 日誌目錄 |
| `~/.cargo/bin/qdrant` | 安裝 | **刪除** - Qdrant 執行檔 |
### Step 1: 停止 Qdrant
```bash
# 找到 Qdrant 進程
ps aux | grep qdrant | grep -v grep
# 停止 Qdrant
pkill qdrant
# 或
kill <PID>
# 確認停止
ps aux | grep qdrant | grep -v grep || echo "Qdrant 已停止"
```
---
### Step 2: 卸載 Qdrant (cargo)
```bash
# 移除 cargo 安裝的 Qdrant
cargo uninstall qdrant
# 移除 plist
sudo launchctl unload /Library/LaunchDaemons/com.momentry.qdrant.plist
sudo rm /Library/LaunchDaemons/com.momentry.qdrant.plist
```
---
### Step 3: 刪除專屬檔案
```bash
# 刪除數據目錄 (可選)
rm -rf /Users/accusys/momentry/var/qdrant
# 刪除日誌 (可選)
rm -f /Users/accusys/momentry/log/qdrant.log
rm -f /opt/homebrew/var/log/qdrant.error.log
```
**注意: 不要刪除以下共用目錄**:
```bash
# 這些是共用的,不要刪除!
# /Users/accusys/momentry/var
# /Users/accusys/momentry/log
```
---
### Step 4: 卸載後檢查清單
```bash
echo "=== Qdrant 卸載後檢查 ==="
# 1. 檢查 Qdrant 進程
echo "1. Qdrant 進程:"
ps aux | grep qdrant | grep -v grep && echo " ✗ 仍在運行" || echo " ✓ 已停止"
# 2. Port 6333
echo "2. Port 6333:"
lsof -i :6333 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
# 3. Port 6334
echo "3. Port 6334:"
lsof -i :6334 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
# 4. qdrant 命令
echo "4. qdrant 命令:"
which qdrant > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 5. cargo 安裝
echo "5. cargo 安裝:"
cargo install --list | grep qdrant > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 6. launchctl 服務
echo "6. launchctl 服務:"
sudo launchctl list | grep qdrant > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 7. 數據目錄 (可選刪除)
echo "7. 數據目錄:"
[ -d "/Users/accusys/momentry/var/qdrant" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
# 8. 日誌目錄 (可選刪除)
echo "8. 日誌目錄:"
[ -d "/Users/accusys/momentry/log" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
```
**預期結果**:
```
=== Qdrant 卸載後檢查 ===
1. Qdrant 進程:
✓ 已停止
2. Port 6333:
✓ 已釋放
3. Port 6334:
✓ 已釋放
4. qdrant 命令:
✓ 已移除
5. cargo 安裝:
✓ 已移除
6. launchctl 服務:
✓ 已移除
7. 數據目錄:
✓ 保留 (或 ✗ 已刪除)
8. 日誌目錄:
✓ 保留 (或 ✗ 已刪除)
```
---
## 手動檢查命令
```bash
# 1. 檢查進程
ps aux | grep qdrant | grep -v grep
# 2. 檢查 Port
lsof -i :6333
lsof -i :6334
# 3. 測試連線 (無認證)
curl http://localhost:6333/collections
# 4. 測試連線 (有認證)
curl -H "api-key: Test3200Test3200Test3200" http://localhost:6333/collections
# 5. 查看所有 collections
curl -s -H "api-key: Test3200Test3200Test3200" http://localhost:6333/collections
# 6. 查看日誌
tail -20 /Users/accusys/momentry/log/qdrant.log
```
---
## 連線資訊
| 項目 | 值 |
|------|-----|
| REST API | http://localhost:6333 |
| gRPC | localhost:6334 |
| API Key | Test3200Test3200Test3200 |
| Qdrant UI | http://localhost:6333/dashboard |
---
## 環境變數
`.env` 中:
```env
QDRANT_URL=http://localhost:6333
QDRANT_API_KEY=Test3200Test3200Test3200
```
---
## 遠端訪問
- Qdrant 綁定到 `0.0.0.0` (所有網路介面)
- 本地網路其他機器可透過 IP 訪問
- 建議設定防火牆規則限制訪問 IP
---
## 故障排除
### Qdrant 無法啟動
```bash
# 檢查日誌
tail -f /Users/accusys/momentry/log/qdrant.log
# 檢查目錄權限
ls -la /Users/accusys/momentry/var/qdrant/
# 重新設定權限
chown -R $(whoami):staff /Users/accusys/momentry/var/qdrant
```
### Port 被佔用
```bash
# 檢查哪個程序佔用 port 6333
lsof -i :6333
# 終止佔用程序
kill <PID>
```
### 需要重新載入 plist
```bash
# 卸載舊服務 (如果存在)
sudo launchctl unload /Library/LaunchDaemons/com.momentry.qdrant.plist 2>/dev/null
# 載入新服務
sudo launchctl load /Library/LaunchDaemons/com.momentry.qdrant.plist
```
---
## 檔案位置
| 類型 | 路徑 | 說明 |
|------|------|------|
| `/Users/accusys/.cargo/bin/qdrant` | 二進制 | cargo 安裝位置 (直接使用) |
| `/opt/homebrew/bin/qdrant` | 符號連結 | ~~已棄用~~ - 不再需要 |
| 數據目錄 | `/Users/accusys/momentry/var/qdrant/` | 數據儲存 |
| 日誌 | `/Users/accusys/momentry/log/qdrant.log` | 執行日誌 |
| 錯誤日誌 | `/opt/homebrew/var/log/qdrant.error.log` | 錯誤日誌 |
| plist | `/Library/LaunchDaemons/com.momentry.qdrant.plist` | 開機啟動 |
| 備份 | `/Users/accusys/momentry/var/qdrant_backup/` | 數據備份 |
---
## 備份與恢復
### 備份
Qdrant 提供兩種備份方式Snapshots (推薦) 和手動複製。
#### 方式一:使用 Snapshots API (推薦)
```bash
# 創建備份目錄
mkdir -p /Users/accusys/momentry/var/qdrant_backup
# 獲取所有 collections
curl -s -H "api-key: Test3200Test3200Test3200" \
http://localhost:6333/collections | jq -r '.result[].name'
# 為每個 collection 創建 snapshot
# 假設 collection 名稱為 "chunks"
curl -X POST -H "api-key: Test3200Test3200Test3200" \
http://localhost:6333/collections/chunks/snapshots \
-o /Users/accusys/momentry/var/qdrant_backup/chunks_snapshot_$(date +%Y%m%d).tar.gz
```
#### 方式二:手動複製數據目錄 (停機備份)
```bash
# 停止 Qdrant
pkill qdrant
# 等待停止
sleep 2
# 複製數據目錄
TIMESTAMP=$(date +%Y%m%d)
mkdir -p /Users/accusys/momentry/var/qdrant_backup
tar -czf /Users/accusys/momentry/var/qdrant_backup/qdrant_data_${TIMESTAMP}.tar.gz \
-C /Users/accusys/momentry/var qdrant/
# 啟動 Qdrant
launchctl load /Library/LaunchDaemons/com.momentry.qdrant.plist
```
#### 自動備份腳本
```bash
#!/bin/bash
# backup_qdrant.sh
set -e
QDRANT_HOST="localhost"
QDRANT_PORT="6333"
QDRANT_API_KEY="Test3200Test3200Test3200"
BACKUP_DIR="/Users/accusys/momentry/var/qdrant_backup"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
mkdir -p "$BACKUP_DIR"
echo "開始 Qdrant 備份..."
# 獲取所有 collections
COLLECTIONS=$(curl -s -H "api-key: $QDRANT_API_KEY" \
http://${QDRANT_HOST}:${QDRANT_PORT}/collections | \
jq -r '.result[].name')
if [ -z "$COLLECTIONS" ]; then
echo "警告: 沒有找到任何 collections"
else
for COLLECTION in $COLLECTIONS; do
echo "備份 collection: $COLLECTION"
curl -X POST -H "api-key: $QDRANT_API_KEY" \
"http://${QDRANT_HOST}:${QDRANT_PORT}/collections/${COLLECTION}/snapshots" \
-o "${BACKUP_DIR}/${COLLECTION}_${TIMESTAMP}.tar.gz" 2>/dev/null || \
echo "警告: $COLLECTION 備份失敗"
done
fi
# 壓縮所有 snapshot
cd "$BACKUP_DIR"
tar -czf qdrant_snapshots_${TIMESTAMP}.tar.gz *.tar.gz 2>/dev/null || true
rm -f *.tar.gz
# 清理 30 天前的備份
find "$BACKUP_DIR" -name "qdrant_snapshots_*.tar.gz" -mtime +30 -delete
echo "Qdrant 備份完成: ${BACKUP_DIR}/qdrant_snapshots_${TIMESTAMP}.tar.gz"
```
### 恢復
```bash
# 停止 Qdrant
pkill qdrant
sleep 2
# 解壓縮備份
TIMESTAMP="20260315"
tar -xzf /Users/accusys/momentry/var/qdrant_backup/qdrant_snapshots_${TIMESTAMP}.tar.gz \
-C /Users/accusys/momentry/var/qdrant_backup/
# 恢復數據目錄 (方式二備份)
# rm -rf /Users/accusys/momentry/var/qdrant/*
# tar -xzf /Users/accusys/momentry/var/qdrant_backup/qdrant_data_${TIMESTAMP}.tar.gz \
# -C /Users/accusys/momentry/var/
# 啟動 Qdrant
launchctl load /Library/LaunchDaemons/com.momentry.qdrant.plist
```
### 排程備份
```bash
# 編輯 crontab
crontab -e
# 添加每天凌晨 3 點執行備份
0 3 * * * /Users/accusys/momentry/scripts/backup_qdrant.sh >> /Users/accusys/momentry/log/backup.log 2>&1
```
---
## 版本資訊
- 版本: 1.17.0
- API Key: Test3200Test3200Test3200
- 數據目錄: /Users/accusys/momentry/var/qdrant/
- 日誌目錄: /Users/accusys/momentry/log/

View File

@@ -1,481 +0,0 @@
# Redis 安裝指南 (本地部署)
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-15 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-15 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
| V1.1 | 2026-03-21 | 更新 rust redis crate 版本至 0.32.7 | OpenCode | - |
| V1.2 | 2026-03-21 | 添加 Redis 用戶配置說明 | OpenCode | - |
---
## 概述
本文檔說明如何在 macOS 上安裝 Redis配置為本地部署支援遠端訪問。
---
## 當前狀態
| 項目 | 狀態 |
|------|------|
| Redis | ✅ 已安裝 v8.4.0 |
| 數據目錄 | /opt/homebrew/var/db/redis/ |
| 日誌目錄 | /Users/accusys/momentry/log/ |
| Plist | /Library/LaunchDaemons/com.momentry.redis.plist |
---
## 安裝步驟
### Step 1: 安裝 Redis (使用 brew)
```bash
# 安裝 Redis
brew install redis
```
**驗證**:
```bash
redis-server --version
# Redis server v8.4.0
```
---
### Step 2: 建立目錄結構
```bash
# 建立數據目錄
mkdir -p /Users/accusys/momentry/var/redis
# 建立配置目錄
mkdir -p /Users/accusys/momentry/etc/redis
# 建立日誌目錄
mkdir -p /Users/accusys/momentry/log
# 建立日誌文件
touch /Users/accusys/momentry/log/redis.log
touch /Users/accusys/momentry/log/redis.error.log
# 設定權限
chown -R accusys:staff /Users/accusys/momentry/var/redis
chown -R accusys:staff /Users/accusys/momentry/etc/redis
chown -R accusys:staff /Users/accusys/momentry/log
```
---
### Step 3: 使用 plist 開機自動啟動
```bash
# 複製 plist 到 LaunchDaemons 目錄
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.redis.plist /Library/LaunchDaemons/
# 載入並啟動
sudo launchctl load /Library/LaunchDaemons/com.momentry.redis.plist
```
---
## 卸載步驟
### 重要: 路徑說明
| 路徑 | 類型 | 說明 |
|------|------|------|
| `/Users/accusys/momentry/var/redis/` | 數據 | **不要刪除** - 數據目錄 |
| `/Users/accusys/momentry/etc/redis/` | 配置 | **不要刪除** - 配置目錄 |
| `/Users/accusys/momentry/log/` | 日誌 | **不要刪除** - 日誌目錄 |
| `/opt/homebrew/opt/redis/` | 安裝 | **刪除** - Redis 安裝目錄 |
### Step 1: 停止 Redis
```bash
# 找到 Redis 進程
ps aux | grep redis | grep -v grep
# 停止 Redis
redis-cli -a accusys SHUTDOWN
# 或
pkill redis-server
# 確認停止
ps aux | grep redis | grep -v grep || echo "Redis 已停止"
```
---
### Step 2: 卸載 Redis
```bash
# 卸載 Redis
brew uninstall redis
# 移除 plist
sudo launchctl unload /Library/LaunchDaemons/com.momentry.redis.plist
sudo rm /Library/LaunchDaemons/com.momentry.redis.plist
```
---
### Step 3: 刪除專屬檔案
```bash
# 刪除數據目錄 (可選)
rm -rf /Users/accusys/momentry/var/redis
# 刪除配置目錄 (可選)
rm -rf /Users/accusys/momentry/etc/redis
# 刪除日誌 (可選)
rm -f /Users/accusys/momentry/log/redis.log
rm -f /Users/accusys/momentry/log/redis.error.log
```
**注意: 不要刪除以下共用目錄**:
```bash
# 這些是共用的,不要刪除!
# /Users/accusys/momentry/var
# /Users/accusys/momentry/etc
# /Users/accusys/momentry/log
```
---
### Step 4: 卸載後檢查清單
```bash
echo "=== Redis 卸載後檢查 ==="
# 1. 檢查 Redis 進程
echo "1. Redis 進程:"
ps aux | grep redis | grep -v grep && echo " ✗ 仍在運行" || echo " ✓ 已停止"
# 2. Port 6379
echo "2. Port 6379:"
lsof -i :6379 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
# 3. redis-server 命令
echo "3. redis-server 命令:"
which redis-server > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 4. brew 安裝
echo "4. brew 安裝:"
brew list redis > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 5. launchctl 服務
echo "5. launchctl 服務:"
sudo launchctl list | grep redis > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
# 6. 數據目錄 (可選刪除)
echo "6. 數據目錄:"
[ -d "/Users/accusys/momentry/var/redis" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
# 7. 日誌目錄 (可選刪除)
echo "7. 日誌目錄:"
[ -d "/Users/accusys/momentry/log" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
```
**預期結果**:
```
=== Redis 卸載後檢查 ===
1. Redis 進程:
✓ 已停止
2. Port 6379:
✓ 已釋放
3. redis-server 命令:
✓ 已移除
4. brew 安裝:
✓ 已移除
5. launchctl 服務:
✓ 已移除
6. 數據目錄:
✓ 保留 (或 ✗ 已刪除)
7. 日誌目錄:
✓ 保留 (或 ✗ 已刪除)
```
---
## 監控配置
### 添加到監控配置
`monitor/config/monitor_config.yaml` 中添加:
```yaml
service:
services:
- name: "redis"
type: "tcp"
port: 6379
host: "localhost"
timeout: 5
enabled: true
```
### 添加健康檢查函數
`monitor/service/health_check.sh` 中添加:
```bash
check_redis() {
local start=$(date +%s%N)
if redis-cli -a accusys ping > /dev/null 2>&1; then
local end=$(date +%s%N)
local ms=$(( (end - start) / 1000000 ))
echo -e "${GREEN}${NC} Redis (6379) - ${ms}ms"
record_service "redis" "up" "$ms" ""
return 0
else
echo -e "${RED}${NC} Redis (6379) - Down"
record_service "redis" "down" "0" "Connection failed"
return 1
fi
}
```
```bash
# 1. 檢查進程
ps aux | grep redis | grep -v grep
# 2. 檢查 Port
lsof -i :6379
# 3. 測試連線 (無認證)
redis-cli -a accusys PING
# 4. 測試連線 (有認證)
redis-cli -a accusys -e "PING"
# 5. 查看所有 keys
redis-cli -a accusys KEYS '*'
# 6. 查看 info
redis-cli -a accusys INFO
# 7. 查看日誌
tail -20 /Users/accusys/momentry/log/redis.log
```
---
## 連線資訊
| 項目 | 值 |
|------|-----|
| Host | localhost |
| Port | 6379 |
| Password | accusys |
---
## 環境變數
`.env` 中:
```env
REDIS_URL=redis://:accusys@localhost:6379
```
---
## 遠端訪問
- Redis 綁定到 `0.0.0.0` (所有網路介面)
- 本地網路其他機器可透過 IP 訪問
- 密碼認證: `accusys`
---
## 故障排除
### Redis 無法啟動
```bash
# 檢查日誌
tail -f /Users/accusys/momentry/log/redis.log
# 檢查目錄權限
ls -la /Users/accusys/momentry/var/redis/
# 重新設定權限
chown -R $(whoami):staff /Users/accusys/momentry/var/redis
```
### Port 被佔用
```bash
# 檢查哪個程序佔用 port 6379
lsof -i :6379
# 終止佔用程序
kill <PID>
```
### 需要重新載入 plist
```bash
# 卸載舊服務 (如果存在)
sudo launchctl unload /Library/LaunchDaemons/com.momentry.redis.plist 2>/dev/null
# 載入新服務
sudo launchctl load /Library/LaunchDaemons/com.momentry.redis.plist
```
---
## 檔案位置
| 類型 | 路徑 | 說明 |
|------|------|------|
| 安裝 | `/opt/homebrew/opt/redis/` | Redis 安裝目錄 |
| 執行檔 | `/opt/homebrew/opt/redis/bin/redis-server` | Redis 執行檔 |
| 數據目錄 | `/Users/accusys/momentry/var/redis/` | 數據儲存 |
| 配置目錄 | `/Users/accusys/momentry/etc/redis/` | 配置儲存 |
| 日誌 | `/Users/accusys/momentry/log/redis.log` | 執行日誌 |
| 錯誤日誌 | `/Users/accusys/momentry/log/redis.error.log` | 錯誤日誌 |
| plist | `/Library/LaunchDaemons/com.momentry.redis.plist` | 開機啟動 |
| 備份 | `/Users/accusys/momentry/var/redis_backup_latest.rdb` | 數據備份 |
---
## 備份與恢復
### 備份
```bash
# 觸發保存並備份
redis-cli -a accusys SAVE
cp /opt/homebrew/var/db/redis/dump.rdb /Users/accusys/momentry/var/redis_backup_latest.rdb
```
### 恢復
```bash
# 停止 Redis
redis-cli -a accusys SHUTDOWN
# 複製備份文件覆蓋
cp /Users/accusys/momentry/var/redis_backup_latest.rdb /Users/accusys/momentry/var/redis/dump.rdb
# 啟動 Redis
sudo launchctl load /Library/LaunchDaemons/com.momentry.redis.plist
```
---
## 版本資訊
| 項目 | 值 |
|------|-----|
| Redis Server | 8.4.0 |
| Rust redis crate | 0.32.7 |
| Port | 6379 |
| Password | accusys |
| 數據目錄 | /Users/accusys/momentry/var/redis/ |
| 日誌目錄 | /Users/accusys/momentry/log/ |
---
## Rust redis crate 版本
Cargo.toml 中的 redis 依賴:
```toml
redis = { version = "0.32", features = ["tokio-comp"] }
```
### 版本歷史
| 版本 | 日期 | 變更 |
|------|------|-------|
| 0.25.4 | - | 原始版本(有未來相容性警告) |
| 0.32.7 | 2026-03-21 | **升級** - 修復 Rust 2024 never type 回退問題 |
### 升級說明
升級到 0.32.x 的優點:
- 修復 Rust 2024 edition 未來相容性問題
- API 完全向後相容
- 無需修改現有程式碼
---
## Redis 用戶配置說明
### 當前狀態
| 項目 | 狀態 |
|------|------|
| 用戶類型 | 僅有 `default` 用戶 |
| 自訂用戶 | ❌ 未配置 |
| ACL 持久化 | ❌ 未配置 |
### Redis ACL 狀態
```bash
# 查看 ACL
redis-cli -a accusys ACL LIST
# 輸出:
# user default on sanitize-payload #hash ~* &* +@all
```
### 連線格式說明
| 格式 | 狀態 | 說明 |
|------|------|------|
| `redis://:accusys@localhost:6379` | ✅ 正確 | 使用默認用戶 + 密碼 |
| `redis://accusys:accusys@localhost:6379` | ❌ 失敗 | 用戶 `accusys` 不存在 |
### 為何用戶名不可用
1. **Redis 啟動方式**:使用 `--requirepass` 參數,僅設定默認用戶密碼
2. **無 ACL 配置文件**:未指定 `--aclfile` 參數
3. **動態建立用戶**:手動建立的用戶不會持久化(重啟後消失)
### 解決方案
#### 方案 A使用默認用戶現行
```env
REDIS_URL=redis://:accusys@localhost:6379
```
**適用於**:單一應用、簡單部署
#### 方案 B建立 ACL 配置文件
```bash
# 1. 建立 ACL 文件
cat > /Users/accusys/momentry/etc/redis/users.acl << 'EOF'
user default on sanitize-payload ~* &* +@all >accusys
user accusys on sanitize-payload ~* &* +@all >accusys
EOF
# 2. 修改 plist (添加 --aclfile 參數)
# --aclfile /Users/accusys/momentry/etc/redis/users.acl
# 3. 重啟 Redis
sudo launchctl unload /Library/LaunchDaemons/com.momentry.redis.plist
sudo launchctl load /Library/LaunchDaemons/com.momentry.redis.plist
```
**適用於**:多應用、需要用戶隔離
### 參考
- 問題追蹤:`docs/PENDING_ISSUES.md` 問題 #5
- 測試結果2026-03-21 Redis 認證測試

View File

@@ -1,300 +0,0 @@
# RustDesk 安裝指南 (本地部署)
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-15 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-15 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
---
## 概述
本文檔說明如何在 macOS 上安裝 RustDesk 遠端桌面服務,配置為本地部署。
---
## 當前狀態
| 項目 | 狀態 |
|------|------|
| RustDesk | ✅ 已安裝 |
| 數據目錄 | /Users/accusys/momentry/var/rustdesk/ |
| 日誌目錄 | /Users/accusys/momentry/log/ |
| HBBS Plist | /Library/LaunchDaemons/com.momentry.rustdesk.hbbs.plist |
| HBBR Plist | /Library/LaunchDaemons/com.momentry.rustdesk.hbbr.plist |
---
## 服務端口
| 服務 | Port | 協議 |
|------|------|------|
| hbbs (TCP) | 21115 | 主端口 |
| hbbs (TCP/UDP) | 21116 | NAT 測試 |
| hbbs (WebSocket) | 21118 | WebSocket |
| hbbr (TCP) | 21117 | 中繼端口 |
| hbbr (TCP) | 21119 | 中繼 extra |
---
## 安裝步驟
### Step 1: 安裝 RustDesk (使用 brew)
```bash
# 安裝 RustDesk
brew install rustdesk
```
**驗證**:
```bash
hbbs --version
hbbr --version
```
---
### Step 2: 建立目錄
```bash
# 建立數據目錄
mkdir -p /Users/accusys/momentry/var/rustdesk
# 建立配置目錄
mkdir -p /Users/accusys/momentry/etc/rustdesk
# 建立日誌目錄
mkdir -p /Users/accusys/momentry/log
# 建立日誌文件
touch /Users/accusys/momentry/log/rustdesk.hbbs.log
touch /Users/accusys/momentry/log/rustdesk.hbbs.error.log
touch /Users/accusys/momentry/log/rustdesk.hbbr.log
touch /Users/accusys/momentry/log/rustdesk.hbbr.error.log
# 設定權限
chown -R accusys:staff /Users/accusys/momentry/var/rustdesk
chown -R accusys:staff /Users/accusys/momentry/etc/rustdesk
chown -R accusys:staff /Users/accusys/momentry/log
```
---
### Step 3: 使用 plist 開機自動啟動
```bash
# 複製 plist 到 LaunchDaemons 目錄
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.rustdesk.hbbs.plist /Library/LaunchDaemons/
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.rustdesk.hbbr.plist /Library/LaunchDaemons/
# 移除舊 plist (如果存在)
sudo launchctl unload /Library/LaunchDaemons/com.rustdesk.hbbs.plist 2>/dev/null
sudo launchctl unload /Library/LaunchDaemons/com.rustdesk.hbbr.plist 2>/dev/null
sudo rm /Library/LaunchDaemons/com.rustdesk.hbbs.plist 2>/dev/null
sudo rm /Library/LaunchDaemons/com.rustdesk.hbbr.plist 2>/dev/null
# 載入並啟動
sudo launchctl load /Library/LaunchDaemons/com.momentry.rustdesk.hbbs.plist
sudo launchctl load /Library/LaunchDaemons/com.momentry.rustdesk.hbbr.plist
```
---
## 監控配置
### 添加到監控配置
`monitor/config/monitor_config.yaml` 中添加:
```yaml
service:
services:
- name: "rustdesk-hbbs"
type: "tcp"
port: 21115
host: "localhost"
timeout: 5
enabled: true
- name: "rustdesk-hbbr"
type: "tcp"
port: 21117
host: "localhost"
timeout: 5
enabled: true
```
---
## 卸載步驟
### 重要: 路徑說明
| 路徑 | 類型 | 說明 |
|------|------|------|
| `/Users/accusys/momentry/var/rustdesk/` | 數據 | **不要刪除** - RustDesk 數據 |
| `/Users/accusys/momentry/log/` | 日誌 | **不要刪除** - 日誌目錄 |
| `/opt/homebrew/bin/hbbr` | 安裝 | **刪除** - RustDesk 安裝 |
| `/opt/homebrew/bin/hbbs` | 安裝 | **刪除** - RustDesk 安裝 |
### Step 1: 停止 RustDesk
```bash
# 停止 RustDesk 服務
sudo launchctl unload /Library/LaunchDaemons/com.momentry.rustdesk.hbbs.plist
sudo launchctl unload /Library/LaunchDaemons/com.momentry.rustdesk.hbbr.plist
# 確認停止
ps aux | grep rustdesk | grep -v grep || echo "RustDesk 已停止"
```
---
### Step 2: 卸載 RustDesk
```bash
# 卸載 RustDesk
brew uninstall rustdesk
# 移除 plist
sudo rm /Library/LaunchDaemons/com.momentry.rustdesk.hbbs.plist
sudo rm /Library/LaunchDaemons/com.momentry.rustdesk.hbbr.plist
```
---
### Step 3: 刪除專屬檔案
```bash
# 刪除數據目錄 (可選)
rm -rf /Users/accusys/momentry/var/rustdesk
# 刪除日誌 (可選)
rm -f /Users/accusys/momentry/log/rustdesk-*.log
```
**注意: 不要刪除以下共用目錄**:
```bash
# 這些是共用的,不要刪除!
# /Users/accusys/momentry/var
# /Users/accusys/momentry/log
```
---
## 手動檢查命令
```bash
# 1. 檢查進程
ps aux | grep rustdesk | grep -v grep
# 2. 檢查 Port
lsof -i :21115
lsof -i :21116
lsof -i :21117
lsof -i :21118
lsof -i :21119
# 3. 測試連線
nc -zv localhost 21115
nc -zv localhost 21116
# 4. 查看日誌
tail -20 /Users/accusys/momentry/log/rustdesk-hbbs.log
tail -20 /Users/accusys/momentry/log/rustdesk-hbbr.log
```
---
## 連線資訊
| 項目 | 值 |
|------|-----|
| Server ID | 59.124.167.225 |
| NAT Test Port | 21116 |
| Relay Port | 21117, 21119 |
---
## 故障排除
### RustDesk 無法啟動
```bash
# 檢查日誌
tail -f /Users/accusys/momentry/log/rustdesk-hbbs.log
tail -f /Users/accusys/momentry/log/rustdesk-hbbr.log
# 檢查數據目錄權限
ls -la /Users/accusys/momentry/var/rustdesk/
# 重新設定權限
chown -R $(whoami):staff /Users/accusys/momentry/var/rustdesk
```
### Port 被佔用
```bash
# 檢查哪個程序佔用 port
lsof -i :21116
# 終止佔用程序
kill <PID>
```
### 需要重新載入 plist
```bash
# 卸載舊服務
sudo launchctl unload /Library/LaunchDaemons/com.momentry.rustdesk.hbbs.plist
sudo launchctl unload /Library/LaunchDaemons/com.momentry.rustdesk.hbbr.plist
# 載入新服務
sudo launchctl load /Library/LaunchDaemons/com.momentry.rustdesk.hbbs.plist
sudo launchctl load /Library/LaunchDaemons/com.momentry.rustdesk.hbbr.plist
```
---
## 檔案位置
| 類型 | 路徑 | 說明 |
|------|------|------|
| 安裝 | `/opt/homebrew/bin/hbbs` | RustDesk Server 執行檔 |
| 安裝 | `/opt/homebrew/bin/hbbr` | RustDesk Relay 執行檔 |
| 數據目錄 | `/Users/accusys/momentry/var/rustdesk/` | 數據儲存 |
| HBBS 日誌 | `/Users/accusys/momentry/log/rustdesk-hbbs.log` | 服務日誌 |
| HBBR 日誌 | `/Users/accusys/momentry/log/rustdesk-hbbr.log` | 中繼日誌 |
| HBBS Plist | `/Library/LaunchDaemons/com.momentry.rustdesk.hbbs.plist` | 開機啟動 |
| HBBR Plist | `/Library/LaunchDaemons/com.momentry.rustdesk.hbbr.plist` | 開機啟動 |
---
## 版本資訊
- 安裝方式: Homebrew (Cask)
- Client 版本: 1.4.6
- Server 版本: 1.1.15 (hbbs/hbbr binaries from homebrew)
- 數據目錄: /Users/accusys/momentry/var/rustdesk/
- 日誌目錄: /Users/accusys/momentry/log/
---
## 注意事項
### Server 版本
Homebrew 的 RustDesk Cask 只提供客戶端應用程序。服務器二進制文件 (hbbs, hbbr) 需要從其他來源安裝或自行編譯。當前使用的版本較舊 (1.1.15)。
如需更新服務器版本,可以考慮:
1. 從源代碼編譯最新版本
2. 使用 RustDesk 官方提供的 Docker 鏡像
3. 等待 Homebrew 添加服務器公式

File diff suppressed because it is too large Load Diff

View File

@@ -1,332 +0,0 @@
# WordPress 安裝指南 (Portal)
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-22 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-22 | 創建文件 | Warren | OpenCode / big-pickle |
---
## 1. 概述
本文檔說明 Momentry Portal 的 WordPress 安裝配置,作為系統入口整合 n8n 自動化與 sftpgo 檔案服務。
---
## 2. 當前狀態
| 項目 | 狀態 |
|------|------|
| WordPress 版本 | 6.x |
| URL | https://wp.momentry.ddns.net |
| 安裝路徑 | `/Users/accusys/wordpress/web` |
| 資料庫 | wordpress (MariaDB) |
| 資料庫用戶 | wp_user |
---
## 3. 目錄結構
```
/Users/accusys/wordpress/
├── web/ # WordPress 主目錄
│ ├── wp-admin/ # WordPress 管理面板
│ ├── wp-content/ # 內容目錄
│ │ ├── ai1wm-backups/ # 備份檔案 (*.wpress)
│ │ ├── languages/ # 語言檔案
│ │ ├── plugins/ # 插件目錄
│ │ ├── themes/ # 主題目錄
│ │ ├── uploads/ # 上傳檔案
│ │ └── cache/ # 快取目錄
│ ├── wp-includes/ # WordPress 核心
│ └── wp-config.php # 配置文件
├── docker-compose.yml # Docker 配置
└── wordpress_backup.sql # 資料庫備份
```
### 空間使用
| 目錄 | 大小 | 說明 |
|------|------|------|
| `ai1wm-backups/` | ~250MB | 完整備份 (保留 2 個) |
| `plugins/` | 80MB | 插件 |
| `themes/` | 14MB | 主題 |
| `uploads/` | 12MB | 上傳檔案 |
---
## 4. 程式碼位置
自訂程式碼存放位置:
| 類型 | 路徑 |
|------|------|
| 主題 | `/Users/accusys/wordpress/web/wp-content/themes/` |
| 插件 | `/Users/accusys/wordpress/web/wp-content/plugins/` |
| 必須插件 | `/Users/accusys/wordpress/web/wp-content/mu-plugins/` |
---
## 5. 插件清單
| 插件 | 用途 | 說明 |
|------|------|------|
| elementor | 頁面建構 | 視覺化頁面編輯器 |
| all-in-one-wp-migration | 網站遷移/備份 | 完整網站備份工具 |
| akismet | 垃圾留言過濾 | 保護留言區域 |
| code-snippets | 自訂程式碼 | 無需修改主題即可添加 PHP |
### Elementor 版本
- 版本: 最新穩定版
- 用途: 頁面建構(開發階段)
### 未來計畫
- Phase 2: OpenCode 重構
- 目標: 交付無 Elementor 依賴版本
---
## 6. 主題清單
| 主題 | 說明 |
|------|------|
| twentytwentyfive | 目前使用主題 |
| twentytwentyfour | 備用 |
| twentytwentythree | 備用 |
---
## 7. 整合計畫
### 7.1 n8n 整合
n8n 作為自動化引擎WordPress 頁面透過 REST API 或 Webhook 與 n8n 通訊。
| 整合方式 | 說明 |
|----------|------|
| REST API | WordPress 呼叫 n8n API |
| Webhook | n8n 觸發 WordPress 動作 |
### 7.2 sftpgo 整合
sftpgo 作為檔案服務WordPress 頁面提供檔案上傳/下載功能。
| 整合方式 | 說明 |
|----------|------|
| WebDAV | 透過 WebDAV API 操作檔案 |
| REST API | 透過 sftpgo API 操作檔案 |
---
## 8. 管理命令
### 8.1 清理 ai1wm 備份
```bash
# 查看現有備份
ls -lt /Users/accusys/wordpress/web/wp-content/ai1wm-backups/*.wpress
# 保留最近 2 個,刪除舊的
ls -t /Users/accusys/wordpress/web/wp-content/ai1wm-backups/*.wpress | tail -n +3 | xargs rm
# 驗證結果
du -sh /Users/accusys/wordpress/web/wp-content/ai1wm-backups/
```
### 8.2 清理 WordPress 快取
```bash
# 刪除 Object Cache
wp cache flush
# 刪除 Elementor 快取
wp elementor flush_css
# 刪除全部快取
wp cache flush && wp elementor flush_css
```
### 8.3 資料庫操作
```bash
# 匯出資料庫
mysqldump -u wp_user -p wordpress > wordpress_backup.sql
# 匯入資料庫
mysql -u wp_user -p wordpress < wordpress_backup.sql
```
### 8.4 權限檢查
```bash
# 檢查目錄權限
ls -la /Users/accusys/wordpress/web/wp-content/
# 確認 wp-content 可寫入
chown -R _www:_www /Users/accusys/wordpress/web/wp-content/
chmod -R 755 /Users/accusys/wordpress/web/wp-content/
```
---
## 9. 故障排除
### 9.1 常見問題
| 問題 | 解決方案 |
|------|----------|
| 頁面載入緩慢 | 清理 Elementor/Object Cache |
| 上傳檔案失敗 | 檢查 wp-content/uploads 權限 |
| 502/504 錯誤 | 重啟 PHP-FPM |
| 資料庫連線失敗 | 檢查 wp-config.php 設定 |
### 9.2 診斷命令
```bash
# 檢查 PHP-FPM 狀態
lsof -i :9000
# 檢查 MySQL/MariaDB 狀態
lsof -i :3306
# 檢查 Apache/Nginx 狀態
lsof -i :80
# 查看 WordPress 錯誤日誌
tail -100 /Users/accusys/momentry/log/php-fpm.log
```
---
## 10. 開發協作
### 10.1 與 marcom 團隊協作
| 角色 | 負責 |
|------|------|
| marcom 團隊 | Figma 設計 / Elementor 建構 |
| OpenCode | 程式碼實作 / 重構 |
### 10.2 開發流程
```
Phase 1: marcom 建構 (現在)
└── Elementor 頁面建構
Phase 2: 交付審視 (TBD)
└── 功能確認 / 重構評估
Phase 3: OpenCode 重構 (討論後)
└── 純程式碼實作
└── 交付客戶 (無 Elementor 依賴)
```
---
## 11. PHP LSP 開發環境
### 11.1 軟體需求
| 軟體 | 版本 | 安裝方式 |
|------|------|----------|
| PHP | 8.0+ | 已安裝 (8.5.2) |
| Composer | 2.0+ | `brew install composer` |
| phpactor | Latest | PHAR 安裝 |
### 11.2 安裝步驟
#### 1. 安裝 Composer
```bash
brew install composer
```
#### 2. 安裝 phpactor
```bash
curl -sSL https://github.com/phpactor/phpactor/releases/latest/download/phpactor.phar -o ~/bin/phpactor
chmod +x ~/bin/phpactor
export PATH="$HOME/bin:$PATH"
```
#### 3. 安裝 WordPress Stubs
```bash
cd /Users/accusys/wordpress/web
composer require --dev php-stubs/wordpress-stubs
```
#### 4. 設定 phpactor
```bash
# 設定檔位置
mkdir -p ~/.config/phpactor
# 設定內容
cat > ~/.config/phpactor/phpactor.json << 'EOF'
{
"core.min_memory_limit": 1610612736,
"worse_reflection.additive_stubs": [
"/Users/accusys/wordpress/web/vendor/php-stubs/wordpress-stubs/wordpress-stubs.php"
]
}
EOF
```
#### 5. 建立索引
```bash
cd /Users/accusys/wordpress/web
~/bin/phpactor index:build --reset
```
### 11.3 OpenCode 使用方式
```bash
# 確認安裝
~/bin/phpactor --version
# 查詢類別
~/bin/phpactor class:search "WP_User"
# 查看類別資訊
~/bin/phpactor index:query WP_User
# 導航到定義
~/bin/phpactor navigate /path/to/file.php
# 查詢參照
~/bin/phpactor references /path/to/file.php
```
### 11.4 常用指令
| 指令 | 用途 |
|------|------|
| `phpactor class:search` | 搜尋類別 |
| `phpactor index:query` | 查詢索引 |
| `phpactor index:build` | 建立索引 |
| `phpactor index:clean` | 清除索引 |
| `phpactor config:dump` | 顯示設定 |
---
## 12. 檔案位置
| 類型 | 路徑 | 說明 |
|------|------|------|
| WordPress 主目錄 | `/Users/accusys/wordpress/web` | 網站根目錄 |
| 備份目錄 | `/Users/accusys/momentry/backup/wordpress/` | 每日備份 |
| 日誌目錄 | `/Users/accusys/momentry/log/` | PHP/Apache 日誌 |
| phpactor | `~/bin/phpactor` | PHP LSP 主程式 |
| phpactor 設定 | `~/.config/phpactor/phpactor.json` | LSP 設定檔 |
| WordPress Stubs | `/Users/accusys/wordpress/web/vendor/php-stubs/` | WordPress 函數定義 |

View File

@@ -1,682 +0,0 @@
# Job Worker 實作計畫
| 項目 | 內容 |
|------|------|
| 建立者 | Warren / OpenCode |
| 建立時間 | 2026-03-24 |
| 文件版本 | V1.1 |
| 狀態 | ✅ 已實作 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 |
|------|------|------|--------|
| V1.0 | 2026-03-24 | 建立實作計畫 | OpenCode |
| V1.1 | 2026-03-25 | 實作完成,更新狀態 | OpenCode |
---
## 實作狀態
### ✅ 已完成
| 元件 | 檔案 | 狀態 |
|------|------|------|
| MonitorJob 結構 | `src/core/db/postgres_db.rs` | ✅ |
| ProcessorResult 結構 | `src/core/db/postgres_db.rs` | ✅ |
| Worker 配置 | `src/worker/config.rs` | ✅ |
| Job Worker | `src/worker/job_worker.rs` | ✅ |
| Processor Pool | `src/worker/processor.rs` | ✅ |
| Worker 模組 | `src/worker/mod.rs` | ✅ |
| PostgreSQL 表格 | `monitor_jobs`, `processor_results` | ✅ |
| 類型修復 | `i32`, `NaiveDateTime` | ✅ |
### 待整合
| 項目 | 說明 |
|------|------|
| Worker 服務啟動 | 需要加入 launchd plist |
| 監控整合 | 需要加入 MOMENTRY_CORE_MONITORING.md |
| 備份涵蓋 | 需要確認備份包含新表格 |
---
## 1. 設計決策
### 1.1 確認的設計決策
| 項目 | 決策 | 理由 |
|------|------|------|
| 觸發方式 | 輪詢Job Worker | 暫無可靠的 API 觸發機制 |
| 並行處理 | 最多 2 個 | 可根據 CPU/GPU 能力調整 |
| 失敗處理 | 獨立模組,部分完成可接續 | 任何模組失敗都產出狀態記錄 |
| Worker 啟動 | 獨立進程 | 隔離、易管理 |
| 並行上限調整 | 環境變數 + 預設值 | 靈活、可調整 |
| 狀態同步 | PostgreSQL + Redis | 可靠 + 即時 |
### 1.2 環境變數
| 變數 | 預設值 | 說明 |
|------|--------|------|
| `MOMENTRY_MAX_CONCURRENT` | 2 | 最大並行 processor 數 |
| `MOMENTRY_POLL_INTERVAL` | 5 | 輪詢間隔(秒) |
| `MOMENTRY_WORKER_ENABLED` | true | 是否啟用 worker |
---
## 2. 系統架構
### 2.1 完整流程圖
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 檔案註冊觸發處理流程 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. SFTPGo 上傳 │
│ │ │
│ ▼ │
│ 2. Hook 呼叫 Register API │
│ │ │
│ ▼ │
│ 3. Register API │
│ ├─► ffprobe 提取 metadata │
│ ├─► 寫入 videos 表 │
│ └─► 建立 monitor_jobs 記錄 (status=pending) │
│ │ │
│ ▼ │
│ 4. Job Worker (獨立進程,輪詢機制) │
│ ├─► 輪詢 pending jobs │
│ ├─► 檢查 videos 表 fs_json 決定需要處理什麼 │
│ ├─► 並行執行 processors (最多 2 個) │
│ └─► 更新 videos, monitor_jobs, processor_results 表 │
│ │ │
│ ▼ │
│ 5. 處理結果 │
│ ├─► 更新 videos 表 (fs_json, psql_chunk, qvector_chunk) │
│ ├─► 更新 monitor_jobs 表 (status, progress) │
│ ├─► 更新 processor_results 表 (每個模組狀態) │
│ └─► Redis Pub/Sub 即時進度 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 2.2 Job Worker 架構
```
┌─────────────────────────────────────────────────────────────────────┐
│ Job Worker 架構 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ PostgreSQL │ ───▶ │ Worker │ ───▶ │ Processor │ │
│ │ Job Queue │ │ Loop │ │ Pool │ │
│ └─────────────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Video State │ │ Processor 1 │ │
│ │ Check │ │ (ASR/YOLO) │ │
│ └─────────────┘ ├─────────────┤ │
│ │ Processor 2 │ │
│ │ (CUT/OCR) │ │
│ └─────────────┘ │
│ │
│ Redis ──── Pub/Sub ──── 即時進度 │
│ │
└─────────────────────────────────────────────────────────────────────┘
```
---
## 3. 資料庫結構
### 3.1 Migration 檔案
**檔案**: `migrations/003_job_worker.sql`
```sql
-- ================================================================
-- Migration 003: Job Worker System
-- ================================================================
-- 3.1.1 更新 videos 表
ALTER TABLE videos ADD COLUMN IF NOT EXISTS status VARCHAR(20) DEFAULT 'pending';
ALTER TABLE videos ADD COLUMN IF NOT EXISTS user_id BIGINT;
ALTER TABLE videos ADD COLUMN IF NOT EXISTS job_id INTEGER REFERENCES monitor_jobs(id);
COMMENT ON COLUMN videos.status IS 'pending, processing, completed, failed';
COMMENT ON COLUMN videos.user_id IS 'WordPress user ID';
COMMENT ON COLUMN videos.job_id IS 'Associated monitor_jobs ID';
-- 3.1.2 更新 monitor_jobs 表
ALTER TABLE monitor_jobs ADD COLUMN IF NOT EXISTS video_id BIGINT REFERENCES videos(id);
ALTER TABLE monitor_jobs ADD COLUMN IF NOT EXISTS user_id BIGINT;
ALTER TABLE monitor_jobs ADD COLUMN IF NOT EXISTS processors VARCHAR(20)[];
ALTER TABLE monitor_jobs ADD COLUMN IF NOT EXISTS completed_processors VARCHAR(20)[];
ALTER TABLE monitor_jobs ADD COLUMN IF NOT EXISTS failed_processors VARCHAR(20)[];
COMMENT ON COLUMN monitor_jobs.processors IS 'Processors to run: asr, cut, yolo, ocr, face, pose, asrx';
COMMENT ON COLUMN monitor_jobs.completed_processors IS 'Successfully completed processors';
COMMENT ON COLUMN monitor_jobs.failed_processors IS 'Failed processors';
-- 3.1.3 新增 processor_results 表
CREATE TABLE IF NOT EXISTS processor_results (
id SERIAL PRIMARY KEY,
job_id INTEGER REFERENCES monitor_jobs(id) ON DELETE CASCADE,
video_id BIGINT REFERENCES videos(id) ON DELETE CASCADE,
processor VARCHAR(20) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
output_path TEXT,
started_at TIMESTAMP,
completed_at TIMESTAMP,
error_message TEXT,
progress_total INT DEFAULT 0,
progress_current INT DEFAULT 0,
last_checkpoint JSONB,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(job_id, processor)
);
CREATE INDEX IF NOT EXISTS idx_processor_results_job ON processor_results(job_id);
CREATE INDEX IF NOT EXISTS idx_processor_results_video ON processor_results(video_id);
CREATE INDEX IF NOT EXISTS idx_processor_results_status ON processor_results(status);
COMMENT ON TABLE processor_results IS 'Tracks individual processor execution status';
COMMENT ON COLUMN processor_results.status IS 'pending, running, completed, failed, skipped';
-- 3.1.4 更新 videos 表標記欄位用途
COMMENT ON COLUMN videos.fs_video IS 'Video file exists on filesystem';
COMMENT ON COLUMN videos.fs_json IS 'All processor JSON files generated';
COMMENT ON COLUMN videos.fs_chunks IS 'Chunk files generated';
COMMENT ON COLUMN videos.fs_vectors IS 'Vector files generated';
COMMENT ON COLUMN videos.psql_chunk IS 'Chunks stored in PostgreSQL';
COMMENT ON COLUMN videos.pvector_chunk IS 'Vectors stored in PostgreSQL';
COMMENT ON COLUMN videos.qvector_chunk IS 'Vectors stored in Qdrant';
```
### 3.2 表關係圖
```
videos monitor_jobs
┌──────────────────────┐ ┌──────────────────────┐
│ id (PK) │◄────────│ video_id (FK) │
│ uuid │ │ user_id │
│ status │ │ processors[] │
│ fs_video │ │ completed_processors[]│
│ fs_json │ │ failed_processors[] │
│ job_id (FK)─────────┼────────►│ status │
│ user_id │ │ id (PK) │
└──────────────────────┘ └──────────────────────┘
processor_results
┌──────────────────────┐
│ job_id (FK) │
│ video_id (FK) │
│ processor │
│ status │
│ progress_current │
│ last_checkpoint │
│ id (PK) │
└──────────────────────┘
```
---
## 4. 模組並行策略
### 4.1 模組分類
| 模組 | 資源需求 | 獨立性 | 建議並行 |
|------|----------|--------|----------|
| ASR | GPU/CPU | 高 | ✅ 可並行 |
| CUT | CPU | 高 | ✅ 可並行 |
| YOLO | GPU | 中 | ✅ 可並行 |
| OCR | GPU/CPU | 高 | ✅ 可並行 |
| Face | GPU | 中 | ✅ 可並行 |
| Pose | GPU | 中 | ✅ 可並行 |
| ASRX | GPU/CPU | 高 | ✅ 可並行 |
### 4.2 建議並行組合
| 組合 | 模組 1 | 模組 2 | 說明 |
|------|---------|---------|------|
| GPU+CPU | YOLO/Pose/Face | ASR/CUT/OCR | 平衡負載 |
| 雙GPU | YOLO | Pose | 雙 GPU 卡片 |
| 雙CPU | ASR | CUT/OCR | 無 GPU 時 |
### 4.3 Worker 配置
```rust
// src/worker/config.rs
#[derive(Debug, Clone)]
pub struct WorkerConfig {
pub max_concurrent: usize, // 預設 2
pub poll_interval_secs: u64, // 預設 5
pub enabled: bool, // 預設 true
}
impl Default for WorkerConfig {
fn default() -> Self {
Self {
max_concurrent: 2,
poll_interval_secs: 5,
enabled: true,
}
}
}
impl WorkerConfig {
pub fn from_env() -> Self {
Self {
max_concurrent: std::env::var("MOMENTRY_MAX_CONCURRENT")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(2),
poll_interval_secs: std::env::var("MOMENTRY_POLL_INTERVAL")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(5),
enabled: std::env::var("MOMENTRY_WORKER_ENABLED")
.ok()
.map(|v| v != "false")
.unwrap_or(true),
}
}
}
```
---
## 5. 失敗處理機制
### 5.1 設計原則
```
每個模組獨立處理:
- 成功 → 產出完整 .jsonstatus=completed
- 失敗 → 產出 .json 包含 error 狀態status=failed
- 部分完成 → 可從 checkpoint 繼續status=running
```
### 5.2 Processor 輸出格式
```json
{
"processor": "asr",
"status": "completed|failed|partial",
"completed_at": "2026-03-24T12:00:00Z",
"result": { ... },
"error": null,
"last_checkpoint": {
"frame": 5000,
"timestamp": 180.5
}
}
```
### 5.3 失敗處理流程
```rust
async fn run_processor(&self, module: &str, video: &Video) -> Result<()> {
let output_path = self.get_output_path(video, module);
match self.execute_processor(module, video, &output_path).await {
Ok(result) => {
// 成功:更新狀態
self.db.update_processor_status(job_id, module, "completed").await?;
self.publish_progress(job_id, module, 100).await?;
}
Err(e) => {
// 失敗:仍然保存部分結果
let partial_result = self.get_partial_result(&output_path);
self.db.update_processor_status(job_id, module, "failed").await?;
self.db.save_error_message(job_id, module, &e.to_string()).await?;
// 記錄錯誤但不中斷其他模組
tracing::warn!("Processor {} failed: {}", module, e);
}
}
Ok(())
}
```
---
## 6. 實作結構
### 6.1 目錄結構
```
src/
├── worker/
│ ├── mod.rs # Worker 模組導出
│ ├── config.rs # Worker 配置
│ ├── worker.rs # Worker 主邏輯
│ ├── processor.rs # Processor 執行器
│ ├── queue.rs # Job 佇列管理
│ └── progress.rs # 進度追蹤
├── api/
│ └── server.rs # 更新 Register API
└── main.rs # 新增 worker 命令
```
### 6.2 核心模組
#### 6.2.1 Worker Config (`src/worker/config.rs`)
```rust
pub struct WorkerConfig {
pub max_concurrent: usize,
pub poll_interval_secs: u64,
pub enabled: bool,
}
impl WorkerConfig {
pub fn from_env() -> Self { ... }
}
```
#### 6.2.2 Worker Loop (`src/worker/worker.rs`)
```rust
pub struct JobWorker {
db: PostgresDb,
redis: RedisCache,
config: WorkerConfig,
semaphore: Arc<Semaphore>,
}
impl JobWorker {
pub async fn run(&self) -> Result<()> {
loop {
if self.config.enabled {
self.process_pending_jobs().await?;
}
tokio::time::sleep(Duration::from_secs(self.config.poll_interval_secs)).await;
}
}
async fn process_pending_jobs(&self) -> Result<()> {
// 1. 檢查並發數
// 2. 取得 pending jobs
// 3. 分配給 worker pool
// 4. 並行執行 processors
}
}
```
#### 6.2.3 Processor Pool (`src/worker/processor.rs`)
```rust
pub struct ProcessorPool {
max_concurrent: usize,
}
impl ProcessorPool {
pub async fn execute(&self, job: &Job, video: &Video) -> Result<ProcessorResult> {
// 根據 videos 表決定需要執行哪些 processor
// 並行執行最多 2 個
// 處理失敗但不中斷其他 processor
}
}
```
---
## 7. API 端點設計
### 7.1 新增端點
| 端點 | 方法 | 說明 |
|------|------|------|
| `/api/v1/jobs` | GET | 列出所有 jobs |
| `/api/v1/jobs/:uuid` | GET | 取得特定 job 詳細 |
| `/api/v1/jobs/:uuid/retry` | POST | 重試失敗的 processor |
| `/api/v1/jobs/:uuid/cancel` | POST | 取消 job |
### 7.2 端點詳情
#### GET /api/v1/jobs
```json
Response:
{
"jobs": [
{
"id": 1,
"uuid": "abc123def456",
"status": "running",
"progress": 60,
"processors": ["asr", "cut", "yolo", "ocr", "face", "pose"],
"completed": ["asr", "cut", "yolo"],
"failed": []
}
]
}
```
#### GET /api/v1/jobs/:uuid
```json
Response:
{
"id": 1,
"uuid": "abc123def456",
"video_id": 10,
"status": "running",
"processors": {
"asr": {"status": "completed", "progress": 100},
"cut": {"status": "completed", "progress": 100},
"yolo": {"status": "running", "progress": 45, "current": 5000, "total": 11000},
"ocr": {"status": "pending"},
"face": {"status": "pending"},
"pose": {"status": "pending"}
},
"created_at": "2026-03-24T12:00:00Z",
"started_at": "2026-03-24T12:01:00Z"
}
```
---
## 8. Redis Key 設計
### 8.1 現有 Key 保持
```bash
momentry:job:{uuid} # Job Hash
momentry:job:{uuid}:processor:{name} # Processor Hash
momentry:progress:{uuid} # Pub/Sub Channel
momentry:jobs:active # Set: 運行中 UUIDs
momentry:jobs:completed # Set: 完成 UUIDs
momentry:jobs:failed # Set: 失敗 UUIDs
```
### 8.2 進度更新時序
```
Processor 執行
├─► 每秒更新 Redis Hash (即時)
├─► 每 10% 或完成時更新 PostgreSQL (持久)
└─► 失敗時立即更新 PostgreSQL (錯誤記錄)
```
---
## 9. 實作順序
### Phase 1: 資料庫遷移
| 任務 | 說明 |
|------|------|
| 1.1 | 建立 `migrations/003_job_worker.sql` |
| 1.2 | 更新 `postgres_db.rs` 對應的 struct |
| 1.3 | 執行 migration 驗證 |
### Phase 2: Worker 框架
| 任務 | 說明 |
|------|------|
| 2.1 | 建立 `src/worker/mod.rs` |
| 2.2 | 建立 `src/worker/config.rs` |
| 2.3 | 建立 `src/worker/worker.rs` |
| 2.4 | 建立 `src/worker/processor.rs` |
### Phase 3: Register API 整合
| 任務 | 說明 |
|------|------|
| 3.1 | 修改 `src/api/server.rs` 的 register 函數 |
| 3.2 | 加入建立 monitor_jobs 的邏輯 |
| 3.3 | 更新 videos 表 status 欄位 |
### Phase 4: Processor 執行
| 任務 | 說明 |
|------|------|
| 4.1 | 實作 processor 並行執行(最多 2 個) |
| 4.2 | 實作失敗處理(保存部分結果) |
| 4.3 | 實作 checkpoint 恢復 |
### Phase 5: 進度追蹤
| 任務 | 說明 |
|------|------|
| 5.1 | Redis Pub/Sub 整合 |
| 5.2 | PostgreSQL 定期同步 |
| 5.3 | API 進度端點更新 |
### Phase 6: API 端點
| 任務 | 說明 |
|------|------|
| 6.1 | GET /api/v1/jobs |
| 6.2 | GET /api/v1/jobs/:uuid |
| 6.3 | POST /api/v1/jobs/:uuid/retry |
| 6.4 | POST /api/v1/jobs/:uuid/cancel |
### Phase 7: CLI 命令
| 任務 | 說明 |
|------|------|
| 7.1 | `cargo run -- worker` 命令 |
| 7.2 | Worker 啟動/停止/狀態顯示 |
| 7.3 | launchd plist 設定 |
### Phase 8: 測試
| 任務 | 說明 |
|------|------|
| 8.1 | 單元測試 |
| 8.2 | 端到端測試 |
| 8.3 | 失敗處理測試 |
| 8.4 | 並行執行測試 |
---
## 10. CLI 命令
### 10.1 Worker 命令
```bash
# 啟動 worker
cargo run -- worker
# 顯示 worker 幫助
cargo run -- worker --help
```
### 10.2 環境變數
```bash
# Worker 配置
export MOMENTRY_MAX_CONCURRENT=2
export MOMENTRY_POLL_INTERVAL=5
export MOMENTRY_WORKER_ENABLED=true
# 現有環境變數
export DATABASE_URL=postgres://accusys@localhost:5432/momentry
export REDIS_URL=redis://:accusys@localhost:6379
```
---
## 11. 預估工時
| Phase | 任務 | 預估工時 |
|-------|------|----------|
| 1 | 資料庫遷移 | 2h |
| 2 | Worker 框架 | 4h |
| 3 | Register API 整合 | 2h |
| 4 | Processor 執行 | 4h |
| 5 | 進度追蹤 | 2h |
| 6 | API 端點 | 3h |
| 7 | CLI 命令 | 2h |
| 8 | 測試 | 4h |
| **總計** | | **23h** |
---
## 12. 參考文件
| 文件 | 用途 |
|------|------|
| `docs/MOMENTRY_CORE_MONITORING.md` | 監控系統規範 |
| `docs/MOMENTRY_CORE_REDIS_KEYS.md` | Redis Key 設計 |
| `docs/PROCESSING_PIPELINE.md` | 處理流程 |
| `docs/CHUNK_DESIGN.md` | 資料庫設計 |
| `docs/API_REFERENCE.md` | API 參考 |
---
## 13. 附錄
### A. 狀態機
```
┌──────────────┐
│ PENDING │
└──────┬───────┘
│ register 後
┌──────────────┐
┌─────▶│ PROCESSING │◀──────┐
│ └──────┬───────┘ │
│ │ │
部分失敗 all completed 全部失敗
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ PARTIAL │ │COMPLETED │ │ FAILED │
└──────────┘ └──────────┘ └──────────┘
```
### B. videos 表 status 欄位
| 值 | 說明 |
|------|------|
| `pending` | 已註冊,等待處理 |
| `processing` | 處理中 |
| `completed` | 所有處理完成 |
| `failed` | 處理失敗 |
### C. processor_results 表 status 欄位
| 值 | 說明 |
|------|------|
| `pending` | 等待執行 |
| `running` | 執行中 |
| `completed` | 執行成功 |
| `failed` | 執行失敗 |
| `skipped` | 跳過(如檔案已存在) |

View File

@@ -1,519 +0,0 @@
# Momentry JSON 輸出檔案規範
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-16 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
---
本文檔定義 Momentry Core 系統中所有 JSON 輸出檔案的結構、命名規範與儲存位置。
---
## 1. 輸出檔案總覽
### 1.1 檔案類型
| 類型 | 前綴 | 說明 | 狀態 |
|------|------|------|------|
| **Probe** | `{uuid}.probe.json` | 影片元數據 | ✅ 已實作 |
| **ASR** | `{uuid}.asr.json` | 語音識別結果 | ✅ 已實作 |
| **ASRx** | `{uuid}.asrx.json` | 說話者分離 | 🔜 規劃中 |
| **OCR** | `{uuid}.ocr.json` | 文字辨識結果 | 🔜 規劃中 |
| **YOLO** | `{uuid}.yolo.json` | 物件偵測結果 | 🔜 規劃中 |
| **Face** | `{uuid}.face.json` | 人臉偵測結果 | 🔜 規劃中 |
| **Pose** | `{uuid}.pose.json` | 姿態估計結果 | 🔜 規劃中 |
| **Thumbnail** | `{uuid}/thumb_XXX.jpg` | 縮圖檔案 | ✅ 已實作 |
### 1.2 命名規範
```
{UUID}.{類型}.json
範例:
1636719dc31f78ac.probe.json - 影片探測結果
1636719dc31f78ac.asr.json - 語音識別結果
1636719dc31f78ac.ocr.json - 文字辨識結果
```
- **UUID**: 16 字元,基於檔案路徑計算
- **類型**: 小寫 snake_case
- **副檔名**: `.json`
---
## 2. 輸出目錄結構
### 2.1 預設輸出位置
```
momentry_core_0.1/
├── {uuid}.probe.json # 影片探測
├── {uuid}.asr.json # 語音識別
├── {uuid}.asrx.json # 說話者分離
├── {uuid}.ocr.json # 文字辨識
├── {uuid}.yolo.json # 物件偵測
├── {uuid}.face.json # 人臉偵測
├── {uuid}.pose.json # 姿態估計
└── thumbnails/
└── {uuid}/
├── thumb_000.jpg
├── thumb_001.jpg
└── ...
```
### 2.2 儲存策略
| 資料類型 | 儲存位置 | 說明 |
|----------|----------|------|
| JSON 檔案 | 專案根目錄 | 方便快速存取 |
| 縮圖 | thumbnails/{uuid}/ | 分離儲存 |
| 資料庫 | PostgreSQL | 長期儲存 |
---
## 3. JSON 結構定義
### 3.1 Probe (影片探測)
**檔案**: `{uuid}.probe.json`
```json
{
"streams": [
{
"index": 0,
"codec_name": "h264",
"codec_type": "video",
"width": 1920,
"height": 1080,
"r_frame_rate": "60000/1001",
"duration": "6879.329524",
"sample_rate": null,
"channels": null
},
{
"index": 1,
"codec_name": "aac",
"codec_type": "audio",
"width": null,
"height": null,
"r_frame_rate": "0/0",
"duration": "6879.245333",
"sample_rate": "48000",
"channels": 2
}
],
"format": {
"filename": "/path/to/video.mov",
"format_name": "mov,mp4,m4a,3gp,3g2,mj2",
"duration": "6879.329524",
"size": "2361629896",
"bit_rate": "2748000"
}
}
```
**欄位說明**:
| 欄位 | 類型 | 說明 |
|------|------|------|
| `streams` | Array | 媒體串流陣列 |
| `streams[].index` | Integer | 串流索引 |
| `streams[].codec_name` | String | 編碼名稱 |
| `streams[].codec_type` | String | 串流類型 (video/audio) |
| `streams[].width` | Integer | 寬度 (video) |
| `streams[].height` | Integer | 高度 (video) |
| `streams[].r_frame_rate` | String | 幀率 |
| `streams[].duration` | String | 持續時間 (秒) |
| `streams[].sample_rate` | String | 採樣率 (audio) |
| `streams[].channels` | Integer | 聲道數 (audio) |
| `format` | Object | 檔案格式資訊 |
| `format.filename` | String | 原始檔案路徑 |
| `format.format_name` | String | 格式名稱 |
| `format.duration` | String | 總時長 (秒) |
| `format.size` | String | 檔案大小 (bytes) |
| `format.bit_rate` | String | 位元率 |
---
### 3.2 ASR (語音識別)
**檔案**: `{uuid}.asr.json`
```json
{
"language": "en",
"language_probability": 0.9945855736732483,
"segments": [
{
"start": 0.0,
"end": 19.04,
"text": "Hello and welcome to the old-time movie show."
},
{
"start": 19.04,
"end": 25.44,
"text": "Today we are featuring the 1963 comedy mystery film Charade."
}
]
}
```
**欄位說明**:
| 欄位 | 類型 | 說明 |
|------|------|------|
| `language` | String | 偵測語言代碼 (ISO 639-1) |
| `language_probability` | Float | 語言偵測機率 (0-1) |
| `segments` | Array | 語音分段陣列 |
| `segments[].start` | Float | 開始時間 (秒) |
| `segments[].end` | Float | 結束時間 (秒) |
| `segments[].text` | String | 識別文字 |
---
### 3.3 ASRx (說話者分離)
**檔案**: `{uuid}.asrx.json`
```json
{
"language": "en",
"language_probability": 0.95,
"segments": [
{
"start": 0.0,
"end": 19.04,
"text": "Hello and welcome to the old-time movie show.",
"speaker_id": "SPEAKER_00",
"speaker_embedding": [0.123, -0.456, ...]
},
{
"start": 19.04,
"end": 25.44,
"text": "Today we are featuring the 1963 comedy mystery film Charade.",
"speaker_id": "SPEAKER_01",
"speaker_embedding": [0.789, -0.123, ...]
}
]
}
```
**欄位說明**:
| 欄位 | 類型 | 說明 |
|------|------|------|
| `language` | String | 偵測語言代碼 |
| `language_probability` | Float | 語言偵測機率 |
| `segments` | Array | 語音分段陣列 |
| `segments[].start` | Float | 開始時間 (秒) |
| `segments[].end` | Float | 結束時間 (秒) |
| `segments[].text` | String | 識別文字 |
| `segments[].speaker_id` | String | 說話者 ID |
| `segments[].speaker_embedding` | Array | 說話者嵌入向量 (可選) |
---
### 3.4 OCR (文字辨識)
**檔案**: `{uuid}.ocr.json`
```json
{
"segments": [
{
"start": 10.5,
"end": 12.3,
"text": "EXAMPLE TEXT",
"boxes": [
{
"x1": 100,
"y1": 50,
"x2": 400,
"y2": 100
}
],
"confidence": 0.95
}
]
}
```
**欄位說明**:
| 欄位 | 類型 | 說明 |
|------|------|------|
| `segments` | Array | OCR 分段陣列 |
| `segments[].start` | Float | 開始時間 (秒) |
| `segments[].end` | Float | 結束時間 (秒) |
| `segments[].text` | String | 辨識文字 |
| `segments[].boxes` | Array | 文字邊界框陣列 |
| `segments[].boxes[].x1` | Integer | 左上 X 座標 |
| `segments[].boxes[].y1` | Integer | 左上 Y 座標 |
| `segments[].boxes[].x2` | Integer | 右下 X 座標 |
| `segments[].boxes[].y2` | Integer | 右下 Y 座標 |
| `segments[].confidence` | Float | 辨識信心度 |
---
### 3.5 YOLO (物件偵測)
**檔案**: `{uuid}.yolo.json`
```json
{
"segments": [
{
"start": 0.0,
"end": 1.0,
"objects": [
{
"class": "person",
"confidence": 0.92,
"box": {
"x1": 150,
"y1": 200,
"x2": 400,
"y2": 800
}
},
{
"class": "car",
"confidence": 0.87,
"box": {
"x1": 800,
"y1": 400,
"x2": 1200,
"y2": 700
}
}
]
}
]
}
```
**欄位說明**:
| 欄位 | 類型 | 說明 |
|------|------|------|
| `segments` | Array | 時間分段陣列 |
| `segments[].start` | Float | 開始時間 (秒) |
| `segments[].end` | Float | 結束時間 (秒) |
| `segments[].objects` | Array | 偵測物件陣列 |
| `segments[].objects[].class` | String | 物件類別 |
| `segments[].objects[].confidence` | Float | 偵測信心度 |
| `segments[].objects[].box` | Object | 邊界框 |
---
### 3.6 Face (人臉偵測)
**檔案**: `{uuid}.face.json`
```json
{
"segments": [
{
"start": 0.0,
"end": 1.0,
"faces": [
{
"face_id": "face_001",
"box": {
"x1": 100,
"y1": 50,
"x2": 300,
"y2": 350
},
"embedding": [0.123, -0.456, ...],
"emotion": "happy",
"age": 35,
"gender": "female"
}
]
}
]
}
```
**欄位說明**:
| 欄位 | 類型 | 說明 |
|------|------|------|
| `segments` | Array | 時間分段陣列 |
| `segments[].start` | Float | 開始時間 (秒) |
| `segments[].end` | Float | 結束時間 (秒) |
| `segments[].faces` | Array | 人臉陣列 |
| `segments[].faces[].face_id` | String | 人臉 ID |
| `segments[].faces[].box` | Object | 邊界框 |
| `segments[].faces[].embedding` | Array | 人臉嵌入向量 |
| `segments[].faces[].emotion` | String | 情緒分類 (可選) |
| `segments[].faces[].age` | Integer | 年齡估計 (可選) |
| `segments[].faces[].gender` | String | 性別估計 (可選) |
---
### 3.7 Pose (姿態估計)
**檔案**: `{uuid}.pose.json`
```json
{
"segments": [
{
"start": 0.0,
"end": 1.0,
"poses": [
{
"person_id": "person_001",
"keypoints": {
"nose": {"x": 320, "y": 120, "confidence": 0.98},
"left_eye": {"x": 335, "y": 110, "confidence": 0.95},
"right_eye": {"x": 305, "y": 110, "confidence": 0.93},
"left_shoulder": {"x": 280, "y": 180, "confidence": 0.91},
"right_shoulder": {"x": 360, "y": 180, "confidence": 0.89}
},
"confidence": 0.92
}
]
}
]
}
```
**欄位說明**:
| 欄位 | 類型 | 說明 |
|------|------|------|
| `segments` | Array | 時間分段陣列 |
| `segments[].start` | Float | 開始時間 (秒) |
| `segments[].end` | Float | 結束時間 (秒) |
| `segments[].poses` | Array | 姿態陣列 |
| `segments[].poses[].person_id` | String | 人員 ID |
| `segments[].poses[].keypoints` | Object | 關鍵點 |
| `segments[].poses[].keypoints.{name}` | Object | 各關鍵點 |
| `segments[].poses[].keypoints.{name}.x` | Integer | X 座標 |
| `segments[].poses[].keypoints.{name}.y` | Integer | Y 座標 |
| `segments[].poses[].keypoints.{name}.confidence` | Float | 信心度 |
| `segments[].poses[].confidence` | Float | 整體信心度 |
---
## 4. 處理流程
### 4.1 處理管線
```
影片檔案
┌─────────────────┐
│ 1. Register │ 建立 UUID註冊影片
└────────┬────────┘
┌────▼────┐
│ 2. Probe │ ffprobe 擷取元數據
└────┬────┘
│ {uuid}.probe.json
┌────▼─────┐
│ 3. ASR │ faster-whisper 語音識別
└────┬─────┘
│ {uuid}.asr.json
┌────▼──────┐
│ 4. ASRx │ 說話者分離 (pyannote)
└────┬──────┘
│ {uuid}.asrx.json
┌────▼────┐
│ 5. OCR │ Tesseract 文字辨識
└────┬────┘
│ {uuid}.ocr.json
┌────▼────┐
│ 6. YOLO │ 物件偵測
└────┬────┘
│ {uuid}.yolo.json
┌────▼────┐
│ 7. Face │ 人臉偵測
└────┬────┘
│ {uuid}.face.json
┌────▼────┐
│ 8. Pose │ 姿態估計
└────┬────┘
│ {uuid}.pose.json
┌────▼──────┐
│ 9. Chunk │ 轉換為資料庫 chunks
└───────────┘
```
### 4.2 失敗處理
| 階段 | 失敗時 | 處理 |
|------|--------|------|
| Probe | 無法讀取影片 | 終止流程,輸出錯誤 |
| ASR | 無音軌 | 產生空 segments繼續流程 |
| OCR/YOLO/Face/Pose | 處理失敗 | 跳過該階段,記錄日誌 |
---
## 5. 資料庫儲存
### 5.1 Chunk 結構
```sql
CREATE TABLE chunks (
id BIGSERIAL PRIMARY KEY,
uuid VARCHAR(16) NOT NULL,
chunk_id VARCHAR(64) NOT NULL,
chunk_index INTEGER NOT NULL,
chunk_type VARCHAR(32) NOT NULL,
start_time DOUBLE PRECISION NOT NULL,
end_time DOUBLE PRECISION NOT NULL,
content JSONB NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
UNIQUE(uuid, chunk_id)
);
```
### 5.2 轉換範例
```rust
// ASR → Chunk (Sentence)
for (i, seg) in asr_result.segments.iter().enumerate() {
let chunk = Chunk::new(
uuid.clone(),
i as u32,
ChunkType::Sentence,
seg.start,
seg.end,
serde_json::json!({"text": seg.text}),
);
db.store_chunk(&chunk).await?;
}
```
---
## 6. 版本歷史
| 版本 | 日期 | 變更 |
|------|------|------|
| 1.0.0 | 2026-03-16 | 初始版本 |
---
## 7. 相關文件
- [RUST_DEVELOPMENT.md](./RUST_DEVELOPMENT.md) - Rust 開發規範
- [AGENTS.md](../AGENTS.md) - 開發規範
- [monitor_config.yaml](../monitor/config/monitor_config.yaml) - 監控配置

View File

@@ -1,782 +0,0 @@
# Momentry 系統自動化安裝計劃
> **計劃階段** - 僅供討論,尚未執行
> **建立時間**: 2026-03-23
> **目標**: Thunderbolt NVMe 外開機完整安裝
---
## 系統概述
### 當前環境
| 項目 | 內容 |
|------|------|
| **主控機** | Mac mini (M4, 16GB RAM) |
| **作業系統** | macOS 26.3.1 (Tahoe) |
| **儲存** | Thunderbolt NVMe (2TB) |
| **用途** | 開機碟 + 完整 Momentry 系統 |
### 目標環境
| 項目 | 內容 |
|------|------|
| **目標主機** | 其他 Mac (Intel 或 Apple Silicon) |
| **安裝方式** | Thunderbolt NVMe 外接開機 |
| **連接方式** | Thunderbolt 3/4 |
| **控制方式** | SSH 遠端管理 |
---
## 系統架構
### 服務列表
| 服務 | 版本 | 用途 | Port |
|------|------|------|------|
| **PostgreSQL** | 18.1 | 主資料庫、n8n 資料庫 | 5432 |
| **MongoDB** | 8.0 | 文件資料庫 | 27017 |
| **MariaDB** | 11.4 | WordPress 資料庫 | 3306 |
| **Redis** | 7.x | 快取、佇列 | 6379 |
| **Qdrant** | 1.7.x | 向量資料庫 | 6333 |
| **Ollama** | 0.13.5 | 本地 LLM | 11434 |
| **Caddy** | 2.x | 反向代理 | 80/443 |
| **Gitea** | 1.21 | Git 服務 | 3000 |
| **PHP-FPM** | 8.5 | WordPress | 9000 |
| **n8n** | 2.3.5 | 工作流程自動化 | 5678 |
| **RustDesk** | hbbs/hbbr | 遠端桌面 | 21115-21119 |
| **SFTPGo** | 2.x | SFTP 服務 | 2022 |
| **Momentry Core** | 0.1.0 | 影片處理核心 | 3002 |
| **Prometheus** | 3.9.1 | 監控 | 9090 |
### 目錄結構
```
/Volumes/Momentry/
├── System/
│ └── macOS/ # macOS 系統
├── Applications/
│ └── Homebrew/ # Homebrew 應用程式
├── momentry/
│ ├── var/ # 資料目錄
│ │ ├── postgresql/ # PostgreSQL 資料
│ │ ├── mongodb/ # MongoDB 資料
│ │ ├── mariadb/ # MariaDB 資料
│ │ ├── redis/ # Redis 資料
│ │ ├── qdrant/ # Qdrant 資料
│ │ ├── n8n/ # n8n 資料
│ │ ├── ollama/ # Ollama 模型
│ │ └── ...
│ ├── etc/ # 配置檔案
│ │ ├── Caddyfile
│ │ ├── gitea/
│ │ ├── php/
│ │ └── ...
│ ├── log/ # 日誌
│ ├── scripts/ # 管理腳本
│ └── backup/ # 備份
├── momentry_core/ # Rust 原始碼
└── momentry_dashboard/ # Web Dashboard
```
---
## 階段一:前置準備
### 1.1 收集目標主機資訊
```bash
# 需要收集的資訊
- Mac 型號 (Intel/Apple Silicon)
- macOS 版本
- Thunderbolt 版本 (3/4)
- 可用記憶體
- 目標磁碟代號 (diskX)
- 網路配置 (DHCP/固定 IP)
```
### 1.2 準備 Thunderbolt NVMe
```bash
# 檢查 Thunderbolt NVMe
diskutil list external
# 預期輸出:
# /dev/diskX (external, physical):
# NAME TYPE SIZE
# Thunderbolt NVMe ...
```
### 1.3 準備主控機腳本
```bash
# 主控機需要準備的腳本
~/momentry/setup/
├── 01_prepare_disk.sh
├── 02_install_macos.sh
├── 03_install_homebrew.sh
├── 04_install_dependencies.sh
├── 05_install_services.sh
├── 06_install_momentry.sh
├── 07_configure_network.sh
├── 08_start_services.sh
└── utils/
├── common.sh
├── backup.sh
└── monitor.sh
```
---
## 階段二Thunderbolt NVMe 準備
### 2.1 分割磁碟方案 A推薦
```bash
# 磁碟分割配置
diskutil partitionDisk /dev/diskX \
GPT \
"APFS System" APFS "Momentry System" 200G \
"APFS Data" APFS "Momentry Data" 1.8T
```
### 2.2 分割磁碟方案 B最小化
```bash
# 統一 APFS 容器
diskutil partitionDisk /dev/diskX \
GPT \
APFS "Momentry" 100%
```
---
## 階段三:安裝 macOS
### 3.1 建立 macOS 安裝碟
```bash
# 下載 macOS Sonoma (或最新版本)
softwareupdate --fetch-full-installer --full-installer-version 14.0
# 建立可開機安裝碟
sudo /Applications/Install\ macOS\ Sonoma.app/Contents/Resources/createinstallinstmedi \
--volume /Volumes/Momentry \
--nointeraction
```
### 3.2 安裝 macOS 到 Thunderbolt NVMe
**兩種方法:**
#### 方法 A: 復原模式安裝
1. 連接 Thunderbolt NVMe
2. 重啟目標主機按住Option鍵
3. 選擇 Thunderbolt NVMe 開機
4. 進入 Recovery Mode (Command+R)
5. 使用 Disk Utility 格式化目標磁碟
6. 安裝 macOS
#### 方法 B: ASR 複製(建議)
```bash
# 從主控機執行
# 將現有系統複製到目標磁碟
sudo asr restore \
--source /Volumes/Macintosh\ HD \
--target /Volumes/Momentry \
--erase --noprompt
```
### 3.3 設定 macOS
```bash
# 自動化設定腳本
./setup/scripts/03_install_homebrew.sh
```
**設定項目:**
- 電腦名稱:`momentry-<serial>`
- 使用者帳號:`momentry` (管理員)
- SSH 遠端登入:啟用
- 螢幕鎖定:關閉
- 節能設定:永不休眠
---
## 階段四:安裝 Homebrew
### 4.1 安裝 Homebrew
```bash
#!/bin/bash
# 04_install_homebrew.sh
# 檢查架構
ARCH=$(uname -m)
if [ "$ARCH" = "arm64" ]; then
# Apple Silicon
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"
elif [ "$ARCH" = "x86_64" ]; then
# Intel
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
echo 'eval "$(/usr/local/bin/brew shellenv)"' >> ~/.zprofile
eval "$(/usr/local/bin/brew shellenv)"
fi
# 驗證
brew --version
```
### 4.2 安裝基礎工具
```bash
# 基礎開發工具
brew install \
git \
curl \
wget \
jq \
yq \
tree \
htop \
tmux \
zsh \
zsh-completions
```
---
## 階段五:安裝服務
### 5.1 安裝資料庫服務
```bash
#!/bin/bash
# 05_install_services.sh
# PostgreSQL
brew install postgresql@18
brew services start postgresql@18
# MongoDB
brew tap mongodb/brew
brew install mongodb-community
brew services start mongodb-community
# MariaDB
brew install mariadb
brew services start mariadb
# Redis
brew install redis
brew services start redis
# Qdrant (需要 Cargo)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
cargo install qdrant
```
### 5.2 安裝應用服務
```bash
# Ollama
brew install ollama
brew services start ollama
# Caddy
brew install caddy
brew services start caddy
# Gitea
brew install gitea
brew services start gitea
# PHP
brew install php
brew services start php
# n8n
brew install n8n
brew services start n8n
```
### 5.3 Launchd 服務配置
```xml
<!-- /Library/LaunchDaemons/com.momentry.postgresql.plist -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.momentry.postgresql</string>
<key>UserName</key>
<string>momentry</string>
<key>ProgramArguments</key>
<array>
<string>/opt/homebrew/opt/postgresql@18/bin/postgres</string>
<string>-D</string>
<string>/Volumes/Momentry/momentry/var/postgresql</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Volumes/Momentry/momentry/log/postgresql.log</string>
<key>StandardErrorPath</key>
<string>/Volumes/Momentry/momentry/log/postgresql.error.log</string>
</dict>
</plist>
```
---
## 階段六:安裝 Momentry Core
### 6.1 複製原始碼
```bash
#!/bin/bash
# 06_install_momentry.sh
# 建立 Momentry 目錄
mkdir -p /Volumes/Momentry/momentry/{var,etc,log,scripts,backup}
mkdir -p /Volumes/Momentry/momentry_core
# 複製原始碼
rsync -av \
--exclude 'target' \
--exclude '.git' \
--exclude 'node_modules' \
/Users/accusys/momentry_core_0.1/ \
/Volumes/Momentry/momentry_core/
# 編譯 Rust 專案
cd /Volumes/Momentry/momentry_core
cargo build --release
```
### 6.2 初始化資料庫
```bash
# 建立 PostgreSQL 資料庫
psql -U postgres <<EOF
CREATE DATABASE momentry;
CREATE DATABASE n8n;
CREATE DATABASE video_register;
CREATE USER momentry WITH PASSWORD 'momentry_password';
CREATE USER n8n WITH PASSWORD 'n8n_password';
GRANT ALL PRIVILEGES ON DATABASE momentry TO momentry;
GRANT ALL PRIVILEGES ON DATABASE n8n TO n8n;
EOF
# 執行 migration
cd /Volumes/Momentry/momentry_core
sqlx migrate run
```
### 6.3 配置環境變數
```bash
# ~/.zshrc 或 ~/.bash_profile
export DATABASE_URL="postgres://momentry:momentry_password@localhost:5432/momentry"
export REDIS_URL="redis://:momentry_password@localhost:6379"
export QDRANT_URL="http://localhost:6333"
export MONGODB_URI="mongodb://localhost:27017/momentry"
export MOMENTRY_OUTPUT_DIR="/Volumes/Momentry/momentry/var/output"
export MOMENTRY_LOG_LEVEL="info"
```
---
## 階段七:網路配置
### 7.1 設定固定 IP可選
```bash
# 網路配置腳本
#!/bin/bash
# 07_configure_network.sh
# 取得網路介面
INTERFACE=$(networksetup -listallnetworkservices | grep "Thunderbolt")
# 設定固定 IP
networksetup -setmanual "$INTERFACE" \
192.168.1.100 \
255.255.255.0 \
192.168.1.1
# 設定 DNS
networksetup -setdnsservers "$INTERFACE" \
8.8.8.8 \
8.8.4.4
```
### 7.2 配置防火牆
```bash
# 開放服務端口
# 使用 macOS Firewall 或 pfctl
```
### 7.3 設定 SSH 金鑰
```bash
# 產生 SSH 金鑰對
ssh-keygen -t ed25519 -C "momentry@$(hostname)"
# 複製公鑰到目標主機
ssh-copy-id momentry@target-host
# 主控機 SSH 配置
# ~/.ssh/config
Host momentry-target
HostName 192.168.1.100
User momentry
IdentityFile ~/.ssh/id_ed25519
```
---
## 階段八:啟動服務
### 8.1 啟動順序
```bash
#!/bin/bash
# 08_start_services.sh
# 1. 基礎服務
launchctl load /Library/LaunchDaemons/com.momentry.postgresql.plist
launchctl load /Library/LaunchDaemons/com.momentry.mongodb.plist
launchctl load /Library/LaunchDaemons/com.momentry.mariadb.plist
launchctl load /Library/LaunchDaemons/com.momentry.redis.plist
sleep 10
# 2. 向量資料庫
launchctl load /Library/LaunchDaemons/com.momentry.qdrant.plist
sleep 5
# 3. 應用服務
launchctl load /Library/LaunchDaemons/com.momentry.ollama.plist
launchctl load /Library/LaunchDaemons/com.momentry.caddy.plist
sleep 5
# 4. 其他服務
launchctl load /Library/LaunchDaemons/com.momentry.gitea.plist
launchctl load /Library/LaunchDaemons/com.momentry.php.plist
launchctl load /Library/LaunchDaemons/com.momentry.n8n.main.plist
launchctl load /Library/LaunchDaemons/com.momentry.n8n.worker.plist
# 5. Momentry Core
launchctl load /Library/LaunchDaemons/com.momentry.sftpgo.plist
launchctl load /Library/LaunchDaemons/com.momentry.rustdesk.hbbs.plist
launchctl load /Library/LaunchDaemons/com.momentry.rustdesk.hbbr.plist
```
### 8.2 驗證服務
```bash
# 檢查所有服務狀態
function check_services() {
services=(
"postgresql"
"mongodb"
"mariadb"
"redis"
"qdrant"
"ollama"
"caddy"
"gitea"
"php"
"n8n"
"sftpgo"
)
for service in "${services[@]}"; do
if launchctl list | grep "$service" | grep -q "running"; then
echo "$service: Running"
else
echo "$service: Not running"
fi
done
}
check_services
```
---
## 階段九:備份與還原
### 9.1 備份策略
```bash
#!/bin/bash
# 備份腳本
BACKUP_DIR="/Volumes/Momentry/backup/$(date +%Y%m%d)"
# 1. PostgreSQL 備份
pg_dump -U momentry momentry > "$BACKUP_DIR/momentry.sql"
pg_dump -U n8n n8n > "$BACKUP_DIR/n8n.sql"
# 2. MongoDB 備份
mongodump --out "$BACKUP_DIR/mongodb"
# 3. Redis 備份
redis-cli BGSAVE
cp /Volumes/Momentry/var/redis/dump.rdb "$BACKUP_DIR/redis.rdb"
# 4. Qdrant 備份
curl -X POST http://localhost:6333/collections/accusysdb/snapshots
# 5. 配置檔案備份
tar -czf "$BACKUP_DIR/config.tar.gz" \
/Volumes/Momentry/momentry/etc/
```
### 9.2 自動備份 Cron
```bash
# crontab -e
0 2 * * * /Volumes/Momentry/scripts/backup.sh
0 3 * * 0 /Volumes/Momentry/scripts/backup_full.sh
```
---
## 階段十:監控與維護
### 10.1 健康檢查腳本
```bash
#!/bin/bash
# health_check.sh
# 檢查所有服務
check_postgresql() {
pg_isready -q && echo "✅ PostgreSQL" || echo "❌ PostgreSQL"
}
check_mongodb() {
mongosh --eval "db.stats()" > /dev/null 2>&1 && echo "✅ MongoDB" || echo "❌ MongoDB"
}
check_redis() {
redis-cli ping > /dev/null 2>&1 && echo "✅ Redis" || echo "❌ Redis"
}
check_qdrant() {
curl -s http://localhost:6333/health && echo "✅ Qdrant" || echo "❌ Qdrant"
}
check_n8n() {
curl -s http://localhost:5678/api/v1/workflows > /dev/null 2>&1 && echo "✅ n8n" || echo "❌ n8n"
}
check_momentry() {
curl -s http://localhost:3002/api/v1/videos > /dev/null 2>&1 && echo "✅ Momentry" || echo "❌ Momentry"
}
```
### 10.2 日誌輪替
```bash
# 新聞日誌配置
/Volumes/Momentry/momentry/log/*.log {
daily
rotate 7
compress
missingok
notifempty
create 644 momentry staff
}
```
---
## 自動化腳本架構
### 主控腳本:部署控制器
```bash
#!/bin/bash
# deploy_controller.sh
# 用於從主控機部署到目標主機
set -e
# 配置
TARGET_HOST="momentry@192.168.1.100"
TARGET_DISK="/dev/disk2"
# 顏色定義
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
function log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
function log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
function log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 階段執行
function run_stage() {
local stage=$1
local script=$2
log_info "執行階段: $stage..."
ssh "$TARGET_HOST" "bash /Volumes/Momentry/scripts/$script"
if [ $? -eq 0 ]; then
log_info "✅ 階段完成: $stage"
else
log_error "❌ 階段失敗: $stage"
exit 1
fi
}
# 主程序
log_info "開始 Momentry 系統部署..."
# 執行各階段
run_stage "磁碟準備" "01_prepare_disk.sh"
run_stage "macOS 安裝" "02_install_macos.sh"
run_stage "Homebrew 安裝" "03_install_homebrew.sh"
run_stage "依賴安裝" "04_install_dependencies.sh"
run_stage "服務安裝" "05_install_services.sh"
run_stage "Momentry 安裝" "06_install_momentry.sh"
run_stage "網路配置" "07_configure_network.sh"
run_stage "啟動服務" "08_start_services.sh"
log_info "✅ 部署完成!"
```
---
## 待確認事項
### 需要與使用者確認
1. **目標主機型號**
- Intel Mac 或 Apple Silicon
- Thunderbolt 版本 (3/4)
2. **網路配置**
- DHCP 或固定 IP
- 目標 IP 網段?
3. **磁碟配置**
- 分割方案 A (200G 系統 + 1.8T 資料)
- 分割方案 B (統一磁碟區)
4. **服務需求**
- 需要安裝全部服務?
- 還是選擇性安裝?
5. **備份策略**
- 本地備份?
- 遠端備份?
- 備份頻率?
6. **監控需求**
- Prometheus + Grafana
- 簡單腳本監控?
---
## 預估時間
| 階段 | 預估時間 | 備註 |
|------|---------|------|
| 前置準備 | 30 分鐘 | 收集資訊、準備腳本 |
| 磁碟準備 | 10 分鐘 | 分割格式化 |
| macOS 安裝 | 30-60 分鐘 | 視 USB 速度 |
| Homebrew 安裝 | 15 分鐘 | 下載速度 |
| 服務安裝 | 60-90 分鐘 | 多個服務 |
| Momentry 安裝 | 20 分鐘 | 編譯 Rust |
| 網路配置 | 10 分鐘 | 固定 IP |
| 服務啟動 | 15 分鐘 | 依序啟動 |
| 驗證測試 | 30 分鐘 | 完整測試 |
| **總計** | **3-4 小時** | 自動化後可縮短 |
---
## 風險與應對
| 風險 | 機率 | 影響 | 應對措施 |
|------|------|------|---------|
| Thunderbolt 不相容 | 低 | 高 | 準備多種驅動 |
| macOS 安裝失敗 | 低 | 高 | 準備還原方案 |
| 服務啟動失敗 | 中 | 中 | 日誌診斷腳本 |
| 網路連線問題 | 中 | 中 | 有線網路備援 |
| 儲存空間不足 | 低 | 高 | 磁碟空間檢查 |
---
## 下一步行動
1. ✅ 確認目標主機規格
2. ✅ 確認 Thunderbolt NVMe 容量
3. ✅ 確認網路配置
4. ✅ 選擇服務清單
5. ✅ 準備安裝腳本
6. ✅ 測試腳本執行
7. ✅ 正式部署
---
## 附錄
### A. 服務端口對照表
| 服務 | Port | 協議 |
|------|------|------|
| PostgreSQL | 5432 | TCP |
| MongoDB | 27017 | TCP |
| MariaDB | 3306 | TCP |
| Redis | 6379 | TCP |
| Qdrant API | 6333 | HTTP |
| Qdrant gRPC | 6334 | gRPC |
| Ollama | 11434 | HTTP |
| Caddy HTTP | 80 | HTTP |
| Caddy HTTPS | 443 | HTTPS |
| Gitea | 3000 | HTTP |
| PHP-FPM | 9000 | FastCGI |
| n8n | 5678 | HTTP |
| SFTPGo | 2022 | SFTP |
| RustDesk hbbs | 21115 | TCP |
| RustDesk hbbr | 21117 | TCP |
| Momentry | 3002 | HTTP |
| Prometheus | 9090 | HTTP |
### B. 環境變數清單
`.env` 範例檔案或 `docs/MOMENTRY_CORE_MONITORING.md`
### C. 疑難排解
`docs/PENDING_ISSUES.md`
---
**計劃狀態**: 📝 草稿 - 等待使用者確認後執行
**負責人**: OpenCode AI Assistant
**最後更新**: 2026-03-23

View File

@@ -1,674 +0,0 @@
# Momentry Core 監控規範 (暫定)
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-17 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-17 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
| V1.1 | 2026-03-25 | 新增可配置 Redis Key Prefix 說明 | Warren | OpenCode / GLM-5 |
| V1.2 | 2026-03-25 | 新增 Job Worker 監控、processor_results 表 | Warren | OpenCode / GLM-5 |
---
> **⚠️ 狀態**: 此文檔為暫定版本momentry_core 仍在開發中,待開發完成後再正式納入監控系統。
---
## 1. 概述
momentry_core 是 Rust 開發的數字資產管理系統,專注於視頻分析和 RAG 功能。
## 2. 監控架構
### Layer 2: Service 監控
| 項目 | 類型 | 檢查方式 | Port | 狀態 |
|------|------|----------|------|------|
| postgresql | TCP | `pg_isready` | 5432 | ✅ 運行中 |
| redis | TCP | `redis-cli ping` | 6379 | ✅ 運行中 |
| n8n | HTTP | `/healthz` | 5678 | ✅ 運行中 |
| sftpgo | HTTP | `/healthz`, `/api/v2/token` | 8080 | ✅ 運行中 |
| qdrant | HTTP | `/collections` | 6333 | ✅ 運行中 |
| momentry_core | CLI | `momentry --version` | - | ⚠️ 待編譯 |
**附屬服務狀態** (非核心但相關):
| gitea | HTTP | `/` | 3000 | ✅ 運行中 |
| ollama | HTTP | `/api/tags` | 11434 | ✅ 運行中 |
| caddy | HTTP | `/` | 80 | ✅ 運行中 |
### Layer 7: Backup 監控
| 項目 | 類型 | 位置 |
|------|------|------|
| momentry | data | `/Users/accusys/momentry/backup/momentry` |
| sftpgo | config + db | `/Users/accusys/momentry/backup/daily/sftpgo/` |
---
## 3. Redis 監控架構
> **⚠️ 注意**: 從 V1.1 開始,所有 Redis Keys 都支援自定義前綴。
> 預設前綴:生產環境為 `momentry:`,開發環境為 `momentry_dev:`
>
> 若使用非預設前綴,請將下方命令中的 `momentry:` 替換為實際前綴。
### 3.1 健康檢查
```bash
# 檢查 Redis 連線
redis-cli -a accusys ping
# 檢查 momentry 健康狀態
redis-cli GET momentry:health:current
```
### 3.2 Job 狀態監控
```bash
# 查看運行中的 Jobs
redis-cli SMEMBERS momentry:jobs:active
# 查看 Job 狀態
redis-cli HGETALL momentry:job:5dea6618a606e7c7
```
### 3.3 即時進度監控
```bash
# 訂閱進度頻道
redis-cli SUBSCRIBE momentry:progress:5dea6618a606e7c7
```
---
## 4. 監控腳本
### 4.1 健康檢查項目細節
基於當前系統狀態,需要監控的核心項目:
#### 4.1.1 資料庫服務檢查
```bash
# PostgreSQL - 檢查伺服器運行與關鍵資料庫
check_postgresql() {
if pg_isready -h localhost -p 5432 > /dev/null 2>&1; then
echo -e "${GREEN}${NC} PostgreSQL"
# 檢查關鍵資料庫是否存在
local missing_dbs=()
for db in momentry video_register n8n sftpgo; do
if ! psql -h localhost -U postgres -lqt 2>/dev/null | cut -d\| -f1 | grep -qw "$db"; then
missing_dbs+=("$db")
fi
done
if [ ${#missing_dbs[@]} -gt 0 ]; then
echo -e " ${YELLOW}${NC} 缺失資料庫: ${missing_dbs[*]}"
record_service "postgresql" "warning" "1" "Missing databases: ${missing_dbs[*]}"
else
record_service "postgresql" "up" "1" ""
fi
else
echo -e "${RED}${NC} PostgreSQL"
record_service "postgresql" "down" "0" "PostgreSQL not responding"
fi
}
```
#### 4.1.2 Redis 檢查
```bash
check_redis() {
if redis-cli -a "$REDIS_PASSWORD" ping 2>/dev/null | grep -q PONG; then
echo -e "${GREEN}${NC} Redis"
record_service "redis" "up" "1" ""
else
echo -e "${RED}${NC} Redis"
record_service "redis" "down" "0" "Redis not responding"
fi
}
```
#### 4.1.3 SFTPGo 檢查
```bash
check_sftpgo() {
# 1. 檢查 HTTP API
if curl -s http://localhost:8080/healthz > /dev/null 2>&1; then
# 2. 檢查 API 認證
if TOKEN=$(curl -s -X GET http://localhost:8080/api/v2/token -u "admin:$SFTPGO_ADMIN_PASSWORD" 2>/dev/null | jq -r '.access_token') && [ -n "$TOKEN" ]; then
echo -e "${GREEN}${NC} SFTPGo"
record_service "sftpgo" "up" "1" ""
else
echo -e "${YELLOW}${NC} SFTPGo (API認證失敗)"
record_service "sftpgo" "warning" "1" "API authentication failed"
fi
else
echo -e "${RED}${NC} SFTPGo"
record_service "sftpgo" "down" "0" "HTTP API not responding"
fi
}
```
#### 4.1.4 n8n 檢查
```bash
check_n8n() {
if curl -s http://localhost:5678/healthz > /dev/null 2>&1; then
echo -e "${GREEN}${NC} n8n"
record_service "n8n" "up" "1" ""
else
echo -e "${RED}${NC} n8n"
record_service "n8n" "down" "0" "API not responding"
fi
}
```
#### 4.1.5 Qdrant 檢查
```bash
check_qdrant() {
if curl -s http://localhost:6333/collections > /dev/null 2>&1; then
echo -e "${GREEN}${NC} Qdrant"
record_service "qdrant" "up" "1" ""
else
echo -e "${RED}${NC} Qdrant"
record_service "qdrant" "down" "0" "API not responding"
fi
}
```
#### 4.1.6 Momentry Core 檢查
```bash
check_momentry_core() {
local binary="/Users/accusys/momentry/target/release/momentry"
if [ ! -f "$binary" ]; then
binary="/Users/accusys/momentry/target/debug/momentry"
fi
if [ -f "$binary" ] && $binary --version > /dev/null 2>&1; then
echo -e "${GREEN}${NC} Momentry Core"
record_service "momentry_core" "up" "1" ""
else
echo -e "${RED}${NC} Momentry Core"
record_service "momentry_core" "down" "0" "Binary not found or not executable"
fi
}
```
### 4.2 檢查腳本範例完整實現
```bash
#!/bin/bash
# monitor/service/health_check.sh
export REDIS_PASSWORD="accusys"
export SFTPGO_ADMIN_PASSWORD="Test3200Test3200"
check_postgresql
check_redis
check_sftpgo
check_n8n
check_qdrant
check_momentry_core
```
### 4.2 Redis Job 監控腳本
**檔案**: `monitor/service/redis_job_monitor.sh`
此腳本專門監控 Redis 中的 Job 狀態與即時進度:
```bash
#!/bin/bash
# Momentry Core Redis Job 監控
REDIS_PASSWORD="${REDIS_PASSWORD:-accusys}"
REDIS_PREFIX="${REDIS_PREFIX:-momentry:}"# 可配置前綴,預設 momentry:
# 檢查 Job 狀態
check_job_status() {
local active_jobs=$(redis-cli -a "$REDIS_PASSWORD" SCARD "${REDIS_PREFIX}jobs:active" 2>/dev/null || echo "0")
local completed_jobs=$(redis-cli -a "$REDIS_PASSWORD" SCARD "${REDIS_PREFIX}jobs:completed" 2>/dev/null || echo "0")
local failed_jobs=$(redis-cli -a "$REDIS_PASSWORD" SCARD "${REDIS_PREFIX}jobs:failed" 2>/dev/null || echo "0")
echo "Momentry Jobs: Active=$active_jobs, Completed=$completed_jobs, Failed=$failed_jobs"
echo "$active_jobs $completed_jobs $failed_jobs"
}
# 檢查特定 Job 進度
check_job_progress() {
local job_uuid=$1
if [ -z "$job_uuid" ]; then
echo "Usage: $0 progress <uuid>"
return 1
fi
local progress=$(redis-cli -a "$REDIS_PASSWORD" HGETALL "${REDIS_PREFIX}job:$job_uuid" 2>/dev/null)
if [ -n "$progress" ]; then
echo "Job $job_uuid progress:"
echo "$progress" | while read -r key value; do
[ -n "$key" ] && echo " $key: $value"
done
else
echo "No progress data for job $job_uuid"
fi
}
# 訂閱即時進度頻道
subscribe_progress() {
local job_uuid=$1
if [ -z "$job_uuid" ]; then
echo "Subscribing to all progress channels..."
redis-cli -a "$REDIS_PASSWORD" PSUBSCRIBE "${REDIS_PREFIX}progress:*" 2>/dev/null
else
echo "Subscribing to job $job_uuid progress..."
redis-cli -a "$REDIS_PASSWORD" SUBSCRIBE "${REDIS_PREFIX}progress:$job_uuid" 2>/dev/null
fi
}
# 列出所有活動 Job
list_active_jobs() {
echo "Active Jobs:"
redis-cli -a "$REDIS_PASSWORD" SMEMBERS "${REDIS_PREFIX}jobs:active" 2>/dev/null | while read -r uuid; do
[ -n "$uuid" ] && echo " - $uuid"
done
}
# 主程序
case "$1" in
status)
check_job_status
;;
progress)
check_job_progress "$2"
;;
subscribe)
subscribe_progress "$2"
;;
list)
list_active_jobs
;;
*)
echo "Usage: $0 {status|progress <uuid>|subscribe [uuid]|list}"
exit 1
;;
esac
```
### 4.3 監控腳本排程
使用 cron 定期執行健康檢查:
```bash
# crontab -e
# 每 5 分鐘執行一次健康檢查
*/5 * * * * /Users/accusys/momentry/scripts/health_check.sh >> /Users/accusys/momentry/log/health_check.log 2>&1
```
### 4.4 監控數據記錄
健康檢查結果應寫入監控數據庫,建議的 `monitor_services` 表結構:
```sql
CREATE TABLE monitor_services (
id SERIAL PRIMARY KEY,
service_name VARCHAR(50) NOT NULL,
status VARCHAR(20) NOT NULL, -- up, down, warning
error_message TEXT,
checked_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_monitor_services_checked ON monitor_services(checked_at);
CREATE INDEX idx_monitor_services_name ON monitor_services(service_name);
```
---
## 5. 配置更新
### 5.1 環境變數
必需監控相關環境變數配置在 `.env` 或系統中:
```bash
# Redis 連接
REDIS_URL=redis://localhost:6379
REDIS_PASSWORD=accusys
# Redis Key Prefix (可選,預設: momentry:)
REDIS_PREFIX=momentry:
# SFTPGo 管理員密碼 (用於 API 健康檢查)
SFTPGO_ADMIN_PASSWORD=Test3200Test3200
# Momentry Core 路徑 (可選,如果不在標準位置)
MOMENTRY_BINARY_PATH="/Users/accusys/momentry/target/release/momentry"
```
### 5.2 監控配置範例
**檔案**: `monitor/config/monitor_config.yaml`
```yaml
services:
# Redis - 消息隊列與狀態存儲
- name: "redis"
type: "tcp"
host: "localhost"
port: 6379
timeout: 3
enabled: true
# PostgreSQL - 數據庫
- name: "postgresql"
type: "tcp"
host: "localhost"
port: 5432
timeout: 5
enabled: true
check_sql: "SELECT 1;" # 可選:執行 SQL 驗證
# n8n - 工作流引擎
- name: "n8n"
type: "http"
host: "localhost"
port: 5678
check_url: "http://localhost:5678/healthz"
timeout: 5
enabled: true
# SFTPGo - 文件上傳服務
- name: "sftpgo"
type: "http"
host: "localhost"
port: 8080
check_url: "http://localhost:8080/healthz"
timeout: 5
enabled: true
# 附加認證檢查(每小時一次)
auth_check:
endpoint: "/api/v2/token"
method: "GET"
username: "admin"
password_env: "SFTPGO_ADMIN_PASSWORD"
# Qdrant - 向量數據庫
- name: "qdrant"
type: "http"
host: "localhost"
port: 6333
check_url: "http://localhost:6333/collections"
timeout: 5
enabled: true
# Momentry Core CLI
- name: "momentry_core"
type: "cli"
binary_path: "/Users/accusys/momentry/target/release/momentry"
args: ["--version"]
timeout: 3
enabled: true
```
### 5.3 備份配置更新
統一備份系統 (`backup_all.sh`) 已包含 SFTPGo 備份,無需額外配置。
備份保留策略 (預設):
- **每日備份**: 保留 7 天
- **每週備份**: 保留 4 週
- **每月備份**: 保留 12 個月
備份存儲位置:
- 配置文件: `/Users/accusys/momentry/backup/daily/<service>/`
- 最新備份鏈接: `/Users/accusys/momentry/backup/latest/` (由 backup_monitor.sh 管理)
### 5.4 監控腳本配置
建立符號鏈接到監控腳本目錄:
```bash
ln -sf /Users/accusys/momentry/scripts/backup_all.sh /usr/local/bin/backup_all
ln -sf /Users/accusys/momentry/scripts/health_check.sh /usr/local/bin/health_check
```
這樣可以在任何地方執行:
```bash
health_check
backup_all status
backup_all restore sftpgo 20260321_101928
```
---
## 6. 報警規則
| 層級 | 異常類型 | 等級 | 處理 |
|------|----------|------|------|
| Service | PostgreSQL 未運行 | Critical | 記錄 + 通知 |
| Service | Redis 未運行 | Critical | 記錄 + 通知 |
| Service | n8n API 無回應 | Critical | 記錄 + 通知 |
| Service | SFTPGo API 無回應 | Critical | 記錄 + 通知 |
| Service | Qdrant 未運行 | Critical | 記錄 + 通知 |
| Service | Momentry CLI 缺失 | Critical | 記錄 + 通知 |
| Database | 關鍵資料庫不存在 | Warning | 記錄 |
| Backup | 備份失敗 | Critical | 記錄 |
| Backup | 備份過期 | Warning | 記錄 |
| Job | 處理失敗 | Warning | 記錄 |
| Job | 處理超時 | Warning | 記錄 |
### 6.1 資料庫缺失處理
當檢查到以下資料庫不存在時,應記錄警告:
- `momentry` - Momentry Core 主資料庫
- `video_register` - 視頻註冊資料庫
- `n8n` - n8n 工作流資料庫
- `sftpgo` - SFTPGo 資料庫
**處理程序**:
1. 確認是否為首次安裝(資料庫尚未建立)
2. 檢查備份並執行還原
3. 如果是意外刪除,立即從最新備份恢復
### 6.2 SFTPGo 監控特殊注意
SFTPGo 使用雙重認證檢查:
1. **健康檢查**: `/healthz` (無需認證)
2. **API 可用性**: `/api/v2/token` (需要 Basic Auth)
`/healthz` 正常但 `/api/v2/token` 失敗,可能是:
- admin 密碼被重置
- 數據庫連接問題
- API 配置錯誤
應立即檢查:
```bash
tail -20 /Users/accusys/momentry/log/sftpgo.log
tail -20 /Users/accusys/momentry/log/sftpgo.error.log
```
---
## 7. 數據庫記錄
### monitor_jobs 表
```sql
CREATE TABLE monitor_jobs (
id SERIAL PRIMARY KEY,
uuid VARCHAR(16) NOT NULL,
video_path VARCHAR(512),
status VARCHAR(20),
current_processor VARCHAR(20),
progress_total INT,
progress_current INT,
error_count INT DEFAULT 0,
last_error TEXT,
started_at TIMESTAMP,
updated_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_monitor_jobs_uuid ON monitor_jobs(uuid);
CREATE INDEX idx_monitor_jobs_status ON monitor_jobs(status);
CREATE INDEX idx_monitor_jobs_created_at ON monitor_jobs(created_at);
```
### processor_results 表
```sql
CREATE TABLE processor_results (
id SERIAL PRIMARY KEY,
job_id INTEGER REFERENCES monitor_jobs(id) ON DELETE CASCADE,
video_id BIGINT REFERENCES videos(id),
processor VARCHAR(20) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
started_at TIMESTAMP,
completed_at TIMESTAMP,
error_message TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_processor_results_job ON processor_results(job_id);
CREATE INDEX idx_processor_results_video ON processor_results(video_id);
CREATE INDEX idx_processor_results_status ON processor_results(status);
```
**processor 狀態值**:
- `pending` - 等待處理
- `running` - 處理中
- `completed` - 已完成
- `failed` - 處理失敗
- `skipped` - 跳過(已在其他處理中完成)
---
## 8. 環境變數
```bash
# 輸出目錄
MOMENTRY_OUTPUT_DIR=/path/to/output
# 備份
MOMENTRY_BACKUP_ENABLED=true
MOMENTRY_BACKUP_DIR=/Users/accusys/momentry/backup/momentry
# Redis
REDIS_URL=redis://localhost:6379
REDIS_PASSWORD=accusys
```
---
## 9. 待實作項目
| # | 項目 | 狀態 | 更新日期 |
|---|------|------|---------|
| 1 | 實作 Redis Pub/Sub 客戶端 | ✅ 已實作 | 2026-03-21 |
| 2 | 修改 Python processors 使用 Redis | ✅ 已實作 | 2026-03-21 |
| 3 | 設計並實現 health_check.sh | ✅ 已實作 | 2026-03-22 |
| 4 | 創建 monitor_jobs 表 | ✅ 已實作 | 2026-03-21 |
| 5 | SFTPGo 備份與還原流程 | ✅ 已實作 | 2026-03-22 |
| 6 | SFTPGo API 管理工具 | ✅ 已實作 | 2026-03-22 |
| 7 | SFTPGo Hook 自動註冊 | ✅ 已實作 | 2026-03-22 |
| 8 | 文檔化監控規範 | ✅ 已實作 | 2026-03-22 |
| 9 | Job Worker 輪詢機制 | ✅ 已實作 | 2026-03-25 |
| 10 | processor_results 表 | ✅ 已實作 | 2026-03-25 |
| 11 | Probe API 端點 | ✅ 已實作 | 2026-03-25 |
| 12 | 整合測試 | 🔜 待實作 | - |
| 13 | 生產環境部署驗證 | ⏳ 待開始 | - |
### Job Worker 監控 (2026-03-25 新增)
**Worker 服務狀態檢查**:
```bash
# 檢查 Worker 程序
ps aux | grep momentry
# 查看 Worker 日誌
tail -f /Users/accusys/momentry/log/worker.log
```
**monitor_jobs 狀態查詢**:
```bash
# 查看待處理工作
psql -U accusys -d momentry -c "SELECT * FROM monitor_jobs WHERE status = 'pending';"
# 查看執行中工作
psql -U accusys -d momentry -c "SELECT * FROM monitor_jobs WHERE status = 'running';"
# 查看失敗工作
psql -U accusys -d momentry -c "SELECT * FROM monitor_jobs WHERE status = 'failed';"
```
**processor_results 狀態查詢**:
```bash
# 查看特定工作的處理器狀態
psql -U accusys -d momentry -c "
SELECT pr.*, mj.uuid
FROM processor_results pr
JOIN monitor_jobs mj ON pr.job_id = mj.id
WHERE mj.uuid = 'a1b10138a6bbb0cd';
"
# 查看所有失敗的處理器
psql -U accusys -d momentry -c "
SELECT pr.processor, COUNT(*) as failures
FROM processor_results pr
WHERE pr.status = 'failed'
GROUP BY pr.processor;
"
```
**Redis 工作狀態**:
```bash
# 查看活躍工作
redis-cli SMEMBERS momentry:jobs:active
# 查看工作詳情
redis-cli HGETALL momentry:job:{uuid}
```
### 已完成實作 (2026-03-22)
**監控系統**:
- 完整健康檢查腳本設計: `docs/MOMENTRY_CORE_MONITORING.md`
- 多層次服務監控 (Layer 2: Service, Layer 7: Backup)
- Redis Job 監控腳本: `monitor/service/redis_job_monitor.sh`
- SFTPGo 特殊監控 (API 認證檢查)
**SFTPGo 管理**:
- 備份還原機制: `backup_all.sh` (第 325-546 行)
- API 管理用戶與組 (完整文件於 `docs/INSTALL_SFTPGO.md`)
- Hook 自動註冊流程: `/Users/accusys/sftpgo_test/register_hook.sh`
- Demo 用戶與組完整測試環境
**文檔更新**:
- `docs/INSTALL_SFTPGO.md`: 新增備份還原、API管理、Hook配置章節
- `docs/MOMENTRY_CORE_MONITORING.md`: 完善監控規範
### 待驗證功能
- [ ] 端到端測試: SFTP 上傳 → Hook → Momentry 註冊 → n8n 工作流
- [ ] Momentry Core API 搜索功能: `GET /api/v1/searchable`
- [ ] 背景處理自動觸發: `cargo run -- process <uuid>`
---
## 10. 參考文檔
- [Redis Key 設計規範](./MOMENTRY_CORE_REDIS_KEYS.md)
- [監控系統總覽](../monitor/MONITORING.md)
- [備份規範](./SERVICE_ADDITION_GUIDE.md)
- [SFTPGo 安裝與管理指南](./INSTALL_SFTPGO.md)
- [API 參考文件](../docs/API_REFERENCE.md)
- [n8n 整合指南](./N8N_INTEGRATION_GUIDE.md)

View File

@@ -1,283 +0,0 @@
# Momentry Core Redis Key 設計規範
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-17 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-17 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
| V1.1 | 2026-03-25 | 新增可配置 Redis Key Prefix | Warren | OpenCode / GLM-5 |
---
## 1. 概述
本文檔說明 momentry_core 如何使用 Redis 作為監控和狀態管理系統。
## 2. 可配置 Redis Key Prefix
### 2.1 環境變數
從 V1.1 開始,所有 Redis Keys 都支援自定義前綴:
```bash
MOMENTRY_REDIS_PREFIX=momentry:
```
此設定允許多個 momentry 實例共用同一個 Redis 伺服器,例如:
- **生產環境**: `MOMENTRY_REDIS_PREFIX=momentry:`
- **開發環境**: `MOMENTRY_REDIS_PREFIX=momentry_dev:`
### 2.2 Key 格式
所有 Key 都遵循以下格式:
```
{prefix}{key_type}:{uuid}
```
範例 (生產環境):
```
momentry:job:5dea6618a606e7c7
momentry:jobs:active
momentry:health:current
```
範例 (開發環境):
```
momentry_dev:job:5dea6618a606e7c7
momentry_dev:jobs:active
momentry_dev:health:current
```
### 2.3 預設值
| Binary | 預設 Port | 預設 Redis Prefix |
|--------|-----------|-------------------|
| `momentry` (生產) | 3002 | `momentry:` |
| `momentry_playground` (開發) | 3003 | `momentry_dev:` |
## 3. UUID 使用時機
### 3.1 全局 Keys無 UUID
- 單一實例全局狀態
- 聚合統計數據
### 3.2 Per-Video KeysUUID 必要)
- 每個視頻獨立處理狀態
- 即時進度追蹤
### 3.3 Per-Processor KeysUUID + Processor 必要)
- 每個 processor 獨立狀態
## 4. Key 命名空間
```
momentry
├── health: # 健康檢查
│ ├── current # 當前狀態 (TTL: 60s)
│ └── services # 依賴服務狀態
├── config: # 配置
├── stats: # 聚合統計
│ ├── total_jobs # 總 Jobs 數
│ ├── processed_today # 今日處理數
│ ├── cpu:current # 當前 CPU 使用
│ └── memory:current # 當前 Memory 使用
├── jobs: # Jobs 管理
│ ├── active # Set: 運行中 UUIDs
│ ├── completed # Set: 完成 UUIDs
│ └── failed # Set: 失敗 UUIDs
├── job:{uuid} # Per-Video Job 狀態 (TTL: 24h)
│ ├── status # 狀態 String
│ ├── video_path # 視頻路徑
│ ├── current_processor # 當前 processor
│ ├── progress_total # 總進度
│ ├── progress_current # 當前進度
│ ├── started_at # 開始時間
│ ├── updated_at # 最後更新
│ └── processor:{name} # Per-Processor 狀態
│ ├── status
│ ├── progress
│ ├── current
│ ├── total
│ └── started_at
├── progress:{uuid} # Pub/Sub 頻道 (即時進度)
├── result:{uuid} # 處理結果 Hash
├── output:{uuid} # 輸出路徑
├── metrics:{uuid} # Per-Video 指標
│ ├── cpu # CPU 歷史 List (100條, TTL: 1h)
│ ├── memory # Memory 歷史 List (100條, TTL: 1h)
│ └── duration # 處理時長
└── log:{uuid} # 處理日誌 String
```
## 5. Key 詳細說明
### 全局 Keys
| Key | Type | TTL | 說明 |
|-----|------|-----|------|
| `momentry:health:current` | String | 60s | 當前健康狀態 |
| `momentry:health:services` | Hash | 60s | 依賴服務健康狀態 |
| `momentry:stats:total_jobs` | String | - | 總 Jobs 數 |
| `momentry:stats:processed_today` | String | 86400s | 今日處理數 |
| `momentry:stats:cpu:current` | String | 10s | 當前 CPU 使用 |
| `momentry:stats:memory:current` | String | 10s | 當前 Memory 使用 |
| `momentry:jobs:active` | Set | - | 運行中 Job UUIDs |
| `momentry:jobs:completed` | Set | - | 完成 Job UUIDs |
| `momentry:jobs:failed` | Set | - | 失敗 Job UUIDs |
### Per-Video Keys
| Key | Type | TTL | 說明 |
|-----|------|-----|------|
| `momentry:job:{uuid}` | Hash | 24h | Job 完整狀態 |
| `momentry:job:{uuid}:status` | String | 24h | Job 狀態 |
| `momentry:progress:{uuid}` | Pub/Sub | - | 即時進度頻道 |
| `momentry:result:{uuid}` | Hash | 24h | 處理結果 |
| `momentry:output:{uuid}` | String | 24h | 輸出路徑 |
| `momentry:metrics:{uuid}:cpu` | List | 1h | CPU 歷史 (100條) |
| `momentry:metrics:{uuid}:memory` | List | 1h | Memory 歷史 (100條) |
| `momentry:metrics:{uuid}:duration` | String | 24h | 處理時長 |
| `momentry:log:{uuid}` | String | 24h | 處理日誌 |
### Per-Processor Keys
| Key | Type | TTL | 說明 |
|-----|------|-----|------|
| `momentry:job:{uuid}:processor:{name}` | Hash | 24h | Processor 狀態 |
| `momentry:job:{uuid}:processor:{name}:status` | String | 24h | 狀態 |
| `momentry:job:{uuid}:processor:{name}:progress` | String | 24h | 進度百分比 |
| `momentry:job:{uuid}:processor:{name}:current` | String | 24h | 當前項目 |
| `momentry:job:{uuid}:processor:{name}:total` | String | 24h | 總項目數 |
| `momentry:job:{uuid}:processor:{name}:started_at` | String | 24h | 開始時間 |
## 6. TTL 策略
| Key 類型 | TTL | 原因 |
|----------|-----|------|
| Health | 60s | 需要定期更新 |
| Job | 24h | 處理完成後保留一天 |
| Processor | 24h | 處理完成後保留一天 |
| Metrics | 1h | 只保留近期歷史 |
| Progress Pub/Sub | - | 不持久,僅即時訊息 |
| Stats | 無 | 持久統計 |
## 7. 訊息格式
### Pub/Sub 訊息 (progress:{uuid})
```json
{
"type": "info | progress | complete | error",
"processor": "yolo | ocr | face | pose | cut | asr | asrx",
"timestamp": 1700000000,
"data": {
"message": "Processing frame 5000",
"current": 5000,
"total": 14315
}
}
```
### Job 狀態 Hash
```json
{
"uuid": "5dea6618a606e7c7",
"video_path": "/path/to/video.mp4",
"status": "running",
"current_processor": "yolo",
"progress_total": 70,
"progress_current": 50,
"started_at": 1700000000,
"updated_at": 1700000100,
"error_count": 0,
"last_error": ""
}
```
### Processor 狀態 Hash
```json
{
"name": "yolo",
"status": "running",
"progress": 70,
"current_frame": 10000,
"total_frames": 14315,
"started_at": 1700000000,
"updated_at": 1700000100
}
```
## 8. 實作函數 (Rust)
所有 Redis Key 生成函數使用 `REDIS_KEY_PREFIX` 靜態變數:
```rust
use crate::core::config::REDIS_KEY_PREFIX;
fn global_key(key: &str) -> String {
format!("{}{}", REDIS_KEY_PREFIX, key)
}
fn job_key(uuid: &str) -> String {
format!("{}job:{}", REDIS_KEY_PREFIX, uuid)
}
fn processor_key(uuid: &str, processor: &str) -> String {
format!("{}job:{}:processor:{}", REDIS_KEY_PREFIX, uuid, processor)
}
fn progress_channel(uuid: &str) -> String {
format!("{}progress:{}", REDIS_KEY_PREFIX, uuid)
}
fn metrics_key(uuid: &str, metric: &str) -> String {
format!("{}metrics:{}:{}", REDIS_KEY_PREFIX, uuid, metric)
}
fn jobs_set_key(status: &str) -> String {
format!("{}jobs:{}", REDIS_KEY_PREFIX, status)
}
```
**注意**: `REDIS_KEY_PREFIX` 定義於 `src/core/config.rs`,由環境變數 `MOMENTRY_REDIS_PREFIX` 控制。
## 9. 環境變數
```bash
# Redis 連接
REDIS_URL=redis://localhost:6379
REDIS_PASSWORD=accusys
REDIS_DB=0
# Redis Key Prefix (可選,預設: momentry:)
MOMENTRY_REDIS_PREFIX=momentry:
# 生產環境範例 (.env)
MOMENTRY_SERVER_PORT=3002
MOMENTRY_REDIS_PREFIX=momentry:
# 開發環境範例 (.env.development)
MOMENTRY_SERVER_PORT=3003
MOMENTRY_REDIS_PREFIX=momentry_dev:
```
## 11. 監控腳本
使用 Redis 進行監控的腳本應參考:
- `monitor/service/momentry_redis_monitor.sh` - Redis 健康檢查
- `monitor/service/momentry_job_monitor.sh` - Job 狀態監控

View File

@@ -1,334 +0,0 @@
# Momentry Core 影片 RAG 系統說明稿
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-22 |
| 文件版本 | V1.1 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-22 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
| V1.1 | 2026-03-25 | 更新API回應格式 (media_url→file_path) 與認證標頭 | OpenCode | deepseek-reasoner |
---
## 系統架構
```
┌─────────────────────────────────────────────────────────────┐
│ 使用者 │
│ (marcom 團隊) │
└─────────────────┬───────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ WordPress 入口 │
│ (wp.momentry.ddns.net) │
└─────────────────┬───────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ n8n 自動化 │
│ (localhost:5678) │
│ │
│ [Webhook] → [HTTP Request] → [處理結果] → [回覆用戶] │
└─────────────────┬───────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Momentry Core API │
│ (localhost:3002) │
│ │
│ POST /api/v1/search → 語意搜尋 │
│ POST /api/v1/n8n/search → n8n 專用格式 │
│ GET /api/v1/videos → 影片列表 │
└─────────────────┬───────────────────────────────────────────┘
┌─────────┴──────────┐
▼ ▼
┌───────────────┐ ┌───────────────┐
│ PostgreSQL │ │ Qdrant │
│ (chunks) │ │ (vectors) │
└───────────────┘ └───────────────┘
```
---
## 資料流程
```
1. 上傳影片 → SFTPGo
2. 影片註冊 → PostgreSQL
3. ASR 處理 → 產生字幕區塊
4. 儲存 chunks → PostgreSQL
5. 向量化 → Qdrant
6. 搜尋查詢 → API
7. 回傳結果 → n8n → 用戶
```
---
## 示範影片
| 項目 | 內容 |
|------|------|
| 檔案名稱 | Old_Time_Movie_Show_-_Charade_1963.HD.mov |
| UUID | a1b10138a6bbb0cd |
| 時長 | 6879 秒(約 1.9 小時) |
| 區塊數 | 3,886 個 |
| 向量數 | 3,688 個 |
---
## API 端點
### 1. 語意搜尋
```
POST http://localhost:3002/api/v1/search
```
**請求:**
```json
{
"query": "charade",
"limit": 5,
"uuid": "a1b10138a6bbb0cd"
}
```
> **注意**:
> 1. **API 認證**: 所有 `/api/v1/*` 端點需要 `X-API-Key` 標頭
> 2. **檔案路徑轉換**: API 現在返回 `file_path`(檔案系統路徑),需要轉換為可訪問的 URL例如透過 SFTPGo 分享連結)
---
### 2. n8n 專用格式
```
POST http://localhost:3002/api/v1/n8n/search
```
**請求:**
```json
{
"query": "charade",
"limit": 5
}
```
**回應:**
```json
{
"query": "charade",
"count": 5,
"hits": [
{
"id": "sentence_0006",
"vid": "a1b10138a6bbb0cd",
"start": 48.8,
"end": 55.44,
"title": "Chunk sentence_0006",
"text": "fun plot twists...",
"score": 0.526,
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
}
]
}
```
---
## 實作範例
### n8n Workflow 設計
```
┌─────────────┐
│ Webhook │ ← 接收用戶搜尋請求
└──────┬──────┘
┌─────────────┐
│ HTTP Request│ → POST /api/v1/n8n/search
└──────┬──────┘
┌─────────────┐
│ Code │ → 處理回傳結果
└──────┬──────┘
┌─────────────┐
│ Telegram │ → 回覆給用戶
│ (或 LINE) │
└─────────────┘
```
---
## Step-by-Step n8n Workflow
### Step 1: 建立 Webhook
1. n8n 開新 Workflow
2. 新增 node: **Webhook**
3. 設定 path: `video-search`
4. 複製 Webhook URL
---
### Step 2: 設定 HTTP Request
1. 新增 node: **HTTP Request**
2. 設定:
```
Method: POST
URL: http://localhost:3002/api/v1/n8n/search
Body Content Type: JSON
Headers: X-API-Key (需設定)
```
3. Body:
```json
{
"query": "={{ $json.body }}",
"limit": 5
}
```
---
### Step 3: 處理結果 (Code)
```javascript
const hits = $input.first().json.hits;
if (!hits || hits.length === 0) {
return {
json: { message: "找不到相關結果" }
};
}
const results = hits.map((hit, index) => ({
number: index + 1,
text: hit.text,
time: `${hit.start}s - ${hit.end}s`,
score: Math.round(hit.score * 100) + "%",
// 注意: API 現在返回 file_path檔案系統路徑需要轉換為可訪問的 URL
url: hit.file_path + "#t=" + hit.start + "," + hit.end // 需實作檔案路徑轉換為 URL
}));
return { json: { results } };
```
> **注意**:
> 1. **API 認證**: 所有 `/api/v1/*` 端點需要 `X-API-Key` 標頭
> 2. **檔案路徑轉換**: API 現在返回 `file_path`(檔案系統路徑),需要轉換為可訪問的 URL例如透過 SFTPGo 分享連結)
---
### Step 4: 格式化輸出
**Telegram 格式:**
```
🎬 搜尋結果: "{{ $json.query }}"
1⃣ "fun plot twists, Woody Dialog and charming performances..."
⏱ 48.8s - 55.4s
📊 相關度: 53%
2⃣ "Don't you like me to say that a pretty girl..."
⏱ 4745.6s - 4748.6s
📊 相關度: 52%
```
---
## 測試指令
### curl 測試
```bash
# 語意搜尋
curl -X POST http://localhost:3002/api/v1/search \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"query": "charade", "limit": 3}'
# n8n 格式
curl -X POST http://localhost:3002/api/v1/n8n/search \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"query": "charade", "limit": 3}'
# 影片列表
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos
# 特定影片區塊
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos/a1b10138a6bbb0cd/chunks
```
---
## 實際搜尋範例
| 搜尋詞 | 結果摘要 |
|--------|----------|
| `charade` | "fun plot twists, Woody Dialog and charming performances..." |
| `woody` | "Well, you thick skull hair, brain half-witted..." |
| `classic movie` | "Hello and welcome to the old-time movie show..." |
| `charming` | "fun plot twists, Woody Dialog and charming performances..." |
---
## 資料庫狀態
| 資料庫 | 資料筆數 | 狀態 |
|--------|----------|------|
| PostgreSQL (videos) | 4 | ✅ |
| PostgreSQL (chunks) | 3,950 | ✅ |
| PostgreSQL (vectors) | 1,870 | ✅ |
| Qdrant (vectors) | 3,688 | ✅ |
| Redis (job cache) | 4 keys | ✅ |
---
## 下一步
1. **建立 SFTPGo 分享連結**
- 開啟 http://localhost:8080
- 登入 demo / demopassword123
- 建立影片分享連結
2. **測試 n8n Workflow**
- 匯入 Postman Collection
- 建立 Webhook
- 測試搜尋
3. **整合到 WordPress**
- 建立表單接收用戶輸入
- 呼叫 n8n Webhook
- 顯示搜尋結果
---
## 快速開始
```bash
# 1. 測試搜尋 API
curl -X POST http://localhost:3002/api/v1/search \
-H "Content-Type: application/json" \
-d '{"query": "charade", "limit": 3}'
# 2. 查看影片列表
curl http://localhost:3002/api/v1/videos
# 3. 查看 n8n 是否運行
curl http://localhost:5678
```

View File

@@ -1,127 +0,0 @@
{
"info": {
"name": "Momentry Core API",
"description": "Video RAG API for Momentry Core - Video search and management",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"variable": [
{
"key": "baseUrl",
"value": "http://localhost:3002/api/v1"
}
],
"item": [
{
"name": "Search",
"item": [
{
"name": "Semantic Search",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"query\": \"charade\",\n \"limit\": 5\n}"
},
"url": {
"raw": "{{baseUrl}}/search",
"host": ["{{baseUrl}}"],
"path": ["search"]
},
"description": "Semantic search across video chunks using vector embeddings"
}
},
{
"name": "Search with UUID Filter",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"query\": \"charade\",\n \"limit\": 5,\n \"uuid\": \"a1b10138a6bbb0cd\"\n}"
},
"url": {
"raw": "{{baseUrl}}/search",
"host": ["{{baseUrl}}"],
"path": ["search"]
},
"description": "Search within a specific video by UUID"
}
},
{
"name": "n8n Integration Search",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"query\": \"charade\",\n \"limit\": 5\n}"
},
"url": {
"raw": "{{baseUrl}}/n8n/search",
"host": ["{{baseUrl}}"],
"path": ["n8n", "search"]
},
"description": "Search formatted for n8n workflow integration"
}
}
]
},
{
"name": "Videos",
"item": [
{
"name": "List All Videos",
"request": {
"method": "GET",
"url": {
"raw": "{{baseUrl}}/videos",
"host": ["{{baseUrl}}"],
"path": ["videos"]
},
"description": "Get list of all registered videos"
}
},
{
"name": "Get Video by UUID",
"request": {
"method": "GET",
"url": {
"raw": "{{baseUrl}}/videos/a1b10138a6bbb0cd",
"host": ["{{baseUrl}}"],
"path": ["videos", "a1b10138a6bbb0cd"]
},
"description": "Get details for a specific video"
}
},
{
"name": "Get Video Chunks",
"request": {
"method": "GET",
"url": {
"raw": "{{baseUrl}}/videos/a1b10138a6bbb0cd/chunks",
"host": ["{{baseUrl}}"],
"path": ["videos", "a1b10138a6bbb0cd", "chunks"]
},
"description": "Get all chunks for a video"
}
}
]
}
]
}

View File

@@ -1,106 +0,0 @@
# n8n REST API Fix Summary
## Issue
n8n REST API was returning 404 errors for all endpoints (`/api/v1/workflows`, `/rest/workflows`, etc.)
## Root Cause
Port 5678 was occupied by the **n8n worker** process, preventing the main n8n instance from starting properly.
## Solution
### 1. Identified Port Conflict
- Worker process was listening on port 5678 (same as main instance)
- Main n8n couldn't start because port was in use
### 2. Fixed Worker Configuration
Updated `/Library/LaunchDaemons/com.momentry.n8n.worker.plist`:
- Added `N8N_PORT=5680` to worker environment variables
- Workers shouldn't need HTTP ports, but this prevents port conflict
### 3. Restarted Services
```bash
# Kill all n8n processes
sudo pkill -9 -f n8n
# Start main n8n (now successfully binds to port 5678)
sudo launchctl enable system/com.momentry.n8n.main
sudo launchctl bootstrap system /Library/LaunchDaemons/com.momentry.n8n.main.plist
```
## Current Status
### n8n Instance
- **URL**: http://localhost:5678
- **Version**: 2.3.5
- **Status**: Running ✅
- **API Enabled**: Yes ✅
### API Key
```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJlNjdiY2UzOS1iY2RkLTRjMjEtYmMwYy0yODNhYmI3ZjVjMjMiLCJpc3MiOiJuOG4iLCJhdWQiOiJwdWJsaWMtYXBpIiwiaWF0IjoxNzc0MTk4NzgwfQ.zke_Qc-saILl_tcwXm2K3J4slCmaXnzCfxVbdVPPvCE
```
### MCP Configuration
File: `~/.config/opencode/opencode.json`
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"gitea": {
"type": "local",
"enabled": true,
"command": [
"/opt/homebrew/bin/gitea-mcp-server",
"-token", "<GITEA_TOKEN>",
"-host", "http://localhost:3000"
]
},
"n8n": {
"type": "local",
"enabled": true,
"command": ["/opt/homebrew/bin/mcp-n8n"],
"environment": {
"N8N_BASE_URL": "http://localhost:5678",
"N8N_API_KEY": "<N8N_API_KEY>"
}
}
}
}
```
## Verified Endpoints
### List Workflows
```bash
curl -H "X-N8N-API-KEY: <API_KEY>" http://localhost:5678/api/v1/workflows
```
**Result**: ✅ 30 workflows returned
### List Executions
```bash
curl -H "X-N8N-API-KEY: <API_KEY>" http://localhost:5678/api/v1/executions
```
**Result**: ✅ 100 executions returned
## Next Steps
1. **Start n8n Worker** (optional for MCP):
Workers handle job processing but aren't required for API access.
2. **Test MCP Integration**:
Restart OpenCode to load the MCP configuration and test n8n integration.
3. **Verify Workflow Management**:
- Create workflow via API
- Execute workflow
- Monitor execution status
## Files Modified
- `/Library/LaunchDaemons/com.momentry.n8n.worker.plist` - Added N8N_PORT=5680
## API Documentation
- Base URL: `http://localhost:5678/api/v1`
- Authentication: Header `X-N8N-API-KEY: <token>`
- Available endpoints: workflows, executions, credentials, users, etc.
See full API reference: https://docs.n8n.io/api/

View File

@@ -1,249 +0,0 @@
# n8n 整合範例
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-18 |
| 文件版本 | V1.1 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-18 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
| V1.1 | 2026-03-25 | 更新API回應格式 (media_url→file_path) 與認證標頭 | OpenCode | deepseek-reasoner |
---
## 基本設定
### API 端點
- **Base URL:** `http://localhost:3002/api/v1`
- **Method:** `POST`
- **Content-Type:** `application/json`
- **Authentication:** `X-API-Key: YOUR_API_KEY` (所有 `/api/v1/*` 端點皆需要)
---
## Workflow 1: 基礎搜尋
### Trigger: Manual / Webhook
```
[Manual Trigger]
[HTTP Request] → POST http://localhost:3002/api/v1/search
[Set] → 設定搜尋詞 "charade"
[Code] → 處理回傳結果
[Respond]
```
### HTTP Request 設定
```json
{
"url": "http://localhost:3002/api/v1/search",
"method": "POST",
"body": {
"query": "={{ $json.searchTerm }}",
"limit": 5
},
"options": {
"headers": {
"Content-Type": "application/json",
"X-API-Key": "YOUR_API_KEY"
}
}
}
```
### Code (處理結果)
```javascript
const results = $input.first().json.results;
const videoUrl = "https://wp.momentry.ddns.net/Old_Time_Movie_Show_-_Charade_1963.HD.mov";
return results.map(r => ({
chunk_id: r.chunk_id,
text: r.text,
start: r.start_time,
end: r.end_time,
score: r.score,
video_url: `${videoUrl}#t=${r.start_time},${r.end_time}`
}));
```
---
## Workflow 2: n8n 專用格式
使用 `/n8n/search` 端點(已包含 file_path
### HTTP Request
```json
{
"url": "http://localhost:3002/api/v1/n8n/search",
"method": "POST",
"body": {
"query": "={{ $json.searchTerm }}",
"limit": 5
},
"options": {
"headers": {
"Content-Type": "application/json",
"X-API-Key": "YOUR_API_KEY"
}
}
}
```
### 回傳格式
```json
{
"query": "charade",
"count": 5,
"hits": [
{
"id": "sentence_0006",
"vid": "a1b10138a6bbb0cd",
"start": 48.8,
"end": 55.44,
"title": "Chunk sentence_0006",
"text": "fun plot twists...",
"score": 0.526,
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
}
]
}
```
> **注意**: API 現在返回 `file_path`(檔案系統路徑)而非 `media_url`(網頁 URL。如需在網頁中播放影片請將檔案路徑轉換為可訪問的 URL例如透過 SFTPGo 分享連結)。
---
## Workflow 3: 訊息機器人整合
### Telegram Bot 範例
```
[Webhook: Telegram]
[Extract: /search charade]
[HTTP Request] → POST /api/v1/search
[Format Response]
[Telegram: Send Message]
```
### 回傳格式
```
🎬 搜尋結果: "charade"
1. "fun plot twists, Woody Dialog and charming performances..."
⏱ 48.8s - 55.4s
📊 分數: 0.526
2. "Don't you like me to say that a pretty girl..."
⏱ 4745.6s - 4748.6s
📊 分數: 0.525
```
---
## Workflow 4: 多影片搜尋
### 取得所有影片
```
[HTTP Request]
GET http://localhost:3002/api/v1/videos
```
### 依 UUID 篩選
```json
{
"query": "charade",
"limit": 5,
"uuid": "a1b10138a6bbb0cd"
}
```
---
## Workflow 5: 定時更新
```
[Cron: 每小時]
[HTTP Request] → GET /api/v1/videos
[Loop Over Items]
[Check: 新影片?]
[Process: 執行 vectorize]
```
---
## 實用場景
### 1. 客服機器人
用戶問「這部片在哪一段有談到 charade
→ 搜尋 API → 回傳時戳 → 直接播放該片段
### 2. 內容推薦
根據用戶輸入的關鍵字,找到相關影片片段
### 3. 自動化剪輯
搜尋多個片段 → 組合成精華影片
---
## 錯誤處理
```javascript
const response = $input.first();
if (!response.json.results || response.json.results.length === 0) {
return {
success: false,
message: "找不到相關結果"
};
}
return {
success: true,
count: response.json.results.length,
data: response.json.results
};
```
---
## 測試用 cURL
```bash
# 基本搜尋
curl -X POST http://localhost:3002/api/v1/search \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"query": "charade", "limit": 3}'
# n8n 格式
curl -X POST http://localhost:3002/api/v1/n8n/search \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"query": "charade", "limit": 3}'
# 取得影片列表
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos
# 取得特定影片的區塊
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos/a1b10138a6bbb0cd/chunks
```

View File

@@ -1,355 +0,0 @@
# n8n Video RAG Demo - API 執行記錄
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-22 |
| 文件版本 | V1.1 |
| 目標 | 完整執行 n8n Video RAG Workflow 並記錄所有 API 呼叫 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-22 | 創建文件 | Warren | OpenCode |
| V1.1 | 2026-03-26 | 更新 API 範例,新增 X-API-Key 驗證標頭 | OpenCode | deepseek-reasoner |
---
## Phase 1: SFTPGo 準備
### Step 1.1: 取得 Demo User Token
**API 呼叫:**
```bash
curl -X GET "http://localhost:8080/api/v2/user/token" \
-u "demo:demopassword123"
```
**Request:**
```
GET /api/v2/user/token
Authorization: Basic ZG9tbzpkZW1vcGFzc3dvcmQxMjM=
```
**Response (200 OK):**
```json
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_at": "2026-03-22T07:05:57Z"
}
```
**Token 有效期限:** 20 分鐘
**Session Token (Demo User):**
```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiQVBJVXNlciIsIjo6MSJdLCJleHAiOjE3NzQxNjMxNTcsImlhdCI6MTc3NDE2MTk1NywianRpIjoiZDZ2cDA5YWcyZnIwMnY3aTlybDAiLCJuYmYiOjE3NzQxNjE5NDcsInN1YiI6IjE3NzQxNjE5NTM0OTMiLCJ1c2VybmFtZSI6ImRlbW8ifQ.yw0UCv8sQXXCkOr7qmK2ejLzuoA8IDrmC9bpgFE4R_Q
```
**結果:** ✅ 成功
---
### Step 1.2: 上傳測試影片到 SFTPGo
**影片選擇:** `Old_Time_Movie_Show_-_Charade_1963.HD.mov` (2.3 GB)
- 路徑: `/Users/accusys/test_video/Old_Time_Movie_Show_-_Charade_1963.HD.mov`
- ASR Segments: 1,917 (已預處理)
**API 呼叫:**
```bash
TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
curl -X POST "http://localhost:8080/api/v2/user/files" \
-H "Authorization: Bearer $TOKEN" \
-F "path=/demo" \
-F "mkdir_parents=true" \
-F "filenames=@/Users/accusys/test_video/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
```
**Request:**
```
POST /api/v2/user/files
Authorization: Bearer <token>
Content-Type: multipart/form-data
path: /demo
mkdir_parents: true
filenames: @/path/to/Old_Time_Movie_Show_-_Charade_1963.HD.mov
```
**Response (201 Created):**
```json
{"message":"Upload completed"}
```
**上傳統計:**
- 檔案大小: 2,361,629,896 bytes (2.3 GB)
- 上傳時間: 7 秒
- 平均速度: ~337 MB/s
**結果:** ✅ 成功
---
### Step 1.3: 建立分享連結
**API 呼叫:**
```bash
curl -X POST "http://localhost:8080/api/v2/user/shares" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Charade_1963_Demo",
"paths": ["/Old_Time_Movie_Show_-_Charade_1963.HD.mov"],
"scope": 1,
"expires_at": 0
}'
```
**Request:**
```json
POST /api/v2/user/shares
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "Charade_1963_Demo",
"paths": ["/Old_Time_Movie_Show_-_Charade_1963.HD.mov"],
"scope": 1,
"expires_at": 0
}
```
**Response (200 OK):**
```json
{"message":"Share created"}
```
**結果:** ✅ 成功
---
### Step 1.4: 驗證上傳結果
**API 呼叫 - 列出分享:**
```bash
curl -X GET "http://localhost:8080/api/v2/user/shares" \
-H "Authorization: Bearer $TOKEN"
```
**Response:**
```json
[
{
"id": "CjmQfrkXY5qDtC46WVZY2S",
"name": "Charade_1963_Demo",
"scope": 1,
"paths": [
"/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
],
"username": "demo",
"created_at": 1774162072853,
"updated_at": 1774162072853,
"password": ""
}
]
```
**分享連結:**
- Share ID: `CjmQfrkXY5qDtC46WVZY2S`
- Browse URL: `http://localhost:8080/web/client/pubshares/CjmQfrkXY5qDtC46WVZY2S/browse`
**本地目錄驗證:**
```
/Users/accusys/sftpgo_test/demo/
└── Old_Time_Movie_Show_-_Charade_1963.HD.mov (2,361,629,896 bytes)
```
**結果:** ✅ 成功
---
## Phase 2: Momentry 註冊
### Step 2.1: 健康檢查
**API 呼叫:**
```bash
curl -X GET "http://localhost:3002/health"
```
**Response:**
```
(待填寫)
```
---
### Step 2.2: 註冊影片
**API 呼叫:**
```bash
curl -X POST "http://localhost:3002/api/v1/register" \
-H "Content-Type: application/json" \
-d '{
"path": "/Users/accusys/sftpgo_test/demo/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
}'
```
**Request:**
```json
POST /api/v1/register
Content-Type: application/json
{
"path": "/Users/accusys/sftpgo_test/demo/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
}
```
**Response:**
```
(待填寫)
{
"uuid": "...",
"video_id": ...,
"file_name": "...",
"duration": ...,
"width": ...,
"height": ...
}
```
---
## Phase 3: 處理進度追蹤
### Step 3.1: 查詢處理進度 (新版 API)
**API 呼叫:**
```bash
curl -X GET "http://localhost:3002/api/v1/progress/{uuid}"
```
**Response (新版 - 包含影片資訊與系統資源):**
```json
{
"uuid": "a1b10138a6bbb0cd",
"user": null,
"group": null,
"file_name": "Old_Time_Movie_Show_-_Charade_1963.HD.mov",
"duration": 6879.33,
"overall_progress": 28,
"cpu_percent": 3.7,
"gpu_percent": null,
"memory_percent": 0.1,
"memory_mb": 19328,
"processors": [
{"name": "asr", "status": "complete", "current": 1867, "total": 0, "progress": 100, "message": "1867 segments"},
{"name": "cut", "status": "complete", "current": 1331, "total": 1331, "progress": 100, "message": "1331 scenes"},
{"name": "asrx", "status": "error", "current": 0, "total": 0, "progress": 0, "message": "0 segments"},
{"name": "yolo", "status": "progress", "current": 69400, "total": 412343, "progress": 16, "message": "frame 69400"},
{"name": "ocr", "status": "pending", "current": 0, "total": 0, "progress": 0, "message": ""},
{"name": "face", "status": "pending", "current": 0, "total": 0, "progress": 0, "message": ""},
{"name": "pose", "status": "pending", "current": 0, "total": 0, "progress": 0, "message": ""}
]
}
```
**欄位說明:**
| 欄位 | 說明 |
|------|------|
| uuid | 影片唯一識別碼 |
| user | 處理所屬用戶 (如已設定) |
| group | 處理所屬群組 (如已設定) |
| file_name | 影片檔案名稱 |
| duration | 影片時長 (秒) |
| overall_progress | 整體進度 (百分比) |
| cpu_percent | CPU 使用率 (%) |
| gpu_percent | GPU 使用率 (%),無 GPU 則為 null |
| memory_percent | 記憶體使用率 (%) |
| memory_mb | 記憶體使用量 (MB) |
| processors | 各處理器狀態陣列 |
```
---
## Phase 4: 自然語言檢索
### Step 4.1: RAG 搜尋
**API 呼叫:**
```bash
curl -X POST "http://localhost:3002/api/v1/search" \
-H "Content-Type: application/json" \
-d '{
"query": "What is the movie about?",
"limit": 10,
"uuid": "..."
}'
```
**Request:**
```json
POST /api/v1/search
Content-Type: application/json
{
"query": "What is the movie about?",
"limit": 10,
"uuid": "<uuid from registration>"
}
```
**Response:**
```
(待填寫)
```
---
### Step 4.2: n8n 搜尋 (含 file_path)
**API 呼叫:**
```bash
curl -X POST "http://localhost:3002/api/v1/n8n/search" \
-H "Content-Type: application/json" \
-H "X-API-Key: demo_api_key_12345" \
-d '{
"query": "What is the movie about?",
"limit": 10,
"uuid": "..."
}'
```
**Response:**
```
(待填寫)
```
---
## 憑證彙整
| 服務 | 項目 | 值 |
|------|------|------|
| SFTPGo | API Base | `http://localhost:8080/api/v2` |
| SFTPGo | Demo User | `demo` |
| SFTPGo | Demo Password | `demopassword123` (已重設) |
| SFTPGo | Demo Home | `/Users/accusys/sftpgo_test/demo` |
| SFTPGo | Token Endpoint | `/api/v2/user/token` |
| SFTPGo | Share ID | `CjmQfrkXY5qDtC46WVZY2S` |
| Momentry | Server | `http://localhost:3002` |
| Momentry | MEDIA_BASE_URL | `https://wp.momentry.ddns.net` |
---
## 版本歷史
| 日期 | 版本 | 變更 |
|------|------|------|
| 2026-03-22 | v1.0 | 初始建立文件 |
| 2026-03-22 | v1.1 | 成功取得 Demo Token |
| 2026-03-22 | v1.2 | Phase 1 完成 (上傳 Charade 2.3GB) |

View File

@@ -1,690 +0,0 @@
# n8n Video RAG Workflow - Node 設計
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-22 |
| 文件版本 | V1.1 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-22 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
| V1.1 | 2026-03-25 | 更新API回應格式 (media_url→file_path) 與認證標頭 | OpenCode | deepseek-reasoner |
---
## 完整 Workflow 架構
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ n8n Workflow: Video RAG Demo │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Phase 1: SFTPGo 準備 (全部在 n8n Node 內執行) │ │
│ │ │ │
│ │ ① Webhook Trigger │ │
│ │ ↓ │ │
│ │ ② Set Variables (解析 file_name, query) │ │
│ │ ↓ │ │
│ │ ③ Get SFTPGo Token │ │
│ │ ↓ │ │
│ │ ④ Upload to SFTPGo │ │
│ │ ↓ │ │
│ │ ⑤ Create Share Link │ │
│ │ ↓ │ │
│ │ ⑥ Verify Upload (List Files + List Shares) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Phase 2: Momentry 註冊 (只處理 ASR, ASRX, STORY) │ │
│ │ │ │
│ │ ⑦ Register Video (modules=asr,asrx,story) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Phase 3: Progress Loop (n8n Logs 記錄) │ │
│ │ │ │
│ │ ⑧ Wait 10s ─────────────────────────────────────────────────┐ │ │
│ │ ↓ │ │
│ │ ⑨ Check Progress (API) │ │
│ │ ↓ │ │
│ │ ⑩ Log Progress (Code Node → n8n Logs) │ │
│ │ ↓ │ │
│ │ ⑪ Is Complete? (IF) │ │
│ │ │ │ │
│ │ ├── NO ──────────────────────────────── Loop Back ─────────┘ │ │
│ │ └── YES ────────────────────────────────────────────── Exit ──┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Phase 4: 搜尋與回應 │ │
│ │ │ │
│ │ ⑫ Hybrid Search (Vector + BM25) │ │
│ │ ↓ │ │
│ │ ⑬ Build Response │ │
│ │ ↓ │ │
│ │ ⑭ Respond to Webhook │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
### 模組說明
| 模組 | 用途 | 輸出 |
|------|------|------|
| `asr` | 語音轉文字 (Whisper) | 字幕/文字稿 |
| `asrx` | 說話者分離 (WhisperX) | 誰在什麼時候說什麼 |
| `story` | 故事線生成 (Parent-Child Chunks) | 敘事結構 + 父子區塊關聯 |
**注意**: 只處理語音和故事相關模組,跳過 YOLO、OCR、Face、Pose 等視覺分析。
┌─────────────────────────────────────────────────────────────────────────────┐
│ n8n Workflow: Video RAG Demo │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Phase 1: SFTPGo 準備 (全部在 n8n Node 內執行) │ │
│ │ │ │
│ │ ① Webhook Trigger │ │
│ │ ↓ │ │
│ │ ② Set Variables (解析 file_name, query) │ │
│ │ ↓ │ │
│ │ ③ Get SFTPGo Token │ │
│ │ ↓ │ │
│ │ ④ Upload to SFTPGo │ │
│ │ ↓ │ │
│ │ ⑤ Create Share Link │ │
│ │ ↓ │ │
│ │ ⑥ Verify Upload (List Files + List Shares) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Phase 2: Momentry 註冊 │ │
│ │ │ │
│ │ ⑦ Register Video │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Phase 3: Progress Loop (n8n Logs 記錄) │ │
│ │ │ │
│ │ ⑧ Wait 10s ─────────────────────────────────────────────────┐ │ │
│ │ ↓ │ │ │
│ │ ⑨ Check Progress (API) │ │ │
│ │ ↓ │ │ │
│ │ ⑩ Log Progress (Code Node → n8n Logs) │ │ │
│ │ ↓ │ │ │
│ │ ⑪ Is Complete? (IF) │ │ │
│ │ │ │ │ │
│ │ ├── NO ──────────────────────────────── Loop Back ─────────┘ │ │
│ │ └── YES ────────────────────────────────────────────── Exit ──┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Phase 4: 搜尋與回應 │ │
│ │ │ │
│ │ ⑫ Natural Language Search │ │
│ │ ↓ │ │
│ │ ⑬ Get File Path (含 file_path) │ │
│ │ ↓ │ │
│ │ ⑭ Build Response │ │
│ │ ↓ │ │
│ │ ⑮ Respond to Webhook │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## Node 詳細配置
### Node ①: Webhook Trigger (觸發器)
```yaml
Node Name: "Webhook Trigger"
Node Type: "Webhook"
Configuration:
HTTP Method: POST
Path: "video-rag"
Response Mode: "Response Node"
Response Node: "Respond to Webhook"
Input JSON Example:
{
"file_name": "Old_Time_Movie_Show_-_Charade_1963.HD.mov",
"query": "What is the movie about?"
}
```
---
### Node ②: Set Variables (變數設定)
```yaml
Node Name: "Set Variables"
Node Type: "Set"
Configuration:
Keep Only Set: true
Variables:
- Name: "file_name"
Value: "{{ $json.body.file_name }}"
- Name: "query"
Value: "{{ $json.body.query }}"
- Name: "sftpgo_path"
Value: "/{{ $json.body.file_name }}"
- Name: "register_path"
Value: "/Users/accusys/sftpgo_test/demo/{{ $json.body.file_name }}"
```
---
### Node ③: Get SFTPGo Token (取得權杖)
```yaml
Node Name: "Get SFTPGo Token"
Node Type: "HTTP Request"
Configuration:
Method: GET
URL: "http://localhost:8080/api/v2/user/token"
Authentication: "Basic Auth"
User: "demo"
Password: "demopassword123"
Output:
{
"access_token": "eyJhbGci...",
"expires_at": "2026-03-22T07:00:00Z"
}
```
---
### Node ④: Upload to SFTPGo (上傳檔案)
```yaml
Node Name: "Upload to SFTPGo"
Node Type: "HTTP Request"
Configuration:
Method: POST
URL: "http://localhost:8080/api/v2/user/files"
Authentication: "Bearer Token"
Bearer Token: "{{ $json.access_token }}"
Body Content Type: "Form-Data Multipart"
Body:
path: /demo
mkdir_parents: true
filenames: @{{ $json.file_name }}
Output:
{"message":"Upload completed"}
```
**檔案來源選項:**
1. **Webhook 接收**: 從 Webhook 的 binary data 取得
2. **固定路徑**: 指定本地檔案路徑
3. **URL 下載**: 先下載遠端檔案再上傳
---
### Node ⑤: Create Share Link (建立分享連結)
```yaml
Node Name: "Create Share Link"
Node Type: "HTTP Request"
Configuration:
Method: POST
URL: "http://localhost:8080/api/v2/user/shares"
Authentication: "Bearer Token"
Bearer Token: "{{ $json.access_token }}"
Body Content Type: "JSON"
Body:
{
"name": "{{ $json.file_name }}_share",
"paths": ["/{{ $json.file_name }}"],
"scope": 1,
"expires_at": 0
}
Output:
{
"id": "CjmQfrkXY5qDtC46WVZY2S",
"name": "Charade_share"
}
```
---
### Node ⑥: Verify Upload (驗證上傳)
```yaml
Node Name: "Verify Upload - List Shares"
Node Type: "HTTP Request"
Configuration:
Method: GET
URL: "http://localhost:8080/api/v2/user/shares"
Authentication: "Bearer Token"
Bearer Token: "{{ $json.access_token }}"
Output:
[
{
"id": "CjmQfrkXY5qDtC46WVZY2S",
"name": "Charade_share",
"paths": ["/Old_Time_Movie_Show_-_Charade_1963.HD.mov"]
}
]
```
---
### Node ⑦: Register Video (註冊影片)
**說明**: 只註冊 ASR、ASRX、STORY 模組處理
```yaml
Node Name: "Register Video"
Node Type: "HTTP Request"
Configuration:
Method: POST
URL: "http://localhost:3002/api/v1/register"
Body Content Type: "JSON"
Body:
{
"path": "{{ $json.register_path }}",
"modules": "asr,asrx,story"
}
Output:
{
"uuid": "a1b10138a6bbb0cd",
"video_id": 7,
"file_name": "Old_Time_Movie_Show_-_Charade_1963.HD.mov",
"duration": 6879.33,
"width": 1920,
"height": 1080
}
```
**可用模組**:
| 模組 | 說明 |
|------|------|
| `asr` | 語音轉文字 (Whisper) |
| `asrx` | 說話者分離 (WhisperX) |
| `story` | 故事線生成 (Parent-Child) |
| `yolo` | 物體偵測 (可選) |
| `cut` | 場景偵測 (可選) |
| `ocr` | 文字辨識 (可選) |
| `face` | 人臉偵測 (可選) |
| `pose` | 姿態估計 (可選) |
---
### Node ⑧: Wait 10 Seconds (輪詢間隔)
```yaml
Node Name: "Wait 10 Seconds"
Node Type: "Wait"
Configuration:
Amount: 10
Unit: "Seconds"
```
---
### Node ⑨: Check Progress (檢查進度)
```yaml
Node Name: "Check Progress"
Node Type: "HTTP Request"
Configuration:
Method: GET
URL: "http://localhost:3002/api/v1/progress/{{ $('Register Video').item.json.uuid }}"
Output:
{
"uuid": "a1b10138a6bbb0cd",
"processors": [
{"name": "asr", "status": "complete", "message": "1867 segments"},
{"name": "asrx", "status": "progress", "message": "ASRX_TRANSCRIBING"},
{"name": "story", "status": "pending", "message": ""}
]
}
```
> **注意**: API 現在返回 `file_path`(檔案系統路徑)而非 `media_url`(網頁 URL。如需在網頁中播放影片請將檔案路徑轉換為可訪問的 URL例如透過 SFTPGo 分享連結)。
---
### Node ⑩: Log Progress (記錄進度)
```yaml
Node Name: "Log Progress"
Node Type: "Code"
Configuration:
Language: "JavaScript"
Code:
```javascript
const progress = $input.first().json;
const processors = progress.processors;
const totalProcessors = processors.length;
const completedProcessors = processors.filter(p => p.status === 'complete').length;
const overallProgress = Math.round((completedProcessors / totalProcessors) * 100);
const currentProcessor = processors.find(p =>
p.status === 'progress' || p.status === 'info'
);
const progressMessage = `
═══════════════════════════════════════════════
📹 Video RAG Processing: ${overallProgress}%
UUID: ${progress.uuid}
${processors.map(p => {
const icon = p.status === 'complete' ? '✅' :
p.status === 'progress' || p.status === 'info' ? '🔄' : '⏳';
return ` ${icon} ${p.name.padEnd(6)} ${p.message || p.status}`;
}).join('\n')}
${currentProcessor ? `Current: ${currentProcessor.name}` : 'All complete!'}
═══════════════════════════════════════════════
`.trim();
console.log(progressMessage);
return {
json: {
uuid: progress.uuid,
overall_progress: overallProgress,
completed_processors: completedProcessors,
total_processors: totalProcessors,
current_processor: currentProcessor?.name || 'idle',
processors: processors,
log_message: progressMessage
}
};
```
Output:
{
"uuid": "a1b10138a6bbb0cd",
"overall_progress": 33,
"log_message": "📹 Video RAG Processing: 33%..."
}
```
---
### Node ⑪: Is Complete? (判斷分支)
```yaml
Node Name: "Is Complete?"
Node Type: "IF"
Configuration:
Condition:
$json.processors.every(p => p.status === 'complete')
Connections:
TRUE (完成): → Node ⑫ Natural Language Search
FALSE (未完成): → Node ⑧ Wait 10 Seconds (Loop)
```
---
### Node ⑫: Natural Language Search (RAG 搜尋)
```yaml
Node Name: "Natural Language Search"
Node Type: "HTTP Request"
Configuration:
Method: POST
URL: "http://localhost:3002/api/v1/search"
Body Content Type: "JSON"
Body:
{
"query": "{{ $('Set Variables').item.json.query }}",
"limit": 10,
"uuid": "{{ $('Register Video').item.json.uuid }}"
}
Output:
{
"results": [
{
"uuid": "a1b10138a6bbb0cd",
"chunk_id": "c_001",
"text": "Hello and welcome to the old-time movie show...",
"score": 0.92
}
]
}
```
---
### Node ⑫B: Hybrid Search (Vector + BM25)
**說明**: 使用混合搜尋,結合向量相似度和全文檢索
```yaml
Node Name: "Hybrid Search"
Node Type: "HTTP Request"
Configuration:
Method: POST
URL: "http://localhost:3002/api/v1/search/hybrid"
Body Content Type: "JSON"
Body:
{
"query": "{{ $('Set Variables').item.json.query }}",
"limit": 10,
"uuid": "{{ $('Register Video').item.json.uuid }}",
"vector_weight": 0.7,
"bm25_weight": 0.3
}
Output:
{
"query": "What is the movie about?",
"results": [
{
"uuid": "a1b10138a6bbb0cd",
"chunk_id": "c_001",
"chunk_type": "sentence",
"start_time": 0.0,
"end_time": 5.0,
"text": "Hello and welcome to the old-time movie show...",
"vector_score": 0.85,
"bm25_score": 0.75,
"combined_score": 0.80
}
]
}
```
**權重建議**:
| 查詢類型 | vector_weight | bm25_weight |
|----------|---------------|-------------|
| 主題查詢 | 0.8 | 0.2 |
| 事實查找 | 0.5 | 0.5 |
| 平衡查詢 | 0.7 | 0.3 |
---
### Node ⑬: Get Media URL (取得媒體連結)
```yaml
Node Name: "Get Media URL"
Node Type: "HTTP Request"
Configuration:
Method: POST
URL: "http://localhost:3002/api/v1/n8n/search"
Body Content Type: "JSON"
Body:
{
"query": "{{ $('Set Variables').item.json.query }}",
"limit": 10,
"uuid": "{{ $('Register Video').item.json.uuid }}"
}
Output:
{
"count": 10,
"hits": [
{
"id": "c_001",
"vid": "a1b10138a6bbb0cd",
"text": "Hello and welcome to the old-time movie show...",
"score": 0.92,
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
}
]
}
```
---
### Node ⑭: Build Response (組合結果)
```yaml
Node Name: "Build Response"
Node Type: "Set"
Configuration:
Keep Only Set: true
Variables:
- Name: "ok"
Value: true
- Name: "uuid"
Value: "{{ $('Register Video').item.json.uuid }}"
- Name: "file_name"
Value: "{{ $('Set Variables').item.json.file_name }}"
- Name: "query"
Value: "{{ $('Set Variables').item.json.query }}"
- Name: "count"
Value: "{{ $('Get Media URL').item.json.count }}"
- Name: "results"
Value: "{{ $('Get Media URL').item.json.hits }}"
- Name: "overall_progress"
Value: "{{ $('Log Progress').item.json.overall_progress }}"
```
---
### Node ⑮: Respond to Webhook (回傳結果)
```yaml
Node Name: "Respond to Webhook"
Node Type: "Respond to Webhook"
Configuration:
Respond With: "JSON"
Response Body:
{
"ok": true,
"uuid": "{{ $json.uuid }}",
"file_name": "{{ $json.file_name }}",
"query": "{{ $json.query }}",
"count": {{ $json.count }},
"results": {{ $json.results }},
"overall_progress": {{ $json.overall_progress }},
"message": "Video RAG completed successfully"
}
```
---
## 快速複製所需資訊
### SFTPGo 設定
| 項目 | 值 |
|------|-----|
| API Base | `http://localhost:8080/api/v2` |
| Demo User | `demo` |
| Demo Password | `demopassword123` |
| Demo Home | `/Users/accusys/sftpgo_test/demo` |
| Token Endpoint | `/api/v2/user/token` |
| Upload Endpoint | `/api/v2/user/files` |
| Share Endpoint | `/api/v2/user/shares` |
### Momentry 設定
| 項目 | 值 |
|------|-----|
| API Base | `http://localhost:3002` |
| Authentication | `X-API-Key` header (所有 `/api/v1/*` 端點) |
| Register | `POST /api/v1/register` |
| Progress | `GET /api/v1/progress/{uuid}` |
| Search | `POST /api/v1/search` |
| n8n Search | `POST /api/v1/n8n/search` |
| Hybrid Search | `POST /api/v1/search/hybrid` |
| Media Base | `https://wp.momentry.ddns.net` (僅供參考API 返回 `file_path` 而非 URL) |
### Demo 測試資料
**Charade (1963) Demo Video**
- UUID: `a1b10138a6bbb0cd`
- 位置: `/Users/accusys/test_video/Old_Time_Movie_Show_-_Charade_1963.HD.mov`
- 時長: 6872 秒 (~1.9 小時)
**已處理檔案**:
| 檔案 | 大小 | 內容 |
|------|------|------|
| `asr.json` | 210KB | 1867 語音區段 |
| `cut.json` | 220KB | 1331 場景 |
| `story.json` | 1.8MB | 641 父子區塊 |
| `transcript.txt` | 40KB | 可讀文字稿 |
**Output 目錄**: `/Users/accusys/momentry_core_0.1/output`
---
## 版本歷史
| 日期 | 版本 | 變更 |
|------|------|------|
| 2026-03-22 | v1.0 | 初始建立 |
| 2026-03-22 | v1.1 | 新增 Hybrid Search (Vector + BM25) 節點 |
| 2026-03-22 | v1.2 | 簡化為只處理 ASR、ASRX、STORY 模組 |

View File

@@ -1,269 +0,0 @@
# n8n HTTP Request Node 設定指南
| 項目 | 內容 |
|------|------|
| 建立者 | OpenCode |
| 建立時間 | 2026-03-26 |
| 文件版本 | V1.1 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-23 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
| V1.1 | 2026-03-26 | 新增 API Key 驗證說明,更新 curl 範例 | OpenCode | deepseek-reasoner |
---
> **API URL 說明**:
> - **本地測試**: `http://localhost:3002`
> - **n8n workflow**: `https://api.momentry.ddns.net`
>
> ⚠️ 在 n8n 中請使用 `api.momentry.ddns.net`,不要使用 `localhost:3002`
---
## 錯誤排除
### 錯誤訊息: "Your request is invalid or could not be processed by the service"
這通常表示 HTTP Request Node 的設定不正確。
---
## 正確的 Node 設定方式
### 方法 1: 使用 JSON Body (推薦)
```
Node: HTTP Request
├── URL: https://api.momentry.ddns.net/api/v1/n8n/search
├── Method: POST
├── Authentication: None
├── Send Body: ✓ (checked)
├── Content Type: JSON
├── Body:
│ {
│ "query": "={{ $json.query }}",
│ "limit": "={{ $json.limit }}"
│ }
├── Send Headers: ✓ (checked)
└── Header Parameters:
└── X-API-Key: {{ $env.MOMENTRY_API_KEY }}
```
### 方法 2: 使用 Raw Body + Headers
```
Node: HTTP Request
├── URL: https://api.momentry.ddns.net/api/v1/n8n/search
├── Method: POST
├── Authentication: None
├── Send Body: ✓ (checked)
├── Specify Body: Using JSON
├── JSON Body:
│ {
│ "query": "charade",
│ "limit": 3
│ }
├── Send Headers: ✓ (checked)
└── Header Parameters:
├── Content-Type: application/json
└── X-API-Key: {{ $env.MOMENTRY_API_KEY }}
```
### 方法 3: 最簡單的 Hardcoded 測試
```
Node: HTTP Request
├── URL: https://api.momentry.ddns.net/api/v1/n8n/search
├── Method: POST
├── Send Body: ✓
├── Content Type: JSON
└── Body:
{
"query": "charade",
"limit": 3
}
```
---
## 常見錯誤與解決
### ❌ 錯誤 1: Body 格式錯誤
**錯誤設定:**
```
Body Parameters:
query = {{ $json.query }}
limit = {{ $json.limit }}
```
**正確設定:**
```
Content Type: JSON
Body:
{
"query": "={{ $json.query }}",
"limit": "={{ $json.limit }}"
}
```
### ❌ 錯誤 2: 缺少引號
**錯誤:**
```json
{
query: "charade",
limit: 3
}
```
**正確:**
```json
{
"query": "charade",
"limit": 3
}
```
### ❌ 錯誤 3: URL 錯誤
**錯誤:**
```
URL: http://localhost:3002/api/v1/n8n/search
```
**正確:**
```
URL: https://api.momentry.ddns.net/api/v1/n8n/search
```
---
## 測試步驟
### 步驟 1: 創建最簡單的測試
1. 新建工作流程
2. 添加 **Manual Trigger** Node
3. 添加 **HTTP Request** Node
4. 設定如下:
- URL: `https://api.momentry.ddns.net/api/v1/n8n/search`
- Method: POST
- Send Body: ✓
- Content Type: JSON
- Body: `{"query": "charade", "limit": 2}`
### 步驟 2: 執行測試
1. 點擊 **Execute Workflow**
2. 查看 HTTP Request Node 的輸出
3. 應該看到 JSON 回應
### 步驟 3: 確認成功
成功的回應應該包含:
```json
{
"query": "charade",
"count": 2,
"hits": [...]
}
```
---
## 直接複製使用的工作流程 JSON
```json
{
"name": "Video Search - Working Example",
"nodes": [
{
"parameters": {},
"name": "When clicking \"Execute Workflow\"",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [250, 300]
},
{
"parameters": {
"url": "https://api.momentry.ddns.net/api/v1/n8n/search",
"method": "POST",
"sendBody": true,
"contentType": "json",
"body": {
"query": "charade",
"limit": 3
},
"options": {}
},
"name": "Search Video API",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [450, 300]
}
],
"connections": {
"When clicking \"Execute Workflow\"": {
"main": [
[
{
"node": "Search Video API",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
}
}
```
**導入方式:**
1. 在 n8n UI 中,點擊左上角的 Menu
2. 選擇 **Import from File**
3. 選擇上面的 JSON 文件
---
## 驗證 API 可用性
在終端機測試:
```bash
# 需要 API Key 驗證 (設定環境變數或直接替換)
export MOMENTRY_API_KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
curl -X POST https://api.momentry.ddns.net/api/v1/n8n/search \
-H "Content-Type: application/json" \
-H "X-API-Key: $MOMENTRY_API_KEY" \
-d '{"query":"charade","limit":2}'
```
如果 curl 成功但 n8n 失敗,問題在於 n8n HTTP Request Node 的設定。
---
## 需要幫助?
如果仍然無法工作:
1. 開啟工作流程
2. 點擊 HTTP Request Node
3. 點擊右上角的 **Execute Node** 單獨執行
4. 查看錯誤訊息的詳細內容
5. 檢查 Network tab 中的 request/response
---
## 相關文件
- [API_INDEX.md](./API_INDEX.md) - 文件總覽(起點)
- [API_N8N_GUIDE.md](./API_N8N_GUIDE.md) - n8n 快速使用指南
- [API_ENDPOINTS.md](./API_ENDPOINTS.md) - 端點完整說明

View File

@@ -1,575 +0,0 @@
# Momentry n8n 整合使用手冊
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-23 |
| 文件版本 | V1.1 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-22 | 創建 n8n 整合手冊 | Warren | OpenCode |
| V1.1 | 2026-03-23 | 新增 API Key 驗證與完整工作流範例 | Warren | OpenCode |
---
**目標讀者**: n8n 使用者、DevOps
---
## 目錄
1. [概述](#1-概述)
2. [前置作業](#2-前置作業)
3. [建立 n8n API Key](#3-建立-n8n-api-key)
4. [在 n8n 中使用 Momentry API](#4-在-n8n-中使用-momentry-api)
5. [工作流範例](#5-工作流範例)
6. [常見問題](#6-常見問題)
---
## 1. 概述
### 1.1 什麼是 n8n
n8n 是一個開源的工作流自動化工具,可以連接各種服務和 API。
### 1.2 為什麼需要整合?
| 場景 | 說明 |
|------|------|
| 自動化影片處理 | 新影片上傳時自動觸發處理流程 |
| 監控告警 | API Key 異常時發送通知 |
| 定時備份 | 定期備份 API Key 資料 |
| 跨系統同步 | 與其他系統同步 API Key 狀態 |
### 1.3 架構圖
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 觸發器 │────▶│ n8n 工作流 │────▶│ Momentry │
│ (Webhook/ │ │ (處理邏輯) │ │ API │
│ Cron) │ └─────────────┘ └─────────────┘
└─────────────┘ │ │
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ 通知 │ │ 動作 │
│ (Slack/Email)│ │ (建立/查詢) │
└─────────────┘ └─────────────┘
```
---
### 1.4 API URL 選擇
| 環境 | URL | 說明 |
|------|-----|------|
| **本地測試** | `http://localhost:3002` | 直接訪問 API |
| **n8n workflow** | `https://api.momentry.ddns.net` | 通過反向代理 |
> ⚠️ **重要**: 在 n8n HTTP Request Node 中,請使用 `https://api.momentry.ddns.net` 而非 `localhost:3002`,因為 n8n 需要從外部訪問 API。
**本地測試時**:
```bash
curl http://localhost:3002/health
```
**n8n Workflow 中**:
```
URL: https://api.momentry.ddns.net/api/v1/n8n/search
```
---
## 2. 前置作業
### 2.1 確認服務狀態
```bash
# 檢查 Momentry API
curl http://localhost:3002/health
# 檢查 n8n
curl https://n8n.momentry.ddns.net/api/v1/workflows \
-H "X-N8N-API-KEY: your-api-key"
```
### 2.2 取得 n8n API Key
#### 方式 A: 透過 Momentry CLI
```bash
# 建立 n8n API Key
momentry n8n create \
--api-key "your-existing-n8n-api-key" \
--label "momentry-integration" \
--expires-in-days 90
# 輸出:
# ✅ n8n API Key created successfully!
# API Key: eyJhbGciOiJIUzI1NiIs...
```
#### 方式 B: 透過 n8n 介面
1. 登入 n8n: https://n8n.momentry.ddns.net
2. 前往 Settings → n8n API
3. 點擊「Create an API Key」
4. 複製 API Key
### 2.3 設定環境變數
在 n8n 中設定以下環境變數:
| 變數名稱 | 值 | 說明 |
|----------|-----|------|
| `MOMENTRY_API_URL` | `http://localhost:3002` | Momentry API URL |
| `MOMENTRY_API_KEY` | `your-api-key` | Momentry API Key |
---
## 3. 建立 n8n API Key
### 3.1 CLI 命令
```bash
# 基本建立
momentry n8n create \
--api-key <existing-n8n-key> \
--label <key-name>
# 設定過期時間
momentry n8n create \
--api-key <existing-n8n-key> \
--label "ci-pipeline" \
--expires-in-days 30
```
### 3.2 範例:建立監控用 Key
```bash
momentry n8n create \
--api-key "n8n_api_1234567890" \
--label "monitoring-key" \
--expires-in-days 180
# 輸出:
# ✅ n8n API Key created successfully!
# Key ID: abc123-def456
# Label: monitoring-key
# API Key: eyJhbGciOiJIUz...
```
### 3.3 列出已建立的 Key
```bash
momentry n8n list --api-key <existing-n8n-key>
# 輸出:
# 📋 n8n API Keys
# ┌────────────────────────────────────────────────────────────────────────────┐
# │ Label │ ID │
# ├────────────────────────────────────────────────────────────────────────────┤
# │ monitoring-key │ abc123-def456 │
# │ ci-pipeline │ xyz789-abc123 │
# └────────────────────────────────────────────────────────────────────────────┘
```
---
## 4. 在 n8n 中使用 Momentry API
> ⚠️ **注意**: API Key 管理端點 (`/api/v1/api-keys/*`) 目前仍在規劃中,尚未實作。以下為規劃中的 API 說明。
### 4.1 設定 HTTP Request 節點
```
Method: POST
URL: {{ $env.MOMENTRY_API_URL }}/api/v1/api-keys
Headers:
X-API-Key: {{ $env.MOMENTRY_API_KEY }}
Content-Type: application/json
Body:
{
"name": "auto-generated-key",
"key_type": "service",
"ttl_days": 90
}
```
### 4.2 可用的 API 端點
> ⚠️ **API Key 管理端點為規劃功能,目前尚未實作**
#### 已實作端點
| 方法 | 端點 | 說明 |
|------|------|------|
| GET | `/health` | 健康檢查 |
| POST | `/api/v1/search` | 語意搜尋 |
| POST | `/api/v1/n8n/search` | n8n 格式搜尋 |
| GET | `/api/v1/videos` | 列出所有影片 |
| GET | `/api/v1/lookup` | 查詢影片 |
| GET | `/api/v1/progress/:uuid` | 處理進度 |
#### 規劃中端點 *(尚未實作)*
| 方法 | 端點 | 說明 |
|------|------|------|
| GET | `/api/v1/api-keys` | 列出所有 API Keys |
| POST | `/api/v1/api-keys` | 建立新的 API Key |
| DELETE | `/api/v1/api-keys/{id}` | 刪除 API Key |
| POST | `/api/v1/api-keys/{id}/rotate` | 請求 Key 輪換 |
---
## 5. 工作流範例
### 範例 1定時檢查 API Key 狀態
**目的**: 每天早上 9 點檢查即將過期的 API Keys
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Schedule │────▶│ HTTP │────▶│ Filter │────▶│ Slack │
│ (每天 9AM) │ │ Request │ │ (7天內過期) │ │ 通知 │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
```
**HTTP Request 設定**:
```json
{
"method": "GET",
"url": "{{ $env.MOMENTRY_API_URL }}/api/v1/api-keys",
"headers": {
"X-API-Key": "{{ $env.MOMENTRY_API_KEY }}"
}
}
```
**Filter 設定**:
```javascript
// 檢查是否在 7 天內過期
const expiresAt = new Date($json.expires_at);
const now = new Date();
const sevenDaysLater = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);
return expiresAt <= sevenDaysLater && expiresAt > now;
```
**Slack 通知格式**:
```
⚠️ API Key 即將過期提醒
以下 API Key 將在 7 天內過期:
{{ $json.map(k => `• ${k.name} (${k.key_id}) - 過期時間: ${k.expires_at}`).join('\n') }}
請及時處理!
```
---
### 範例 2新影片上傳時自動建立 API Key
**目的**: 當有新影片上傳時,自動為該影片建立專用 API Key
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Webhook │────▶│ HTTP │────▶│ Set │────▶│ Respond │
│ 觸發器 │ │ Request │ │ (組裝回應) │ │ Webhook │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
```
**Webhook 設定**:
```
Method: POST
Path: /new-video
```
**HTTP Request 設定**:
```json
{
"method": "POST",
"url": "{{ $env.MOMENTRY_API_URL }}/api/v1/api-keys",
"headers": {
"X-API-Key": "{{ $env.MOMENTRY_API_KEY }}"
},
"body": {
"name": "video-{{ $json.video_id }}",
"key_type": "service",
"permissions": ["read"],
"ttl_days": 30
}
}
```
**回應格式**:
```json
{
"success": true,
"video_id": "{{ $json.video_id }}",
"api_key_id": "{{ $json.key_id }}",
"message": "API Key 已建立"
}
```
---
### 範例 3API Key 異常告警
**目的**: 監控 API Key 異常使用,發送告警通知
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Webhook │────▶│ Switch │────▶│ Email │ │ Slack │
│ (異常通知) │ │ (嚴重程度) │────▶│ 通知 │ │ 通知 │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
```
**Webhook 觸發設定** (在 Momentry 中):
```bash
export WEBHOOK_URL="https://n8n.momentry.ddns.net/webhook/anomaly-alert"
export WEBHOOK_EVENTS="anomaly_detected,rate_limited,ip_blocked"
```
**Switch 節點設定**:
```javascript
// 根據嚴重程度分流
switch ($json.data.severity) {
case 'critical':
return 0; // Email + Slack
case 'high':
return 1; // Slack only
default:
return 2; // Log only
}
```
**Email 通知格式**:
```
Subject: [{{ $json.data.severity }}] API Key 異常告警 - {{ $json.data.key_id }}
Dear Admin,
偵測到 API Key 異常使用:
Key ID: {{ $json.data.key_id }}
異常類型: {{ $json.data.anomaly_type }}
嚴重程度: {{ $json.data.severity }}
時間: {{ $json.timestamp }}
請立即檢查系統狀態。
Best regards,
Momentry Monitoring
```
---
### 範例 4定時備份 API Key 統計
**目的**: 每週一早上備份 API Key 統計報表
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Schedule │────▶│ HTTP │────▶│ Google │
│ (每週一) │ │ Request │ │ Sheets │
└─────────────┘ └─────────────┘ └─────────────┘
```
**HTTP Request 設定**:
```json
{
"method": "GET",
"url": "{{ $env.MOMENTRY_API_URL }}/api/v1/api-keys/stats",
"headers": {
"X-API-Key": "{{ $env.MOMENTRY_API_KEY }}"
}
}
```
**Google Sheets 設定**:
```
Spreadsheet: Momentry API Key Reports
Sheet: Weekly Stats
Append Row:
- Date: {{ $now }}
- Total Keys: {{ $json.total_keys }}
- Active Keys: {{ $json.active_keys }}
- Expired Keys: {{ $json.expired_keys }}
- Anomalies (24h): {{ $json.anomalies_last_24h }}
```
---
### 範例 5自動輪換即將過期的 Key
**目的**: 自動為即將過期的 Key 請求輪換
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Schedule │────▶│ HTTP │────▶│ Filter │────▶│ HTTP │
│ (每天) │ │ (取得 Keys) │ │ (即將過期) │ │ (請求輪換) │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
```
**第一個 HTTP Request (取得 Keys)**:
```json
{
"method": "GET",
"url": "{{ $env.MOMENTRY_API_URL }}/api/v1/api-keys",
"headers": {
"X-API-Key": "{{ $env.MOMENTRY_API_KEY }}"
}
}
```
**Filter 設定**:
```javascript
// 14 天內過期且未請求輪換的 Key
const expiresAt = new Date($json.expires_at);
const now = new Date();
const fourteenDaysLater = new Date(now.getTime() + 14 * 24 * 60 * 60 * 1000);
return expiresAt <= fourteenDaysLater &&
!$json.rotation_required &&
$json.status === 'active';
```
**第二個 HTTP Request (請求輪換)**:
```json
{
"method": "POST",
"url": "{{ $env.MOMENTRY_API_URL }}/api/v1/api-keys/{{ $json.key_id }}/rotate",
"headers": {
"X-API-Key": "{{ $env.MOMENTRY_API_KEY }}"
},
"body": {
"reason": "auto_rotation_approaching_expiry"
}
}
```
---
## 6. 常見問題
### Q1: n8n 無法連接到 Momentry API
**檢查項目**:
```bash
# 1. 確認 API 是否運行
curl http://localhost:3002/health
# 2. 確認 API Key 是否有效
momentry api-key validate --key "your-key"
# 3. 檢查防火牆設定
nc -zv localhost 3002
```
### Q2: API Key 建立失敗
**可能原因**:
- API Key 已存在
- 權限不足
- 格式錯誤
**解決方式**:
```bash
# 檢查現有 Keys
momentry api-key list
# 使用不同的 label
momentry n8n create --api-key "..." --label "unique-label-$(date +%s)"
```
### Q3: Webhook 通知沒有收到
**檢查項目**:
```bash
# 1. 確認 Webhook URL 設定
echo $WEBHOOK_URL
# 2. 測試 Webhook
curl -X POST $WEBHOOK_URL \
-H "Content-Type: application/json" \
-d '{"test": true}'
# 3. 檢查 n8n 工作流是否啟用
# 登入 n8n → 檢查工作流狀態
```
### Q4: 如何撤銷 n8n API Key
```bash
# 列出所有 Keys
momentry n8n list --api-key "your-admin-key"
# 刪除指定 Key
momentry n8n delete \
--api-key "your-admin-key" \
--label "key-to-delete"
```
---
## 附錄
### A. n8n 工作流匯出
將上述範例工作流匯入 n8n
1. 複製工作流 JSON
2. 登入 n8n
3. 點擊「+」→「Import from File」
4. 貼上 JSON 並儲存
### B. 環境變數總覽
```bash
# Momentry
MOMENTRY_API_URL=http://localhost:3002
MOMENTRY_API_KEY=your-api-key
# n8n
N8N_URL=https://n8n.momentry.ddns.net
N8N_API_KEY=your-n8n-api-key
# Webhook
WEBHOOK_URL=https://n8n.momentry.ddns.net/webhook/alerts
WEBHOOK_SECRET=your-secret
WEBHOOK_EVENTS=anomaly_detected,key_expired,key_revoked
```
### C. 相關文件
| 文件 | 說明 |
|------|------|
| [API_INDEX.md](./API_INDEX.md) | 文件總覽(起點) |
| [API_N8N_GUIDE.md](./API_N8N_GUIDE.md) | n8n 快速使用指南 |
| `docs/API_KEY_MANAGEMENT.md` | API Key 管理系統設計 |
| `docs/API_KEY_ARCHITECTURE.md` | 系統架構圖 |
| `docs/API_KEY_INTEGRATION_TESTS.md` | 整合測試文件 |

View File

@@ -1,227 +0,0 @@
# OpenCode n8n MCP 整合設定
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-23 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-23 | 創建 n8n MCP 整合設定文件 | Warren | OpenCode |
---
> 建立時間: 2026-03-23
> 更新時間: 2026-03-23
---
## n8n MCP 工具列表 (43 個)
### Workflows (10)
| 工具 | 說明 |
|------|------|
| `n8n_list_workflows` | 列出所有 workflows |
| `n8n_get_workflow` | 取得 workflow 詳情 |
| `n8n_create_workflow` | 建立新 workflow |
| `n8n_update_workflow` | 更新 workflow |
| `n8n_delete_workflow` | 刪除 workflow |
| `n8n_activate_workflow` | 啟用 workflow |
| `n8n_deactivate_workflow` | 停用 workflow |
| `n8n_execute_workflow` | 執行 workflow |
| `n8n_get_workflow_tags` | 取得 workflow 標籤 |
| `n8n_update_workflow_tags` | 更新 workflow 標籤 |
### Executions (3)
| 工具 | 說明 |
|------|------|
| `n8n_list_executions` | 列出執行記錄 |
| `n8n_get_execution` | 取得執行詳情 |
| `n8n_delete_execution` | 刪除執行記錄 |
### Data Tables (8)
| 工具 | 說明 |
|------|------|
| `n8n_list_datatables` | 列出資料表 |
| `n8n_create_datatable` | 建立資料表 |
| `n8n_get_datatable` | 取得資料表結構 |
| `n8n_get_datatable_rows` | 取得資料表列 |
| `n8n_insert_datatable_rows` | 插入資料列 |
| `n8n_update_datatable_rows` | 更新資料列 |
| `n8n_upsert_datatable_row` | 插入或更新資料列 |
| `n8n_delete_datatable_rows` | 刪除資料列 |
### Tags (5)
| 工具 | 說明 |
|------|------|
| `n8n_list_tags` | 列出所有標籤 |
| `n8n_get_tag` | 取得標籤 |
| `n8n_create_tag` | 建立標籤 |
| `n8n_update_tag` | 更新標籤 |
| `n8n_delete_tag` | 刪除標籤 |
### Credentials (4)
| 工具 | 說明 |
|------|------|
| `n8n_list_credentials` | 列出憑證 |
| `n8n_create_credential` | 建立憑證 |
| `n8n_delete_credential` | 刪除憑證 |
| `n8n_get_credential_schema` | 取得憑證 schema |
### Users (3)
| 工具 | 說明 |
|------|------|
| `n8n_list_users` | 列出使用者 |
| `n8n_get_user` | 取得使用者 |
| `n8n_delete_user` | 刪除使用者 |
### Variables (3)
| 工具 | 說明 |
|------|------|
| `n8n_list_variables` | 列出變數 |
| `n8n_create_variable` | 建立變數 |
| `n8n_delete_variable` | 刪除變數 |
### 其他 (7)
| 工具 | 說明 |
|------|------|
| `n8n_list_projects` | 列出專案 |
| `n8n_create_project` | 建立專案 |
| `n8n_update_project` | 更新專案 |
| `n8n_delete_project` | 刪除專案 |
| `n8n_generate_audit` | 產生安全審計報告 |
| `n8n_health_check` | 健康檢查 |
| `n8n_trigger_webhook` | 觸發 webhook |
---
## 安裝步驟
### 1. 安裝 n8n MCP Server
```bash
npm install -g @nextoolsolutions/mcp-n8n
```
驗證:
```bash
which mcp-n8n
# /opt/homebrew/bin/mcp-n8n
```
### 2. 取得 n8n API Key
1. 開啟 n8n UI: `http://localhost:5678`
2. 登入後點擊右上角 **Settings****API**
3. 點擊 **Create New API Key**
4. 複製產生的 key
### 3. 設定 OpenCode MCP 設定檔
建立或編輯 `~/.config/opencode/opencode.json`
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"gitea": {
"type": "local",
"enabled": true,
"command": [
"/opt/homebrew/bin/gitea-mcp-server",
"-token", "<GITEA_TOKEN>",
"-host", "http://localhost:3000"
]
},
"n8n": {
"type": "local",
"enabled": true,
"command": ["/opt/homebrew/bin/mcp-n8n"],
"environment": {
"N8N_BASE_URL": "http://localhost:5678",
"N8N_API_KEY": "<N8N_API_KEY>"
}
}
}
}
```
### 4. 驗證 MCP 運作
重啟 OpenCode確認 n8n MCP tools 可用。
---
## n8n API 端點
n8n v2 REST API 路徑為 `/rest/`(不是 `/api/v1/`
| 端點 | 方法 | 說明 |
|------|------|------|
| `/rest/workflows` | GET | 列出 workflows |
| `/rest/workflows/:id` | GET | 取得 workflow |
| `/rest/workflows` | POST | 建立 workflow |
| `/rest/workflows/:id` | PUT | 更新 workflow |
| `/rest/workflows/:id` | DELETE | 刪除 workflow |
| `/rest/workflows/:id/activate` | POST | 啟用 workflow |
| `/rest/workflows/:id/deactivate` | POST | 停用 workflow |
| `/rest/workflows/:id/execute` | POST | 執行 workflow |
**認證方式:**
```bash
curl -H "X-N8N-API-KEY: YOUR_API_KEY" \
http://localhost:5678/rest/workflows
```
---
## 疑難排解
### API 404 問題
如果 API 傳回 404檢查
1. **n8n 是否運行中**
```bash
curl http://localhost:5678
```
2. **n8n 初始設定(重要!)**
- 第一次使用必須在瀏覽器完成初始化
- 開啟 `http://localhost:5678`
- 按照畫面指示建立管理員帳號
- 完成後才能使用 API
3. **API Key 是否正確**
```bash
curl -H "X-N8N-API-KEY: YOUR_KEY" \
http://localhost:5678/rest/workflows
```
### n8n 初始設定(第一次使用)
1. 開啟瀏覽器: `http://localhost:5678`
2. 輸入 email 和密碼建立管理員帳號
3. 完成後進入 Settings → API
4. 建立 API Key 並複製
### CLI Import vs PostgreSQL
n8n 使用 PostgreSQL 儲存資料:
- CLI `n8n import:workflow` 可能寫入 SQLite
- 手動在 UI import 會寫入 PostgreSQL
建議直接使用 UI 或 MCP import。
---
## 相關文件
- [OPENCODE_GUIDE.md](./OPENCODE_GUIDE.md) - OpenCode 使用規範
- [INSTALL_N8N.md](./INSTALL_N8N.md) - n8n 安裝指南
- [N8N_DEMO_WORKFLOW.md](./N8N_DEMO_WORKFLOW.md) - n8n Workflow 範例

View File

@@ -1,194 +0,0 @@
# n8n MCP 整合測試報告
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-23 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-23 | 創建測試報告 | Warren | OpenCode |
---
## 測試日期
2026-03-23
## 測試環境
- **n8n Version**: 2.3.5
- **n8n URL**: http://localhost:5678
- **MCP Server**: @nextoolsolutions/mcp-n8n v2.0.0
- **OpenCode Config**: ~/.config/opencode/opencode.json
## 測試結果
### ✅ 所有測試通過
| 測試項目 | 狀態 | 詳細說明 |
|---------|------|---------|
| MCP 伺服器初始化 | ✅ 通過 | Protocol version 2024-11-05 |
| 工具列表載入 | ✅ 通過 | 43 個工具可用 |
| 工具呼叫 (list_workflows) | ✅ 通過 | 成功返回 5 個 workflows |
| API 連線 | ✅ 通過 | http://localhost:5678 |
## 可用工具 (43 個)
### Workflows (10)
- `n8n_list_workflows` - 列出所有工作流程
- `n8n_get_workflow` - 取得工作流程詳情
- `n8n_create_workflow` - 建立新工作流程
- `n8n_update_workflow` - 更新工作流程
- `n8n_delete_workflow` - 刪除工作流程
- `n8n_activate_workflow` - 啟動工作流程
- `n8n_deactivate_workflow` - 停止工作流程
- `n8n_execute_workflow` - 執行工作流程
- `n8n_get_workflow_tags` - 取得工作流程標籤
- `n8n_update_workflow_tags` - 更新工作流程標籤
### Executions (3)
- `n8n_list_executions` - 列出執行記錄
- `n8n_get_execution` - 取得執行詳情
- `n8n_delete_execution` - 刪除執行記錄
### Data Tables (6)
- `n8n_list_datatables` - 列出資料表
- `n8n_create_datatable` - 建立資料表
- `n8n_get_datatable` - 取得資料表
- `n8n_get_datatable_rows` - 查詢資料表資料
- `n8n_insert_datatable_rows` - 插入資料
- `n8n_update_datatable_rows` - 更新資料
- `n8n_upsert_datatable_row` - 更新或插入
- `n8n_delete_datatable_rows` - 刪除資料
### Tags (5)
- `n8n_list_tags` - 列出標籤
- `n8n_get_tag` - 取得標籤
- `n8n_create_tag` - 建立標籤
- `n8n_update_tag` - 更新標籤
- `n8n_delete_tag` - 刪除標籤
### Credentials (4)
- `n8n_list_credentials` - 列出認證
- `n8n_create_credential` - 建立認證
- `n8n_delete_credential` - 刪除認證
- `n8n_get_credential_schema` - 取得認證結構
### Users (3)
- `n8n_list_users` - 列出使用者
- `n8n_get_user` - 取得使用者
- `n8n_delete_user` - 刪除使用者
### Variables (3)
- `n8n_list_variables` - 列出變數
- `n8n_create_variable` - 建立變數
- `n8n_delete_variable` - 刪除變數
### Projects (4)
- `n8n_list_projects` - 列出專案
- `n8n_create_project` - 建立專案
- `n8n_update_project` - 更新專案
- `n8n_delete_project` - 刪除專案
### System (3)
- `n8n_generate_audit` - 產生安全稽核報告
- `n8n_health_check` - 健康檢查
- `n8n_trigger_webhook` - 觸發 Webhook
## 配置檔案
### ~/.config/opencode/opencode.json
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"gitea": {
"type": "local",
"enabled": true,
"command": [
"/opt/homebrew/bin/gitea-mcp-server",
"-token", "<GITEA_TOKEN>",
"-host", "http://localhost:3000"
]
},
"n8n": {
"type": "local",
"enabled": true,
"command": ["/opt/homebrew/bin/mcp-n8n"],
"environment": {
"N8N_BASE_URL": "http://localhost:5678",
"N8N_API_KEY": "<N8N_API_KEY>"
}
}
}
}
```
## 測試範例
### 列出工作流程
```bash
# 使用 curl
curl -H "X-N8N-API-KEY: <API_KEY>" http://localhost:5678/api/v1/workflows
# 使用 MCP
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"n8n_list_workflows","arguments":{"limit":5}}}' | mcp-n8n
```
### 建立工作流程
```bash
# 使用 curl
curl -X POST \
-H "Content-Type: application/json" \
-H "X-N8N-API-KEY: <API_KEY>" \
-d '{"name":"My Workflow","nodes":[],"connections":{}}' \
http://localhost:5678/api/v1/workflows
# 使用 MCP
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"n8n_create_workflow","arguments":{"workflow":{"name":"My Workflow","nodes":[],"connections":{}}}}' | mcp-n8n
```
## 現有工作流程 (範例)
測試中成功讀取的工作流程:
1. **Diagnostic: Environment Test** (ID: 4vaf6dKznkTccuyC)
- 狀態: Active
- 用途: 環境測試與時間同步驗證
2. **Simple Test Webhook v2** (ID: 38bbM14sGo0eVCuW)
- 狀態: Active
- 用途: Webhook 測試
3. **HL Chat Searching - RAG Only** (ID: 6Y9c7mGtye4DjuENR5Kbg)
- 狀態: Inactive
- 用途: RAG 聊天搜尋整合
4. **HL Embedding with AccusysDB** (ID: 61nRs3BeNGlBtuYJFLSFn)
- 狀態: Inactive
- 用途: Qdrant 向量資料庫嵌入
5. **HL Embedding with AccusysDB (local)** (ID: 017oYPE7cDpvybAn)
- 狀態: Archived
- 用途: 本地測試版本
## 結論
**n8n MCP 整合測試全部通過!**
MCP 伺服器已成功配置並運作,可以透過 OpenCode 使用所有 43 個 n8n 管理工具。
### 建議用途
1. **自動化工作流程管理** - 使用 AI 協助建立、修改、監控工作流程
2. **批次執行** - 透過 MCP 批量管理工作流程
3. **監控與稽核** - 自動化執行記錄檢視與安全稽核
4. **整合測試** - 與 Momentry Core Video RAG 整合測試
### 下一步
- 使用 OpenCode 建立 Video RAG 整合工作流程
- 設定自動化監控與告警
- 建立工作流程模板庫

View File

@@ -1,152 +0,0 @@
# Momentry Video RAG - n8n 工作流程設定完成
## ✅ 最終成功版本
| 項目 | 內容 |
|------|------|
| **工作流程名稱** | Video Search - Working v3 |
| **ID** | 4vQo8I4SXEaR5E1A |
| **狀態** | ✅ SUCCESS |
| **執行 ID** | 1620 |
---
## 成功關鍵
### HTTP Request Node 正確設定
```json
{
"url": "https://api.momentry.ddns.net/api/v1/n8n/search",
"method": "POST",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "{\"query\":\"charade\",\"limit\":3}",
"options": {}
}
```
**重點**:
-`specifyBody`: "json" (不是 "body")
-`jsonBody`: 字串格式 (不是物件)
- ✅ 使用 `"{\"query\":\"..."}` 轉義引號
---
## 所有可用工作流程
| 工作流程 | ID | 狀態 | 說明 |
|---------|-----|------|------|
| Video Search - Working v3 | 4vQo8I4SXEaR5E1A | ✅ 成功 | **推薦使用** |
| Video Search - HTTP Only | tZbljQCFZDOJ4C0s | ❌ 失敗 | body 格式錯誤 |
| Video Search - Debug Simple | e2CMjonwILMUYjp0 | ⚠️ 待測 | Code Node 版本 |
| Video Search - Instant | zC5K3TbFzWGAh0la | ❌ 失敗 | `$httpRequest` 不可用 |
---
## 如何使用
### 方法 1: 直接執行
```bash
# 開啟工作流程
open https://n8n.momentry.ddns.net/workflow/4vQo8I4SXEaR5E1A
```
然後:
1. 點擊 **"Execute Workflow"** ▶️
2. 點擊 **"Show Result"** 節點
3. 查看 JSON 結果
### 方法 2: 修改搜尋關鍵字
1. 點擊 **"Search API"** 節點
2. 修改 `jsonBody`:
```json
"{\"query\":\"您的關鍵字\",\"limit\":5}"
```
3. 儲存並重新執行
---
## API 端點
### Momentry Core API
```
POST https://api.momentry.ddns.net/api/v1/n8n/search
Content-Type: application/json
Body:
{
"query": "charade",
"limit": 3,
"uuid": "可選的影片UUID"
}
```
### 直接測試
```bash
curl -X POST https://api.momentry.ddns.net/api/v1/n8n/search \
-H "Content-Type: application/json" \
-d '{"query":"charade","limit":3}'
```
---
## 已建立的文件
| 文件 | 路徑 | 內容 |
|------|------|------|
| API URL 範例 | `docs/API_URL_EXAMPLES.md` | 完整 URL 和 curl 指令 |
| HTTP Request 指南 | `docs/N8N_HTTP_REQUEST_GUIDE.md` | Node 設定說明 |
| 輸出查看指南 | `docs/N8N_VIEW_OUTPUT_GUIDE.md` | 如何查看結果 |
| MCP 測試報告 | `docs/N8N_MCP_TEST_REPORT.md` | 43 個 MCP 工具 |
| API 修復總結 | `docs/N8N_API_FIX_SUMMARY.md` | 問題修復過程 |
| 工作流程 JSON | `docs/n8n_workflow_video_rag_mcp.json` | 原始工作流程 |
| 測試腳本 | `docs/test_all.sh` | 自動測試腳本 |
---
## 服務狀態
✅ **Momentry Core**: https://api.momentry.ddns.net (Port 3002)
✅ **n8n**: https://n8n.momentry.ddns.net (Port 5678)
✅ **MCP 整合**: 43 個工具可用
---
## 下一步建議
### 1. 建立帶有參數的工作流程
修改現有工作流程,讓 query 和 limit 可以動態輸入:
- 添加 Webhook Node 接收外部請求
- 或使用 Set Node 設定變數
### 2. 建立完整的 RAG 流程
結合 OpenAI
- 搜尋影片片段
- 使用 GPT 生成回答
- 回傳格式化的 RAG 結果
### 3. 自動化監控
- 建立定時執行的工作流程
- 監控 API 健康狀態
- 發送 Telegram/Email 通知
---
## 問題排除
如果再次遇到 "Your request is invalid"
1. 檢查 `specifyBody` 必須設為 `"json"`
2. `jsonBody` 必須是字串格式,不是物件
3. 確保使用正確的 JSON 轉義: `{\"key\":\"value\"}`
---
## 完成!🎉
所有設定已完成:
- ✅ n8n REST API 修復並運作正常
- ✅ MCP 整合完成 (43 個工具)
- ✅ Momentry Core API 可外部存取
- ✅ 成功的工作流程已創建並測試
您可以開始使用 n8n 自動化管理 Momentry Core 了!

View File

@@ -1,301 +0,0 @@
# n8n Video Search 工作流程 - 成功設定指南
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-22 |
| 文件版本 | V1.1 |
| 適用版本 | n8n 2.3.5 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-22 | 創建文件 | Warren | OpenCode |
| V1.1 | 2026-03-26 | 更新 API 範例,新增 X-API-Key 驗證標頭 | OpenCode | deepseek-reasoner |
---
## ✅ 成功案例
| 項目 | 內容 |
|------|------|
| **工作流程名稱** | Video Search - Working v3 |
| **ID** | 4vQo8I4SXEaR5E1A |
| **狀態** | ✅ 成功運作 |
| **測試結果** | 成功搜尋 "charade",返回 3 個結果 |
---
## 正確的 HTTP Request Node 設定
### 成功的設定方式
```json
{
"url": "https://api.momentry.ddns.net/api/v1/n8n/search",
"method": "POST",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "{\"query\":\"charade\",\"limit\":3}",
"options": {
"headers": {
"X-API-Key": "demo_api_key_12345"
}
}
}
```
### 關鍵設定說明
| 設定項目 | 正確值 | 錯誤值 | 說明 |
|---------|--------|--------|------|
| **specifyBody** | `"json"` | `"body"` | 必須選擇 `"json"` |
| **jsonBody** | 字串 `"{...}"` | 物件 `{}` | 必須是 JSON 字串格式 |
| **轉義** | `\"query\"` | `"query"` | 引號需要轉義 |
---
## 工作流程架構
```
┌─────────────────────────┐
│ Manual Trigger │ ← 點擊 "Execute Workflow"
└───────────┬─────────────┘
┌─────────────────────────┐
│ HTTP Request Node │ ← 呼叫 Momentry API
│ - specifyBody: "json" │
│ - jsonBody: "{...}" │
└───────────┬─────────────┘
┌─────────────────────────┐
│ Code Node │ ← 格式化輸出
│ - console.log() │
│ - return json │
└─────────────────────────┘
```
---
## 完整的 Workflow JSON
```json
{
"name": "Video Search - Working v3",
"nodes": [
{
"parameters": {},
"name": "When clicking \"Execute Workflow\"",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [250, 300]
},
{
"parameters": {
"url": "https://api.momentry.ddns.net/api/v1/n8n/search",
"method": "POST",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "{\"query\":\"charade\",\"limit\":3}",
"options": {
"headers": {
"X-API-Key": "demo_api_key_12345"
}
}
},
"name": "Search API",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [450, 300]
},
{
"parameters": {
"jsCode": "const data = $input.first().json;\nconsole.log('Response:', JSON.stringify(data, null, 2));\nreturn [{ json: data }];"
},
"name": "Show Result",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [650, 300]
}
],
"connections": {
"When clicking \"Execute Workflow\"": {
"main": [[{"node": "Search API", "type": "main", "index": 0}]]
},
"Search API": {
"main": [[{"node": "Show Result", "type": "main", "index": 0}]]
}
},
"settings": {"executionOrder": "v1"},
"staticData": null
}
```
---
## 使用步驟
### 步驟 1: 導入工作流程
1. 開啟 n8n UI: `https://n8n.momentry.ddns.net`
2. 點擊 **Add Workflow** (+)
3. 點擊 **Import from File**
4. 選擇上面的 JSON 檔案
### 步驟 2: 執行測試
1. 點擊 **"Execute Workflow"** 按鈕 (▶️)
2. 等待執行完成
3. 點擊 **"Show Result"** 節點
4. 查看右側 **JSON** 面板
### 步驟 3: 修改搜尋關鍵字
1. 點擊 **"Search API"** 節點
2. 修改 `jsonBody`:
```json
"{\"query\":\"您的關鍵字\",\"limit\":5}"
```
3. 點擊 **Save** (Ctrl+S)
4. 重新執行
---
## 成功的回應範例
```json
{
"query": "charade",
"count": 3,
"hits": [
{
"id": "sentence_0006",
"vid": "a1b10138a6bbb0cd",
"start": 48.8,
"end": 55.44,
"title": "Chunk sentence_0006",
"text": "fun plot twists, Woody Dialog and charming performances...",
"score": 0.526,
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
}
]
}
```
---
## 常見錯誤與解決
### ❌ 錯誤 1: "Your request is invalid"
**原因**: `specifyBody` 設為 `"body"` 而不是 `"json"`
**解決**:
```json
✅ "specifyBody": "json"
❌ "specifyBody": "body"
```
### ❌ 錯誤 2: "$httpRequest is not defined"
**原因**: Code Node 中使用 `$httpRequest`,但您的 n8n 版本不支援
**解決**: 使用 **HTTP Request Node** 代替 Code Node
### ❌ 錯誤 3: Body 格式錯誤
**原因**: `body` 使用物件格式 `{query: "..."}`
**解決**: 使用 `jsonBody` 字串格式 `{"query":"..."}`
### ❌ 錯誤 4: JSON 引號未轉義
**原因**: `"{query: "charade"}"` - 引號衝突
**解決**: `\"` 轉義 `"{\"query\":\"charade\"}"`
---
## 測試指令
### 直接測試 API
```bash
curl -X POST https://api.momentry.ddns.net/api/v1/n8n/search \
-H "Content-Type: application/json" \
-H "X-API-Key: demo_api_key_12345" \
-d '{"query":"charade","limit":3}'
```
### 驗證服務狀態
```bash
# 檢查 Momentry Core
curl -H "X-API-Key: demo_api_key_12345" https://api.momentry.ddns.net/api/v1/videos
# 檢查 n8n
curl http://localhost:5678/api/v1/workflows \
-H "X-N8N-API-KEY: <your_api_key>"
```
---
## 服務資訊
| 服務 | URL | 說明 |
|------|-----|------|
| **n8n UI** | https://n8n.momentry.ddns.net | 工作流程管理 |
| **Momentry API** | https://api.momentry.ddns.net | 影片搜尋 API |
| **工作流程** | https://n8n.momentry.ddns.net/workflow/4vQo8I4SXEaR5E1A | 成功案例 |
---
## 進階使用
### 添加 Webhook 觸發器
如果你想從外部呼叫這個工作流程:
1. 在第一個節點前添加 **Webhook** Node
2. 設定:
```
Method: POST
Path: video-search
Response Mode: Last Node
```
3. 將 Webhook 連接到 Search API
4. 儲存並執行
5. 使用生成的 Webhook URL 呼叫:
```bash
curl -X POST <webhook_url> \
-d '{"query":"charade","limit":3}'
```
### 使用動態變數
修改 jsonBody 使用表達式:
```json
"{\"query\":\"={{ $json.query }}\",\"limit\":{{ $json.limit }}}"
```
然後在前面添加 Set Node 設定變數。
---
## 相關文件
- `docs/N8N_SETUP_COMPLETE.md` - 完整設定總結
- `docs/N8N_HTTP_REQUEST_GUIDE.md` - HTTP Request 詳細指南
- `docs/API_URL_EXAMPLES.md` - API URL 範例
---
## 完成!🎉
您現在擁有一個可以成功搜尋影片的 n8n 工作流程!
**關鍵成功要素**:
1. ✅ 使用 `specifyBody: "json"`
2. ✅ 使用 `jsonBody` 字串格式
3. ✅ 正確轉義 JSON 引號
4. ✅ 使用外部 API URL (`https://api.momentry.ddns.net`)

View File

@@ -1,141 +0,0 @@
# n8n 工作流程輸出查看指南
## 問題:"Node executed successfully but no output data"
這是正常的!在 n8n 中,你需要**點擊節點**才能看到輸出資料。
---
## 如何查看輸出資料
### 方法 1: 點擊節點查看
1. 執行工作流程後(點擊 Execute Workflow
2. **點擊任何一個節點**Node
3. 在右側面板會顯示該節點的輸出
4. 查看 **JSON** 分頁看到完整資料
### 方法 2: 查看執行記錄
1. 執行工作流程
2. 點擊左側的 **"Executions"** 選單
3. 找到最近的執行記錄
4. 點擊展開查看每個節點的輸出
### 方法 3: 使用 Respond to Webhook
如果你想直接看到結果,添加一個 Respond to Webhook 節點:
```javascript
// 在最後一個節點之後添加:
Node: Respond to Webhook
Response Mode: Last Node
Response Body: {{ JSON.stringify($json) }}
```
---
## 新增的工作流程
### ✅ Video Search - Debug Simple
- **ID**: e2CMjonwILMUYjp0
- **URL**: https://n8n.momentry.ddns.net/workflow/e2CMjonwILMUYjp0
這個版本保證有輸出!
### 執行步驟
1. 開啟工作流程
2. 點擊 **"Execute Workflow"** ▶️
3. 等待執行完成(約 3-5 秒)
4. **點擊最後一個節點** "Step 3 - Show Results"
5. 查看右側的 **JSON** 分頁
### 預期看到的輸出
```json
{
"status": "SUCCESS",
"query": "charade",
"totalResults": 2,
"firstHit": {
"text": "fun plot twists, Woody Dialog and charming perform...",
"time": "48.8s - 55.44s",
"score": "53%"
}
}
```
或如果失敗:
```json
{
"status": "FAILED",
"error": "error message here"
}
```
---
## 截圖說明
### 執行後的畫面
```
┌─────────────────────────────────────┐
│ When clicking "Execute Workflow" │ ✅ (綠色勾)
├─────────────────────────────────────┤
│ Step 1 - Set Query │ ✅ (綠色勾)
├─────────────────────────────────────┤
│ Step 2 - Call API │ ✅ (綠色勾) ← 點擊這裡
├─────────────────────────────────────┤
│ Step 3 - Show Results │ ✅ (綠色勾) ← 或這個
└─────────────────────────────────────┘
[右側面板 - 點擊節點後顯示]
┌─────────────────────────────────────┐
│ Node: Step 2 - Call API │
│ │
│ [JSON] [Table] [Schema] │ ← 點擊 JSON
│ │
│ { │
│ "success": true, │
│ "query": "charade", │
│ ... │
│ } │
└─────────────────────────────────────┘
```
---
## 快速測試
如果不想用瀏覽器,直接在這裡執行:
```bash
# 開啟簡化版工作流程
open https://n8n.momentry.ddns.net/workflow/e2CMjonwILMUYjp0
```
然後:
1. 點擊 **Execute Workflow**
2. 點擊 **"Step 3 - Show Results"** 節點
3. 看右側 JSON 面板
---
## 如果仍然看不到資料
檢查:
1. ✅ 工作流程已儲存
2. ✅ 點擊了正確的節點(有綠色勾的)
3. ✅ 右側面板已展開(點擊 JSON 分頁)
4. ✅ 沒有紅色錯誤訊息
---
## 聯絡支援
如果還是有問題,請告訴我:
1. 你點擊的是哪個節點?
2. 右側面板顯示什麼?
3. 有沒有紅色錯誤訊息?

View File

@@ -1,169 +0,0 @@
# Momentry Video RAG MCP Workflow
## 工作流程資訊
- **名稱**: Momentry Video RAG MCP
- **ID**: WlVvpX2OeKK83QOK
- **Webhook Path**: `video-rag-mcp`
- **狀態**: ✅ Active (已啟動)
- **建立時間**: 2026-03-22
## 工作流程架構
```
┌─────────────────┐ ┌──────────────────────┐ ┌───────────────────┐ ┌─────────────────┐
│ Webhook │────▶│ Search Momentry │────▶│ Process RAG │────▶│ Respond to │
│ Trigger │ │ Core │ │ Results │ │ Webhook │
└─────────────────┘ └──────────────────────┘ └───────────────────┘ └─────────────────┘
│ POST http://localhost:5678/webhook/video-rag-mcp
{
"query": "搜尋關鍵字",
"limit": 5,
"uuid": "可選的影片UUID"
}
```
## Node 說明
### 1. Webhook Trigger
- **類型**: Webhook
- **Method**: POST
- **Path**: `video-rag-mcp`
- **Response Mode**: Last Node (等待最後一個節點完成後回應)
### 2. Search Momentry Core
- **類型**: HTTP Request
- **URL**: `http://localhost:3002/api/v1/n8n/search`
- **Method**: POST
- **Body**:
```json
{
"query": "搜尋關鍵字",
"limit": 5,
"uuid": "可選的影片UUID"
}
```
- **Timeout**: 30秒
### 3. Process RAG Results
- **類型**: Code (JavaScript)
- **功能**:
- 處理 Momentry Core 搜尋結果
- 格式化 hits 為結構化資料
- 建立 RAG context用於 LLM 問答)
- 計算相關度百分比
**輸出格式**:
```json
{
"success": true,
"query": "搜尋關鍵字",
"totalFound": 5,
"context": "[1] 文本內容... (Video: 影片標題, Time: 10s-20s)\n\n[2] ...",
"results": [
{
"index": 1,
"id": "chunk_id",
"title": "影片標題",
"text": "文本內容",
"startTime": 10,
"endTime": 20,
"relevance": "85%",
"videoUuid": "uuid",
"mediaUrl": "影片URL",
"deepLink": "影片URL#t=10,20"
}
]
}
```
### 4. Respond to Webhook
- **類型**: Respond to Webhook
- **Response**: JSON 格式結果
- **Status Code**: 200
## 使用方式
### 直接呼叫 Webhook
```bash
curl -X POST http://localhost:5678/webhook/video-rag-mcp \
-H "Content-Type: application/json" \
-d '{
"query": "charade",
"limit": 5
}'
```
### 指定特定影片搜尋
```bash
curl -X POST http://localhost:5678/webhook/video-rag-mcp \
-H "Content-Type: application/json" \
-d '{
"query": "audrey hepburn",
"limit": 3,
"uuid": "a1b10138a6bbb0cd"
}'
```
### 在 n8n 工作流程中使用
可以將此 Webhook 作為子工作流程觸發器,或使用 HTTP Request Node 呼叫:
```json
{
"name": "Call Video RAG",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "http://localhost:5678/webhook/video-rag-mcp",
"method": "POST",
"body": {
"query": "={{ $json.searchTerm }}",
"limit": 5
}
}
}
```
## RAG Context 用途
工作流程產生的 `context` 欄位可直接用於 LLM 提示:
```javascript
// Example: 使用 context 進行問答
const prompt = `
基於以下影片片段資訊回答問題:
${context}
問題:${userQuestion}
請根據上述內容提供準確的答案。
`;
```
## 相關文件
- [Momentry Core API 文件](./API_ACCESS.md)
- [n8n MCP 測試報告](./N8N_MCP_TEST_REPORT.md)
- [N8N_DEMO_WORKFLOW.md](./N8N_DEMO_WORKFLOW.md) - 完整工作流程設計
## MCP 建立指令
此工作流程是透過 MCP 工具建立的:
```bash
# 使用 MCP 建立工作流程
node create_workflow.js | mcp-n8n
# 使用 MCP 啟動工作流程
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"n8n_activate_workflow","arguments":{"workflowId":"WlVvpX2OeKK83QOK"}}}' | mcp-n8n
```
## 工作流程檔案
- 原始檔案: `docs/n8n_workflow_video_rag_mcp.json`

View File

@@ -1,450 +0,0 @@
# Node.js 開發指南
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-16 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
---
## 概述
本文檔說明 Momentry 專案中 Node.js 環境的配置、管理與監控。
---
## 當前狀態
| 項目 | 狀態 |
|------|------|
| 系統預設 Node.js | v25.8.1 |
| 鎖定 Node.js (n8n) | v22.22.1 |
| n8n 版本 | v2.3.5 |
| npm 路徑 (預設) | /opt/homebrew/bin/npm |
| node 路徑 (預設) | /opt/homebrew/bin/node |
| node@22 路徑 | /opt/homebrew/opt/node@22/bin/node |
---
## 版本策略
### 為什麼需要版本鎖定?
n8n 要求 Node.js 版本為 22.x LTS。系統預設的 Node.js 25.x 會導致:
- n8n 啟動警告
- Task-runner 執行錯誤
- 相容性問題
### 版本對照表
| 用途 | Node.js 版本 | 路徑 |
|------|-------------|------|
| 系統預設 | 25.8.1 | /opt/homebrew/bin/node |
| n8n 專用 | 22.22.1 | /opt/homebrew/opt/node@22/bin/node |
| 開發測試 | 22.x | /opt/homebrew/opt/node@22/bin/node |
---
## 安裝步驟
### 安裝特定版本 Node.js
```bash
# 安裝 Node.js 22.x (LTS)
brew install node@22
# 驗證安裝
/opt/homebrew/opt/node@22/bin/node --version
# v22.22.1
```
### 安裝全局工具
```bash
# 使用 node@22 安裝全局工具
/opt/homebrew/opt/node@22/bin/npm install -g <package-name>
# 例如安裝 n8n
/opt/homebrew/opt/node@22/bin/npm install -g n8n
```
---
## 使用方式
### 方式 1直接使用完整路徑
```bash
# 使用 node@22
/opt/homebrew/opt/node@22/bin/node script.js
# 使用 npm@22
/opt/homebrew/opt/node@22/bin/npm install
```
### 方式 2修改 PATH 環境變數
在 shell 配置檔案 (~/.zshrc) 中加入:
```bash
# 優先使用 node@22
export PATH="/opt/homebrew/opt/node@22/bin:$PATH"
# 重新載入
source ~/.zshrc
# 驗證
node --version
# v22.22.1
```
### 方式 3使用 nvm (推薦)
```bash
# 安裝 nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
# 安裝 Node.js 22.x
nvm install 22
nvm use 22
# 設定預設版本
nvm alias default 22
```
---
## n8n 配置
### 環境變數設定
在 plist 或啟動腳本中設定:
```xml
<key>ProgramArguments</key>
<array>
<string>/opt/homebrew/opt/node@22/bin/node</string>
<string>/opt/homebrew/lib/node_modules/n8n/bin/n8n</string>
<string>start</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/opt/homebrew/opt/node@22/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
</dict>
```
### Task Runner 配置
n8n Task Runner 用於執行 Code node 中的 JavaScript 代碼。
確保 PATH 中 node@22 在前面,這樣 task-runner 子進程會使用正確版本。
---
## 監控配置
### 健康檢查
`monitor/service/health_check.sh` 已包含 Node.js 版本檢查:
```bash
# 檢查 n8n 進程是否使用正確的 Node.js 版本
check_node() {
local LOCKED_NODE_VERSION="22"
# ...
}
```
### 執行監控
```bash
# 單獨檢查 Node.js
bash /Users/accusys/momentry_core_0.1/monitor/control/monitor_control.sh check node
# 檢查 Python
bash /Users/accusys/momentry_core_0.1/monitor/control/monitor_control.sh check python
```
### 查看版本狀態
```bash
# 查詢資料庫中的版本記錄
psql -U accusys -h localhost -d momentry -c "SELECT * FROM node_version_baseline;"
```
---
## 應用Registry
記錄系統中所有使用 Node.js 的應用程式。
| 應用 | Node.js 版本 | 執行路徑 | Port | 狀態 | 說明 |
|------|-------------|----------|------|------|------|
| n8n | 22.22.1 | /opt/homebrew/opt/node@22/bin/node | 5678/5679 | ✅ 執行中 | 工作流自動化平台 |
| markdownlint-cli | 25.x | /opt/homebrew/bin/npm | - | ✅ 已安裝 | Markdown lint 工具 |
| - | - | - | - | - | 新增應用請填入此表 |
---
## 新增 Node.js 應用決策
### 決策樹
```
新應用需要 Node.js?
├─ 支援 Node.js 22.x ────→ 使用現有 node@22
│ (路徑: /opt/homebrew/opt/node@22/bin/node)
├─ 需要特定版本 (如 18.x, 20.x) ────→ 安裝新版本 node@XX
│ │
│ ├─ 建立獨立目錄: /opt/homebrew/opt/node@XX/
│ ├─ 更新本文檔 Registry
│ └─ 建立獨立 plist 使用完整路徑
└─ 需要最新版本 ────→ 使用系統預設 node@25
(路徑: /opt/homebrew/bin/node)
```
### 步驟清單
1. **確認版本需求**
```bash
# 查看應用支援的 Node.js 版本
# 通常在 package.json 或官方文件中說明
```
2. **選擇現有版本或安裝新版本**
- 若支援 22.x → 使用 node@22
- 若需要特定版本 → 使用完整路徑隔離
3. **安裝新版本 (如需要)**
```bash
# 安裝特定版本
brew install node@20
# 或
brew install node@18
# 驗證安裝
/opt/homebrew/opt/node@20/bin/node --version
```
4. **建立隔離的服務配置**
- 使用完整路徑,不要依賴 PATH
- plist 中明确指定 node 路徑
- 設定獨立的環境變數
5. **更新文件**
- 更新本文檔 Registry 表格
- 新增應用的安裝文檔
- 更新監控配置
### 隔離原則
| 原則 | 說明 |
|------|------|
| **完整路徑** | 使用 `/opt/homebrew/opt/node@XX/bin/node` 而非 `node` |
| **獨立 PATH** | plist 中設定隔離的 PATH 環境變數 |
| **獨立目錄** | 不同版本的 node 安裝在各自目錄 |
| **獨立全局套件** | 每個版本有自己的 node_modules |
---
## 版本衝突處理
### 常見衝突場景
| 場景 | 原因 | 解決方案 |
|------|------|----------|
| n8n 顯示版本警告 | 使用 Node.js 25.x 啟動 | 確認 plist 使用 node@22 路徑 |
| task-runner 執行錯誤 | 子進程繼承錯誤版本 | 在 plist 中設定正確的 PATH |
| 全域套件找不到 | 跨版本安裝 | 使用正確版本的 npm |
| Port 被佔用 | 多個應用使用相同 Port | 分配獨立 Port |
### 診斷命令
```bash
# 1. 查看程序使用的 node 版本
ps aux | grep node
# 2. 查看程序 PID 使用的執行檔
lsof -p <PID> | grep bin/node
# 3. 確認路徑優先順序
echo $PATH
# 4. 查看 launchctl 服務的環境變數
sudo launchctl list | grep <service-name>
# 5. 檢查 Port 佔用
lsof -i :<port-number>
```
### 預防措施
1. **每次新增服務都更新 Registry**
2. **使用完整路徑而非 PATH**
3. **建立服務時指定版本**
4. **定期檢查執行中的程序**
---
## 故障排除
### 問題n8n 顯示 Node.js 版本警告
**原因**n8n 檢測到 Node.js 版本不是 22.x
**解決方案**
1. 確認 plist 中 ProgramArguments 使用正確路徑
2. 確認 PATH 環境變數中 node@22 在前面
3. 重載服務:
```bash
sudo launchctl unload /Library/LaunchDaemons/com.momentry.n8n.main.plist
sudo launchctl unload /Library/LaunchDaemons/com.momentry.n8n.worker.plist
sudo launchctl load /Library/LaunchDaemons/com.momentry.n8n.main.plist
sudo launchctl load /Library/LaunchDaemons/com.momentry.n8n.worker.plist
```
### 問題Task Runner 使用錯誤版本
**原因**PATH 環境變數設定不正確
**診斷**
```bash
# 查看 task-runner 進程使用的 node
ps aux | grep "task-runner"
lsof -p <PID> | grep "txt" | grep node
```
**解決方案**
更新 plist 中的 PATH確保 `/opt/homebrew/opt/node@22/bin` 在最前面。
### 問題npm 全域套件找不到
**原因**:使用錯誤的 node 版本安裝
**解決方案**
```bash
# 確認使用的是哪個 node
which node
node --version
# 使用正確版本重新安裝
/opt/homebrew/opt/node@22/bin/npm install -g <package>
```
---
## 版本切換腳本
建立快速切換腳本 `~/bin/switch-node.sh`
```bash
#!/bin/bash
VERSION=$1
case $VERSION in
22)
export PATH="/opt/homebrew/opt/node@22/bin:$PATH"
echo "切換到 Node.js 22.x"
;;
system|25)
export PATH="/opt/homebrew/bin:$PATH"
echo "切換到系統 Node.js 25.x"
;;
*)
echo "用法: $0 {22|system}"
echo " 22 - Node.js 22.x (n8n)"
echo " system - Node.js 25.x (系統預設)"
exit 1
;;
esac
node --version
```
賦予執行權限:
```bash
chmod +x ~/bin/switch-node.sh
```
使用方式:
```bash
~/bin/switch-node.sh 22 # 切換到 22.x
~/bin/switch-node.sh system # 切換到系統預設
```
---
## 常用指令
```bash
# 查看 node 版本
node --version
# 查看 npm 版本
npm --version
# 查看 node 安裝位置
which node
# 查看 node@22 版本
/opt/homebrew/opt/node@22/bin/node --version
# 安裝全域套件 (使用 node@22)
/opt/homebrew/opt/node@22/bin/npm install -g n8n
# 檢查 n8n 進程
ps aux | grep n8n | grep -v grep
# 檢查 task-runner
ps aux | grep task-runner | grep -v grep
# 查看 task-runner 使用的 node 版本
lsof -p <PID> | grep "txt" | grep node
```
---
## 檔案位置
| 類型 | 路徑 | 說明 |
|------|------|------|
| node (系統) | /opt/homebrew/bin/node | 預設 node |
| node@22 | /opt/homebrew/opt/node@22/bin/node | n8n 專用 |
| npm (系統) | /opt/homebrew/bin/npm | 預設 npm |
| npm@22 | /opt/homebrew/opt/node@22/bin/npm | n8n 專用 |
| n8n 安裝 | /opt/homebrew/lib/node_modules/n8n/ | n8n 目錄 |
| n8n main plist | /Library/LaunchDaemons/com.momentry.n8n.main.plist | 開機啟動 |
| n8n worker plist | /Library/LaunchDaemons/com.momentry.n8n.worker.plist | 開機啟動 |
---
## 版本速查
| 版本 | 用途 | 路徑 |
|------|------|------|
| 22.22.1 | n8n 專用 | /opt/homebrew/opt/node@22/bin/node |
| 25.x | 系統預設 | /opt/homebrew/bin/node |
---
## 相關文件
- [INSTALL_N8N.md](./INSTALL_N8N.md) - n8n 安裝指南 (包含 Node.js 配置範例)
- [RUST_DEVELOPMENT.md](./RUST_DEVELOPMENT.md) - Rust 開發規範
- [monitor_config.yaml](../monitor/config/monitor_config.yaml) - 監控配置
- [node_monitor.sh](../monitor/service/node_monitor.sh) - Node.js 監控腳本

View File

@@ -1,430 +0,0 @@
# OpenCode 使用規範
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-21 |
| 文件版本 | V1.1 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 |
|------|------|------|--------|
| V1.0 | 2026-03-21 | 創建文件 | Warren |
| V1.1 | 2026-03-21 | 新增 MCP 設定章節 | OpenCode |
---
## 概述
本文檔定義使用 OpenCode 進行專案開發的最佳實踐,確保開發效率和程式碼品質。
---
## 任務管理
### 任務批次策略
**原則**: 一次處理 1-2 個功能,完成後驗證,再繼續下一個。
| 批次大小 | 適用場景 | 說明 |
|----------|----------|------|
| 1 個 | 緊急修復、簡單任務 | 快速完成 |
| 2-3 個 | 一般功能開發 | 平衡效率與品質 |
| 3+ 個 | 大型重構 | 需要更詳細的追蹤 |
### 驗證流程
每個任務完成後必須執行:
```bash
# 1. 編譯檢查
cargo check
# 2. Lint 檢查
cargo clippy --lib
# 3. 單元測試
cargo test --lib
# 4. 格式化檢查
cargo fmt -- --check
```
---
## 溝通模式
### 有效的任務描述
**建議格式**:
```
執行 [功能名稱]
- 優先級: 高/中/低
- 驗收標準: [明確的標準]
- 約束: [限制條件]
```
**範例**:
```
實作 monitor_jobs 表
- 優先級: 高
- 驗收標準: CRUD 操作可用,單元測試通過
- 約束: 使用現有架構
```
### 明確的暫停點
在每個階段完成後主動詢問:
```
[任務] 完成。要繼續:
1. 實作功能 A
2. 添加測試
3. 更新文檔
```
### 不建議的溝通
| 模式 | 問題 | 建議 |
|------|------|------|
| 一次指定太多項目 | 增加複雜度,難以追蹤 | 分批處理 |
| 模糊的任務描述 | 難以評估進度 | 使用明確的驗收標準 |
| 從不驗證 | 累積問題 | 每步驟完成後驗證 |
---
## 決策點
### 常見決策點
| 階段 | 問題 | 選項 |
|------|------|------|
| 開始 | 如何開始? | 先了解現況 / 直接實作 |
| 實作 | 實作方式? | 保持現有架構 / 重構 |
| 驗證 | 通過了嗎? | 繼續 / 修復問題 |
| 完成 | 還有什麼? | 下一個任務 / 結束 |
### 決策準則
1. **安全優先**: 破壞性變更需要明確確認
2. **驗證後繼續**: 每步驟完成後驗證
3. **文檔同步**: 變更後更新文檔
---
## 文檔使用
### 必讀文檔
| 文檔 | 用途 | 查閱時機 |
|------|------|----------|
| `AGENTS.md` | 專案規範 | 每次對話開始 |
| `docs/*.md` | 技術規格 | 功能實作前 |
### 文檔更新時機
| 變更類型 | 需要更新 |
|----------|----------|
| 新功能 | `AGENTS.md` + 相關技術文檔 |
| 架構變更 | `ARCHITECTURE_EVALUATION.md` |
| 問題修復 | `PENDING_ISSUES.md` |
| 環境變更 | `INSTALL_*.md` |
---
## 審查清單
### 實作完成後檢查
- [ ] `cargo clippy --lib` 通過
- [ ] `cargo test --lib` 通過
- [ ] `cargo fmt -- --check` 通過
- [ ] 文檔已更新
- [ ] 新功能有單元測試
- [ ] Pre-commit hook 通過
### 對話結束前
- [ ] 所有變更已驗證
- [ ] 文檔已同步
- [ ] 下一步計劃明確
---
## 範例流程
### 範例 1: 實作新功能
```
用戶: 實作使用者認證功能
OpenCode:
1. 分析現有架構
2. 創建任務清單
3. 實作核心功能
4. 添加單元測試
5. 更新文檔
6. 驗證通過
用戶: 完成,繼續下一個
```
### 範例 2: 修復問題
```
用戶: 修復登入超時問題
OpenCode:
1. 重現問題
2. 分析根因
3. 實作修復
4. 驗證修復
5. 添加測試防止回歸
用戶: 確認修復完成
```
---
## 常見問題
### Q: 如何避免一次處理太多?
**A**: 使用 `/todo` 追蹤任務,分批處理。
### Q: 如何確保文檔同步?
**A**: 每個任務完成後檢查是否需要更新文檔。
### Q: 何時應該結束對話?
**A**: 當主要任務完成,且沒有緊急的後續步驟時。
---
## MCP 設定
### 設定檔案
OpenCode MCP 設定位於 `~/.config/opencode/opencode.json`
### 已啟用的 MCP Servers
| Server | 用途 | 命令 |
|--------|------|------|
| gitea | Gitea API 操作 | `/opt/homebrew/bin/gitea-mcp-server` |
| n8n | n8n Workflow 操作 | `/opt/homebrew/bin/mcp-n8n` |
| postgres | PostgreSQL 資料庫查詢 | `/opt/homebrew/bin/mcp-server-postgres` |
| redis | Redis 快取操作 | `/opt/homebrew/bin/mcp-server-redis` |
| qdrant | Qdrant 向量搜尋 | `/opt/homebrew/bin/mcp-server-qdrant` |
| filesystem | 檔案系統操作 | `/opt/homebrew/bin/mcp-server-filesystem` |
### MCP 設定格式
**Schema 參考**: `https://opencode.ai/config.json`
**必要欄位**:
| 欄位 | 類型 | 說明 |
|------|------|------|
| `type` | string | `"local"``"remote"` |
| `command` | array | 命令和參數local 必要) |
| `url` | string | 遠端 URLremote 必要) |
**可選欄位**:
| 欄位 | 類型 | 說明 |
|------|------|------|
| `environment` | object | 環境變數local only |
| `enabled` | boolean | 是否啟用 |
| `timeout` | number | 請求超時(毫秒) |
| `headers` | object | 請求頭remote only |
**Local MCP 範例**:
```json
{
"mcp": {
"gitea": {
"type": "local",
"enabled": true,
"command": [
"/opt/homebrew/bin/gitea-mcp-server",
"-token", "<GITEA_TOKEN>",
"-host", "http://localhost:3000"
]
},
"n8n": {
"type": "local",
"enabled": true,
"command": ["/opt/homebrew/bin/mcp-n8n"],
"environment": {
"N8N_BASE_URL": "http://localhost:5678",
"N8N_API_KEY": "<N8N_API_KEY>"
}
},
"postgres": {
"type": "local",
"enabled": true,
"command": ["/opt/homebrew/bin/mcp-server-postgres"],
"environment": {
"DATABASE_URL": "postgresql://accusys:accusys@localhost:5432/momentry"
}
},
"redis": {
"type": "local",
"enabled": true,
"command": ["/opt/homebrew/bin/mcp-server-redis"],
"environment": {
"REDIS_URL": "redis://:accusys@localhost:6379"
}
},
"qdrant": {
"type": "local",
"enabled": true,
"command": ["/opt/homebrew/bin/mcp-server-qdrant"],
"environment": {
"QDRANT_URL": "http://localhost:6333",
"QDRANT_API_KEY": ""
}
},
"filesystem": {
"type": "local",
"enabled": true,
"command": ["/opt/homebrew/bin/mcp-server-filesystem"],
"args": ["/Users/accusys/momentry"]
}
}
}
```
**Remote MCP 範例**:
```json
{
"mcp": {
"jira": {
"type": "remote",
"url": "https://jira.example.com/mcp",
"enabled": true,
"headers": {
"Authorization": "Bearer your-token"
}
}
}
}
```
### n8n MCP 工具 (43 個)
#### Workflows (10)
| 工具 | 說明 |
|------|------|
| `n8n_list_workflows` | 列出所有 workflows |
| `n8n_get_workflow` | 取得 workflow 詳情 |
| `n8n_create_workflow` | 建立新 workflow |
| `n8n_update_workflow` | 更新 workflow |
| `n8n_delete_workflow` | 刪除 workflow |
| `n8n_activate_workflow` | 啟用 workflow |
| `n8n_deactivate_workflow` | 停用 workflow |
| `n8n_execute_workflow` | 執行 workflow |
| `n8n_get_workflow_tags` | 取得 workflow 標籤 |
| `n8n_update_workflow_tags` | 更新 workflow 標籤 |
#### Executions (3)
| 工具 | 說明 |
|------|------|
| `n8n_list_executions` | 列出執行記錄 |
| `n8n_get_execution` | 取得執行詳情 |
| `n8n_delete_execution` | 刪除執行記錄 |
#### Data Tables (8)
| 工具 | 說明 |
|------|------|
| `n8n_list_datatables` | 列出資料表 |
| `n8n_create_datatable` | 建立資料表 |
| `n8n_get_datatable` | 取得資料表結構 |
| `n8n_get_datatable_rows` | 取得資料表列 |
| `n8n_insert_datatable_rows` | 插入資料列 |
| `n8n_update_datatable_rows` | 更新資料列 |
| `n8n_upsert_datatable_row` | 插入或更新資料列 |
| `n8n_delete_datatable_rows` | 刪除資料列 |
#### Tags (5)
| 工具 | 說明 |
|------|------|
| `n8n_list_tags` | 列出所有標籤 |
| `n8n_get_tag` | 取得標籤 |
| `n8n_create_tag` | 建立標籤 |
| `n8n_update_tag` | 更新標籤 |
| `n8n_delete_tag` | 刪除標籤 |
#### Credentials (4)
| 工具 | 說明 |
|------|------|
| `n8n_list_credentials` | 列出憑證 |
| `n8n_create_credential` | 建立憑證 |
| `n8n_delete_credential` | 刪除憑證 |
| `n8n_get_credential_schema` | 取得憑證 schema |
#### Users (3)
| 工具 | 說明 |
|------|------|
| `n8n_list_users` | 列出使用者 |
| `n8n_get_user` | 取得使用者 |
| `n8n_delete_user` | 刪除使用者 |
#### Variables (3)
| 工具 | 說明 |
|------|------|
| `n8n_list_variables` | 列出變數 |
| `n8n_create_variable` | 建立變數 |
| `n8n_delete_variable` | 刪除變數 |
#### 其他 (7)
| 工具 | 說明 |
|------|------|
| `n8n_list_projects` | 列出專案 |
| `n8n_create_project` | 建立專案 |
| `n8n_update_project` | 更新專案 |
| `n8n_delete_project` | 刪除專案 |
| `n8n_generate_audit` | 產生安全審計報告 |
| `n8n_health_check` | 健康檢查 |
| `n8n_trigger_webhook` | 觸發 webhook |
### 安裝 n8n MCP
```bash
npm install -g @nextoolsolutions/mcp-n8n
```
### n8n API Key 設定
1. 開啟 n8n UI (http://localhost:5678)
2. 前往 Settings → API
3. 建立 API Key
4. 將 API Key 加入 `opencode.json``N8N_API_KEY`
### 驗證 MCP 運作
```bash
# 測試 MCP server
N8N_BASE_URL=https://n8n.momentry.ddns.net \
N8N_API_KEY="your-key" \
mcp-n8n
# 測試工具列表
echo '{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": {"name": "test", "version": "1.0"}}}
{"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}}' | \
N8N_BASE_URL=https://n8n.momentry.ddns.net \
N8N_API_KEY="your-key" \
mcp-n8n
```
---
## 相關文件
- [AGENTS.md](../AGENTS.md) - 專案開發規範
- [ARCHITECTURE_EVALUATION.md](./ARCHITECTURE_EVALUATION.md) - 架構優化評估
- [PENDING_ISSUES.md](./PENDING_ISSUES.md) - 待解決問題追蹤
- [INSTALL_N8N.md](./INSTALL_N8N.md) - n8n 安裝指南

View File

@@ -1,535 +0,0 @@
# OpenCode MCP Servers 安裝指南
| 項目 | 內容 |
|------|------|
| 建立者 | OpenCode |
| 建立時間 | 2026-03-24 |
| 文件版本 | V1.1 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-24 | 創建文件 | OpenCode | OpenCode / big-pickle |
| V1.1 | 2026-03-24 | 新增 sentry, context7, playwright MCP | OpenCode | OpenCode / big-pickle |
---
## 概述
本文檔說明如何在 macOS 上為 OpenCode 安裝和配置 MCP (Model Context Protocol) Servers透過標準化的協定讓 AI 助手能夠存取各種外部服務和工具。
MCP Servers 提供:
- 標準化的工具調用介面
- 安全的資源存取控制
- 統一的配置和管理方式
---
## 已安裝的 MCP Servers
| Server | 安裝方式 | 用途 | 狀態 |
|--------|----------|------|------|
| gitea | Homebrew | Gitea API 操作 | ✅ |
| n8n | NPM (@nextoolsolutions/mcp-n8n) | n8n Workflow 管理 | ✅ |
| postgres | NPM (@modelcontextprotocol/server-postgres) | PostgreSQL 資料庫查詢 | ✅ |
| redis | NPM (@modelcontextprotocol/server-redis) | Redis 快取操作 | ✅ |
| mongodb | NPM (mongodb-mcp-server) | MongoDB 文件資料庫 | ✅ |
| qdrant | Python (qdrant/mcp-server-qdrant) | Qdrant 向量搜尋 | ✅ |
| filesystem | NPM (@modelcontextprotocol/server-filesystem) | 檔案系統操作 | ✅ |
| sentry | NPM (@sentry/mcp-server) | 錯誤追蹤和監控 | ⏳ |
| context7 | NPM (@upstash/context7-mcp) | 技術文檔搜尋 | ✅ |
| playwright | NPM (@playwright/mcp) | 瀏覽器自動化 | ✅ |
---
## 前置條件
- OpenCode 已安裝
- Node.js (用於 NPM 套件)
- Python 3.11+ (用於 qdrant-mcp)
- 相關服務已運行 (PostgreSQL, Redis, Qdrant, Gitea, n8n, MongoDB)
---
## 安裝步驟
### Step 1: 安裝 NPM MCP Servers
```bash
npm install -g @modelcontextprotocol/server-postgres
npm install -g @modelcontextprotocol/server-redis
npm install -g @modelcontextprotocol/server-filesystem
npm install -g @modelcontextprotocol/server-everything
npm install -g @nextoolsolutions/mcp-n8n
npm install -g mongodb-mcp-server
npm install -g @sentry/mcp-server
npm install -g @upstash/context7-mcp
npm install -g @playwright/mcp
```
**驗證**:
```bash
which mcp-server-postgres
which mcp-server-redis
which mcp-server-filesystem
which mcp-n8n
which mongodb-mcp-server
which sentry-mcp
which context7-mcp
which playwright-mcp
```
---
### Step 2: 安裝 gitea-mcp-server
```bash
brew install gitea-mcp-server
```
**驗證**:
```bash
which gitea-mcp-server
```
---
### Step 3: 安裝 MongoDB MCP Server (已包含在 Step 1)
MongoDB MCP 已透過 NPM 安裝:
```bash
npm install -g mongodb-mcp-server
```
---
### Step 4: 安裝 Qdrant MCP Server (Python)
```bash
cd /tmp
git clone https://github.com/qdrant/mcp-server-qdrant.git
cd mcp-server-qdrant
/opt/homebrew/bin/python3.11 -m pip install -e .
```
**驗證**:
```bash
which mcp-server-qdrant
```
---
### Step 5: 配置 OpenCode MCP 設定
編輯 `~/.config/opencode/opencode.json`
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"gitea": {
"type": "local",
"enabled": true,
"command": [
"/opt/homebrew/bin/gitea-mcp-server",
"-token",
"<GITEA_TOKEN>",
"-host",
"http://localhost:3000"
]
},
"n8n": {
"type": "local",
"enabled": true,
"command": ["/opt/homebrew/bin/mcp-n8n"],
"environment": {
"N8N_BASE_URL": "http://localhost:5678",
"N8N_API_KEY": "<N8N_API_KEY>"
}
},
"postgres": {
"type": "local",
"enabled": true,
"command": [
"/opt/homebrew/bin/mcp-server-postgres",
"postgresql://accusys:accusys@localhost:5432/momentry"
]
},
"redis": {
"type": "local",
"enabled": true,
"command": [
"/opt/homebrew/bin/mcp-server-redis",
"redis://:accusys@localhost:6379"
]
},
"mongodb": {
"type": "local",
"enabled": true,
"command": ["/opt/homebrew/bin/mongodb-mcp-server"],
"environment": {
"MONGODB_URI": "mongodb://localhost:27017"
}
},
"qdrant": {
"type": "local",
"enabled": true,
"command": ["/opt/homebrew/bin/mcp-server-qdrant"],
"environment": {
"QDRANT_URL": "http://localhost:6333",
"QDRANT_API_KEY": ""
}
},
"filesystem": {
"type": "local",
"enabled": true,
"command": [
"/opt/homebrew/bin/mcp-server-filesystem",
"/Users/accusys/momentry"
]
},
"sentry": {
"type": "local",
"enabled": true,
"command": [
"/opt/homebrew/bin/sentry-mcp",
"--access-token",
"<SENTRY_ACCESS_TOKEN>"
]
},
"context7": {
"type": "local",
"enabled": true,
"command": ["/opt/homebrew/bin/context7-mcp"]
},
"playwright": {
"type": "local",
"enabled": true,
"command": ["/opt/homebrew/bin/playwright-mcp"]
}
}
}
```
---
### Step 6: 驗證 MCP Servers
```bash
# 列出所有 MCP servers
opencode mcp ls
# 或在 OpenCode 中執行
/mcps
```
---
## MCP Server 工具說明
### gitea
| 工具 | 說明 |
|------|------|
| list_repos | 列出倉庫 |
| get_repo | 取得倉庫詳情 |
| list_issues | 列出 Issues |
| create_issue | 建立 Issue |
| list_pulls | 列出 Pull Requests |
---
### n8n (43 個工具)
#### Workflows (10)
| 工具 | 說明 |
|------|------|
| n8n_list_workflows | 列出所有 workflows |
| n8n_get_workflow | 取得 workflow 詳情 |
| n8n_create_workflow | 建立新 workflow |
| n8n_update_workflow | 更新 workflow |
| n8n_delete_workflow | 刪除 workflow |
| n8n_activate_workflow | 啟用 workflow |
| n8n_deactivate_workflow | 停用 workflow |
| n8n_execute_workflow | 執行 workflow |
| n8n_get_workflow_tags | 取得 workflow 標籤 |
| n8n_update_workflow_tags | 更新 workflow 標籤 |
#### Executions (3)
| 工具 | 說明 |
|------|------|
| n8n_list_executions | 列出執行記錄 |
| n8n_get_execution | 取得執行詳情 |
| n8n_delete_execution | 刪除執行記錄 |
#### Data Tables (8)
| 工具 | 說明 |
|------|------|
| n8n_list_datatables | 列出資料表 |
| n8n_create_datatable | 建立資料表 |
| n8n_get_datatable | 取得資料表結構 |
| n8n_get_datatable_rows | 取得資料表列 |
| n8n_insert_datatable_rows | 插入資料列 |
| n8n_update_datatable_rows | 更新資料列 |
| n8n_upsert_datatable_row | 插入或更新資料列 |
| n8n_delete_datatable_rows | 刪除資料列 |
#### Tags (5)
| 工具 | 說明 |
|------|------|
| n8n_list_tags | 列出所有標籤 |
| n8n_get_tag | 取得標籤 |
| n8n_create_tag | 建立標籤 |
| n8n_update_tag | 更新標籤 |
| n8n_delete_tag | 刪除標籤 |
#### 其他 (17)
| 工具 | 說明 |
|------|------|
| n8n_list_credentials | 列出憑證 |
| n8n_create_credential | 建立憑證 |
| n8n_delete_credential | 刪除憑證 |
| n8n_get_credential_schema | 取得憑證 schema |
| n8n_list_users | 列出使用者 |
| n8n_get_user | 取得使用者 |
| n8n_delete_user | 刪除使用者 |
| n8n_list_variables | 列出變數 |
| n8n_create_variable | 建立變數 |
| n8n_delete_variable | 刪除變數 |
| n8n_list_projects | 列出專案 |
| n8n_create_project | 建立專案 |
| n8n_update_project | 更新專案 |
| n8n_delete_project | 刪除專案 |
| n8n_generate_audit | 產生安全審計報告 |
| n8n_health_check | 健康檢查 |
| n8n_trigger_webhook | 觸發 webhook |
---
### postgres
| 工具 | 說明 |
|------|------|
| query | 執行唯讀 SQL 查詢 |
**範例**:
```json
{
"sql": "SELECT * FROM users LIMIT 10;"
}
```
---
### redis
| 工具 | 說明 |
|------|------|
| set | 設定 key-value 配對 |
| get | 取得 key 的值 |
| delete | 刪除 key(s) |
| list | 列出符合模式的 keys |
---
### mongodb
| 工具 | 說明 |
|------|------|
| mongodb_list_databases | 列出所有資料庫 |
| mongodb_list_collections | 列出資料庫中的集合 |
| mongodb_find | 查詢文件 |
| mongodb_insert_one | 插入單個文件 |
| mongodb_insert_many | 插入多個文件 |
| mongodb_update_one | 更新單個文件 |
| mongodb_update_many | 更新多個文件 |
| mongodb_delete_one | 刪除單個文件 |
| mongodb_delete_many | 刪除多個文件 |
| mongodb_aggregate | 聚合查詢 |
**範例**:
```json
{
"database": "momentry",
"collection": "videos",
"filter": {"uuid": "abc123"}
}
```
---
### qdrant
| 工具 | 說明 |
|------|------|
| 查詢工具 | 向量搜尋和集合管理 |
---
### filesystem
| 工具 | 說明 |
|------|------|
| read_file | 讀取檔案 |
| write_file | 寫入檔案 |
| list_directory | 列出目錄 |
| create_directory | 建立目錄 |
---
### sentry
| 工具 | 說明 |
|------|------|
| sentry_list_issues | 列出 issues |
| sentry_get_issue | 取得 issue 詳情 |
| sentry_create_issue | 建立 issue |
| sentry_update_issue | 更新 issue |
| sentry_list_projects | 列出專案 |
| sentry_search_docs | 搜尋 Sentry 文件 |
**注意**: 需要設定 `SENTRY_ACCESS_TOKEN`
**取得 Token**:
1. 登入 https://sentry.io
2. Settings → Developer Tools → API Keys
3. 建立新 Token (需要 `event:read`, `project:read` 權限)
---
### context7
| 工具 | 說明 |
|------|------|
| context7_resolve-library-id | 解析函式庫 ID |
| context7_query-docs | 查詢函式庫文件 |
**用途**: 即時技術文件和函式庫查詢,獲取最新的 API 語法和範例。
---
### playwright
| 工具 | 說明 |
|------|------|
| playwright_navigate | 導航到 URL |
| playwright_screenshot | 截圖 |
| playwright_click | 點擊元素 |
| playwright_type | 輸入文字 |
| playwright_evaluate | 執行 JavaScript |
**用途**: 瀏覽器自動化測試、網頁截圖、網頁內容抓取。
---
## 管理指令
### 新增 MCP Server
```bash
opencode mcp add
```
### 列出 MCP Servers
```bash
opencode mcp ls
```
### 認證 OAuth Server
```bash
opencode mcp auth <server-name>
```
---
## 故障排除
### MCP Server 連線失敗
1. 檢查服務是否運行:
```bash
# PostgreSQL
pg_isready -h localhost -p 5432
# Redis
redis-cli -a accusys ping
# Qdrant
curl -s http://localhost:6333/
```
2. 檢查 MCP Server 是否安裝:
```bash
which mcp-server-postgres
which mcp-server-redis
```
3. 驗證配置格式正確
---
### Token 或 API Key 無效
1. PostgreSQL: 確認使用者名稱和密碼
2. Redis: 確認密碼
3. n8n: 確認 API Key 未過期
4. Gitea: 確認 Token 有效
---
## 檔案位置
| 類型 | 路徑 | 說明 |
|------|------|------|
| OpenCode 配置 | ~/.config/opencode/opencode.json | MCP 設定檔 |
| Gitea MCP | /opt/homebrew/bin/gitea-mcp-server | 安裝路徑 |
| n8n MCP | /opt/homebrew/bin/mcp-n8n | 安裝路徑 |
| Postgres MCP | /opt/homebrew/bin/mcp-server-postgres | 安裝路徑 |
| Redis MCP | /opt/homebrew/bin/mcp-server-redis | 安裝路徑 |
| Qdrant MCP | /opt/homebrew/bin/mcp-server-qdrant | 安裝路徑 |
| Filesystem MCP | /opt/homebrew/bin/mcp-server-filesystem | 安裝路徑 |
---
## 常用指令
```bash
# 列出所有 MCP servers
opencode mcp ls
# 測試 PostgreSQL MCP
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"query","arguments":{"sql":"SELECT 1;"}}}' | \
/opt/homebrew/bin/mcp-server-postgres postgresql://accusys:accusys@localhost:5432/momentry
# 測試 Redis MCP
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"ping","arguments":{}}}' | \
/opt/homebrew/bin/mcp-server-redis redis://:accusys@localhost:6379
```
---
## 版本資訊
- 版本: 1.0
- 安裝日期: 2026-03-24
- 文件更新: 2026-03-24
---
## 相關文件
- [OpenCode Guide](./OPENCODE_GUIDE.md) - OpenCode 使用指南
- [Gitea MCP 安裝](./INSTALL_GITEA_MCP.md) - Gitea MCP 詳細設定
- [n8n MCP 設定](./N8N_MCP_SETUP.md) - n8n MCP 詳細設定
- [服務總覽](./SERVICES.md) - 所有服務狀態總覽

View File

@@ -1,813 +0,0 @@
# 待解決問題追蹤
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-17 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-17 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
| V1.1 | 2026-03-21 | 更新問題狀態 | OpenCode | - |
| V1.2 | 2026-03-21 | 添加備份機制優化待辦 | OpenCode | - |
| V1.3 | 2026-03-21 | 完成清理硬編碼密碼 | OpenCode | - |
| V1.4 | 2026-03-21 | 完成 OpenCode n8n MCP 整合 | OpenCode | - |
| V1.5 | 2026-03-21 | 完成 API Key Management 核心模組 | OpenCode | - |
| V1.6 | 2026-03-23 | 添加 Momentry Core API launchd 待辦 | OpenCode | - |
| V1.7 | 2026-03-23 | 完成 Momentry Core API launchd 設定 | OpenCode | - |
| V1.8 | 2026-03-24 | 完成服務統一遷移,所有服務使用自定義 plist | OpenCode | big-pickle |
| V1.9 | 2026-03-24 | 建立統一會員系統實作計畫 | OpenCode | big-pickle |
| V2.0 | 2026-03-24 | 建立 Job Worker 實作計畫 | OpenCode | big-pickle |
---
## 問題 #1: sqlx async INSERT 不會實際寫入數據庫
### 問題描述
使用 sqlx async 執行 INSERT 時報告成功rows_affected=1但數據沒有實際寫入數據庫。
### 嘗試過的解決方案
| # | 嘗試方法 | 結果 |
|---|---------|------|
| 1 | 使用 `execute()` | 報告成功但未寫入 |
| 2 | 使用 `fetch()` | 同樣問題 |
| 3 | 使用交易 | 同樣問題 |
| 4 | 使用連接池 `acquire()` | 同樣問題 |
| 5 | 每個操作創建新連接池 | 同樣問題 |
| 6 | 使用 `std::process::Command` 同步調用 psql | 同樣問題 |
| 7 | 使用 `tokio::task::spawn_blocking` | 同樣問題 |
### 觀察到的現象
- 直接用 psql 命令行可以成功寫入
- 用另一個 PostgreSQL client 可以成功寫入
- sqlx 查詢 COUNT(*) 可以正確讀取數據
- 但 sqlx INSERT 報告成功卻不寫入
### 懷疑方向
- sqlx 連接池與 PostgreSQL 的某種交互問題
- Rust async runtime 與 PostgreSQL client 的問題
- postgresql.conf 配置問題
### 臨時解決方案
- Qdrant 向量存儲正常工作(不受影響)
- 存儲狀態追蹤功能正常運作
### 負責人
-
### 建立日期
2026-03-17
### 備註
影響 `store_vector` 函數PVector 存儲無法正常工作,但 QVector 正常運作
### 2026-03-21 調查結果
#### 測試結果
- 直接 psql INSERT: ✅ 成功
- 資料寫入驗證: ✅ 成功
#### 發現的問題
`store_vector` 函數 (`postgres_db.rs:819-860`) 存在以下問題:
```rust
// 問題 1: 錯誤被靜默忽略
match join_result {
Ok((cid, Ok(output))) => {
if !output.status.success() {
let err = String::from_utf8_lossy(&output.stderr);
tracing::error!("psql error for {}: {}", cid, err);
// 沒有返回錯誤!只是記錄日誌
}
}
// ...
}
Ok(()) // 即使失敗也返回 Ok
```
#### 建議修復
1.`store_vector` 返回實際結果
2. 在失敗時返回 `Err`
3. 添加單元測試驗證
#### 下一步
- [x] 修復 `store_vector` 錯誤處理
- [x] 添加單元測試
- [ ] 重現並確認問題根因
### 2026-03-21 修復完成
已修復 `store_vector` 函數的錯誤處理:
```rust
// 修復前:錯誤被靜默忽略
match join_result {
Ok((cid, Ok(output))) => {
if !output.status.success() {
tracing::error!("..."); // 只記錄,不返回錯誤
}
}
}
Ok(()) // 即使失敗也返回 Ok
// 修復後:正確傳播錯誤
let (cid, output) = result;
let output = output.map_err(|e| anyhow::anyhow!(...))?;
if !output.status.success() {
anyhow::bail!("psql INSERT failed...");
}
```
---
## 問題 #2: TUI 與 stdout 輸出混合
### 問題描述
Python processors 的 progress 輸出蓋過 TUI導致使用者無法清楚看到處理進度。
### 解決方案
~~使用 TUI 渲染到 stderr~~ → 使用 Redis Pub/Sub 作為消息總線
### 當前狀態
| 子項目 | 狀態 |
|--------|------|
| RedisPublisher Python 端 | ✅ 已實作 |
| Rust Redis 客戶端 | ✅ 已實作 (`redis_client.rs`) |
| Rust 訂閱更新 TUI | ✅ 已實作 (main.rs) |
| 中斷後繼續/重做 | 🔜 待實作 |
### 負責人
-
### 建立日期
2026-03-17
### 更新日期
2026-03-21
### 參考文檔
- `docs/MOMENTRY_CORE_REDIS_KEYS.md`
- `scripts/redis_publisher.py`
- `src/core/db/redis_client.rs`
---
## 問題 #3: Redis Message Bus 尚未實作
### 問題描述
根據設計規範,需要使用 Redis 作為監控和狀態管理系統。
### 當前狀態
| 實作項目 | 狀態 | 說明 |
|---------|------|------|
| Redis 客戶端 (Hash) | ✅ | `redis_client.rs` |
| Redis 客戶端 (Pub/Sub) | ✅ | `redis_client.rs::subscribe_progress()` |
| Python RedisPublisher | ✅ | `scripts/redis_publisher.py` |
| Rust 訂閱頻道 | ✅ | `main.rs` 中的 redis 訂閱邏輯 |
| 監控腳本 | ✅ | `monitor/service/health_check.sh` |
### 負責人
-
### 建立日期
2026-03-17
### 更新日期
2026-03-21
### 優先級
~~高~~ → 中 - 核心功能已完成
### 參考文檔
- `docs/MOMENTRY_CORE_REDIS_KEYS.md`
- `docs/MOMENTRY_CORE_MONITORING.md`
---
## 架構優化待評估
詳細內容請參考 [ARCHITECTURE_EVALUATION.md](./ARCHITECTURE_EVALUATION.md)
| 項目 | 複雜度 | 優先級 |
|------|--------|--------|
| PostgreSQL → Redis 故障轉移 | 中 | 待評估 |
| 連接池監控 | 低 | 待評估 |
| Processor 重試機制 | 中 | 待評估 |
| PyO3 整合 | 高 | 低 |
| HTTP 健康端點 | 低 | ✅ 已完成 |
| Commit Message Lint | 低 | ✅ 已完成 |
---
## 備份機制優化 (2026-03-21)
### 問題分析
| 問題 | 嚴重性 | 說明 |
|------|--------|------|
| 溫冷分層未啟用 | 中 | weekly/monthly/archive 目錄為空 |
| 清理策略未執行 | 高 | 每日備份保留過多,空間浪費 |
| 無異地備份 | 高 | 無遠程備份(雲端/另一設備) |
| 備份驗證未自動化 | 中 | 只檢查文件存在,沒驗證可恢復性 |
| Gitea 大文件 | 中 | 每次備份 1GB頻率過高 |
| 密碼硬編碼 | 高 | 腳本中有多處明文密碼 |
### 待辦事項
| # | 任務 | 優先級 | 狀態 |
|---|------|--------|------|
| 1 | 啟用溫冷分層 (`backup_monitor.sh tier`) | 高 | 待辦 |
| 2 | 啟用清理策略 (`backup_monitor.sh cleanup`) | 高 | 待辦 |
| 3 | 添加備份驗證腳本 | 高 | 待辦 |
| 4 | 優化 Gitea 備份頻率 (改為週備份) | 中 | 待辦 |
| 5 | 創建外部備份腳本 (rsync/雲端) | 高 | 待辦 |
| 6 | 清理腳本中的硬編碼密碼 | 高 | ✅ 已完成 |
### 推薦備份策略
| 層級 | 保留時間 | 頻率 | 目標 |
|------|----------|------|------|
| Hot | 7 天 | 每日 | 本地 SSD |
| Warm | 30 天 | 每週 | 本地 HDD |
| Cold | 90 天 | 每月 | 外部存儲 |
| Archive | 1 年 | 每季 | 離線/雲端 |
### 建議的 Crontab
```bash
# 每日備份 (排除 Gitea)
0 3 * * 1-6 /Users/accusys/momentry/scripts/backup_all.sh all_except_gitea
# 每週完整備份 (含 Gitea)
0 3 * * 0 /Users/accusys/momentry/scripts/backup_all.sh all
# 每週溫冷分層
0 4 * * 0 /Users/accusys/momentry_core_0.1/monitor/storage/backup_monitor.sh tier
# 每週清理
0 5 * * 0 /Users/accusys/momentry_core_0.1/monitor/storage/backup_monitor.sh cleanup
# 每月驗證
0 6 1 * * /Users/accusys/momentry/scripts/verify_backup.sh
```
### 負責人
-
### 參考文件
- `/Users/accusys/momentry/scripts/backup_all.sh`
- `/Users/accusys/momentry_core_0.1/monitor/storage/backup_monitor.sh`
- `/Users/accusys/momentry/backup/`
---
## OpenCode n8n MCP 整合 (2026-03-21)
### 完成狀態
| 項目 | 狀態 | 說明 |
|------|------|------|
| n8n REST API 啟用 | ✅ | `N8N_PUBLIC_API_ENABLED=true` |
| API Key 生成 | ✅ | Settings → API |
| MCP Server 安裝 | ✅ | `@nextoolsolutions/mcp-n8n` |
| OpenCode 設定 | ✅ | `~/.config/opencode/opencode.json` |
| 43 工具可用 | ✅ | workflows, executions, datatables, tags 等 |
### 設定檔案
`~/.config/opencode/opencode.json`:
```json
{
"mcp": {
"gitea": {
"type": "local",
"enabled": true,
"command": [
"/opt/homebrew/bin/gitea-mcp-server",
"-token", "<GITEA_TOKEN>",
"-host", "http://localhost:3000"
]
},
"n8n": {
"type": "local",
"enabled": true,
"command": ["/opt/homebrew/bin/mcp-n8n"],
"environment": {
"N8N_BASE_URL": "http://localhost:5678",
"N8N_API_KEY": "<N8N_API_KEY>"
}
}
}
}
```
### 重要提醒
1. **API Key 安全**: 避免提交到 Git
2. **n8n 需通過反向代理**: localhost:5678 無法直接訪問 API需通過 Caddy
3. **重啟生效**: 修改 `opencode.json` 後需重啟 OpenCode
### 參考文件
- `docs/OPENCODE_GUIDE.md` - MCP 設定章節
- `docs/INSTALL_N8N.md` - n8n 安裝指南
---
## n8n API 備份腳本 (2026-03-21)
### 腳本位置
| 腳本 | 說明 | 依賴 |
|------|------|------|
| `monitor/workflow/backup_n8n_api.py` | REST API 備份 | requests |
| `monitor/workflow/backup_n8n_mcp.py` | MCP 備份(開發中) | mcp SDK |
### 使用方式
```bash
# 備份所有 workflows
N8N_API_KEY="..." python3.11 backup_n8n_api.py
# 只顯示變更(不備份)
N8N_API_KEY="..." python3.11 backup_n8n_api.py --diff
# 差異備份(只備份變更的 workflows
N8N_API_KEY="..." python3.11 backup_n8n_api.py --incremental
# 列出可用備份
python3.11 backup_n8n_api.py --list
# 驗證最新備份
python3.11 backup_n8n_api.py --verify
# 顯示備份統計
python3.11 backup_n8n_api.py --stats
# 只備份啟用的 workflows
N8N_API_KEY="..." python3.11 backup_n8n_api.py --active-only
# 備份失敗的執行記錄
N8N_API_KEY="..." python3.11 backup_n8n_api.py --failed-only
```
### 功能
- [x] REST API 備份
- [x] 變更偵測
- [x] SHA256 校驗
- [x] 備份版本化
- [x] 差異備份(`--incremental`
- [x] 備份驗證(`--verify`
- [x] 備份統計(`--stats`
- [x] 失敗執行記錄備份(`--failed-only`
- [ ] 選擇性備份(按 Tags
### 備份位置
```
/Users/accusys/momentry/backup/n8n_workflows/api/
├── 20260321_122059/
│ ├── workflows.json # 21 workflows
│ ├── workflows.json.sha256 # SHA256 校驗
│ ├── tags.json # Tags若有
│ └── manifest.json # 元數據
└── ...
```
### Crontab 建議
```bash
# 每日備份(下午 3 點)
0 15 * * * N8N_API_KEY="..." /opt/homebrew/bin/python3.11 /Users/accusys/momentry_core_0.1/monitor/workflow/backup_n8n_api.py >> /Users/accusys/momentry/log/monitor/workflow_backup_api.log 2>&1
```
---
## API Key Management System (2026-03-21)
### 設計目標
為 Momentry Core 實現完整的 API Key 管理系統,包括:
- API Key 生成(安全隨機)
- Key 哈希SHA256
- 異常檢測
- 強制輪換機制
- 審計日誌
### 模組架構
```
src/core/api_key/
├── mod.rs # 模組導出
├── models.rs # 數據模型和類型
├── service.rs # 核心服務邏輯
├── anomaly.rs # 異常檢測
└── rotation.rs # 輪換管理
```
### Key 類型
| 類型 | 前綴 | 默認 TTL | 寬限期 |
|------|------|----------|--------|
| System | `msys_` | 365 天 | 72 小時 |
| User | `muser_` | 90 天 | 24 小時 |
| Service | `msvc_` | 180 天 | 48 小時 |
| Integration | `mint_` | 30 天 | 24 小時 |
| Emergency | `memg_` | 1 天 | 0 小時 |
### Key 格式
```
{prefix}{uuid}_{timestamp}_{random}
例如: muser_a1b2c3d4e5f6_1710998400_abc12345
```
### 異常檢測閾值
| 指標 | 閾值 |
|------|------|
| 每分鐘請求數 | 1000 |
| 每小時請求數 | 10000 |
| 錯誤率 | 50% |
| 每小時唯一 IP 數 | 5 |
| 鎖定閾值 | 3 次觸發 |
### 實現狀態
| 組件 | 狀態 | 說明 |
|------|------|------|
| 數據模型 | ✅ 完成 | `models.rs` |
| Key 生成/哈希 | ✅ 完成 | `service.rs` |
| 異常檢測 | ✅ 完成 | `anomaly.rs` |
| 輪換機制 | ✅ 完成 | `rotation.rs` |
| CLI 命令 | ✅ 完成 | `main.rs` |
| 數據庫集成 | ✅ 完成 | `postgres_db.rs` |
| Redis 告警 | ✅ 完成 | `redis_client.rs` |
| 數據庫遷移 | ✅ 完成 | `migrations/001_api_key_management.sql` |
| 單元測試 | ✅ 完成 | 55 個測試通過 |
### CLI 命令
```bash
# 創建 API Key
momentry api-key create <name> --key-type service --ttl 90
# 列出所有 Keys
momentry api-key list
# 驗證 Key
momentry api-key validate --key <key>
# 撤銷 Key
momentry api-key revoke --key <key>
# 請求輪換
momentry api-key rotate --key <key>
# 顯示統計
momentry api-key stats
```
### 參考文件
- `src/core/api_key/` - API Key 模組
- `docs/API_KEY_MANAGEMENT.md` - 設計文檔
- `migrations/001_api_key_management.sql` - 數據庫遷移
---
## 問題 #5: Redis 用戶名問題 (2026-03-21)
### 問題描述
Redis 僅有 `default` 用戶,無 `accusys` 用戶。`.env` 檔案使用 `redis://accusys:accusys@localhost:6379` 格式會導致認證失敗。
### 測試結果
| URL 格式 | 結果 |
|----------|------|
| `redis://accusys:accusys@localhost:6379` | ❌ AUTH failed |
| `redis://:accusys@localhost:6379` | ✅ PONG |
### Redis ACL 狀態
```
user default on sanitize-payload #1bd51c... ~* &* +@all
requirepass: accusys
```
### 根本原因
1. Redis 啟動時僅設定 `--requirepass accusys`
2. 未建立自訂用戶 `accusys`
3. ACL 變更不會持久化(無 config file
### 已執行修復
| 項目 | 修改 |
|------|------|
| `.env` | `redis://accusys:accusys@localhost:6379``redis://:accusys@localhost:6379` |
### 待解決問題
1. **ACL 持久化**Redis 啟動後手動建立的用戶不會保留(重啟後消失)
2. **需配置 ACL 文件**:建議建立 `users.acl` 並在 plist 中指定
### 建議解決方案
#### 方案 A使用默認用戶現行
```bash
# .env
REDIS_URL=redis://:accusys@localhost:6379
```
**優點**:簡單,無需修改 Redis 配置
**缺點**:所有應用共享默認用戶
#### 方案 B建立 ACL 配置文件
```bash
# 1. 創建 ACL 文件
cat > /Users/accusys/momentry/etc/redis/users.acl << 'EOF'
user default on sanitize-payload ~* &* +@all >accusys
user accusys on sanitize-payload ~* &* +@all >accusys
EOF
# 2. 修改 plist 添加 --aclfile 參數
--aclfile /Users/accusys/momentry/etc/redis/users.acl
# 3. 重啟 Redis
sudo launchctl unload /Library/LaunchDaemons/com.momentry.redis.plist
sudo launchctl load /Library/LaunchDaemons/com.momentry.redis.plist
```
**優點**支持多用戶ACL 持久化
**缺點**:需修改 plist 並重啟
### 影響範圍
- `src/core/config.rs` - REDIS_URL 讀取
- `src/core/db/redis_client.rs` - Redis 連線
- `momentry api-key` 命令 - 異常告警
### 狀態
- [x] 已確認問題存在
- [x] 已修改 `.env` 使用默認用戶
- [ ] 待決定是否實施 ACL 方案
---
## 問題 #6: Momentry Core API 未開機自動啟動 (2026-03-23)
### 問題描述
Momentry Core API 服務 (`momentry server --port 3002`) 未設定 launchd導致
1. 系統重啟後 API 服務不會自動啟動
2. `api.momentry.ddns.net` 返回 502 Bad Gateway
3. n8n workflow 呼叫 API 時失敗
### 發現過程
1. n8n workflow 呼叫 `https://api.momentry.ddns.net/api/v1/n8n/search` 返回 502
2. 檢查發現 port 3002 無服務運行
3. Caddy 配置正向確,但後端服務未啟動
4. 手動啟動服務後 API 正常運作
### 配置需求
| 項目 | 值 |
|------|-----|
| 服務名稱 | `com.momentry.api` |
| 二進位檔 | `/Users/accusys/momentry_core_0.1/target/release/momentry` |
| 命令 | `server --port 3002` |
| Port | 3002 |
| 環境變數 | `DATABASE_URL`, `REDIS_URL` 等 |
### 待辦事項
- [x] 建立 `docs/INSTALL_MOMENTRY_API.md` 安裝文件
- [x] 建立 `/Library/LaunchDaemons/com.momentry.api.plist`
- [x] 設定環境變數 (`DATABASE_URL`, `REDIS_URL` 等)
- [x] 測試 launchctl load/unload
- [x] 驗證開機自動啟動 (launchd 載入成功)
### 完成日期
2026-03-23
### 參考文件
- `/Library/LaunchDaemons/com.momentry.n8n.main.plist` - n8n plist 範例
- `docs/INSTALL_N8N.md` - plist 配置說明
---
## 服務統一遷移 (2026-03-24)
### 問題描述
Reboot 後發現 n8n workflow 數量從 42 變成 41確認是 PostgreSQL 資料庫問題。經過調查發現:
1. **兩組不同的 PostgreSQL 資料目錄**
- Homebrew plist: `/opt/homebrew/var/postgresql@18` (有最新資料)
- Custom plist: `/Users/accusys/momentry/var/postgresql` (可能是舊資料)
2. **Reboot 時 custom plist 搶先啟動**,使用了錯誤的資料目錄
### 解決方案
1. **統一使用 custom plist**
- 刪除 homebrew plist (`~/Library/LaunchAgents/homebrew.mxcl.postgresql@18.plist`)
- Custom plist 使用 `/Users/accusys/momentry/var/postgresql` 作為資料目錄
- 將所有 14 個服務的 plist 註冊到 launchd
2. **所有已遷移的服務**
| 服務 | Plist | 資料目錄 |
|------|-------|----------|
| PostgreSQL | ✅ | `/Users/accusys/momentry/var/postgresql` |
| MariaDB | ✅ | `/Users/accusys/momentry/var/mariadb` |
| MongoDB | ✅ | `/opt/homebrew/var/mongodb` |
| Redis | ✅ | - |
| Ollama | ✅ | - |
| Qdrant | ✅ | - |
| n8n Main | ✅ | - |
| n8n Worker | ✅ | - |
| Caddy | ✅ | - |
| SFTPGo | ✅ | - |
| Gitea | ✅ | - |
| Gitea MCP | ✅ | - |
| PHP | ✅ | - |
| Momentry API | ✅ | - |
| RustDesk HBBR | ✅ | - |
| RustDesk HBBS | ✅ | - |
### 還發現的 Homebrew 服務 (未遷移)
| 服務 | 建議 |
|------|------|
| homebrew.mxcl.grafana | ⚠️ 考慮遷移 |
| homebrew.mxcl.prometheus | ⚠️ 考慮遷移 |
| homebrew.mxcl.openwebui | ⚠️ 考慮遷移 |
| homebrew.mxcl.kafka | ⚠️ 考慮遷移 |
| homebrew.mxcl.seaweedfs | ⚠️ 考慮遷移 |
| homebrew.mxcl.netdata | ⚠️ 考慮遷移 |
| homebrew.mxcl.ddclient | ⚠️ 動態 DNS |
| homebrew.mxcl.shadowsocks-rust | ⚠️ VPN |
### 預防措施
1. **確保統一資料目錄**:所有服務只使用一個資料目錄
2. **Reboot 測試**:遷移完成後需進行 Reboot 測試
3. **文件同步**plist 檔案同步到 repo
### 完成日期
2026-03-24
### 參考文件
- `docs/SERVICES.md` - 服務管理文檔
- `docs/SERVICE_ADDITION_GUIDE.md` - 服務添加規範
- `momentry_runtime/plist/` - plist 檔案存放位置
---
## Job Worker 實作 (2026-03-24)
### 目標
實作輪詢式 Job Worker實現檔案註冊後自動觸發處理
1. **輪詢機制**Worker 定期輪詢 jobs 佇列
2. **並行處理**:最多 2 個 processor 同時執行
3. **失敗容忍**:任一模組獨立,失敗可接續
### 設計決策
| 項目 | 決策 | 理由 |
|------|------|------|
| 觸發方式 | 輪詢Job Worker | 暫無可靠 API 觸發 |
| 並行處理 | 最多 2 個 | 可根據 CPU/GPU 調整 |
| 失敗處理 | 獨立模組,部分完成可接續 | 任何模組失敗都產出狀態 |
| Worker 啟動 | 獨立進程 | 隔離、易管理 |
| 並行上限 | 環境變數 + 預設值 | 靈活調整 |
| 狀態同步 | PostgreSQL + Redis | 可靠 + 即時 |
### 環境變數
| 變數 | 預設值 | 說明 |
|------|--------|------|
| `MOMENTRY_MAX_CONCURRENT` | 2 | 最大並行 processor 數 |
| `MOMENTRY_POLL_INTERVAL` | 5 | 輪詢間隔(秒) |
| `MOMENTRY_WORKER_ENABLED` | true | 是否啟用 worker |
### 實作計畫
詳細內容請參考 [JOB_WORKER_IMPLEMENTATION_PLAN.md](./JOB_WORKER_IMPLEMENTATION_PLAN.md)
### Phase 規劃
| Phase | 任務 | 預估工時 |
|-------|------|----------|
| 1 | 資料庫遷移 | 2h |
| 2 | Worker 框架 | 4h |
| 3 | Register API 整合 | 2h |
| 4 | Processor 執行 | 4h |
| 5 | 進度追蹤 | 2h |
| 6 | API 端點 | 3h |
| 7 | CLI 命令 | 2h |
| 8 | 測試 | 4h |
**總預估**: ~23h
### 實作結構
```
src/
├── worker/
│ ├── mod.rs # Worker 模組導出
│ ├── config.rs # Worker 配置
│ ├── worker.rs # Worker 主邏輯
│ ├── processor.rs # Processor 執行器
│ ├── queue.rs # Job 佇列管理
│ └── progress.rs # 進度追蹤
├── api/
│ └── server.rs # 更新 Register API
└── main.rs # 新增 worker 命令
```
### 狀態
- [x] 系統分析完成
- [x] 實作計畫文件建立
- [ ] Phase 1: 資料庫遷移
- [ ] Phase 2: Worker 框架
- [ ] Phase 3: Register API 整合
- [ ] Phase 4: Processor 執行
- [ ] Phase 5-8: 依序實作
### 參考文件
- `docs/JOB_WORKER_IMPLEMENTATION_PLAN.md` - 完整實作計畫
- `docs/PROCESSING_PIPELINE.md` - 處理流程
- `docs/MOMENTRY_CORE_REDIS_KEYS.md` - Redis Key 設計
---
## 統一會員系統 + 影片歸屬追蹤 (2026-03-24)
### 目標
建立統一的會員系統:
1. WordPress 作為唯一登入入口
2. 每個影片關聯到 user_id追蹤歸屬
3. Per-user 配額管理
4. API 端點啟用認證
### 實作計畫
詳細內容請參考 [USER_MANAGEMENT_PLAN.md](./USER_MANAGEMENT_PLAN.md)
### Phase 規劃
| Phase | 任務 | 複雜度 | 優先級 | 預估工時 |
|-------|------|--------|--------|----------|
| 1 | WordPress Application Passwords 測試 | 低 | P0 | 1.5h |
| 2 | 資料庫遷移 (users 表) | 中 | P0 | 3h |
| 3 | API auth middleware | 中 | P0 | 4h |
| 4 | Register API 更新 | 低 | P0 | 2h |
| 5 | Admin users API | 中 | P1 | 4h |
| 6 | n8n workflow | 中 | P1 | 6h |
| 7 | 配額管理 | 中 | P2 | 4h |
| 8 | 測試驗證 | 中 | P2 | 4h |
**總預估**: ~28.5h
### 待確認事項
- [ ] WordPress 用戶建立方式(手動/Elementor表單
- [ ] API Key 格式確認
- [ ] SFTPGo 整合方式
- [ ] 配額管理策略
- [ ] 用戶刪除同步流程
### 狀態
- [x] 系統分析完成
- [x] 實作計畫文件建立
- [ ] Phase 1: WordPress 認證測試
- [ ] Phase 2: 資料庫遷移
- [ ] Phase 3-8: 依序實作
### 參考文件
- `docs/USER_MANAGEMENT_PLAN.md` - 完整實作計畫
- `docs/API_KEY_MANAGEMENT.md` - API Key 管理
- `docs/SFTPGO_DEMO_USER.md` - SFTPGo 用戶設定

View File

@@ -1,392 +0,0 @@
# Playground Binary Implementation Plan
| Item | Content |
|------|---------|
| Author | Warren |
| Created | 2026-03-23 |
| Document Version | V1.0 |
---
## Version History
| Version | Date | Purpose | Operator | Tool/Model |
|---------|------|---------|----------|------------|
| V1.0 | 2026-03-23 | Create implementation plan | Warren | OpenCode |
---
## Overview
Create separate `momentry_playground` binary with distinct configuration from `momentry` (production).
| Aspect | Production (`momentry`) | Development (`momentry_playground`) |
|--------|------------------------|-------------------------------------|
| **Port** | 3002 | 3003 |
| **Redis Prefix** | `momentry:` | `momentry_dev:` |
| **Worker** | Enabled | Disabled |
| **Purpose** | Production deployment | Testing/Development |
---
## Files to Modify
```
Files Changed: 6 files (+1 new)
├── src/core/config.rs ← Add server_port(), redis_key_prefix()
├── src/core/db/redis_client.rs ← Replace hardcoded prefixes
├── src/core/cache/redis_cache.rs ← Use configurable prefix
├── src/main.rs ← Update CLI defaults
├── src/playground.rs ← NEW: Development binary
├── Cargo.toml ← Add new binary
└── .env.development ← NEW: Dev environment config
```
---
## Implementation Steps
### Step 1: Update `src/core/config.rs`
Add after line 51 (after `MEDIA_BASE_URL`):
```rust
pub static SERVER_PORT: Lazy<u16> = Lazy::new(|| {
env::var("MOMENTRY_SERVER_PORT")
.unwrap_or_else(|_| "3002".to_string())
.parse()
.unwrap_or(3002)
});
pub static REDIS_KEY_PREFIX: Lazy<String> = Lazy::new(|| {
env::var("MOMENTRY_REDIS_PREFIX")
.unwrap_or_else(|_| "momentry:".to_string())
});
```
---
### Step 2: Update `src/core/db/redis_client.rs`
Replace all hardcoded `momentry:` prefixes with configurable prefix.
**Import at top:**
```rust
use crate::core::config::REDIS_KEY_PREFIX;
```
**Pattern for each method:**
```rust
let prefix = REDIS_KEY_PREFIX.as_str();
let key = format!("{}job:{}", prefix, uuid);
```
**Affected lines:**
| Line | Key Pattern |
|------|-------------|
| 47 | `job:{uuid}` |
| 81, 109 | `job:{uuid}:processor:{processor}` |
| 136, 146 | `progress:{uuid}` |
| 172 | `jobs:active` |
| 179 | `jobs:active``jobs:completed` |
| 187 | `jobs:active``jobs:failed` |
| 194 | `jobs:active` |
| 201, 208 | `health:momentry_core` |
| 214 | `monitor:job:{uuid}` |
| 242, 300 | `errors:{uuid}` |
| 258, 281 | `anomaly:alerts`, `anomaly:key:{key_id}` |
| 317, 346, 364, 392, 397 | `worker:job:{uuid}...` |
| 406, 410 | `worker:job:*` |
---
### Step 3: Update `src/core/cache/redis_cache.rs`
**Import:**
```rust
use crate::core::config::REDIS_KEY_PREFIX;
```
**Replace line 10:**
```rust
// Remove: const KEY_PREFIX: &str = "momentry:cache:";
```
**Update `prefixed_key` method (line 24):**
```rust
fn prefixed_key(&self, key: &str) -> String {
format!("{}cache:{}", REDIS_KEY_PREFIX.as_str(), key)
}
```
**Update tests (lines 161-162):**
```rust
#[test]
fn test_prefixed_key() {
// Note: This test will use the configured prefix
let cache = RedisCache::new().unwrap();
// With default prefix "momentry:"
assert_eq!(cache.prefixed_key("test"), "momentry:cache:test");
assert_eq!(cache.prefixed_key("video:abc"), "momentry:cache:video:abc");
}
```
---
### Step 4: Update `src/main.rs`
**Change CLI defaults (Lines 691-695):**
```rust
// Before:
#[arg(long, default_value = "3000")]
port: u16,
// After:
#[arg(long)]
port: Option<u16>,
```
**Update Server match arm (around line 2398):**
```rust
Commands::Server { host, port } => {
let port = port.unwrap_or_else(|| *crate::core::config::SERVER_PORT);
momentry_core::api::start_server(&host, port).await?;
Ok(())
}
```
**Update Redis key usage (Line 1098):**
```rust
// Before:
let key = format!("momentry:job:{}:processor:{}", uuid, processor);
// After:
let key = format!(
"{}job:{}:processor:{}",
crate::core::config::REDIS_KEY_PREFIX.as_str(),
uuid,
processor
);
```
---
### Step 5: Create `src/playground.rs`
```rust
use anyhow::{Context, Result};
use clap::{Parser, Subcommand};
// ... same imports as main.rs ...
fn main() -> Result<()> {
// Load development environment first
dotenv::from_filename(".env.development").ok();
tracing_subscriber::fmt::init();
tracing::info!("Starting momentry_playground (development binary)");
tracing::info!("Port: {}", *momentry_core::core::config::SERVER_PORT);
tracing::info!("Redis prefix: {}", *momentry_core::core::config::REDIS_KEY_PREFIX);
let cli = Cli::parse();
// ... rest identical to main.rs ...
}
```
---
### Step 6: Update `Cargo.toml`
**Add after line 90:**
```toml
[[bin]]
name = "momentry_playground"
path = "src/playground.rs"
```
**Add dependency (if not present):**
```toml
dotenv = "0.15"
```
---
### Step 7: Create `.env.development`
```bash
# Development Environment Configuration
# Used by: momentry_playground binary
# Server Configuration
MOMENTRY_SERVER_PORT=3003
MOMENTRY_REDIS_PREFIX=momentry_dev:
# Worker Configuration (disabled for development)
MOMENTRY_WORKER_ENABLED=false
MOMENTRY_MAX_CONCURRENT=1
MOMENTRY_POLL_INTERVAL=10
# Database (can use separate dev database)
DATABASE_URL=postgres://accusys@localhost:5432/momentry
MONGODB_URL=mongodb://accusys:Test3200Test3200@localhost:27017/admin
# Redis
REDIS_URL=redis://:accusys@localhost:6379
```
---
### Step 8: Update `.env` (Production)
Add these lines:
```bash
# Production Environment Configuration
# Used by: momentry binary
# Server Configuration
MOMENTRY_SERVER_PORT=3002
MOMENTRY_REDIS_PREFIX=momentry:
# Worker Configuration
MOMENTRY_WORKER_ENABLED=true
MOMENTRY_MAX_CONCURRENT=2
MOMENTRY_POLL_INTERVAL=5
```
---
## Testing Checklist
### 1. Build and Run Production Binary
```bash
cargo build --release --bin momentry
cargo run --bin momentry -- server
# Expected: Listening on http://127.0.0.1:3002
cargo run --bin momentry -- worker
# Expected: Worker started with momentry: prefix
```
### 2. Build and Run Development Binary
```bash
cargo build --bin momentry_playground
cargo run --bin momentry_playground -- server
# Expected: Listening on http://127.0.0.1:3003
```
### 3. Verify Redis Key Isolation
```bash
# Production data
redis-cli KEYS "momentry:*"
# Development data
redis-cli KEYS "momentry_dev:*"
# Should be separate
```
### 4. Run Both Simultaneously
```bash
# Terminal 1: Production
cargo run --bin momentry -- server
# Terminal 2: Development
cargo run --bin momentry_playground -- server
# Both should run without port conflicts
```
### 5. Unit Tests
```bash
cargo test --lib
# All tests should pass
```
---
## Redis Key Structure
### Production (`momentry:`)
```
momentry:job:{uuid} # Job status
momentry:job:{uuid}:processor:{name} # Processor progress
momentry:progress:{uuid} # Progress pub/sub
momentry:jobs:active # Active job set
momentry:jobs:completed # Completed job set
momentry:jobs:failed # Failed job set
momentry:health:momentry_core # Health status
momentry:cache:{key} # Cache entries
momentry:worker:job:{uuid} # Worker job
momentry:worker:job:{uuid}:processor:{name}
```
### Development (`momentry_dev:`)
```
momentry_dev:job:{uuid}
momentry_dev:job:{uuid}:processor:{name}
momentry_dev:progress:{uuid}
momentry_dev:jobs:active
momentry_dev:jobs:completed
momentry_dev:jobs:failed
momentry_dev:health:momentry_core
momentry_dev:cache:{key}
momentry_dev:worker:job:{uuid}
momentry_dev:worker:job:{uuid}:processor:{name}
```
---
## Potential Issues & Solutions
| Issue | Solution |
|-------|----------|
| `dotenv` crate not in dependencies | Add to Cargo.toml |
| Tests use hardcoded prefix | Update tests to use config, or use `#[cfg(test)]` defaults |
| Worker starts in playground | Check `MOMENTRY_WORKER_ENABLED=false` in `.env.development` |
| Port already in use | Graceful error message with suggestion to use `--port` flag |
| Mixed data in Redis | Ensure prefix is loaded before any Redis operations |
---
## Files Summary
| File | Lines Changed | Purpose |
|------|---------------|---------|
| `src/core/config.rs` | +15 | Add SERVER_PORT and REDIS_KEY_PREFIX |
| `src/core/db/redis_client.rs` | ~50 | Replace hardcoded prefixes |
| `src/core/cache/redis_cache.rs` | ~10 | Use configurable prefix |
| `src/main.rs` | ~15 | Update CLI defaults, Redis key usage |
| `src/playground.rs` | NEW (~2800) | Development binary |
| `Cargo.toml` | +4 | Add binary definition |
| `.env.development` | NEW (~20) | Development environment |
**Total**: ~60 lines modified + ~2800 lines new file
---
## Reference Documents
| Document | Purpose |
|----------|---------|
| `docs/SERVICES.md` | Port allocations |
| `docs/MOMENTRY_CORE_REDIS_KEYS.md` | Redis key design |
| `AGENTS.md` | Code style and conventions |
---
## Version History
| Version | Date | Author | Changes |
|---------|------|--------|---------|
| 1.0 | 2025-03-25 | OpenCode | Initial implementation plan |

View File

@@ -1,292 +0,0 @@
# Video Processing Pipeline - 處理流程
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-22 |
| 文件版本 | V1.1 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-22 | 創建文件 | Warren | OpenCode |
| V1.1 | 2026-03-26 | 更新流程圖文字 (media_url→file_path) | OpenCode | deepseek-reasoner |
---
## 處理流程架構
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ Video Processing Pipeline │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Stage 1: JSON 生成 (Process) │ │
│ │ │ │
│ │ video.mp4 ──→ [ASR] ──→ asr.json (語音辨識) │ │
│ │ ──→ [CUT] ──→ cut.json (場景偵測) │ │
│ │ ──→ [ASRX] ──→ asrx.json (說話者分離) │ │
│ │ ──→ [YOLO] ──→ yolo.json (物體偵測) │ │
│ │ ──→ [OCR] ──→ ocr.json (文字辨識) │ │
│ │ ──→ [Face] ──→ face.json (人臉偵測) │ │
│ │ ──→ [Pose] ──→ pose.json (姿態估計) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Stage 2: 入庫 (Import) │ │
│ │ │ │
│ │ .json files ──→ PostgreSQL (fs_json = true) │ │
│ │ ↓ │ │
│ │ pre_chunks 表 (from ASR, CUT) │ │
│ │ frames 表 (from YOLO, OCR, Face, Pose) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Stage 3: Chunk 生成 (Chunk) │ │
│ │ │ │
│ │ pre_chunks ──→ [Chunk Rule] ──→ chunks 表 │ │
│ │ ↓ │ │
│ │ 清洗 → 純文字 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Stage 4: 向量化 (Vectorize) │ │
│ │ │ │
│ │ chunks ──→ [Embedding Model] ──→ vectors │ │
│ │ ↓ │ │
│ │ Qdrant (主要向量庫) │ │
│ │ PGVector (備份向量庫) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Stage 5: 搜尋 (Search) │ │
│ │ │ │
│ │ Natural Language Query ──→ [Embedding] ──→ [Qdrant Search] │ │
│ │ ↓ │ │
│ │ 返回結果含 file_path │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## CLI 命令
### Stage 1: JSON 生成 (Process)
```bash
# 基本用法
cargo run --bin momentry -- process <uuid_or_path>
# 只處理特定模組
cargo run --bin momentry -- process <uuid> --modules asr,cut
# 強制重新處理(忽略完整性檢查)
cargo run --bin momentry -- process <uuid> --force
# 從中斷點續傳
cargo run --bin momentry -- process <uuid> --resume
# 模組使用雲端處理
cargo run --bin momentry -- process <uuid> --modules yolo,face --cloud yolo
# 完整範例
cargo run --bin momentry -- process /path/to/video.mp4 \
--modules asr,cut,yolo,ocr \
--cloud yolo
```
### Stage 2: 入庫 (Import)
```bash
# 目前入庫在 process 完成後自動執行
# 計劃新增獨立的 import 命令
# cargo run --bin momentry -- import <uuid>
```
### Stage 3: Chunk 生成
```bash
# 生成 chunks
cargo run --bin momentry -- chunk <uuid>
```
### Stage 4: 向量化
```bash
# 向量化 chunks使用預設模型 nomic-embed-text-v2-moe:latest
cargo run --bin momentry -- vectorize <uuid>
# 明確指定模型
cargo run --bin momentry -- vectorize <uuid> --model nomic-embed-text-v2-moe:latest
```
---
## 處理模式選項
### --force (強制重新處理)
- 刪除現有的 JSON 檔案
- 從頭開始處理
- 適用於:處理失敗、模型更新、需要重新處理
```bash
# 強制重新處理 YOLO
cargo run --bin momentry -- process <uuid> --modules yolo --force
```
### --resume (續傳)
- 檢查現有 JSON 的進度
- 從中斷點繼續處理
- 適用於:處理中斷、系統崩潰後恢復
```bash
# 從上次中斷點繼續
cargo run --bin momentry -- process <uuid> --resume
```
### 預設行為 (Smart Mode)
- 如果 JSON 完全:跳過
- 如果 JSON 不完整:警告 + 跳過(需要 --resume 或 --force
- 如果 JSON 不存在:處理
```
Output:
ASR: ✓ Already complete, skipping
⚠️ Found incomplete JSON file: /path/to/yolo.json
Progress: 73800/412343 (17.9%)
Use --resume to continue from checkpoint
Use --force to reprocess from scratch
YOLO: ✓ Already complete, skipping
```
---
## 可用模組
| 模組 | 功能 | 輸出 | 用途 |
|------|------|------|------|
| asr | 自動語音辨識 | asr.json | 語音轉文字 |
| cut | 場景偵測 | cut.json | 影片分段 |
| asrx | 說話者分離 | asrx.json | 多人對話分析 |
| yolo | 物體偵測 | yolo.json | 物體辨識 |
| ocr | 文字辨識 | ocr.json | 畫面文字 |
| face | 人臉偵測 | face.json | 人臉辨識 |
| pose | 姿態估計 | pose.json | 人體姿態 |
---
## 向量化模型選擇
### 統一嵌入模型
Momentry Core 統一使用 **`nomic-embed-text-v2-moe:latest`** 作為所有規則的嵌入模型:
```bash
# 統一模型(所有 Rule 1/2/3 使用)
--model nomic-embed-text-v2-moe:latest
```
### 模型特性
| 特性 | 說明 |
|------|------|
| **模型名稱** | `nomic-embed-text-v2-moe:latest` |
| **向量維度** | 768 維 |
| **多語言支持** | ✅ 完整支持(英語、中文、日語、韓語等) |
| **模型架構** | Mixture of Experts (MoE) |
| **推理速度** | 快速,適合實時應用 |
### 使用方式
```bash
# 向量化命令
cargo run --bin momentry -- vectorize <uuid> --model nomic-embed-text-v2-moe:latest
```
---
## 資料庫儲存
### PostgreSQL (主要關聯式資料庫)
- 影片資訊
- Chunks 資料
- Pre-chunks 資料
- Frames 資料
- 使用者資料
### Qdrant (主要向量資料庫)
- Chunk 向量
- 相似度搜尋
### PGVector (備份向量資料庫)
- Chunk 向量副本
- 備援機制
---
## Pipeline 狀態追蹤
### PostgreSQL 狀態欄位
```sql
-- 影片處理狀態
videos.status: 'pending' | 'processing' | 'completed' | 'failed'
-- 檔案處理狀態
videos.fs_json: true/false
videos.fs_chunks: true/false
videos.fs_vectors: true/false
-- pre_chunks 狀態
pre_chunks.imported: true/false
-- frames 狀態
frames.imported: true/false
-- chunks 狀態
chunks.cleaned: true/false
chunks.vectorized: true/false
```
### 進度查詢 API
```bash
# 查詢處理進度
curl http://localhost:3002/api/v1/progress/{uuid}
# 回應範例
{
"uuid": "a1b10138a6bbb0cd",
"file_name": "video.mp4",
"overall_progress": 65,
"cpu_percent": 45.2,
"gpu_percent": 98.5,
"memory_mb": 8500,
"processors": [
{"name": "asr", "status": "complete", "progress": 100},
{"name": "cut", "status": "complete", "progress": 100},
{"name": "yolo", "status": "progress", "progress": 45},
{"name": "ocr", "status": "pending", "progress": 0}
]
}
```
---
## 下一步
1. **API 端點** - 支援 --modules 和 --cloud 參數
2. **獨立 Import 命令** - 分離入庫流程
3. **獨立 Chunk 命令** - 分離 chunk 生成
4. **獨立 Vectorize 命令** - 分離向量化流程
5. **模型管理** - 新增、選擇、預覽模型

View File

@@ -1,568 +0,0 @@
# Python 開發規範
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-16 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
| V1.1 | 2026-03-21 | 新增 RedisPublisher API 文檔 | OpenCode | - |
---
## 概述
本文檔定義 Momentry 專案中 Python 程式碼的開發標準與最佳實踐。
---
## 版本管理
### 鎖定版本
| 版本 | 用途 | 路徑 |
|------|------|------|
| Python 3.11.14 | Momentry venv | /Users/accusys/momentry_core_0.1/venv/bin/python |
| Python 3.11.14 | 系統安裝 | /opt/homebrew/bin/python3.11 |
| Python 3.14.3 | 系統預設 | /opt/homebrew/bin/python3 |
| Python 3.9.6 | 系統預設 (備用) | /usr/bin/python3 |
### 版本選擇原則
- **Momentry 專案**:使用 venv 中的 Python 3.11.14
- **新專案**:建議使用 venv 管理
- **系統工具**:可使用系統預設版本
---
## 腳本規範
### Shebang 宣告
所有 Momentry Python 腳本必須在第一行宣告明確的 Python 路徑:
```python
#!/opt/homebrew/bin/python3.11
```
**錯誤範例**
```python
#!/usr/bin/env python3 # 會解析到系統預設 (3.14.3)
#!/usr/bin/python3 # 會使用系統 Python (3.9.6)
```
**正確範例**
```python
#!/opt/homebrew/bin/python3.11
import sys
...
```
### 檔案結構
```
scripts/
├── asr_processor.py # ASR 處理腳本
├── thumbnail_extractor.py # 縮圖提取腳本
└── new_script.py # 新腳本模板
```
### 腳本模板
```python
#!/opt/homebrew/bin/python3.11
"""
腳本名稱
簡短描述腳本功能
用法:
python3.11 script.py <args>
作者: Momentry Team
版本: 1.0.0
"""
import argparse
import json
import logging
import sys
from pathlib import Path
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
stream=sys.stderr,
)
logger = logging.getLogger(__name__)
def main():
parser = argparse.ArgumentParser(description="腳本功能描述")
parser.add_argument("input", help="輸入檔案或參數")
parser.add_argument("-o", "--output", default="output.json", help="輸出檔案")
parser.add_argument("-v", "--verbose", action="store_true", help="詳細輸出")
parser.add_argument("-c", "--count", type=int, default=10, help="數量")
args = parser.parse_args()
if args.verbose:
logger.setLevel(logging.DEBUG)
# 業務邏輯
result = process_data(args.input, args.count)
# 輸出 JSON結果
print(json.dumps(result))
def process_data(input_path: str, count: int) -> dict:
"""處理資料並返回結果"""
logger.info(f"Processing: {input_path}")
# TODO: 實作業務邏輯
return {
"status": "success",
"input": input_path,
"count": count,
}
if __name__ == "__main__":
main()
```
---
## 與 Rust 整合
### 使用 venv (目前採用)
Momentry 使用 venv 管理 Python 環境,避免與系統其他程式衝突。
#### 建立 venv
```bash
# 建立虛擬環境
cd /Users/accusys/momentry_core_0.1
/opt/homebrew/bin/python3.11 -m venv venv
# 啟用虛擬環境
source venv/bin/activate
# 安裝依賴
pip install -r requirements.txt
```
#### 從 Rust 呼叫 venv Python
```rust
use std::path::Path;
let script_path = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("scripts")
.join("asr_processor.py");
// 使用 venv 中的 Python
let venv_python = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("venv")
.join("bin")
.join("python");
let output = Command::new(venv_python)
.arg(script_path)
.arg(video_path)
.output()
.context("Failed to run processor")?;
```
**優點**
- 專案依賴隔離
- 不同專案可使用不同 Python 版本
- 易於重現環境
- 不影響系統其他程式
---
## 依賴管理
### venv 目錄結構
```
momentry_core_0.1/
├── venv/ # 虛擬環境
│ ├── bin/
│ │ ├── python # Python 3.11.14
│ │ ├── pip
│ │ └── ...
│ └── lib/python3.11/ # 安裝的套件
├── requirements.txt # 依賴列表
├── scripts/ # Python 腳本
│ ├── asr_processor.py
│ └── thumbnail_extractor.py
└── src/ # Rust 程式碼
```
### 使用虛擬環境
```bash
# 啟用虛擬環境
source venv/bin/activate
# 安裝依賴
pip install faster-whisper
# 退出虛擬環境
deactivate
```
# 退出虛擬環境
deactivate
```
### 依賴列表格式
建立 `requirements.txt`
```
faster-whisper>=1.0.0
ffmpeg-python>=0.2.0
Pillow>=10.0.0
```
### 安裝專案依賴
```bash
# 使用 python3.11 安裝
/opt/homebrew/bin/python3.11 -m pip install -r requirements.txt
```
---
## RedisPublisher 進度發布
### 概述
`redis_publisher.py` 提供統一的進度發布介面,用於 Python 處理器向 Rust 端的 TUI 即時回報進度。
### 基本用法
```python
#!/opt/homebrew/bin/python3.11
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from redis_publisher import RedisPublisher
def process_video(video_path: str, uuid: str):
pub = RedisPublisher(uuid)
pub.info("asr", "Starting ASR processing")
pub.progress("asr", current=50, total=100, message="Processing segment")
pub.complete("asr", "Transcription complete")
```
### API 參考
| 方法 | 說明 | 範例 |
|------|------|------|
| `info(proc, msg)` | 發布資訊訊息 | `pub.info("asr", "Model loaded")` |
| `progress(proc, cur, tot, msg)` | 發布進度 | `pub.progress("asr", 50, 100, "...")` |
| `complete(proc, msg)` | 發布完成 | `pub.complete("asr", "Done")` |
| `error(proc, msg)` | 發布錯誤 | `pub.error("asr", "Failed")` |
| `warning(proc, msg)` | 發布警告 | `pub.warning("asr", "Retry...")` |
| `percentage(proc, pct, msg)` | 發布百分比 | `pub.percentage("asr", 50.5, "50%")` |
### 結構化訊息格式
```python
from redis_publisher import MessageType, ProgressContext
# 使用 Context Manager
with ProgressContext(pub, "asr"):
# 自動發布開始/完成/錯誤
run_asr()
# 帶 extra 資料
pub.progress("asr", current=50, total=100, message="...",
extra={"fps": 30.5, "model": "tiny"})
```
### 環境變數
| 變數 | 預設值 | 說明 |
|------|--------|------|
| `REDIS_URL` | `redis://:accusys@localhost:6379` | Redis 連線 URL |
| `REDIS_PASSWORD` | `accusys` | Redis 密碼 |
---
## 程式碼規範
### Import 排序
```python
# 1. 標準庫
import sys
import os
import json
import logging
from pathlib import Path
from typing import Optional
# 2. 第三方庫
import numpy as np
import pandas as pd
from faster_whisper import WhisperModel
# 3. 本地模組
from . import local_module
from ..package import module
```
### 命名規範
| 類型 | 規範 | 範例 |
|------|------|------|
| 模組/檔案 | snake_case | `asr_processor.py` |
| 類別 | PascalCase | `class VideoProcessor` |
| 函數/變數 | snake_case | `def process_video()` |
| 常量 | UPPER_SNAKE_CASE | `MAX_WORKERS = 4` |
| 私有成員 | _leading_underscore | `_private_method()` |
### 類型提示
```python
from typing import Optional, List, Dict
def process_video(
video_path: str,
options: Optional[Dict[str, int]] = None,
) -> List[Dict[str, float]]:
"""處理影片並返回結果"""
...
```
### 錯誤處理
```python
import logging
from pathlib import Path
logger = logging.getLogger(__name__)
def process_video(video_path: str) -> dict:
path = Path(video_path)
if not path.exists():
logger.error(f"Video file not found: {video_path}")
raise FileNotFoundError(f"Video not found: {video_path}")
try:
result = _do_process(path)
logger.info(f"Processed successfully: {path}")
return result
except Exception as e:
logger.exception(f"Processing failed: {e}")
raise
```
### 日誌規範
```python
import logging
import sys
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
stream=sys.stderr,
)
logger = logging.getLogger(__name__)
# 使用說明
logger.info("Starting process...")
logger.debug(f"Input: {input_path}")
logger.warning(f"Using fallback: {reason}")
logger.error(f"Failed: {error}")
```
---
## 測試規範
### 測試結構
```
tests/
├── __init__.py
├── test_asr_processor.py
└── test_thumbnail_extractor.py
```
### 測試範例
```python
import pytest
from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).parent.parent / "scripts"))
from asr_processor import run_asr
def test_asr_processor_with_valid_video(tmp_path):
video_path = tmp_path / "test.mp4"
output_path = tmp_path / "output.json"
# 建立測試影片
video_path.write_text("dummy")
# 執行
result = run_asr(str(video_path), str(output_path))
# 斷言
assert output_path.exists()
assert result["segments"]
def test_asr_processor_with_invalid_video():
with pytest.raises(FileNotFoundError):
run_asr("/nonexistent/video.mp4", "/tmp/output.json")
```
### 執行測試
```bash
# 使用 python3.11 執行測試
/opt/homebrew/bin/python3.11 -m pytest tests/ -v
# 包含覆蓋率
/opt/homebrew/bin/python3.11 -m pytest tests/ --cov=scripts
```
---
## 監控配置
### 監控腳本
`monitor/config/monitor_config.yaml` 中配置:
```yaml
service:
- name: "python"
type: "process"
process_name: "python3"
enabled: true
check_interval: 60
version_lock: "3.11.14"
scripts:
- "/Users/accusys/momentry_core_0.1/scripts/asr_processor.py"
- "/Users/accusys/momentry_core_0.1/scripts/thumbnail_extractor.py"
```
### 檢查版本
```bash
# 執行 Python 監控
bash /Users/accusys/momentry_core_0.1/monitor/control/monitor_control.sh check python
# 查看資料庫記錄
psql -U accusys -h localhost -d momentry -c "SELECT * FROM python_version_baseline;"
```
---
## CI/CD 考量
### GitHub Actions 範例
```yaml
name: Python Tests
on: [push, pull_request]
jobs:
test:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Run tests
run: |
python -m pytest tests/ -v
```
---
## 常見問題
### Q: 為什麼腳本要用 `#!/opt/homebrew/bin/python3.11` 而不是 `#!/usr/bin/env python3`
A: `#!/usr/bin/env python3` 會解析 PATH 中的第一個 `python3`,在 macOS 上可能是:
- `/opt/homebrew/bin/python3` (3.14.3)
- `/usr/bin/python3` (3.9.6)
明確指定路徑可確保使用正確版本。
### Q: Rust 呼叫 Python 腳本時如何確保版本正確?
A: 有三種方式:
1. Rust 程式碼中使用明確路徑:`Command::new("/opt/homebrew/bin/python3.11")`
2. 設定環境變數 PATH
3. 建立系統別名(不推薦,影響其他程式)
### Q: 如何管理多個 Python 版本?
A: 建議使用:
- **pyenv**:管理多個 Python 版本
- **venv**:隔離專案依賴
- **Docker**:容器化環境
---
## 檢查清單
新增 Python 腳本時確認:
- [ ] 使用 `#!/opt/homebrew/bin/python3.11` shebang
- [ ] 包含 docstring 說明功能
- [ ] 使用 argparse 處理命令行參數
- [ ] 使用 logging 進行日誌輸出
- [ ] 錯誤處理適當
- [ ] 類型提示完整
- [ ] 更新監控配置
- [ ] 建立測試案例
---
## 版本速查
| 版本 | 用途 | 路徑 |
|------|------|------|
| 3.11.14 | Momentry venv | /Users/accusys/momentry_core_0.1/venv/bin/python |
| 3.11.14 | 系統安裝 | /opt/homebrew/bin/python3.11 |
| 3.14.3 | 系統預設 | /opt/homebrew/bin/python3 |
---
## 相關文件
- [NODEJS.md](./NODEJS.md) - Node.js 開發指南
- [RUST_DEVELOPMENT.md](./RUST_DEVELOPMENT.md) - Rust 開發規範
- [monitor_config.yaml](../monitor/config/monitor_config.yaml) - 監控配置
- [python_monitor.sh](../monitor/service/python_monitor.sh) - Python 監控腳本

View File

@@ -1,990 +0,0 @@
# Rust 開發規範 - Momentry Core
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-16 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
| V1.1 | 2026-03-21 | 新增 PythonExecutor 模組說明 | OpenCode | - |
---
本規範定義 Momentry Core 專案的 Rust 開發標準,確保程式碼品質與一致性。
## 1. 專案結構
### 1.1 目錄架構
```
src/
├── main.rs # CLI 入口點
├── lib.rs # 函式庫導出
├── cli/
│ ├── mod.rs
│ └── commands/ # CLI 命令模組
├── core/
│ ├── mod.rs
│ ├── chunk/ # 影片分段邏輯
│ │ ├── mod.rs
│ │ ├── splitter.rs
│ │ └── types.rs
│ ├── db/ # 資料庫抽象層
│ │ ├── mod.rs
│ │ ├── postgres_db.rs
│ │ ├── mongodb_db.rs
│ │ ├── redis_db.rs
│ │ └── qdrant_db.rs
│ ├── processor/ # 影片處理器
│ │ ├── mod.rs
│ │ ├── executor.rs # Python 腳本統一執行器 (含超時控制)
│ │ ├── asr.rs # 語音識別
│ │ ├── asrx.rs # 說話者分離
│ │ ├── ocr.rs # 文字辨識
│ │ ├── yolo.rs # 物件偵測
│ │ ├── face.rs # 人臉偵測
│ │ └── pose.rs # 姿態估計
│ ├── embedding/ # 向量嵌入
│ ├── probe/ # ffprobe 整合
│ ├── storage/ # 檔案管理
│ └── thumbnail/ # 縮圖生成
├── api/ # HTTP API
│ ├── mod.rs
│ └── routes/
├── player/ # 影片播放
└── watcher/ # 檔案監控
```
### 1.2 模組設計原則
- **單一職責**: 每個模組專注於一項功能
- **介面抽象**: 使用 trait 定義資料庫、操作器等介面
- **依賴注入**: 透過建構函式注入依賴
```rust
pub trait VideoProcessor: Send + Sync {
async fn process(&self, video_path: &str) -> Result<ProcessResult>;
}
```
## 2. 程式碼風格
### 2.1 命名規範
| 類型 | 規範 | 範例 |
|------|------|------|
| 結構體/列舉 | PascalCase | `VideoRecord`, `ChunkType` |
| 函式/變數 | snake_case | `get_video_by_uuid` |
| Trait | PascalCase + er 尾碼 | `Database`, `ChunkStore` |
| 檔案 | snake_case | `postgres_db.rs` |
| 常量 | SCREAMING_SNAKE_CASE | `MAX_CHUNK_SIZE` |
| 模組 | snake_case | `chunk`, `processor` |
### 2.2 匯入順序
```rust
// 1. 標準庫
use std::path::Path;
use std::process::Command;
// 2. 外部庫
use anyhow::{Context, Result};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use tokio::fs;
// 3. 內部模組
use crate::core::chunk::Chunk;
use crate::core::db::PostgresDb;
```
### 2.3 行寬與格式
- 最大行寬: 100 字元
- 使用 4 空格縮排
- 啟用 clippy 與 fmt
```bash
# 格式化
cargo fmt
# 檢查格式
cargo fmt -- --check
# Lint
cargo clippy --all-features
```
## 3. 錯誤處理
### 3.1 錯誤類型選擇
| 情境 | 錯誤類型 | 原因 |
|------|----------|------|
| 應用程式 | `anyhow::Result<T>` | 提供靈活的錯誤傳播 |
| 函式庫 | `thiserror` | 定義明確的錯誤類型 |
| API 錯誤 | 自定義 Error enum | 提供客戶端錯誤碼 |
### 3.2 錯誤處理範例
```rust
use anyhow::{Context, Result, bail};
fn process_video(video_path: &str) -> Result<VideoMetadata> {
// 使用 context 提供錯誤上下文
let output = Command::new("ffprobe")
.args(["-v", "quiet", "-print_format", "json", "-show_format", video_path])
.output()
.context("Failed to run ffprobe")?;
// 使用 bail 進行早期返回
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
bail!("ffprobe failed: {}", stderr);
}
// 解析輸出
let metadata: Metadata = serde_json::from_slice(&output.stdout)
.context("Failed to parse ffprobe output")?;
Ok(metadata)
}
```
### 3.3 自定義錯誤 (適用於函式庫)
```rust
use thiserror::Error;
#[derive(Error, Debug)]
pub enum VideoError {
#[error("Video not found: {0}")]
NotFound(String),
#[error("Invalid codec: {0}")]
InvalidCodec(String),
#[error("Processing failed: {0}")]
ProcessingError(#[from] std::io::Error),
}
```
## 4. 异步編程
### 4.1 Tokio 配置
```rust
// Cargo.toml
tokio = { version = "1", features = ["full"] }
```
### 4.2 Async Trait
```rust
use async_trait::async_trait;
#[async_trait]
pub trait Database: Send + Sync {
async fn init() -> Result<Self>
where Self: Sized;
async fn get_video(&self, uuid: &str) -> Result<Option<VideoRecord>>;
async fn store_chunk(&self, chunk: &Chunk) -> Result<()>;
}
```
### 4.3 避免常見陷阱
```rust
// ❌ 錯誤: 在同步上下文中調用 async 函式
fn bad_example() {
let result = db.get_video("xxx"); // 編譯錯誤
}
// ✅ 正確: 使用 #[tokio::main]
#[tokio::main]
async fn main() {
let result = db.get_video("xxx").await;
}
// ❌ 錯誤: 阻塞執行緒池
async fn bad_practice() {
let data = std::fs::read_to_string("file.txt").unwrap(); // 阻塞
}
// ✅ 正確: 使用 tokio::fs
async fn good_practice() {
let data = tokio::fs::read_to_string("file.txt").await.unwrap();
}
```
## 5. 外部程序整合
當需要使用 Python 生態系工具 (如 faster-whisper, YOLO) 時:
```rust
pub async fn process_asr(video_path: &str, output_path: &str) -> Result<AsrResult> {
let script_path = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("scripts")
.join("asr_processor.py");
// 使用 venv 中的 Python確保版本隔離
let venv_python = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("venv")
.join("bin")
.join("python");
// 執行腳本
let output = Command::new(venv_python)
.arg(script_path)
.arg(video_path)
.arg(output_path)
.output()
.context("Failed to run ASR processor")?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
bail!("ASR failed: {}", stderr);
}
// 讀取輸出
let json_str = std::fs::read_to_string(output_path)
.context("Failed to read ASR output")?;
let result: AsrResult = serde_json::from_str(&json_str)
.context("Failed to parse ASR output")?;
Ok(result)
}
```
### 5.2 進度回報
透過 stderr 回報進度,供 Rust 端解析:
```python
# Python 腳本
import sys
print(f"ASR_START", file=sys.stderr)
print(f"ASR_LANGUAGE:{detected_lang}", file=sys.stderr)
print(f"ASR_PROGRESS:{count}", file=sys.stderr)
print(f"ASR_COMPLETE:{total}", file=sys.stderr)
```
```rust
// Rust 端解析
let stderr = String::from_utf8_lossy(&output.stderr);
for line in stderr.lines() {
if line.starts_with("ASR_PROGRESS:") {
let count = line.trim_start_matches("ASR_PROGRESS:");
println!("[ASR] Processed {} segments...", count);
}
}
```
### 5.3 PythonExecutor 統一執行器
使用 `PythonExecutor` 封裝 Python 腳本執行邏輯:
```rust
use momentry_core::core::processor::{PythonExecutor, validate_python_env};
// 驗證 Python 環境
fn init() -> Result<()> {
validate_python_env()?;
Ok(())
}
// 使用 Executor 執行腳本
async fn run_script() -> Result<()> {
let executor = PythonExecutor::new()?;
executor.run(
"asr_processor.py",
&["/path/to/video.mp4", "/path/to/output.json"],
Some("job-uuid"),
"ASR",
Some(Duration::from_secs(3600)), // 1小時超時
).await?;
Ok(())
}
```
#### Processor 超時設定
| Processor | 超時 | 說明 |
|----------|------|------|
| ASR | 1 小時 | 語音識別 |
| ASRx | 2 小時 | 說話者分離 |
| YOLO | 2 小時 | 物件偵測 |
| OCR | 2 小時 | 文字辨識 |
| Face | 2 小時 | 人臉偵測 |
| Pose | 2 小時 | 姿態估計 |
| Cut | 1 小時 | 場景偵測 |
---
## 6. Python 與 Node.js 混用規範
本專案同時使用 Python 和 Node.js (n8n),需建立明確的版本隔離與管理規範。
### 6.1 架構概述
```
┌─────────────────────────────────────────────────────────────┐
│ Momentry Core │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Rust │ │ Python │ │ Node.js │ │
│ │ (Core) │───▶ │ (Scripts) │ │ (n8n) │ │
│ │ │ │ │ │ │ │
│ │ - CLI │ │ - ASR │ │ - Workflow │ │
│ │ - DB │ │ - Thumb │ │ - API │ │
│ │ - Storage │ │ - OCR │ │ - Webhooks │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 資料庫 / 檔案系統 / Qdrant │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
### 6.2 Python 版本管理
#### 6.2.1 版本鎖定
| 版本 | 用途 | 路徑 |
|------|------|------|
| 3.11.14 | 影片處理腳本 | `/opt/homebrew/bin/python3.11` |
#### 6.2.2 虛擬環境
使用專案隔離的 venv
```bash
# 建立虛擬環境
cd /Users/accusys/momentry_core_0.1
python3.11 -m venv venv
# 啟用
source venv/bin/activate
# 安裝依賴
pip install faster-whisper opencv-python python-dotenv
```
#### 6.2.3 Rust 呼叫 Python
```rust
let venv_python = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("venv")
.join("bin")
.join("python");
let output = Command::new(venv_python)
.arg(script_path)
.arg(video_path)
.output()
.context("Failed to run Python script")?;
```
### 6.3 Node.js 版本管理
#### 6.3.1 版本鎖定
參考 `docs/NODEJS.md`
| 版本 | 用途 | 路徑 |
|------|------|------|
| 22.22.1 | n8n | `/opt/homebrew/opt/node@22/bin/node` |
#### 6.3.2 n8n 服務配置
使用 launchd plist 隔離:
```xml
<!-- com.momentry.n8n.main.plist -->
<key>ProgramArguments</key>
<array>
<string>/opt/homebrew/opt/node@22/bin/node</string>
<string>/opt/homebrew/lib/node_modules/n8n/bin/n8n</string>
<string>start</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/opt/homebrew/opt/node@22/bin:/opt/homebrew/bin:...</string>
</dict>
```
### 6.4 Python + Node.js 共存原則
#### 6.4.1 隔離原則
| 原則 | 說明 |
|------|------|
| **獨立路徑** | Python 用 venv 路徑Node.js 用 node@22 路徑 |
| **獨立環境** | n8n 服務使用 launchd plist不與 Rust 共享環境 |
| **明確版本** | 所有腳本明確指定直譯器路徑 |
| **PORT 分配** | n8n: 5678/5679, API: 另行分配 |
#### 6.4.2 環境變數隔離
```bash
# Rust 專案 .env
DATABASE_URL=postgres://...
# n8n plist
N8N_ENCRYPTION_KEY=xxx
N8N_BASIC_AUTH_ACTIVE=true
# 勿混用,避免 Rust 讀到 n8n 環境變數
```
### 6.5 工作流程整合
#### 6.5.1 Rust → Python
```
Rust CLI ──▶ Python Script ──▶ JSON Output ──▶ Rust Parse
│ │
└── venv/bin/python └── faster-whisper
```
#### 6.5.2 Rust → n8n Webhook
```rust
// 觸發 n8n workflow
use reqwest;
pub async fn trigger_n8n_webhook(webhook_url: &str, payload: &str) -> Result<()> {
let client = reqwest::Client::new();
client.post(webhook_url)
.json(payload)
.send()
.await
.context("Failed to trigger n8n webhook")?;
Ok(())
}
```
#### 6.5.3 n8n → Rust API
```
n8n Workflow ──▶ HTTP Request Node ──▶ Rust API Server
┌───────┴───────┐
│ axum server │
│ /api/webhook │
└───────────────┘
```
### 6.6 監控配置
#### 6.6.1 獨立監控腳本
```bash
# monitor/service/node_monitor.sh
# 監控 n8n Node.js 版本
# monitor/service/python_monitor.sh
# 監控 Python 腳本執行狀態
```
#### 6.6.2 健康檢查
```yaml
# monitor_config.yaml
services:
- name: "n8n"
type: "http"
port: 5678
check_url: "http://localhost:5678/"
- name: "Python Scripts"
type: "process"
check: "pgrep -f asr_processor.py"
```
### 6.7 排程管理
#### 6.7.1 備份排程 (Python 腳本)
```bash
# crontab
0 3 * * * /Users/accusys/momentry/scripts/backup_all.sh
```
#### 6.7.2 n8n 工作流排程
- 由 n8n 內建排程節點管理
- 不與 crontab 衝突
### 6.8 故障排除
#### 6.8.1 常見問題
| 問題 | 原因 | 解決方案 |
|------|------|----------|
| n8n 版本警告 | 使用 Node 25.x | 確認 plist 使用 node@22 |
| Python 腳本找不到模組 | 未啟用 venv | 使用 venv/bin/python |
| 執行權限錯誤 | shebang 錯誤 | 確認 #!/opt/homebrew/bin/python3.11 |
| Port 被佔用 | 多個服務使用相同 port | 分配獨立 port |
#### 6.8.2 診斷命令
```bash
# 檢查 Python 版本
which python
/opt/homebrew/bin/python3.11 --version
# 檢查 Node.js 版本
/opt/homebrew/opt/node@22/bin/node --version
# 檢查 n8n 程序
ps aux | grep n8n
# 檢查 Python 程序
ps aux | grep python
# 檢查 Port 佔用
lsof -i :5678 # n8n
```
### 6.9 新增服務決策
```
新服務需要哪種執行環境?
├─ Python 腳本 ──▶ 使用專案 venv
│ (路徑: venv/bin/python)
├─ Node.js 工具 ──▶ 評估版本需求
│ │
│ ├─ 支援 Node 22 ──▶ 使用 node@22
│ │
│ └─ 需要其他版本 ──▶ 安裝新版本 (如 node@20)
└─ 現有服務依賴 ──▶ 根據現有服務配置
```
### 6.10 文件維護
當新增 Python 或 Node.js 服務時:
1. 更新本文檔的版本表格
2. 建立對應的監控腳本
3. 如需 launchd plist建立並加入 `momentry_runtime/plist/`
4. 更新 `docs/NODEJS.md``docs/PYTHON.md`
### 5.2 進度回報
透過 stderr 回報進度,供 Rust 端解析:
```python
# Python 腳本
import sys
print(f"ASR_START", file=sys.stderr)
print(f"ASR_LANGUAGE:{detected_lang}", file=sys.stderr)
print(f"ASR_PROGRESS:{count}", file=sys.stderr)
print(f"ASR_COMPLETE:{total}", file=sys.stderr)
```
```rust
// Rust 端解析
let stderr = String::from_utf8_lossy(&output.stderr);
for line in stderr.lines() {
if line.starts_with("ASR_PROGRESS:") {
let count = line.trim_start_matches("ASR_PROGRESS:");
println!("[ASR] Processed {} segments...", count);
}
}
```
## 7. 測試策略
### 6.1 單元測試
```rust
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_chunk_creation() {
let chunk = Chunk::new(
"test-uuid".to_string(),
0,
ChunkType::Sentence,
0.0,
10.0,
serde_json::json!({"text": "Hello"}),
);
assert_eq!(chunk.uuid, "test-uuid");
assert_eq!(chunk.chunk_type, ChunkType::Sentence);
}
}
```
### 6.2 整合測試
```rust
#[cfg(test)]
mod integration {
use super::*;
#[tokio::test]
async fn test_database_connection() {
let db = PostgresDb::init().await.unwrap();
let videos = db.list_videos().await.unwrap();
assert!(videos.len() >= 0);
}
}
```
### 6.3 測試資料
- 使用測試資料庫隔離測試環境
- 避免在測試中使用真實敏感資料
- 使用 mock 物件模擬外部依賴
```rust
#[cfg(test)]
mod mocks {
pub struct MockVideoProcessor {
pub result: AsrResult,
}
impl VideoProcessor for MockVideoProcessor {
async fn process(&self, _video_path: &str) -> Result<AsrResult> {
Ok(self.result.clone())
}
}
}
```
## 8. 日誌與監控
### 7.1 日誌規範
- **使用 tracing**: 不要使用 `println!`
- **結構化日誌**: 使用訊息 + 欄位
```rust
use tracing::{info, warn, error};
fn process_video(uuid: &str) -> Result<()> {
info!(uuid = uuid, "Starting video processing");
match do_processing(uuid) {
Ok(_) => info!(uuid = uuid, "Processing completed"),
Err(e) => {
error!(uuid = uuid, error = %e, "Processing failed");
return Err(e);
}
}
}
```
### 7.2 初始化日誌
```rust
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
fn init_logging() {
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "momentry_core=info,tokio=warn".into()),
)
.with(tracing_subscriber::fmt::layer())
.init();
}
```
## 9. 性能優化
### 8.1 避免拷貝
```rust
// ❌ 拷貝
let data = record.clone();
// ✅ 引用
fn process(data: &Data) { }
// ✅ 或使用 Arc 共用
use std::sync::Arc;
let shared = Arc::new(data);
```
### 8.2 批量操作
```rust
// ❌ 逐筆插入
for item in items {
db.insert(&item).await?;
}
// ✅ 批量插入
db.insert_batch(&items).await?;
```
### 8.3 連線池
```rust
// 使用 sqlx 連線池
let pool = SqlxPool::connect(&DATABASE_URL).await?;
let db = PostgresDb::new(pool);
```
## 10. 安全考量
### 9.1 敏感資訊
- **不要**將密碼、API Key 寫入程式碼
- 使用環境變數或設定檔
- .env 檔案加入 .gitignore
```rust
// ❌ 硬編碼密碼
let password = "secret123";
// ✅ 使用環境變數
let password = std::env::var("DATABASE_PASSWORD")
.context("DATABASE_PASSWORD must be set")?;
```
### 9.2 命令注入
```rust
// ❌ 危險: 直接使用使用者輸入
let cmd = format!("ffprobe {}", user_input);
// ✅ 安全: 使用參數化
Command::new("ffprobe")
.arg(user_input) // 自動轉義
.output();
```
## 11. 文件編寫
### 10.1 結構體/函式文件
```rust
/// 代表一個影片記錄
///
/// # Fields
/// * `id` - 資料庫 ID
/// * `uuid` - 唯一識別碼
/// * `duration` - 影片時長 (秒)
///
/// # Example
/// ```
/// let video = VideoRecord {
/// id: 1,
/// uuid: "abc123".to_string(),
/// duration: 120.5,
/// };
/// ```
pub struct VideoRecord {
pub id: i64,
pub uuid: String,
pub duration: f64,
}
```
### 10.2 API 文件
```rust
/// 取得影片記錄
///
/// # Arguments
/// * `uuid` - 影片的 UUID
///
/// # Returns
/// * `Ok(Some(VideoRecord))` - 找到影片
/// * `Ok(None)` - 影片不存在
/// * `Err` - 資料庫錯誤
///
/// # Errors
/// 如果資料庫連線失敗,返回資料庫錯誤
pub async fn get_video(&self, uuid: &str) -> Result<Option<VideoRecord>>;
```
## 12. CLI 命令設計
### 11.1 命令結構
使用 clap derive
```rust
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(name = "momentry")]
#[command(about = "Digital asset management system")]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Register a video file
Register {
/// Path to video file
path: String,
},
/// Process video
Process {
/// UUID or path
target: String,
},
/// Generate chunks
Chunk {
/// Video UUID
uuid: String,
},
}
```
### 11.2 錯誤處理
```rust
match &cli.command {
Commands::Register { path } => {
if !Path::new(path).exists() {
eprintln!("Error: File not found: {}", path);
std::process::exit(1);
}
// ...
}
}
```
## 13. 依賴管理
### 12.1 版本約束
```toml
# Cargo.toml
[dependencies]
anyhow = "1.0" # 精確版本
tokio = { version = "1", features = ["full"] } # 範圍版本
serde = "1.0" # 精確版本
```
### 12.2 避免依賴地獄
- 審查依賴數量
- 優先使用標準庫
- 選擇維護良好的套件
## 14. 建構與部署
### 13.1 建構命令
```bash
# 開發建構
cargo build
# 發布建構
cargo build --release
# 單一二進制
cargo build --bin momentry
```
### 13.2 檢查清單
```bash
# 格式化
cargo fmt -- --check
# Lint
cargo clippy --all-features -- -D warnings
# 類型檢查
cargo check --all-features
# 測試
cargo test
```
## 15. 版本控制
### 14.1 提交訊息規範
```
<type>(<scope>): <subject>
<body>
<footer>
```
類型:
- `feat`: 新功能
- `fix`: 錯誤修復
- `docs`: 文件變更
- `style`: 格式調整
- `refactor`: 重構
- `test`: 測試變更
- `chore`: 建構/工具變更
範例:
```
feat(processor): Add ASR progress reporting
- Add stderr parsing for progress updates
- Support ASR_START, ASR_PROGRESS, ASR_COMPLETE markers
Closes #123
```
### 14.2 分支策略
- `main`: 穩定版本
- `feature/*`: 新功能開發
- `fix/*`: 錯誤修復
- `refactor/*`: 重構
---
## 附錄: 快速參考
### 建構
```bash
cargo build --release
cargo run -- --help
```
### 品質檢查
```bash
cargo fmt -- --check
cargo clippy --all-features
cargo check --all-features
cargo test
```
### 依賴
```bash
cargo add <package>
cargo tree
cargo outdated
```

View File

@@ -1,121 +0,0 @@
# 搜尋範例 Prompt
## 基本搜尋測試
### 1. 簡單關鍵字搜尋
```bash
curl -X POST http://localhost:3002/api/v1/search \
-H "Content-Type: application/json" \
-d '{"query": "charade", "limit": 5}'
```
### 2. 電影相關詞
```
charade
woody allen
audrey hepburn
classic movie
old time movie
romantic comedy
```
### 3. 場景描述
```
widowed woman
secret agent
chase scene
paris
```
---
## 進階搜尋測試
### 4. 短語搜尋
```bash
curl -X POST http://localhost:3002/api/v1/search \
-H "Content-Type: application/json" \
-d '{"query": "fun plot twists", "limit": 3}'
```
### 5. 情感/描述詞
```
charming performances
hilarious
suspenseful
dramatic
```
### 6. 動作場景
```
running
chase
fighting
dancing
```
---
## 整合範例
### n8n Workflow
```
搜尋詞: "charade"
→ 取得 chunk 的 start_time, end_time
→ 組裝成影片 URL
→ 回傳給用戶
```
### PHP 範例
```php
$searchTerms = ['charade', 'woody', 'audrey', 'classic'];
// 搜尋每個詞
foreach ($searchTerms as $term) {
$ch = curl_init('http://localhost:3002/api/v1/search');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'query' => $term,
'limit' => 5
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
$response = curl_exec($ch);
$data = json_decode($response, true);
// 處理結果
foreach ($data['results'] as $result) {
echo "{$result['text']} (score: {$result['score']})\n";
}
}
```
---
## 預期回傳格式
```json
{
"results": [
{
"uuid": "a1b10138a6bbb0cd",
"chunk_id": "sentence_0006",
"chunk_type": "sentence",
"start_time": 48.8,
"end_time": 55.44,
"text": "fun plot twists, Woody Dialog and charming performances...",
"score": 0.526
}
],
"query": "charade"
}
```
---
## 測試檢查清單
- [ ] 基本關鍵字搜尋
- [ ] n8n 整合格式
- [ ] 影片時戳取得
- [ ] 多筆結果排序
- [ ] 不同 chunk_type 搜尋

File diff suppressed because it is too large Load Diff

View File

@@ -1,698 +0,0 @@
# Momentry 服務添加規範 v2.1
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-16 |
| 更新時間 | 2026-03-24 |
| 文件版本 | V2.1 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
| V2.1 | 2026-03-24 | 更新 launchctl 命令,使用 bootstrap | OpenCode | OpenCode / big-pickle |
---
## 一、概述
本文檔定義在 Momentry 系統中添加新服務的標準流程和規範。
**重要原則**
- 使用 `launchctl` 管理服務,勿使用 `brew services`
- 所有服務使用 `com.momentry.*` 作為 plist Label
- 數據存放於 `/Users/accusys/momentry/` 目錄
- 每個服務需提供完整的監控腳本
- 所有服務 Plist 存放於 `/Library/LaunchDaemons/`
- 所有服務以 `accusys` 用戶運行,確保 accusys 可以管理
---
## 二、服務運行方式
### 2.1 運行分類
| 類型 | 說明 | 示例 |
|------|------|------|
| **開機自動運行** | 電腦開機後立即自動啟動 | PostgreSQL, Redis, n8n, Caddy 等核心服務 |
| **登入時運行** | 用戶登入後才啟動 | 開發工具、臨時服務 |
**當前所有服務**:均為開機自動運行
### 2.2 Plist 存放位置
所有 Momentry 服務統一存放於:
```
/Library/LaunchDaemons/com.momentry.{service_name}.plist
```
---
## 三、服務命名規範
### 3.1 Plist 文件命名
```
com.momentry.{service_name}.plist
```
示例:
- `com.momentry.redis.plist`
- `com.momentry.n8n.main.plist`
- `com.momentry.rustdesk.hbbs.plist`
### 3.2 目錄命名
服務相關目錄統一放置於:
```
/Users/accusys/momentry/
├── var/{service_name}/ # 服務數據
├── etc/{service_name}/ # 服務配置
├── log/{service_name}.log # 服務日誌 (stdout)
└── log/{service_name}.error.log # 錯誤日誌 (stderr)
```
---
## 四、Plist 文件模板
### 4.1 標準服務模板
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.momentry.{service_name}</string>
<key>UserName</key>
<string>accusys</string>
<key>WorkingDirectory</key>
<string>/Users/accusys/momentry/var/{service_name}</string>
<key>ProgramArguments</key>
<array>
<string>/path/to/executable</string>
<string>--arg1</string>
<string>value1</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
<!-- 其他環境變數 -->
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/accusys/momentry/log/{service_name}.log</string>
<key>StandardErrorPath</key>
<string>/Users/accusys/momentry/log/{service_name}.error.log</string>
</dict>
</plist>
```
### 4.2 日誌文件規範
每個服務必須創建兩個日誌文件:
| 文件 | 說明 | 路徑 |
|------|------|------|
| StandardOutPath | 標準輸出日誌 | `/Users/accusys/momentry/log/{service_name}.log` |
| StandardErrorPath | 錯誤輸出日誌 | `/Users/accusys/momentry/log/{service_name}.error.log` |
**創建日誌文件**
```bash
touch /Users/accusys/momentry/log/{service_name}.log
touch /Users/accusys/momentry/log/{service_name}.error.log
chmod 644 /Users/accusys/momentry/log/{service_name}.log
chmod 644 /Users/accusys/momentry/log/{service_name}.error.log
```
---
## 五、添加服務步驟
### 步驟 1創建目錄結構
```bash
# 創建服務目錄
mkdir -p /Users/accusys/momentry/var/{service_name}
mkdir -p /Users/accusys/momentry/etc/{service_name}
# 創建日誌文件
touch /Users/accusys/momentry/log/{service_name}.log
touch /Users/accusys/momentry/log/{service_name}.error.log
```
### 步驟 2創建 Plist 文件
```bash
# 複製模板並編輯
cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/template.service.plist \
/Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.{service_name}.plist
# 編輯 plist 文件
vim /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.{service_name}.plist
```
### 步驟 3複製到系統 LaunchDaemons
```bash
# 複製到 /Library/LaunchDaemons/
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.{service_name}.plist \
/Library/LaunchDaemons/
```
### 步驟 4載入服務
```bash
# 載入服務
sudo launchctl load /Library/LaunchDaemons/com.momentry.{service_name}.plist
# 驗證服務狀態
launchctl list | grep momentry
```
### 步驟 5添加監控
`monitor/config/monitor_config.yaml` 中添加服務配置:
```yaml
service:
services:
- name: "{service_name}"
type: "http" # 或 "process", "tcp"
port: {port_number}
host: "localhost"
check_url: "http://localhost:{port}/health"
timeout: 5
enabled: true
```
### 步驟 6添加文檔
`docs/INSTALL_{SERVICE_NAME}.md` 中記錄:
- 安裝步驟
- 配置說明
- 健康檢查命令
- 故障排除
---
## 六、服務分類
### 按功能分類
| 類別 | 服務 |
|------|------|
| 資料庫 | PostgreSQL, Redis, MariaDB, MongoDB |
| 應用 | n8n, Gitea, SFTPGo |
| 網頁 | Caddy, PHP-FPM |
| AI/ML | Ollama, Qdrant |
| 遠程 | RustDesk |
### 按運行方式分類
| 運行方式 | 數量 | 服務 |
|----------|------|------|
| 開機自動運行 | 15 | PostgreSQL, Redis, n8n, Caddy, Gitea, SFTPGo, Ollama, Qdrant, MariaDB, PHP-FPM, RustDesk, MongoDB, Agent |
| 登入時運行 | 0 | (暫無) |
---
## 七、監控要求
每個服務必須提供:
### 7.1 健康檢查
`monitor/service/health_check.sh` 中添加檢查函數:
```bash
check_{service_name}() {
local start=$(date +%s%N)
if nc -z localhost {port} > /dev/null 2>&1; then
local end=$(date +%s%N)
local ms=$(( (end - start) / 1000000 ))
echo -e "${GREEN}${NC} {service_name} ({port}) - ${ms}ms"
record_service "{service_name}" "up" "$ms" ""
return 0
else
echo -e "${RED}${NC} {service_name} ({port}) - Down"
record_service "{service_name}" "down" "0" "Connection failed"
return 1
fi
}
```
### 7.2 數據庫記錄
```sql
-- 添加服務監控記錄函數
record_service() {
local service=$1
local status=$2
local response_time=$3
local error_msg=$4
psql -U accusys -h localhost -d momentry << EOF
INSERT INTO monitor_services (service_name, service_type, status, response_time_ms, error_message, checked_at)
VALUES ('$service', 'service', '$status', $response_time, '$error_msg', NOW());
EOF
}
```
---
## 八、服務管理命令
### 8.1 基本操作
```bash
# 啟動服務 (使用 launchctl bootstrap)
sudo launchctl bootstrap system /Library/LaunchDaemons/com.momentry.{service}.plist
# 停止服務 (使用 launchctl bootout)
sudo launchctl bootout system/com.momentry.{service}.plist
# 重新載入服務
sudo launchctl bootout system/com.momentry.{service}.plist
sudo launchctl bootstrap system /Library/LaunchDaemons/com.momentry.{service}.plist
# 查看服務狀態
launchctl list | grep com.momentry
# 查看特定服務狀態
launchctl list | grep com.momentry.{service}
# 查看服務日誌
tail -f /Users/accusys/momentry/log/{service}.log
tail -f /Users/accusys/momentry/log/{service}.error.log
```
### 8.2 批量管理
```bash
# 啟動所有 Momentry 服務
for plist in /Library/LaunchDaemons/com.momentry.*.plist; do
sudo launchctl bootstrap system "$plist"
done
# 停止所有 Momentry 服務
for svc in $(launchctl list | grep com.momentry | awk '{print $3}'); do
sudo launchctl bootout system/$svc 2>/dev/null
done
# 查看所有 Momentry 服務狀態
launchctl list | grep com.momentry
```
### 8.2 故障排除
```bash
# 檢查服務是否運行
pgrep -f "{service_process_name}"
# 檢查端口是否監聽
lsof -i :{port}
# 檢查錯誤日誌
tail -100 /Users/accusys/momentry/log/{service}.error.log
```
---
## 九、服務備份作業
### 9.1 備份內容
每個服務需要備份的內容:
| 類別 | 路徑 | 說明 |
|------|------|------|
| 數據 | `/Users/accusys/momentry/var/{service}/` | 服務運行數據 |
| 配置 | `/Users/accusys/momentry/etc/{service}/` | 服務配置文件 |
| Plist | `/Library/LaunchDaemons/com.momentry.{service}.plist` | 啟動配置 |
| 日誌 | `/Users/accusys/momentry/log/{service}.log` | 運行日誌 |
### 9.2 備份命名規範
**格式**: `{service}_{type}_{YYYYMMDD}_{HHMMSS}[_{suffix}].{ext}`
**組成部分**:
| 位置 | 說明 | 範例 |
|------|------|------|
| `{service}` | 服務名稱 (小寫) | `postgresql`, `redis`, `n8n` |
| `{type}` | 備份類型 | `full`, `db`, `cfg`, `data` |
| `{YYYYMMDD}` | 備份日期 | `20260315` |
| `{HHMMSS}` | 備份時間 (24小時制) | `030000` |
| `{suffix}` | 可選標記 | `incremental`, `verified` |
| `{ext}` | 檔案擴展名 | `sql.gz`, `tar.gz`, `rdb`, `zip` |
**類型說明**:
| 類型 | 說明 | 包含內容 |
|------|------|---------|
| `full` | 完整備份 | 數據 + 配置 + 日誌 |
| `db` | 數據庫備份 | 資料庫導出 (sql, rdb) |
| `cfg` | 配置備份 | 配置文件 |
| `data` | 數據備份 | var 目錄 |
**範例**:
```
postgresql_db_20260315_030000.sql.gz # PostgreSQL 完整資料庫 (壓縮)
redis_rdb_20260315_030000.rdb # Redis RDB 快照
n8n_full_20260315_030000.tar.gz # n8n 完整備份
mariadb_db_wordpress_20260315_030000.sql.gz # MariaDB WP 資料庫
gitea_full_20260315_030000.zip # Gitea 完整備份
qdrant_snapshot_20260315_030000.tar.gz # Qdrant 向量庫
ollama_cfg_20260315_030000.tar.gz # Ollama 配置
caddy_cfg_20260315_030000.tar.gz # Caddy 配置
```
**可信斷點標記**:
- 檔名本身即為可信時間點
- 還原時直接使用檔名中的時間戳
- 建議配合 `backup_registry` 資料庫記錄完整元數據
**校驗和命名**:
```
postgresql_db_20260315_030000.sql.gz.sha256
```
### 9.3 備份腳本
```bash
#!/bin/bash
# 標準化備份腳本範本
# 遵循命名規範: {service}_{type}_{YYYYMMDD}_{HHMMSS}.{ext}
set -e
SERVICE_NAME="{service_name}"
BACKUP_TYPE="{type}" # full, db, cfg, data
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/Users/accusys/momentry/backup/${SERVICE_NAME}"
mkdir -p "$BACKUP_DIR"
# 根據類型執行備份
case "$BACKUP_TYPE" in
full)
echo "[$TIMESTAMP] 執行 $SERVICE_NAME 完整備份..."
tar -czf "$BACKUP_DIR/${SERVICE_NAME}_full_${TIMESTAMP}.tar.gz" \
/Users/accusys/momentry/var/${SERVICE_NAME}/ \
/Users/accusys/momentry/etc/${SERVICE_NAME}/ 2>/dev/null
;;
db)
echo "[$TIMESTAMP] 執行 $SERVICE_NAME 資料庫備份..."
if [ "$SERVICE_NAME" = "postgresql" ]; then
pg_dump -U accusys ${SERVICE_NAME} | gzip > \
"$BACKUP_DIR/${SERVICE_NAME}_db_${TIMESTAMP}.sql.gz"
elif [ "$SERVICE_NAME" = "mariadb" ]; then
mysqldump -u root -p --all-databases | gzip > \
"$BACKUP_DIR/${SERVICE_NAME}_db_${TIMESTAMP}.sql.gz"
elif [ "$SERVICE_NAME" = "redis" ]; then
redis-cli -a accusys SAVE
cp /opt/homebrew/var/db/redis/dump.rdb \
"$BACKUP_DIR/${SERVICE_NAME}_rdb_${TIMESTAMP}.rdb"
fi
;;
cfg)
echo "[$TIMESTAMP] 執行 $SERVICE_NAME 配置備份..."
tar -czf "$BACKUP_DIR/${SERVICE_NAME}_cfg_${TIMESTAMP}.tar.gz" \
/Users/accusys/momentry/etc/${SERVICE_NAME}/ 2>/dev/null
;;
data)
echo "[$TIMESTAMP] 執行 $SERVICE_NAME 數據備份..."
tar -czf "$BACKUP_DIR/${SERVICE_NAME}_data_${TIMESTAMP}.tar.gz" \
/Users/accusys/momentry/var/${SERVICE_NAME}/ 2>/dev/null
;;
esac
# 生成校驗和
if [ -f "$BACKUP_DIR/${SERVICE_NAME}_${BACKUP_TYPE}_${TIMESTAMP}"* ]; then
sha256sum "$BACKUP_DIR/${SERVICE_NAME}_${BACKUP_TYPE}_${TIMESTAMP}"* > \
"$BACKUP_DIR/${SERVICE_NAME}_${BACKUP_TYPE}_${TIMESTAMP}.sha256"
fi
# 清理舊備份 (保留 30 天)
find "$BACKUP_DIR" -name "*_${TIMESTAMP%%_*}_*.tar.gz" -mtime +30 -delete 2>/dev/null
find "$BACKUP_DIR" -name "*_${TIMESTAMP%%_*}_*.sql.gz" -mtime +30 -delete 2>/dev/null
find "$BACKUP_DIR" -name "*_${TIMESTAMP%%_*}_*.rdb" -mtime +30 -delete 2>/dev/null
find "$BACKUP_DIR" -name "*.sha256" -mtime +30 -delete 2>/dev/null
echo "備份完成: ${SERVICE_NAME}_${BACKUP_TYPE}_${TIMESTAMP}"
```
### 9.4 備份排程
建議使用 cron 進行自動備份:
```bash
# 編輯 crontab
crontab -e
# 添加備份任務 (每天凌晨 3 點)
0 3 * * * /Users/accusys/momentry/scripts/backup_{service}.sh >> /Users/accusys/momentry/log/backup.log 2>&1
# 每週日凌晨 3 點執行完整備份
0 3 * * 0 /Users/accusys/momentry/scripts/backup_{service}.sh full >> /Users/accusys/momentry/log/backup.log 2>&1
```
### 9.5 備份驗證
```bash
# 查看備份列表 (按時間排序)
ls -lt /Users/accusys/momentry/backup/{service}/
# 驗證備份完整性
# 1. 檢查校驗和
sha256sum -c /Users/accusys/momentry/backup/{service}/*.sha256
# 2. 驗證 tar 壓縮
tar -tzf /Users/accusys/momentry/backup/{service}/{service}_full_20260315_030000.tar.gz
# 3. 驗證 SQL 備份
zcat /Users/accusys/momentry/backup/{service}/{service}_db_20260315_030000.sql.gz | head -5
# 驗證備份完整性
tar -tzf /Users/accusys/momentry/backup/{service}/{service}_var_20260315.tar.gz
```
---
## 十、服務完整刪除作業
### 10.1 刪除前確認
**警告**:此操作不可逆,請確保已完成備份!
- [ ] 確認服務已停止運行
- [ ] 確認數據已備份
- [ ] 確認無其他服務依賴此服務
### 10.2 刪除步驟
**步驟 1停止服務**
```bash
# 停止服務
sudo launchctl unload /Library/LaunchDaemons/com.momentry.{service}.plist
# 驗證服務已停止
launchctl list | grep momentry.{service}
```
**步驟 2刪除 Plist**
```bash
# 刪除系統 Plist
sudo rm /Library/LaunchDaemons/com.momentry.{service}.plist
# 刪除專案 Plist
rm /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.{service}.plist
```
**步驟 3刪除數據和配置**
```bash
# 刪除數據目錄
sudo rm -rf /Users/accusys/momentry/var/{service}/
# 刪除配置目錄
sudo rm -rf /Users/accusys/momentry/etc/{service}/
# 刪除日誌
rm -f /Users/accusys/momentry/log/{service}.log
rm -f /Users/accusys/momentry/log/{service}.error.log
```
**步驟 4清理監控配置**
```bash
# 從監控配置中移除服務
vim /Users/accusys/momentry_core_0.1/monitor/config/monitor_config.yaml
# 刪除該服務的監控配置
# 從監控腳本中移除
vim /Users/accusys/momentry_core_0.1/monitor/service/health_check.sh
# 移除該服務的檢查函數
```
**步驟 5清理監控數據可選**
```bash
# 保留歷史數據還是刪除?
# 刪除監控數據
psql -U accusys -h localhost -d momentry -c "
DELETE FROM monitor_services WHERE service_name = '{service}';
"
```
### 10.3 驗證刪除
```bash
# 確認服務已停止
launchctl list | grep momentry.{service}
# 確認目錄已刪除
ls /Users/accusys/momentry/var/{service}/ 2>/dev/null || echo "已刪除"
# 確認 Plist 已刪除
ls /Library/LaunchDaemons/com.momentry.{service}.plist 2>/dev/null || echo "已刪除"
```
### 10.4 完整刪除腳本
```bash
#!/bin/bash
SERVICE_NAME="{service_name}"
echo "========== 服務完整刪除 =========="
echo "服務: $SERVICE_NAME"
echo "警告:此操作不可逆!"
read -p "確認繼續 (yes/no): " confirm
if [ "$confirm" != "yes" ]; then
echo "取消刪除"
exit 0
fi
# 停止服務
echo "[1/6] 停止服務..."
sudo launchctl unload /Library/LaunchDaemons/com.momentry.${SERVICE_NAME}.plist 2>/dev/null
# 刪除 Plist
echo "[2/6] 刪除 Plist..."
sudo rm -f /Library/LaunchDaemons/com.momentry.${SERVICE_NAME}.plist
rm -f /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.${SERVICE_NAME}.plist
# 刪除數據
echo "[3/6] 刪除數據..."
sudo rm -rf /Users/accusys/momentry/var/${SERVICE_NAME}/
# 刪除配置
echo "[4/6] 刪除配置..."
sudo rm -rf /Users/accusys/momentry/etc/${SERVICE_NAME}/
# 刪除日誌
echo "[5/6] 刪除日誌..."
rm -f /Users/accusys/momentry/log/${SERVICE_NAME}.log
rm -f /Users/accusys/momentry/log/${SERVICE_NAME}.error.log
# 清理監控數據
echo "[6/6] 清理監控數據..."
psql -U accusys -h localhost -d momentry -c "
DELETE FROM monitor_services WHERE service_name = '${SERVICE_NAME}';
" 2>/dev/null
echo "========== 刪除完成 =========="
```
---
## 十一、檢查清單
添加新服務時,請確認以下項目:
- [ ] 創建服務目錄 (`var/`, `etc/`)
- [ ] 配置日誌文件 (`.log` + `.error.log`)
- [ ] 創建 plist 文件UserName 設為 `accusys`
- [ ] 複製到 `/Library/LaunchDaemons/`
- [ ] 使用 launchctl 載入服務
- [ ] 驗證服務運行
- [ ] 添加監控配置
- [ ] 測試監控腳本
- [ ] 創建安裝文檔
- [ ] 更新 SERVICES.md 服務清單
- [ ] 更新 MOMENTRY_INTEGRATION_GUIDE.md
---
## 十二、模板文件
### Plist 模板位置
```
/Users/accusys/momentry_core_0.1/momentry_runtime/plist/
├── template.service.plist # 服務模板
├── com.momentry.redis.plist # 服務示例
└── com.momentry.n8n.main.plist # 複雜服務示例
```
### 創建模板命令
```bash
# 創建服務模板
cat > /Users/accusys/momentry_core_0.1/momentry_runtime/plist/template.service.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.momentry.SERVICE_NAME</string>
<key>UserName</key>
<string>accusys</string>
<key>WorkingDirectory</key>
<string>/Users/accusys/momentry/var/SERVICE_NAME</string>
<key>ProgramArguments</key>
<array>
<string>/path/to/executable</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/accusys/momentry/log/SERVICE_NAME.log</string>
<key>StandardErrorPath</key>
<string>/Users/accusys/momentry/log/SERVICE_NAME.error.log</string>
</dict>
</plist>
EOF
```
---
## 十一、版本歷史
| 版本 | 日期 | 內容 |
|------|------|------|
| 1.0 | 2026-03-15 | 初始版本 |
| 2.0 | 2026-03-15 | 統一 Plist 位置、移除 root/用戶區分、加入運行方式分類 |
| 2.1 | 2026-03-15 | 新增服務備份作業、服務完整刪除作業 |
| 2.1 | 2026-03-24 | 更新 launchctl 命令,使用 `bootstrap`/`bootout` 替代 `load`/`unload` | |

View File

@@ -1,504 +0,0 @@
# SFTPGo Demo 用戶指南
## Web 管理介面
**URL**: https://sftpgo.momentry.ddns.net
### 登入方式
| 角色 | 用戶名 | 密碼 |
|------|--------|------|
| **Demo 用戶** | `demo` | `demopassword123` |
### 可用功能
- 瀏覽個人目錄結構
- 上傳、下載檔案
- 查看上傳記錄
---
## 快速連線資訊
| 項目 | 值 |
|------|-----|
| **主機** | `sftpgo.momentry.ddns.net` |
| **SFTP 連接埠** | `2022` |
| **用戶名** | `demo` |
| **密碼** | `demopassword123` |
| **主目錄** | `/demo` |
---
## 連線方式
### 1. 命令列 SFTP
```bash
# 使用密碼連線
sshpass -p "demopassword123" sftp -P 2022 demo@sftpgo.momentry.ddns.net
# 使用金鑰連線 (需先設定)
sftp -P 2022 -i ~/.ssh/id_rsa demo@sftpgo.momentry.ddns.net
```
### 2. FileZilla
1. **主機**: `sftp://sftpgo.momentry.ddns.net`
2. **連接埠**: `2022`
3. **協定**: `SFTP`
4. **登入類型**: `一般`
5. **用戶名**: `demo`
6. **密碼**: `demopassword123`
### 3. Cyberduck (macOS)
1. 選擇 **連線 > 新連線**
2. 協定選擇 **SFTP (SSH File Transfer Protocol)**
3. 伺服器: `sftpgo.momentry.ddns.net`
4. 連接埠: `2022`
5. 使用者名稱: `demo`
6. 密碼: `demopassword123`
### 4. curl 上傳
```bash
curl -u demo:demopassword123 \
-T /path/to/video.mp4 \
sftp://sftpgo.momentry.ddns.net:2022/demo/
```
---
## SFTP 基本操作
### 連線後常用指令
```bash
# 進入互動式模式
sftp demo@sftpgo.momentry.ddns.net -P 2022
# 常用指令
sftp> pwd # 顯示目前目錄
sftp> ls # 列出檔案
sftp> ls -la # 詳細列表
sftp> cd uploads # 切換目錄
sftp> mkdir videos # 建立目錄
sftp> put local.mp4 # 上傳檔案
sftp> get remote.mp4 # 下載檔案
sftp> rm old.mp4 # 刪除檔案
sftp> exit # 斷線
```
### 批次上傳
```bash
# 上傳多個檔案
sshpass -p "demopassword123" sftp -P 2022 demo@sftpgo.momentry.ddns.net <<EOF
cd uploads
put video1.mp4
put video2.mp4
put video3.mp4
bye
EOF
# 使用 glob 上傳
sshpass -p "demopassword123" sftp -P 2022 demo@sftpgo.momentry.ddns.net <<EOF
mput /path/to/videos/*.mp4
bye
EOF
```
---
## 自動上傳腳本
### Bash 腳本
```bash
#!/bin/bash
# upload.sh - 上傳視頻到 Momentry
HOST="sftpgo.momentry.ddns.net"
PORT="2022"
USER="demo"
PASS="demopassword123"
REMOTE_DIR="/demo/uploads"
# 要上傳的檔案
FILE="$1"
if [ -z "$FILE" ]; then
echo "用法: $0 <檔案路徑>"
exit 1
fi
sshpass -p "$PASS" sftp -P $PORT $USER@$HOST <<EOF
mkdir $REMOTE_DIR
cd $REMOTE_DIR
put "$FILE"
bye
EOF
echo "上傳完成: $FILE"
```
使用方式:
```bash
chmod +x upload.sh
./upload.sh /path/to/video.mp4
```
### Python 腳本
```python
#!/usr/bin/env python3
"""上傳檔案到 Momentry SFTP"""
import paramiko
import sys
import os
def upload_file(local_path, remote_dir="/demo/uploads"):
host = "sftpgo.momentry.ddns.net"
port = 2022
username = "demo"
password = "demopassword123"
transport = paramiko.Transport((host, port))
transport.connect(username=username, password=password)
sftp = paramiko.SFTPClient.from_transport(transport)
filename = os.path.basename(local_path)
remote_path = f"{remote_dir}/{filename}"
sftp.put(local_path, remote_path)
print(f"已上傳: {filename} -> {remote_path}")
sftp.close()
transport.close()
if __name__ == "__main__":
if len(sys.argv) < 2:
print("用法: python upload_sftp.py <檔案路徑>")
sys.exit(1)
upload_file(sys.argv[1])
```
安裝依賴:
```bash
pip install paramiko
```
---
## WebDAV 替代方案
如果 SFTP 連線有問題,可使用 WebDAV:
| 項目 | 值 |
|------|-----|
| **URL** | `https://momentry.ddns.net/webdav/` |
| **用戶名** | `demo` |
| **密碼** | `demopassword123` |
### curl 使用 WebDAV
```bash
# 上傳
curl -u demo:demopassword123 \
-T video.mp4 \
"https://momentry.ddns.net/webdav/demo/uploads/"
# 下載
curl -u demo:demopassword123 \
-o video.mp4 \
"https://momentry.ddns.net/webdav/demo/uploads/video.mp4"
# 列出目錄
curl -u demo:demopassword123 \
-X PROPFIND \
"https://momentry.ddns.net/webdav/demo/" \
-H "Depth: 1"
```
---
## 故障排除
### 連線被拒絕
```bash
# 檢查 SFTPGo 是否運行
curl -s http://localhost:8080/api/v2/status | jq .status
# 檢查連接埠
nc -zv momentry.ddns.net 2022
```
### 認證失敗
確認密碼是否正確:
```bash
# 測試認證
curl -u demo:demopassword123 \
"https://momentry.ddns.net/webdav/" -I
```
### 權限不足
上傳目錄可能需要先建立:
```bash
sshpass -p "demopassword123" sftp -P 2022 demo@sftpgo.momentry.ddns.net <<EOF
mkdir uploads
mkdir videos
bye
EOF
```
---
## 檔案上傳後自動化
上傳後SFTPGo 會自動:
1. 觸發 Hook 腳本
2. 記錄上傳事件到 `/Users/accusys/sftpgo_test/hook.log`
3. 呼叫 Momentry Core API 註冊視頻
查看上傳日誌:
```bash
tail -f /Users/accusys/sftpgo_test/hook.log
```
---
## 管理手冊
### 管理員帳戶
| 角色 | 用戶名 | 密碼 | 說明 |
|------|--------|------|------|
| **WebAdmin** | `admin` | `Test3200Test3200` | SFTPGo 管理介面 |
**WebAdmin URL**: https://sftpgo.momentry.ddns.net
### Admin 創建方式
根據官方文檔SFTPGo 有兩種方式創建管理員:
#### 方式 1: Web UI (首次設定)
1. 訪問 `http://localhost:8080/web/admin`
2. 如果沒有管理員,會顯示設定畫面
3. 輸入用戶名和密碼創建第一個管理員
#### 方式 2: 自動創建 (推薦)
需要同時滿足以下條件:
1. 配置文件中設定 `"create_default_admin": true`
2. 設定環境變數 `SFTPGO_DEFAULT_ADMIN_USERNAME``SFTPGO_DEFAULT_ADMIN_PASSWORD`
### 設定步驟
#### Step 1: 確保配置文件正確
確認 `/Users/accusys/momentry/etc/sftpgo/sftpgo.json` 中有:
```json
{
"data_provider": {
"create_default_admin": true
},
"httpd": {
"setup": {
"installation_code": "Test3200Test3200"
}
}
}
```
#### Step 2: 更新 plist 加入環境變數
編輯 `/Library/LaunchDaemons/com.momentry.sftpgo.plist`,加入:
```xml
<key>EnvironmentVariables</key>
<dict>
<key>SFTPGO_DEFAULT_ADMIN_USERNAME</key>
<string>admin</string>
<key>SFTPGO_DEFAULT_ADMIN_PASSWORD</key>
<string>Test3200Test3200</string>
</dict>
```
#### Step 3: 重啟 SFTPGo
```bash
launchctl unload homebrew.mxcl.sftpgo
launchctl load homebrew.mxcl.sftpgo
```
#### Step 4: 驗證管理員
```bash
curl -s -X GET "http://localhost:8080/api/v2/token" \
-u "admin:Test3200Test3200"
```
成功回應:
```json
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 1200
}
```
### REST API 認證流程
#### 1. 獲取 Token
```bash
curl -s -X GET "http://localhost:8080/api/v2/token" \
-u "admin:Test3200Test3200"
```
#### 2. 使用 Token 訪問 API
```bash
TOKEN=$(curl -s -X GET "http://localhost:8080/api/v2/token" \
-u "admin:Test3200Test3200" | jq -r '.access_token')
# 查看所有用戶
curl -s http://localhost:8080/api/v2/users \
-H "Authorization: Bearer $TOKEN"
# 查看系統狀態
curl -s http://localhost:8080/api/v2/status \
-H "Authorization: Bearer $TOKEN"
```
### 常用管理操作
#### 查看所有用戶
```bash
TOKEN=$(curl -s -X GET "http://localhost:8080/api/v2/token" \
-u "admin:Test3200Test3200" | jq -r '.access_token')
curl -s http://localhost:8080/api/v2/users \
-H "Authorization: Bearer $TOKEN" | jq .
```
#### 建立新用戶
```bash
TOKEN=$(curl -s -X GET "http://localhost:8080/api/v2/token" \
-u "admin:Test3200Test3200" | jq -r '.access_token')
curl -s -X POST http://localhost:8080/api/v2/users \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"username": "newuser",
"password": "userpassword123",
"email": "user@example.com",
"status": 1,
"home_dir": "/Users/accusys/momentry/var/sftpgo/data/newuser",
"uid": 501,
"gid": 20,
"permissions": {
"/": ["*"]
}
}'
```
#### 建立用戶組
```bash
TOKEN=$(curl -s -X GET "http://localhost:8080/api/v2/token" \
-u "admin:Test3200Test3200" | jq -r '.access_token')
curl -s -X POST http://localhost:8080/api/v2/groups \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "editors",
"description": "Editor group with upload permissions"
}'
```
#### 刪除用戶
```bash
TOKEN=$(curl -s -X GET "http://localhost:8080/api/v2/token" \
-u "admin:Test3200Test3200" | jq -r '.access_token')
curl -s -X DELETE http://localhost:8080/api/v2/users/username \
-H "Authorization: Bearer $TOKEN"
```
#### 修改用戶密碼
```bash
TOKEN=$(curl -s -X GET "http://localhost:8080/api/v2/token" \
-u "admin:Test3200Test3200" | jq -r '.access_token')
curl -s -X PUT http://localhost:8080/api/v2/users/demo \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"password": "newpassword456"
}'
```
### 重要設定
| 設定項目 | 值 | 說明 |
|----------|-----|------|
| **SFTP 連接埠** | `2022` | SSH 檔案傳輸協定 |
| **HTTP/WebDAV 連接埠** | `8080` | 內部 HTTP 服務 |
| **WebAdmin 連接埠** | `8080` | 管理介面 (`/web/admin`) |
| **WebClient 連接埠** | `8080` | 用戶介面 (`/web/client`) |
| **資料庫** | PostgreSQL | 用戶和設定儲存 |
| **Hook 腳本** | `/Users/accusys/sftpgo_test/register_hook.sh` | 上傳後自動化處理 |
| **安裝碼** | `Test3200Test3200` | 首次設定管理員所需 |
| **create_default_admin** | `true` | 自動創建管理員 |
| **Token 有效期** | 1200 秒 (20分鐘) | JWT 過期時間 |
### 用戶目錄結構
所有 SFTPGo 用戶資料統一存放在 `/Users/accusys/momentry/var/sftpgo/data/` 目錄下:
| 用戶 | 資料夾路徑 | 密碼 | 說明 |
|------|------------|------|------|
| **demo** | `/Users/accusys/momentry/var/sftpgo/data/demo` | `demopassword123` | Demo 用戶上傳目錄 |
| **momentry** | `/Users/accusys/momentry/var/sftpgo/data/momentry` | `momentry123` | Momentry 系統用戶 |
| **warren** | `/Users/accusys/momentry/var/sftpgo/data/warren` | `warren123` | 其他用戶 |
### API Token 獲取方式
```bash
# 注意:使用 GET 而非 POST
curl -s -X GET "http://localhost:8080/api/v2/token" \
-u "admin:Test3200Test3200" | jq .access_token
```
### 故障排除
| 問題 | 解決方案 |
|------|----------|
| 無法獲取 Token | 確認環境變數已正確設定並重啟 SFTPGo |
| SFTP 連線被拒絕 | 檢查 SFTPGo 服務: `launchctl list \| grep sftpgo` |
| 無法登入 WebAdmin | 確認 admin 用戶存在: 檢查 plist 中環境變數是否正確 |
| 上傳失敗 | 檢查 Hook 腳本: `tail -f /Users/accusys/momentry/log/sftpgo.error.log` |
| 權限不足 | 檢查用戶權限或更新 `permissions` 設定 |
| API 返回 401 | Token 過期,需重新獲取: `curl -X POST .../token -u "admin:pass"` | |
---
## 安全注意事項
- **密碼保護**: `demopassword123` 為 demo 帳戶密碼
- **限制存取**: Demo 用戶只能訪問 `/demo` 目錄
- **監控**: 所有上傳都有日誌記錄
- **生產環境**: 正式環境應使用更強的密碼和金鑰認證

File diff suppressed because it is too large Load Diff

View File

@@ -1,425 +0,0 @@
# 統一會員系統 + 影片歸屬追蹤實作計畫
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-24 |
| 文件版本 | V1.0 |
| 狀態 | 待確認 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 |
|------|------|------|--------|
| V1.0 | 2026-03-24 | 創建實作計畫 | OpenCode |
---
## 1. 背景與目標
### 1.1 現有問題
目前 Momentry 生態系統中,各服務有獨立的用戶管理:
| 服務 | 用戶系統 | 問題 |
|------|----------|------|
| WordPress | wp_users (2 admin) | 無會員系統,無 API 認證 |
| SFTPGo | users 表 (3 users) | 獨立管理 |
| n8n | users 表 | 獨立管理 |
| Gitea | `user` 表 | 獨立管理 |
| Momentry Core | api_keys (未啟用) | 無 user 關聯 |
**問題**
1. 無法追蹤影片歸屬(誰上傳的影片)
2. 無法實作 per-user 配額管理
3. API 端點全部公開,無認證
4. 用戶創建需要多處操作
### 1.2 目標
建立統一的會員系統,讓 WordPress 成為唯一登入入口:
```
┌─────────────────────────────────────────────────────────────────┐
│ 目標架構 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ WordPress (會員系統) │
│ │ │
│ ├─► SFTPGo (檔案上傳) │
│ ├─► Momentry Core (影片處理) │
│ └─► n8n (自動化流程) │
│ │
│ 統一的 user_id 追蹤 │
│ │ │
│ └─► videos 表關聯 user_id │
│ └─► monitor_jobs 表關聯 user_id │
│ └─► per-user 配額管理 │
│ │
└─────────────────────────────────────────────────────────────────┘
```
---
## 2. 現有系統分析
### 2.1 WordPress
| 項目 | 狀態 |
|------|------|
| 安裝插件 | Elementor, Akismet, Code Snippets, All-in-One WP Migration |
| 用戶表 | wp_users (2 users: wp_user, sc_demo) |
| 會員插件 | 無 |
| REST API | 標準端點 (`/wp-json/wp/v2/users`) |
| 認證方式 | Cookie / Application Passwords |
| JWT | 無 |
### 2.2 SFTPGo
| 項目 | 值 |
|------|-----|
| 用戶數 | 3 (demo, warren, momentry) |
| API | REST API v2 (`/api/v2/users`) |
| Admin | admin:Test3200Test3200 |
| Hook | `/Users/accusys/sftpgo_test/register_hook.sh` |
### 2.3 Momentry Core
| 項目 | 狀態 |
|------|------|
| api_keys 表 | 已存在 |
| users 表 | 不存在 |
| videos.user_id | 不存在 |
| API 認證 | 未啟用(所有端點公開) |
---
## 3. 實作計畫
### Phase 1: WordPress 認證機制啟用
#### 1.1 啟用 Application Passwords
**WordPress 5.6+ 內建功能**,無需額外插件。
```php
// wp-config.php (如需自訂設定)
define('WP APPLICATION_PASSWORDS_ENABLED', true);
```
**使用方式**
```bash
# Basic Auth 格式
curl -X POST "https://wp.momentry.ddns.net/wp-json/wp/v2/users" \
-u "username:application_password" \
-H "Content-Type: application/json" \
-d '{"username": "newuser", "email": "user@example.com", "password": "password"}'
```
#### 1.2 測試 WordPress REST API
```bash
# 取得用戶列表(需要 admin 權限)
curl -s -u "wp_user:xxxx xxxx xxxx xxxx xxxx xxxx" \
"https://wp.momentry.ddns.net/wp-json/wp/v2/users"
# 創建新用戶
curl -X POST "https://wp.momentry.ddns.net/wp-json/wp/v2/users" \
-u "wp_user:xxxx xxxx xxxx xxxx xxxx xxxx" \
-H "Content-Type: application/json" \
-d '{
"username": "testuser",
"email": "test@example.com",
"password": "TestPass123!",
"roles": ["subscriber"]
}'
```
---
### Phase 2: 資料庫結構調整
#### 2.1 新增 users 表Momentry Core
```sql
-- migrations/002_user_management.sql
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
wordpress_id BIGINT UNIQUE NOT NULL,
username VARCHAR(60) NOT NULL,
email VARCHAR(100) NOT NULL,
api_key_hash VARCHAR(64),
quota_size BIGINT DEFAULT 10737418240, -- 10GB
quota_used BIGINT DEFAULT 0,
sftpgo_username VARCHAR(60),
status VARCHAR(20) DEFAULT 'active', -- active, suspended, deleted
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_users_wordpress_id ON users(wordpress_id);
CREATE INDEX idx_users_username ON users(username);
-- videos 表新增 user_id
ALTER TABLE videos ADD COLUMN user_id BIGINT REFERENCES users(id);
CREATE INDEX idx_videos_user_id ON videos(user_id);
-- monitor_jobs 表新增 user_id
ALTER TABLE monitor_jobs ADD COLUMN user_id BIGINT REFERENCES users(id);
CREATE INDEX idx_monitor_jobs_user_id ON monitor_jobs(user_id);
-- api_keys 表新增 user_id
ALTER TABLE api_keys ADD COLUMN user_id BIGINT REFERENCES users(id);
```
#### 2.2 更新 api_keys 表結構
```sql
-- 新增欄位
ALTER TABLE api_keys ADD COLUMN user_id BIGINT REFERENCES users(id);
ALTER TABLE api_keys ADD COLUMN wordpress_id BIGINT;
ALTER TABLE api_keys ADD COLUMN sftpgo_username VARCHAR(60);
```
---
### Phase 3: API 認證中介層
#### 3.1 中介層設計
```rust
// src/api/middleware/auth.rs
#[derive(Clone)]
pub struct AuthState {
pub db: Arc<PostgresDb>,
pub cache: Arc<RedisCache>,
}
pub async fn auth_middleware(
req: Request,
next: Next,
state: AuthState,
) -> Result<Response, StatusCode> {
// 1. 從 Header 提取 API Key
// Header: X-API-Key: muser_xxx
// 或: Authorization: Bearer muser_xxx
// 2. 驗證並取得 user_id
let user_id = validate_api_key(&req, &state).await?;
// 3. 附加到 request extensions
req.extensions_mut().insert(UserContext { user_id });
// 4. 執行 handler
next.call(req).await
}
#[derive(Clone)]
pub struct UserContext {
pub user_id: i64,
}
```
#### 3.2 API Key 格式更新
```
新格式: muser_{uuid}_{timestamp}_{random}_{user_id_hash}
```
| 欄位 | 說明 |
|------|------|
| 前綴 | `muser_` = User 類型 |
| uuid | 唯一識別碼 |
| timestamp | 創建時間戳 |
| random | 隨機字串 |
| user_id_hash | 壓縮的 user_id |
---
### Phase 4: 更新 Register API
#### 4.1 修改 register 端點
```rust
// POST /api/v1/register
pub async fn register(
State(state): State<ApiState>,
Json(req): Json<RegisterRequest>,
Extension(ctx): Extension<UserContext>, // 新增
) -> Result<Json<RegisterResponse>, StatusCode> {
// ... 現有邏輯 ...
// 驗證用戶配額
let user = state.db.get_user(ctx.user_id).await?;
if user.quota_used + file_size > user.quota_size {
return Err(StatusCode::FORBIDDEN);
}
// 關聯 user_id 到影片
let video_uuid = state.db.create_video(req, Some(ctx.user_id)).await?;
// 建立 processing job帶 user_id
state.db.create_monitor_job(
job_type: "auto_ingestion",
video_uuid,
user_id: Some(ctx.user_id),
processors: vec!["asr", "cut", "yolo", "ocr", "face", "pose"],
).await?;
Ok(Json(RegisterResponse { uuid: video_uuid }))
}
```
---
### Phase 5: n8n 自動化流程
#### 5.1 用戶註冊 Workflow
```
┌─────────────────────────────────────────────────────────────────┐
│ WordPress 用戶註冊自動化流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Trigger: Webhook (或 WordPress Plugin) │
│ │
│ Step 1: 驗證管理員權限 │
│ └─► 檢查 WordPress REST API 憑證 │
│ │
│ Step 2: 在 Momentry Core 建立用戶記錄 │
│ └─► POST /api/v1/admin/users │
│ └─► 產生 API Key │
│ │
│ Step 3: 在 SFTPGo 建立用戶 │
│ └─► POST /api/v2/users (SFTPGo API) │
│ └─► 設定 home_dir: /data/{username} │
│ │
│ Step 4: 更新用戶記錄 │
│ └─► 關聯 sftpgo_username │
│ │
│ Step 5: 發送歡迎 email │
│ └─► 包含 SFTP 登入資訊 │
│ └─► 包含 API Key │
│ │
└─────────────────────────────────────────────────────────────────┘
```
#### 5.2 SFTPGo Hook 更新
```bash
# /Users/accusys/sftpgo_test/register_hook.sh
# 修改為傳遞 user_id
curl -X POST "http://localhost:3002/api/v1/register" \
-H "X-API-Key: ${SFTPGO_USER_API_KEY}" \
-H "X-SFTPGo-User: ${SFTPGO_USERNAME}" \
-d "{\"path\": \"${SFTPGO_FILE_PATH}\"}"
```
---
## 4. 實作優先順序
| Phase | 任務 | 複雜度 | 優先級 | 預估工時 |
|-------|------|--------|--------|----------|
| 1.1 | 測試 WordPress Application Passwords | 低 | P0 | 1h |
| 1.2 | 為 WordPress 產生 Application Password | 低 | P0 | 0.5h |
| 2.1 | 建立 users 表 migration | 中 | P0 | 2h |
| 2.2 | 更新 videos, monitor_jobs 表 | 低 | P0 | 1h |
| 3.1 | 實作 API auth middleware | 中 | P0 | 4h |
| 3.2 | 更新 register API 接受 user_id | 低 | P0 | 2h |
| 4 | 建立 admin users API | 中 | P1 | 4h |
| 5.1 | 建立 n8n 用戶註冊 workflow | 中 | P1 | 6h |
| 5.2 | 更新 SFTPGo hook | 低 | P1 | 2h |
| 6 | 實作配額管理 | 中 | P2 | 4h |
| 7 | 測試與驗證 | 中 | P2 | 4h |
**總預估工時**: ~30.5h
---
## 5. 待確認事項
### 5.1 WordPress 用戶建立方式
- [ ] 手動在 wp-admin 建立?還是透過 Elementor 表單?
- [ ] 是否需要 email 驗證?
- [ ] 初始角色設定subscriber / contributor
### 5.2 API Key 格式
- [ ] 維持現有 `muser_` 前綴格式?
- [ ] 還是建立新的用戶專用 key 格式?
- [ ] 是否需要 JWT token
### 5.3 SFTPGo 整合
- [ ] 每個 WordPress 用戶對應一個 SFTPGo 用戶?
- [ ] home_dir 命名規則?(如 `data/{wordpress_username}`
- [ ] SFTPGo 配額是否同步?
### 5.4 配額管理
- [ ] 每人預設 10GB 空間?
- [ ] 超出配額如何處理?(阻止上傳 / 警告)
- [ ] 配額用完後是否暫停 SFTPGo 用戶?
### 5.5 資料同步
- [ ] WordPress 用戶刪除時是否同步刪除其他系統?
- [ ] 用戶停權時的處理流程?
---
## 6. 參考文件
### 內部文件
| 文件 | 用途 |
|------|------|
| `docs/PENDING_ISSUES.md` | 待解決問題追蹤 |
| `docs/API_KEY_MANAGEMENT.md` | API Key 管理系統 |
| `docs/API_REFERENCE.md` | API 端點參考 |
| `docs/SFTPGO_DEMO_USER.md` | SFTPGo 用戶設定 |
| `docs/N8N_INTEGRATION_GUIDE.md` | n8n 整合指南 |
| `docs/INSTALL_WORDPRESS.md` | WordPress 安裝指南 |
### 外部資源
| 資源 | URL |
|------|-----|
| WordPress REST API | https://developer.wordpress.org/rest-api/ |
| WordPress Application Passwords | https://developer.wordpress.org/rest-api/using-the-rest-api/authentication/#authentication-plugins |
| SFTPGo REST API | https://docs.sftpgo.com/latest/rest-api/ |
---
## 7. 附錄
### A. 現有使用者資料
#### WordPress (wp_users)
| ID | user_login | user_email | display_name |
|----|------------|------------|--------------|
| 1 | wp_user | marketing@accusys.com.tw | wp_user |
| 2 | sc_demo | susan.cheng@accusys.com.tw | Susan Cheng |
#### SFTPGo (users)
| username | email | home_dir | status |
|----------|-------|----------|--------|
| demo | demo@momentry.local | /Users/accusys/momentry/var/sftpgo/data/demo | Active |
| warren | warren@momentry.local | /Users/accusys/momentry/var/sftpgo/data/warren | Active |
| momentry | system@momentry.local | /Users/accusys/momentry/var/sftpgo/data/momentry | Active |
### B. 服務端口
| 服務 | Port | URL |
|------|------|-----|
| WordPress | 9000 (PHP-FPM) | https://wp.momentry.ddns.net |
| SFTPGo | 8080 | http://localhost:8080 |
| Momentry API | 3002 | http://localhost:3002 |
| n8n | 5678 | http://localhost:5678 |

View File

@@ -1,481 +0,0 @@
# Momentry 使用手冊
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-21 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-21 | 創建使用手冊 | Warren | OpenCode |
---
**目標讀者**: 系統管理員、開發者
---
## 目錄
1. [快速開始](#1-快速開始)
2. [安裝與設定](#2-安裝與設定)
3. [CLI 命令參考](#3-cli-命令參考)
4. [影片管理](#4-影片管理)
5. [API Key 管理](#5-api-key-管理)
6. [第三方整合](#6-第三方整合)
7. [監控與維護](#7-監控與維護)
8. [疑難排解](#8-疑難排解)
---
## 1. 快速開始
### 1.1 最小啟動流程
```bash
# 1. 啟動服務
sudo launchctl load /Library/LaunchDaemons/com.momentry.postgresql.plist
sudo launchctl load /Library/LaunchDaemons/com.momentry.redis.plist
# 2. 設定環境變數
source .env
# 3. 啟動 API 伺服器
momentry server --host 127.0.0.1 --port 3000
# 4. 建立 API Key
momentry api-key create my-first-key --key-type user --ttl 90
```
### 1.2 驗證安裝
```bash
# 檢查系統狀態
curl http://localhost:3002/health
# 檢查版本
momentry --help
```
---
## 2. 安裝與設定
### 2.1 環境需求
| 項目 | 需求 |
|------|------|
| 作業系統 | macOS (Apple Silicon) |
| Rust | 1.70+ |
| PostgreSQL | 15+ |
| Redis | 7+ |
| Python | 3.11+ (用於 AI 處理) |
### 2.2 安裝步驟
```bash
# 1. 複製專案
git clone <repository-url>
cd momentry_core_0.1
# 2. 編譯
cargo build --release
# 3. 安裝到系統
cp target/release/momentry /usr/local/bin/
```
### 2.3 環境變數設定
建立 `.env` 檔案:
```bash
# Database
DATABASE_URL=postgres://accusys@localhost:5432/momentry
# Redis
REDIS_URL=redis://:accusys@localhost:6379
# Gitea (選用)
GITEA_URL=http://localhost:3000
# n8n (選用)
N8N_URL=https://n8n.momentry.ddns.net
# API Server
API_HOST=127.0.0.1
API_PORT=3000
# 監控目錄
WATCH_DIRECTORIES=~/Videos
```
---
## 3. CLI 命令參考
### 3.1 一般命令
| 命令 | 說明 |
|------|------|
| `momentry --help` | 顯示幫助 |
| `momentry server` | 啟動 API 伺服器 |
| `momentry register <path>` | 註冊影片 |
| `momentry process <uuid>` | 處理影片 |
| `momentry query <text>` | RAG 查詢 |
### 3.2 影片管理
```bash
# 註冊影片
momentry register /path/to/video.mp4
# 處理影片
momentry process <uuid>
# 生成縮圖
momentry thumbnails <uuid> --count 6
# 查看狀態
momentry status <uuid>
```
### 3.3 API Key 管理
```bash
# 建立 Key
momentry api-key create <name> --key-type <type> --ttl <days>
# 列出 Keys
momentry api-key list
# 驗證 Key
momentry api-key validate --key <key>
# 撤銷 Key
momentry api-key revoke --key <key>
# 請求輪換
momentry api-key rotate --key <key>
# 統計資訊
momentry api-key stats
```
### 3.4 Gitea 整合
```bash
# 建立 Token
momentry gitea create \
--username <user> \
--password <pwd> \
--token-name <name> \
--scopes "read:repository,write:repository"
# 列出 Tokens
momentry gitea list --username <user> --password <pwd>
# 刪除 Token
momentry gitea delete \
--username <user> \
--password <pwd> \
--token-name <name>
```
### 3.5 n8n 整合
```bash
# 建立 API Key
momentry n8n create \
--api-key <existing-key> \
--label <name> \
--expires-in-days 90
# 列出 Keys
momentry n8n list --api-key <key>
# 刪除 Key
momentry n8n delete --api-key <key> --label <name>
```
### 3.6 備份管理
```bash
# 列出備份
momentry backup list
# 清理舊備份
momentry backup cleanup --days 30
```
---
## 4. 影片管理
### 4.1 影片生命週期
```
上傳 → 註冊 → 處理 → 儲存 → 查詢
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
檔案 資料庫 AI分析 向量庫 RAG
```
### 4.2 註冊影片
```bash
# 自動偵測格式
momentry register ~/Videos/my-video.mp4
# 輸出:
# UUID: a1b2c3d4e5f6g7h8
# Duration: 120.5s
# Resolution: 1920x1080
```
### 4.3 處理流程
處理包含以下階段:
| 階段 | 說明 | 時間 (約) |
|------|------|-----------|
| Probe | 影片資訊分析 | 5s |
| ASR | 語音辨識 | 視長度 |
| OCR | 文字辨識 | 視長度 |
| YOLO | 物件偵測 | 視長度 |
| Cut | 場景切割 | 30s |
| Chunk | 內容分段 | 10s |
| Vector | 向量化 | 20s |
### 4.4 查詢影片
```bash
# RAG 查詢
momentry query "影片中有什麼內容?"
# 取得特定影片
momentry resolve <uuid>
```
---
## 5. API Key 管理
### 5.1 Key 類型
| 類型 | 前綴 | 用途 | 預設 TTL |
|------|------|------|----------|
| System | `msys_` | 系統內部 | 365 天 |
| User | `muser_` | 個人用戶 | 90 天 |
| Service | `msvc_` | 服務間通訊 | 180 天 |
| Integration | `mint_` | 第三方整合 | 30 天 |
| Emergency | `memg_` | 緊急存取 | 1 天 |
### 5.2 建立 Key
```bash
# 一般 Key
momentry api-key create my-service --key-type service --ttl 90
# 緊急 Key (24小時有效)
momentry api-key create emergency-access --key-type emergency
# 輸出:
# ✅ API Key created successfully!
# Key ID: msvc_xxxxxxxx
# API Key: msvc_xxxxxxxx_xxxxx_xxxxx
# Expires: 2026-06-19
```
### 5.3 Key 生命週期
```
建立 → 使用 → 過期/撤銷 → 清理
│ │ │ │
▼ ▼ ▼ ▼
資料庫 驗證 停用 定期刪除
```
### 5.4 安全建議
| 建議 | 說明 |
|------|------|
| 定期輪換 | 每 90 天更新 Key |
| 最小權限 | 只授予必要權限 |
| 監控使用 | 定期檢查使用統計 |
| 及時撤銷 | 異常時立即撤銷 |
---
## 6. 第三方整合
### 6.1 Gitea
```bash
# 建立 CI/CD 用 Token
momentry gitea create \
--username admin \
--password "your-password" \
--token-name "ci-pipeline" \
--scopes "read:repository,write:repository"
# 在 CI 中使用
export GITEA_TOKEN="token-sha1-value"
curl -H "Authorization: token $GITEA_TOKEN" \
http://localhost:3000/api/v1/user
```
### 6.2 n8n
```bash
# 建立工作流用 Key
momentry n8n create \
--api-key "existing-n8n-key" \
--label "workflow-key" \
--expires-in-days 90
# 在 n8n 中使用
# HTTP Request Header: X-N8N-API-Key: <key>
```
### 6.3 Webhook 通知
```bash
# 設定 Webhook
export WEBHOOK_URL="https://n8n.example.com/webhook/alerts"
export WEBHOOK_EVENTS="anomaly_detected,key_expired"
```
---
## 7. 監控與維護
### 7.1 系統監控
```bash
# 檢查服務狀態
ps aux | grep momentry
ps aux | grep postgres
redis-cli -a accusys ping
# 檢查日誌
tail -f /Users/accusys/momentry/log/momentry.log
tail -f /Users/accusys/momentry/log/redis.log
```
### 7.2 資料庫維護
```bash
# 檢查資料庫大小
psql -U accusys -d momentry -c "SELECT pg_size_pretty(pg_database_size('momentry'));"
# 清理過期記錄
momentry api-key stats # 檢查統計
# 定期清理由系統自動執行
```
### 7.3 備份
```bash
# 手動備份 PostgreSQL
pg_dump -U accusys momentry > backup_$(date +%Y%m%d).sql
# 恢復備份
psql -U accusys momentry < backup_20260321.sql
```
---
## 8. 疑難排解
### 8.1 常見問題
#### Q: 無法連接資料庫
```bash
# 檢查 PostgreSQL 狀態
pg_isready -h localhost -p 5432
# 檢查連線
psql -U accusys -d momentry -c "SELECT 1;"
```
#### Q: Redis 連線失敗
```bash
# 檢查 Redis 狀態
redis-cli -a accusys ping
# 檢查認證
redis-cli -a accusys INFO server | grep redis_version
```
#### Q: API Key 驗證失敗
```bash
# 檢查 Key 狀態
momentry api-key validate --key "your-key"
# 檢查是否過期
momentry api-key list
```
### 8.2 錯誤碼對照
| 錯誤碼 | 說明 | 解決方式 |
|--------|------|----------|
| `E001` | 資料庫連線失敗 | 檢查 PostgreSQL |
| `E002` | Redis 連線失敗 | 檢查 Redis |
| `E003` | API Key 無效 | 重新建立 Key |
| `E004` | 影片不存在 | 檢查 UUID |
| `E005` | 處理失敗 | 檢查日誌 |
### 8.3 日誌位置
| 日誌 | 路徑 |
|------|------|
| Momentry | `/Users/accusys/momentry/log/momentry.log` |
| PostgreSQL | `/Users/accusys/momentry/log/postgresql.log` |
| Redis | `/Users/accusys/momentry/log/redis.log` |
| Gitea | `/Users/accusys/momentry/log/gitea.log` |
---
## 附錄
### A. 完整命令列表
```bash
momentry --help
momentry register --help
momentry process --help
momentry api-key --help
momentry gitea --help
momentry n8n --help
momentry backup --help
```
### B. 環境變數總覽
| 變數 | 預設值 | 說明 |
|------|--------|------|
| `DATABASE_URL` | `postgres://accusys@localhost:5432/momentry` | PostgreSQL |
| `REDIS_URL` | `redis://:accusys@localhost:6379` | Redis |
| `GITEA_URL` | `http://localhost:3000` | Gitea |
| `N8N_URL` | `https://n8n.momentry.ddns.net` | n8n |
| `API_HOST` | `127.0.0.1` | API 主機 |
| `API_PORT` | `3000` | API 埠號 |
### C. 相關文件
| 文件 | 說明 |
|------|------|
| `docs/API_CURL_EXAMPLES.md` | API curl 範例 |
| `docs/N8N_INTEGRATION_GUIDE.md` | n8n 整合指南 |
| `docs/API_KEY_MANAGEMENT.md` | API Key 設計 |
| `CHANGELOG.md` | 版本記錄 |

View File

@@ -1,261 +0,0 @@
# Momentry Core 版本管理規範
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-23 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-23 | 創建版本管理規範 | Warren | OpenCode |
---
## 1. 版本與通訊埠對照表
| 版本 | Binary | Port | Redis Prefix | 用途 |
|------|--------|------|--------------|------|
| **Production** | `momentry` | **3002** | `momentry:` | 正式環境 |
| **Development** | `momentry_playground` | **3003** | `momentry_dev:` | 開發測試 |
### 通訊埠嚴禁事項
- ❌ 開發版嚴禁使用 3002
- ❌ 任何 `cargo run` 直接啟動的 server 嚴禁綁定 3002
- ❌ Debug build 嚴禁部署到 3002
---
## 2. 開發環境隔離原則
### 2.1 開發流程
```bash
# 永遠在 3003 開發測試
cd /Users/accusys/momentry_core_0.1
# 開發版啟動 (3003)
cargo run --bin momentry_playground -- server
# 或
cargo run --bin momentry -- server --port 3003
```
### 2.2 測試完成後
1. 確認所有功能在 3003 正常運作
2. 進行 `cargo clippy --lib` 檢查
3. 進行 `cargo test --lib` 測試
4. 確認後才能進行 release
### 2.3 環境變數隔離
```bash
# Development
export MOMENTRY_SERVER_PORT=3003
export MOMENTRY_REDIS_PREFIX=momentry_dev:
# Production (launchd 管理,勿手動設定)
# MOMENTRY_SERVER_PORT=3002
# MOMENTRY_REDIS_PREFIX=momentry:
```
---
## 3. Release 版本管理
### 3.1 Release 前檢查清單
```
□ 開發版 (3003) 功能測試完成
□ cargo clippy --lib 通過
□ cargo test --lib 通過
□ cargo fmt -- --check 通過
□ 所有修改已 commit 到 Gitea
```
### 3.2 Release 流程
```bash
# 1. 確保目前是乾淨的工作目錄
git status
# 2. 備份當前 production binary
BACKUP_DIR="/Users/accusys/momentry/backup/bin"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
cp /Users/accusys/momentry/bin/momentry "${BACKUP_DIR}/momentry_${TIMESTAMP}"
# 3. 停止 production server
sudo launchctl unload /Library/LaunchDaemons/com.momentry.api.plist
# 或
pkill -f "target/release/momentry server"
# 4. 編譯 release 版本
cargo build --release --bin momentry
# 5. 部署到正式位置
cp target/release/momentry /Users/accusys/momentry/bin/momentry
# 6. 啟動 production server
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
# 7. 驗證
curl http://localhost:3002/health
```
### 3.3 Backup 存放位置
```
/Users/accusys/momentry/backup/bin/
├── momentry_20260325_143000 (backup)
├── momentry_20260324_100000
├── momentry_20260323_090000
└── ...
```
---
## 4. Gitea 版本控制
### 4.1 Commit 規範
```
feat: 新功能
fix: 錯誤修復
refactor: 重構
docs: 文件更新
chore: 杂项
test: 测试
```
### 4.2 Release Tag 規範
```bash
# 建立 release tag
git tag -a v0.1.1 -m "Release v0.1.1 - API Key Authentication"
git push origin v0.1.1
```
### 4.3 版本號命名
```
v{major}.{minor}.{patch}
│ │ └── Patch version (bug fix)
│ └───────── Minor version (新功能)
└──────────────── Major version (破壞性變更)
```
### 4.4 Gitea Release 建立
1. 在 Gitea Repo > Releases > New Release
2. 選擇對應的 Tag
3. 填寫 Release Notes
4. 上傳 compiled binary如需要
---
## 5. 服務管理
### 5.1 Production Service (launchd)
```bash
# Plist 位置
/Library/LaunchDaemons/com.momentry.api.plist
# 管理指令
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist # 啟動
sudo launchctl unload /Library/LaunchDaemons/com.momentry.api.plist # 停止
sudo launchctl list | grep momentry # 狀態
```
### 5.2 緊急回滾
```bash
# 1. 停止當前服務
sudo launchctl unload /Library/LaunchDaemons/com.momentry.api.plist
# 2. 恢復上一個 backup
BACKUP_FILE=$(ls -t /Users/accusys/momentry/backup/bin/ | head -1)
cp "/Users/accusys/momentry/backup/bin/${BACKUP_FILE}" /Users/accusys/momentry/bin/momentry
# 3. 重啟服務
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
# 4. 驗證
curl http://localhost:3002/health
```
---
## 6. 快速參考卡片
### Development
```bash
# 啟動開發版
cd /Users/accusys/momentry_core_0.1
cargo run --bin momentry_playground -- server
# 或手動指定 port
cargo run --bin momentry -- server --port 3003
# 測試端點
curl -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
http://localhost:3003/api/v1/jobs
```
### Production
```bash
# 查看狀態
sudo launchctl list | grep momentry
# 重啟服務
sudo launchctl unload /Library/LaunchDaemons/com.momentry.api.plist
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
# 查看日誌
tail -f /Users/accusys/momentry/log/momentry_release.log
```
### 常用指令
```bash
# 檢查 port 使用
lsof -i :3002 # Production
lsof -i :3003 # Development
# 檢查 process
ps aux | grep momentry | grep server
# 停止所有 momentry server
pkill -9 -f "momentry.*server"
```
---
## 7. 禁止事項
| 項目 | 說明 |
|------|------|
| ❌ 禁止在 3002 測試 | 3002 是 Production嚴禁用於測試 |
| ❌ 禁止覆蓋 production binary | 使用 backup + deploy 流程 |
| ❌ 禁止跳過測試直接 release | 必須完成檢查清單 |
| ❌ 禁止在未備份的情況下部署 | 每次部署前必須備份 |
---
## 8. 疑難排解
### Q: 3002 無法綁定怎麼辦?
```bash
# 檢查誰在使用
lsof -i :3002
# 停止舊的 server
pkill -9 -f "momentry.*server"
```
### Q: 如何確認使用的是哪個版本?
```bash
# 檢查 binary 位置和版本
file $(which momentry)
./target/release/momentry --version 2>/dev/null || echo "No version flag"
```
### Q: 如何確認有沒有 API Key 驗證?
```bash
# 沒有 API Key 應該返回 401
curl -s http://localhost:3002/api/v1/jobs
# HTTP/1.1 401 Unauthorized
```

File diff suppressed because it is too large Load Diff

View File

@@ -1,246 +0,0 @@
# Video Registration
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-25 |
| 文件版本 | V1.1 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-25 | 創建文件 | Warren | OpenCode |
| V1.1 | 2026-03-26 | 修正 curl 範例,新增 API Key 驗證標頭 | OpenCode | deepseek-reasoner |
---
## 概述
影片註冊 API (`POST /api/v1/register`) 用於將影片加入 Momentry Core 系統進行處理。
## 路徑格式
### 支援的路徑格式
| 格式 | 範例 | 說明 |
|------|------|------|
| 相對路徑 | `./demo/video.mp4` | 推薦格式 |
| 相對路徑(無 ./ | `demo/video.mp4` | 自動加上 `./` |
| 絕對路徑 | `/Users/.../sftpgo/data/demo/video.mp4` | 支援但不推薦 |
### 路徑結構
```
./username/filepath
│ │ │
│ │ └── 檔案路徑(可以是多層目錄)
│ └── 使用者名稱SFTPgo 用戶目錄名稱)
└── 相對路徑前綴
```
**範例**
- `./demo/video.mp4` → username=`demo`, filepath=`video.mp4`
- `./demo/movies/2024/video.mp4` → username=`demo`, filepath=`movies/2024/video.mp4`
- `./warren/project1/interview.mp4` → username=`warren`, filepath=`project1/interview.mp4`
## UUID 計算
### 計算規則
```
UUID = SHA256(username/filepath)[0:16]
```
**範例**
```rust
// 路徑: ./demo/video.mp4
// username: "demo"
// filepath: "video.mp4"
// key: "demo/video.mp4"
// UUID: SHA256("demo/video.mp4")[0:16]
```
### 特性
| 特性 | 說明 |
|------|------|
| 用戶隔離 | 不同用戶的相同檔名會產生不同 UUID |
| 一致性 | 相同相對路徑一定產生相同 UUID |
| 遷移安全 | SFTPgo 資料路徑變更後 UUID 保持一致 |
### 範例
```rust
// 用戶 demo 的影片
compute_uuid_from_relative_path("./demo/video.mp4")
// → "9760d0820f0cf9a7"
// 用戶 warren 的相同檔名影片
compute_uuid_from_relative_path("./warren/video.mp4")
// → "a1b2c3d4e5f6g7h8" (不同的 UUID)
```
## 重複註冊檢查
### 行為
1. 系統檢查 UUID 是否已存在於資料庫
2. 如果存在,返回 `already_exists: true` 和現有影片資訊
3. 如果不存在,創建新的影片記錄
### API 回應
**新註冊**
```json
{
"uuid": "9760d0820f0cf9a7",
"video_id": 18,
"job_id": 2,
"file_name": "video.mp4",
"duration": 159.637188,
"width": 640,
"height": 360,
"already_exists": false
}
```
**重複註冊**
```json
{
"uuid": "9760d0820f0cf9a7",
"video_id": 18,
"job_id": 2,
"file_name": "video.mp4",
"duration": 159.637188,
"width": 640,
"height": 360,
"already_exists": true
}
```
## SFTPgo 整合
### 目錄結構
SFTPgo 的用戶目錄結構:
```
/Users/accusys/momentry/var/sftpgo/data/
├── demo/ ← 用戶目錄
│ ├── video.mp4
│ └── movies/
│ └── movie1.mp4
├── warren/ ← 用戶目錄
│ └── project1/
│ └── interview.mp4
└── momentry/ ← 用戶目錄
└── presentation.mp4
```
### 註冊流程
1. SFTPgo 用戶上傳檔案到各自的目錄
2. n8n 或其他服務調用註冊 API
3. 使用相對路徑格式:`./username/filepath`
4. 系統計算 UUID 並檢查重複
5. 創建處理任務
## 程式碼範例
### 註冊影片
```bash
# 使用相對路徑註冊
curl -X POST http://localhost:3002/api/v1/register \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"path": "./demo/video.mp4"}'
# 或使用多層目錄
curl -X POST http://localhost:3002/api/v1/register \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"path": "./demo/movies/2024/video.mp4"}'
```
### UUID 計算函數
```rust
// 使用相對路徑計算 UUID
pub fn compute_uuid_from_relative_path(relative_path: &str) -> String {
let (username, filepath) = extract_user_from_relative_path(relative_path);
compute_uuid(&username, &filepath)
}
// 從相對路徑提取用戶名和檔案路徑
pub fn extract_user_from_relative_path(relative_path: &str) -> (String, String) {
let path = relative_path.strip_prefix("./").unwrap_or(relative_path);
let path_buf = PathBuf::from(path);
let mut components = path_buf.components();
let username = components
.next()
.map(|c| c.as_os_str().to_string_lossy().to_string())
.unwrap_or_default();
let filepath: String = components
.map(|c| c.as_os_str().to_string_lossy().to_string())
.collect::<Vec<_>>()
.join("/");
(username, filepath)
}
```
## 相關 API
### Probe API僅探測不註冊
如果只需要取得影片資訊而不註冊,可以使用 Probe API
```bash
curl -X POST http://localhost:3002/api/v1/probe \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"path": "./demo/video.mp4"}'
```
**回應範例**
```json
{
"uuid": "a1b10138a6bbb0cd",
"file_name": "video.mp4",
"duration": 120.5,
"width": 1920,
"height": 1080,
"fps": 30.0,
"cached": false,
"format": {...},
"streams": [...]
}
```
**與 Register API 的差異**
| 功能 | Probe API | Register API |
|------|-----------|---------------|
| 計算 UUID | ✓ | ✓ |
| 執行 ffprobe | ✓ | ✓ |
| 儲存 probe.json | ✓ | ✓ |
| 寫入 videos 表 | ✗ | ✓ |
| 建立 monitor_job | ✗ | ✓ |
| 返回 job_id | ✗ | ✓ |
| 適用場景 | 預覽影片資訊 | 註冊並處理影片 |
## 相關檔案
| 檔案 | 說明 |
|------|------|
| `src/core/storage/uuid.rs` | UUID 計算邏輯 |
| `src/api/server.rs` | 註冊與 Probe API 實現 |
| `src/core/probe/ffprobe.rs` | ffprobe 整合 |
| `docs/SFTPGO_DEMO_USER.md` | SFTPgo 用戶設置 |
| `docs/API_ENDPOINTS.md` | API 端點總覽 |

View File

@@ -1,102 +0,0 @@
# YOLO Resume 功能整合規劃
## 現有資源
### 1. video_yolo_player 專案
**位置**: `/Users/accusys/video_yolo_player/video_yolo_object_prescan.py`
**功能**:
- ✅ Ctrl+C 暫停並保存進度
- ✅ 自動從上一次繼續 (自動偵測 last_processed_frame)
- ✅ 可配置自動保存間隔 (預設 30 秒)
- ✅ 完整 metadata 追蹤 (處理時間、檢測數量等)
- ✅ 互動式詢問是否繼續
### 2. momentry_core_0.1 專案
**位置**: `/Users/accusys/momentry_core_0.1/scripts/yolo_processor.py`
## 整合狀態
| 項目 | 狀態 | 完成日期 |
|------|------|----------|
| Python script 整合 | ✅ 已完成 | 2026-03-22 |
| Rust JSON 格式支援 | ✅ 已完成 | 2026-03-22 |
| Auto-save 功能 (時間) | ✅ 已完成 | 2026-03-22 |
| Auto-save 功能 (frame) | ✅ 已完成 | 2026-03-22 |
| Ctrl+C 信號處理 | ✅ 已完成 | 2026-03-22 |
| --force 參數 | ✅ 已完成 | 2026-03-22 |
| --auto-save-frames 參數 | ✅ 已完成 | 2026-03-22 |
## 已實作功能
### momentry_core_0.1/scripts/yolo_processor.py
```python
# 新增功能:
def load_existing_data(output_file) # 載入現有資料
def save_detection_data(output_file) # 保存進度
def signal_handler(signum, frame) # Ctrl+C 處理
# 新增參數:
--auto-save 30 # 自動保存間隔 (秒)
--auto-save-frames 300 # 每 N frames 保存一次 (先到為準)
--force # 強制從頭開始
```
### 輸出格式
```json
{
"metadata": {
"video_path": "...",
"fps": 24.0,
"total_frames": 1000,
"status": "in_progress" | "completed" | "interrupted",
"total_detections": 5000,
"processing_time": 120.5,
"auto_save_count": 4,
"auto_save_interval": 30,
"auto_save_frames": 300,
"last_saved_frame": 12500,
"last_saved_at": "ISO timestamp"
},
"frames": {
"1": { "frame_number": 1, "time_seconds": 0.0, "detections": [...] },
"2": { "frame_number": 2, "time_seconds": 0.042, "detections": [...] }
}
}
```
### Auto-save 觸發條件
**滿足以下任一條件就會寫入磁碟:**
1. 距離上次儲存已超過 `--auto-save` 秒數
2. 距離上次儲存已處理超過 `--auto-save-frames` 個 frames
### 使用方式
```bash
# 第一次執行 (預設: 30秒 或 300 frames)
python yolo_processor.py video.mp4 output.json --uuid "abc123"
# 中斷後繼續 (自動偵測)
python yolo_processor.py video.mp4 output.json --uuid "abc123"
# 強制從頭開始
python yolo_processor.py video.mp4 output.json --force
# 自訂自動保存間隔
python yolo_processor.py video.mp4 output.json --auto-save 10 --auto-save-frames 100
```
### Rust 端支援
`check_json_completeness()` 已更新以支援新格式:
- 讀取 `frames` dict 的最後一個 key 作為 `last_processed_frame`
- 檢查 `metadata.status` 判斷是否完成
## 待測試項目
- [ ] 實際執行中斷後繼續
- [ ] 驗證 large video (如 Old_Time_Movie_Show) 繼續處理
- [ ] 驗證 Rust --resume 參數正確傳遞

View File

@@ -1,123 +0,0 @@
{
"name": "Momentry Video Search - Simple",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "video-search-simple",
"responseMode": "lastNode",
"options": {}
},
"id": "webhook-simple",
"name": "Webhook (Simple)",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [
250,
300
],
"webhookId": "video-search-simple"
},
{
"parameters": {
"url": "http://localhost:3002/api/v1/n8n/search",
"method": "POST",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "query",
"value": "={{ $json.body }}"
},
{
"name": "limit",
"value": 5
}
]
},
"options": {
"headers": {
"Content-Type": "application/json",
"X-API-Key": "demo_api_key_12345"
}
}
},
"id": "http-search",
"name": "搜尋 Momentry",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 3,
"position": [
500,
300
]
},
{
"parameters": {
"jsCode": "// 處理 Momentry 搜尋結果\nconst data = $input.first().json;\nconst hits = data.hits;\n\nif (!hits || hits.length === 0) {\n return {\n json: {\n success: false,\n message: '找不到相關結果',\n query: data.query\n }\n };\n}\n\n// 格式化結果\nconst formattedResults = hits.map((hit, idx) => {\n return {\n index: idx + 1,\n id: hit.id,\n title: hit.title,\n text: hit.text,\n startTime: hit.start,\n endTime: hit.end,\n relevance: Math.round(hit.score * 100) + '%',\n file_path: hit.file_path\n };\n});\n\nreturn {\n json: {\n success: true,\n query: data.query,\n totalFound: data.count,\n results: formattedResults\n }\n};"
},
"id": "code-process-simple",
"name": "處理結果",
"type": "n8n-nodes-base.code",
"typeVersion": 1,
"position": [
750,
300
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ $json }}"
},
"id": "respond-webhook",
"name": "回傳結果",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
1000,
300
]
}
],
"connections": {
"Webhook (Simple)": {
"main": [
[
{
"node": "搜尋 Momentry",
"type": "main",
"index": 0
}
]
]
},
"搜尋 Momentry": {
"main": [
[
{
"node": "處理結果",
"type": "main",
"index": 0
}
]
]
},
"處理結果": {
"main": [
[
{
"node": "回傳結果",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {},
"id": "momentry-video-search-simple",
"versionId": "1",
"createdAt": "2026-03-23T00:00:00.000Z",
"updatedAt": "2026-03-23T00:00:00.000Z"
}

View File

@@ -1,89 +0,0 @@
{
"name": "Momentry Video Search Test",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "video-search-test",
"responseMode": "lastNode",
"options": {}
},
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [250, 300]
},
{
"parameters": {
"url": "http://localhost:3002/api/v1/n8n/search",
"method": "POST",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ query: $json.body.query || \"test\", limit: $json.body.limit || 5 }) }}",
"options": {}
},
"name": "HTTP Request",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [500, 300]
},
{
"parameters": {
"jsCode": "return $input.first().json;"
},
"name": "Code",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [750, 300]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify($json) }}"
},
"name": "Respond to Webhook",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [1000, 300]
}
],
"connections": {
"Webhook": {
"main": [
[
{
"node": "HTTP Request",
"type": "main",
"index": 0
}
]
]
},
"HTTP Request": {
"main": [
[
{
"node": "Code",
"type": "main",
"index": 0
}
]
]
},
"Code": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null
}

View File

@@ -1,109 +0,0 @@
{
"name": "Momentry Video RAG MCP",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "video-rag-mcp",
"responseMode": "lastNode",
"options": {}
},
"name": "Webhook Trigger",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [
250,
300
]
},
{
"parameters": {
"url": "http://localhost:3002/api/v1/n8n/search",
"method": "POST",
"sendBody": true,
"contentType": "json",
"body": "={{ JSON.stringify({query: $json.body.query || $json.body, limit: $json.body.limit || 5, uuid: $json.body.uuid}) }}",
"options": {
"timeout": 30000,
"headers": {
"X-API-Key": "demo_api_key_12345"
}
}
},
"name": "Search Momentry Core",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
500,
300
]
},
{
"parameters": {
"jsCode": "// Process Momentry Core search results\nconst data = $input.first().json;\nconst hits = data.hits || [];\n\nif (hits.length === 0) {\n return {\n json: {\n success: false,\n message: 'No relevant results found',\n query: data.query,\n results: []\n }\n };\n}\n\n// Format results for RAG\nconst formattedResults = hits.map((hit, idx) => {\n return {\n index: idx + 1,\n id: hit.id || hit.chunk_id,\n title: hit.title || 'Unknown Video',\n text: hit.text || hit.content || '',\n startTime: hit.start_time || hit.start || 0,\n endTime: hit.end_time || hit.end || 0,\n relevance: Math.round((hit.score || 0) * 100) + '%',\n videoUuid: hit.video_uuid || hit.uuid,\n file_path: hit.file_path || ''\n };\n});\n\n// Build context for RAG\nconst context = formattedResults\n .map(r => \\`[\\${r.index}] \\${r.text} (Video: \\${r.title}, Time: \\${r.startTime}s-\\${r.endTime}s)\\`)\n .join('\\n\\n');\n\nreturn {\n json: {\n success: true,\n query: data.query,\n totalFound: data.count || hits.length,\n context: context,\n results: formattedResults\n }\n};"
},
"name": "Process RAG Results",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
750,
300
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify($json) }}",
"options": {
"statusCode": 200
}
},
"name": "Respond to Webhook",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
1000,
300
]
}
],
"connections": {
"Webhook Trigger": {
"main": [
[
{
"node": "Search Momentry Core",
"type": "main",
"index": 0
}
]
]
},
"Search Momentry Core": {
"main": [
[
{
"node": "Process RAG Results",
"type": "main",
"index": 0
}
]
]
},
"Process RAG Results": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null
}

View File

@@ -1,138 +0,0 @@
{
"name": "Momentry Video Search",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "video-search",
"responseMode": "lastNode",
"options": {}
},
"id": "webhook-trigger",
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [
250,
300
],
"webhookId": "video-search"
},
{
"parameters": {
"url": "http://localhost:3002/api/v1/n8n/search",
"method": "POST",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "query",
"value": "={{ $json.body }}"
},
{
"name": "limit",
"value": 5
}
]
},
"options": {
"headers": {
"Content-Type": "application/json",
"X-API-Key": "demo_api_key_12345"
}
}
},
"id": "http-request-search",
"name": "搜尋 Momentry",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 3,
"position": [
500,
300
]
},
{
"parameters": {
"jsCode": "const hits = $input.first().json.hits;\n\nif (!hits || hits.length === 0) {\n return {\n json: {\n message: '找不到相關結果',\n query: $input.first().json.query\n }\n };\n}\n\nconst results = hits.map((hit, index) => {\n return {\n number: index + 1,\n text: hit.text,\n start: hit.start,\n end: hit.end,\n score: Math.round(hit.score * 100) + '%',\n video_title: hit.title,\n file_path: hit.file_path\n };\n});\n\nreturn {\n json: {\n query: $input.first().json.query,\n count: $input.first().json.count,\n results: results\n }\n};"
},
"id": "code-process",
"name": "處理結果",
"type": "n8n-nodes-base.code",
"typeVersion": 1,
"position": [
750,
300
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.telegram.org/bot{{ $env.TELEGRAM_BOT_TOKEN }}/sendMessage",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "chat_id",
"value": "={{ $json.chat_id }}"
},
{
"name": "text",
"value": "=🎬 搜尋結果: \"{{ $json.query }}\"\n\n{{ $json.results.map(r => r.number + '️⃣ \"' + r.text.substring(0, 50) + '...\"\n⏱ ' + r.start + 's - ' + r.end + 's\n📊 相關度: ' + r.score).join('\n\n') }}"
}
]
},
"options": {}
},
"id": "telegram-send",
"name": "Telegram 通知",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 3,
"position": [
1000,
300
],
"continueOnFail": true
}
],
"connections": {
"Webhook": {
"main": [
[
{
"node": "搜尋 Momentry",
"type": "main",
"index": 0
}
]
]
},
"搜尋 Momentry": {
"main": [
[
{
"node": "處理結果",
"type": "main",
"index": 0
}
]
]
},
"處理結果": {
"main": [
[
{
"node": "Telegram 通知",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {},
"id": "momentry-video-search",
"versionId": "1",
"createdAt": "2026-03-23T00:00:00.000Z",
"updatedAt": "2026-03-23T00:00:00.000Z"
}

View File

@@ -1,100 +0,0 @@
#!/bin/bash
echo "=========================================="
echo "Momentry Core API 測試腳本"
echo "=========================================="
echo ""
# 顏色定義
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 測試 1: Momentry Core 搜尋
echo -e "${YELLOW}測試 1: Momentry Core 搜尋 API${NC}"
echo "URL: http://localhost:3002/api/v1/n8n/search"
echo ""
RESPONSE=$(curl -s -X POST http://localhost:3002/api/v1/n8n/search \
-H "Content-Type: application/json" \
-d '{"query":"charade","limit":2}')
if echo "$RESPONSE" | grep -q '"hits"'; then
echo -e "${GREEN}✅ 成功!${NC}"
echo "$RESPONSE" | python3 -m json.tool | grep -E '"query"|"count"' | head -3
echo ""
echo "前 2 個結果:"
echo "$RESPONSE" | python3 -c "
import sys, json
data = json.load(sys.stdin)
for i, hit in enumerate(data.get('hits', [])[:2]):
print(f\" [{i+1}] {hit.get('text', '')[:50]}...\")
print(f\" 時間: {hit.get('start')}s - {hit.get('end')}s\")
print(f\" 影片: {hit.get('title')}\")
"
else
echo -e "${RED}❌ 失敗${NC}"
echo "$RESPONSE"
fi
echo ""
# 測試 2: 列出影片
echo -e "${YELLOW}測試 2: 列出所有影片${NC}"
echo "URL: http://localhost:3002/api/v1/videos"
echo ""
RESPONSE=$(curl -s http://localhost:3002/api/v1/videos)
if echo "$RESPONSE" | grep -q '"videos"'; then
echo -e "${GREEN}✅ 成功!${NC}"
echo "$RESPONSE" | python3 -c "
import sys, json
data = json.load(sys.stdin)
print(f\"找到 {len(data.get('videos', []))} 個影片:\")
for v in data.get('videos', []):
print(f\" - {v.get('file_name')} (UUID: {v.get('uuid')[:8]}...)\")
"
else
echo -e "${RED}❌ 失敗${NC}"
fi
echo ""
# 測試 3: n8n Webhook (Test Mode)
echo -e "${YELLOW}測試 3: n8n Webhook (Test Mode)${NC}"
echo "URL: http://localhost:5678/webhook-test/video-rag-mcp"
echo ""
echo "⚠️ 注意: 請先在 n8n UI 中點擊 'Execute workflow' 按鈕"
echo ""
RESPONSE=$(curl -s -X POST http://localhost:5678/webhook-test/video-rag-mcp \
-H "Content-Type: application/json" \
-d '{"query":"charade","limit":2}')
if echo "$RESPONSE" | grep -q '"success": true'; then
echo -e "${GREEN}✅ Webhook 測試成功!${NC}"
echo "$RESPONSE" | python3 -c "
import sys, json
data = json.load(sys.stdin)
print(f\"查詢: {data.get('query')}\")
print(f\"找到: {data.get('totalFound')} 個結果\")
print(f\"Context 長度: {len(data.get('context', ''))} 字元\")
"
elif echo "$RESPONSE" | grep -q '404'; then
echo -e "${RED}❌ Webhook 未找到${NC}"
echo "請在 n8n UI 中:"
echo " 1. 開啟 'Momentry Video RAG MCP' 工作流程"
echo " 2. 點擊 'Execute workflow' 按鈕"
echo " 3. 30 秒內再次執行此腳本"
else
echo -e "${RED}❌ 錯誤${NC}"
echo "$RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$RESPONSE"
fi
echo ""
echo "=========================================="
echo "測試完成!"
echo "=========================================="
echo ""
echo "快速參考:"
echo " Momentry API: http://localhost:3002/api/v1"
echo " n8n UI: https://n8n.momentry.ddns.net"
echo " Webhook Test: http://localhost:5678/webhook-test/video-rag-mcp"

View File

@@ -1,33 +0,0 @@
#!/bin/bash
# Test Momentry Core API directly
# This bypasses n8n and tests the API directly
echo "=========================================="
echo "Testing Momentry Core API Directly"
echo "=========================================="
echo ""
# Test 1: Health check
echo "Test 1: Health Check"
curl -s http://localhost:3002/api/v1/health 2>&1 | head -5
echo ""
# Test 2: Search API
echo "Test 2: Search API"
echo "Query: 'charade'"
curl -s -X POST http://localhost:3002/api/v1/n8n/search \
-H "Content-Type: application/json" \
-d '{"query":"charade","limit":3}' | python3 -m json.tool
echo ""
# Test 3: List videos
echo "Test 3: List Videos"
curl -s http://localhost:3002/api/v1/videos | python3 -m json.tool 2>/dev/null || echo "No videos or endpoint error"
echo ""
echo "=========================================="
echo "If all tests show JSON responses, API is working!"
echo ""
echo "Next step: Use the API in n8n workflows"
echo "=========================================="

View File

@@ -1,104 +0,0 @@
#!/bin/bash
# Test Momentry Video RAG MCP Workflow
# Usage: ./test_workflow.sh [query] [limit] [uuid]
N8N_URL="http://localhost:5678"
WEBHOOK_PATH="webhook-test/video-rag-mcp"
echo "⚠️ 注意:使用 Test Webhook URL"
echo "(生產環境 Webhook 需要工作流程手動激活後才能使用)"
echo ""
# Default values
QUERY="${1:-charade}"
LIMIT="${2:-3}"
UUID="${3:-}"
echo "=========================================="
echo "Testing Video RAG Workflow"
echo "=========================================="
echo "Query: $QUERY"
echo "Limit: $LIMIT"
if [ -n "$UUID" ]; then
echo "UUID: $UUID"
fi
echo ""
# Build JSON payload
if [ -n "$UUID" ]; then
PAYLOAD=$(cat <<JSON
{
"query": "$QUERY",
"limit": $LIMIT,
"uuid": "$UUID"
}
JSON
)
else
PAYLOAD=$(cat <<JSON
{
"query": "$QUERY",
"limit": $LIMIT
}
JSON
)
fi
echo "Request:"
echo "$PAYLOAD" | python3 -m json.tool 2>/dev/null || echo "$PAYLOAD"
echo ""
echo "=========================================="
echo "Response:"
echo "=========================================="
echo "URL: ${N8N_URL}/${WEBHOOK_PATH}"
echo ""
# Make the request
RESPONSE=$(curl -s -X POST "${N8N_URL}/${WEBHOOK_PATH}" \
-H "Content-Type: application/json" \
-d "$PAYLOAD")
# Format and display response
echo "$RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$RESPONSE"
echo ""
echo "=========================================="
# Check if successful
if echo "$RESPONSE" | grep -q '"success": true'; then
echo "✅ Test PASSED"
# Extract and display summary
echo ""
echo "Summary:"
echo "$RESPONSE" | python3 -c "
import sys, json
data = json.load(sys.stdin)
if data.get('success'):
print(f\" Query: {data.get('query', 'N/A')}\")
print(f\" Total Found: {data.get('totalFound', 0)}\")
results = data.get('results', [])
print(f\" Results Count: {len(results)}\")
print()
print(' Top Results:')
for r in results[:3]:
print(f\" [{r.get('index')}] {r.get('title', 'Unknown')}\")
text = r.get('text', '')[:50]
print(f\" Text: {text}...\")
print(f\" Time: {r.get('startTime')}s - {r.get('endTime')}s\")
print(f\" Relevance: {r.get('relevance')}\")
print()
" 2>/dev/null || true
elif echo "$RESPONSE" | grep -q '"code": 404'; then
echo "❌ Webhook not found"
echo ""
echo "可能的解決方案:"
echo " 1. 在 n8n UI 中開啟工作流程: https://n8n.momentry.ddns.net"
echo " 2. 點擊右上角的 'Active' 開關"
echo " 3. 或者使用 test webhook: webhook-test/video-rag-mcp"
else
echo "❌ Test FAILED or no results found"
fi
echo "=========================================="