feat: Initial v0.9 release with API Key authentication
## v0.9.20260325_144654 ### Features - API Key Authentication System - Job Worker System - V2 Backup Versioning ### Bug Fixes - get_processor_results_by_job column mapping Co-authored-by: OpenCode
This commit is contained in:
21
.markdownlint.json
Normal file
21
.markdownlint.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"default": true,
|
||||||
|
"MD003": false,
|
||||||
|
"MD009": false,
|
||||||
|
"MD010": false,
|
||||||
|
"MD013": false,
|
||||||
|
"MD022": false,
|
||||||
|
"MD024": false,
|
||||||
|
"MD025": false,
|
||||||
|
"MD031": false,
|
||||||
|
"MD032": false,
|
||||||
|
"MD033": false,
|
||||||
|
"MD034": false,
|
||||||
|
"MD036": false,
|
||||||
|
"MD040": false,
|
||||||
|
"MD046": false,
|
||||||
|
"MD055": false,
|
||||||
|
"MD056": false,
|
||||||
|
"MD058": false,
|
||||||
|
"MD060": false
|
||||||
|
}
|
||||||
21
.markdownlintrc
Normal file
21
.markdownlintrc
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"default": true,
|
||||||
|
"MD003": false,
|
||||||
|
"MD009": false,
|
||||||
|
"MD010": false,
|
||||||
|
"MD013": false,
|
||||||
|
"MD022": false,
|
||||||
|
"MD024": false,
|
||||||
|
"MD025": false,
|
||||||
|
"MD031": false,
|
||||||
|
"MD032": false,
|
||||||
|
"MD033": false,
|
||||||
|
"MD034": false,
|
||||||
|
"MD036": false,
|
||||||
|
"MD040": false,
|
||||||
|
"MD046": false,
|
||||||
|
"MD055": false,
|
||||||
|
"MD056": false,
|
||||||
|
"MD058": false,
|
||||||
|
"MD060": false
|
||||||
|
}
|
||||||
143
CHANGELOG.md
Normal file
143
CHANGELOG.md
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Gitea API token integration
|
||||||
|
- n8n API key integration
|
||||||
|
- API key caching with Moka
|
||||||
|
- Rate limiting for API key validation
|
||||||
|
- Constant-time hash comparison
|
||||||
|
- OpenAPI documentation with utoipa
|
||||||
|
|
||||||
|
## [0.1.0] - 2026-03-21
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
#### API Key Management System
|
||||||
|
- API key generation with secure random (UUID v4)
|
||||||
|
- SHA256 key hashing
|
||||||
|
- 5 key types: System, User, Service, Integration, Emergency
|
||||||
|
- Key expiration with configurable TTL
|
||||||
|
- Grace period for key rotation
|
||||||
|
|
||||||
|
#### Anomaly Detection
|
||||||
|
- High request rate detection (>1000/min)
|
||||||
|
- High error rate detection (>50%)
|
||||||
|
- Multiple IP detection (>5/hour)
|
||||||
|
- Unusual time activity detection
|
||||||
|
- Redis Pub/Sub for anomaly alerts
|
||||||
|
|
||||||
|
#### Rotation Mechanism
|
||||||
|
- Automatic rotation scheduling
|
||||||
|
- Manual rotation requests
|
||||||
|
- Forced rotation for security incidents
|
||||||
|
- Grace period management per key type:
|
||||||
|
- System: 72 hours
|
||||||
|
- User: 24 hours
|
||||||
|
- Service: 48 hours
|
||||||
|
- Integration: 24 hours
|
||||||
|
- Emergency: 0 hours (immediate)
|
||||||
|
|
||||||
|
#### PostgreSQL Integration
|
||||||
|
- `api_keys` table for key storage
|
||||||
|
- `api_key_audit_log` table for audit trail
|
||||||
|
- `api_key_anomalies` table for anomaly records
|
||||||
|
- Full CRUD operations for API keys
|
||||||
|
|
||||||
|
#### Redis Integration
|
||||||
|
- Anomaly alert Pub/Sub (`momentry:anomaly:alerts`)
|
||||||
|
- Key anomaly state tracking
|
||||||
|
- Real-time alert notifications
|
||||||
|
|
||||||
|
#### CLI Commands
|
||||||
|
- `momentry api-key create` - Create new API key
|
||||||
|
- `momentry api-key list` - List all API keys
|
||||||
|
- `momentry api-key validate` - Validate an API key
|
||||||
|
- `momentry api-key revoke` - Revoke an API key
|
||||||
|
- `momentry api-key rotate` - Request key rotation
|
||||||
|
- `momentry api-key stats` - Show statistics
|
||||||
|
|
||||||
|
#### Gitea Integration
|
||||||
|
- Create Gitea Personal Access Tokens
|
||||||
|
- List user tokens
|
||||||
|
- Delete tokens
|
||||||
|
- Local token tracking
|
||||||
|
- CLI commands:
|
||||||
|
- `momentry gitea create`
|
||||||
|
- `momentry gitea list`
|
||||||
|
- `momentry gitea delete`
|
||||||
|
- `momentry gitea verify`
|
||||||
|
|
||||||
|
#### n8n Integration
|
||||||
|
- Create n8n API keys
|
||||||
|
- List API keys
|
||||||
|
- Delete API keys
|
||||||
|
- Local key tracking
|
||||||
|
- CLI commands:
|
||||||
|
- `momentry n8n create`
|
||||||
|
- `momentry n8n list`
|
||||||
|
- `momentry n8n delete`
|
||||||
|
- `momentry n8n verify`
|
||||||
|
|
||||||
|
#### Security Features
|
||||||
|
- Constant-time hash comparison (subtle crate)
|
||||||
|
- Rate limiting for validation attempts
|
||||||
|
- IP-based lockout after failed attempts
|
||||||
|
- Configurable thresholds via environment variables
|
||||||
|
|
||||||
|
#### Performance Optimizations
|
||||||
|
- Moka-based API key validation cache
|
||||||
|
- Configurable TTL and capacity
|
||||||
|
- Reduced database queries for hot keys
|
||||||
|
|
||||||
|
#### Documentation
|
||||||
|
- API Key Management design document
|
||||||
|
- Redis user configuration guide
|
||||||
|
- Gitea token integration guide
|
||||||
|
- n8n API key integration guide
|
||||||
|
- Optimization plan with task codes
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
#### API Key Configuration
|
||||||
|
```
|
||||||
|
CACHE_TTL_SECONDS=300 # Cache TTL (default: 300)
|
||||||
|
CACHE_MAX_CAPACITY=10000 # Max cache entries (default: 10000)
|
||||||
|
RATE_LIMIT_MAX_ATTEMPTS=5 # Max failed attempts (default: 5)
|
||||||
|
RATE_LIMIT_WINDOW_SECONDS=900 # Lockout duration (default: 900)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Service URLs
|
||||||
|
```
|
||||||
|
GITEA_URL=http://localhost:3000
|
||||||
|
N8N_URL=https://n8n.momentry.ddns.net
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Schema
|
||||||
|
|
||||||
|
#### Tables Created
|
||||||
|
- `api_keys` - API key storage
|
||||||
|
- `api_key_audit_log` - Audit trail
|
||||||
|
- `api_key_anomalies` - Anomaly records
|
||||||
|
- `gitea_tokens` - Gitea token tracking
|
||||||
|
- `n8n_api_keys` - n8n API key tracking
|
||||||
|
|
||||||
|
### Dependencies Added
|
||||||
|
- `uuid` - UUID generation
|
||||||
|
- `subtle` - Constant-time comparison
|
||||||
|
- `moka` - Async cache
|
||||||
|
- `utoipa` - OpenAPI documentation
|
||||||
|
- `utoipa-swagger-ui` - Swagger UI
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Version History
|
||||||
|
|
||||||
|
| Version | Date | Description |
|
||||||
|
|---------|------|-------------|
|
||||||
|
| 0.1.0 | 2026-03-21 | Initial release with API Key Management |
|
||||||
1
a1b10138a6bbb0cd.cut.json
Symbolic link
1
a1b10138a6bbb0cd.cut.json
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/Users/accusys/momentry_core_0.1/output/a1b10138a6bbb0cd.cut.json
|
||||||
1
a1b10138a6bbb0cd.face.json
Symbolic link
1
a1b10138a6bbb0cd.face.json
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/Users/accusys/momentry_core_0.1/output/a1b10138a6bbb0cd.face.json
|
||||||
1
a1b10138a6bbb0cd.ocr.json
Symbolic link
1
a1b10138a6bbb0cd.ocr.json
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/Users/accusys/momentry_core_0.1/output/a1b10138a6bbb0cd.ocr.json
|
||||||
1
a1b10138a6bbb0cd.pose.json
Symbolic link
1
a1b10138a6bbb0cd.pose.json
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/Users/accusys/momentry_core_0.1/output/a1b10138a6bbb0cd.pose.json
|
||||||
1
a1b10138a6bbb0cd.story.json
Symbolic link
1
a1b10138a6bbb0cd.story.json
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/Users/accusys/momentry_core_0.1/output/a1b10138a6bbb0cd.story.json
|
||||||
1
a1b10138a6bbb0cd.yolo.json
Symbolic link
1
a1b10138a6bbb0cd.yolo.json
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/Users/accusys/momentry_core_0.1/output/a1b10138a6bbb0cd.yolo.json
|
||||||
193
docs/API_ACCESS.md
Normal file
193
docs/API_ACCESS.md
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
# Momentry Core API 存取指南
|
||||||
|
|
||||||
|
## 基本網址
|
||||||
|
|
||||||
|
| 環境 | URL | 說明 |
|
||||||
|
|------|-----|------|
|
||||||
|
| **本地開發** | `http://localhost:3002` | 直接訪問 API,繞過反向代理 |
|
||||||
|
| **外部訪問** | `https://api.momentry.ddns.net` | 通過 Caddy 反向代理訪問,需網路可達 |
|
||||||
|
|
||||||
|
### 何時使用哪個 URL
|
||||||
|
|
||||||
|
**使用 `localhost:3002`:**
|
||||||
|
- 開發/測試環境
|
||||||
|
- 直接在伺服器上操作
|
||||||
|
- 當反向代理有問題時
|
||||||
|
|
||||||
|
**使用 `api.momentry.ddns.net`:**
|
||||||
|
- n8n workflow 中呼叫 API
|
||||||
|
- 外部系統整合
|
||||||
|
- 生產環境
|
||||||
|
|
||||||
|
## 認證
|
||||||
|
目前為開放狀態(示範用途無需認證)。正式環境將實作 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,
|
||||||
|
"media_url": "https://wp.momentry.ddns.net/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 影片管理 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' },
|
||||||
|
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']);
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$data = json_decode($response, true);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 影片嵌入網址
|
||||||
|
|
||||||
|
影片可透過 SFTPGo 分享連結存取:
|
||||||
|
```
|
||||||
|
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 個(句子 + 場景 + 時間)
|
||||||
492
docs/API_CURL_EXAMPLES.md
Normal file
492
docs/API_CURL_EXAMPLES.md
Normal file
@@ -0,0 +1,492 @@
|
|||||||
|
# Momentry API 使用說明 (curl 範例)
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 版本 | V1.2 |
|
||||||
|
| 日期 | 2026-03-23 |
|
||||||
|
| Base URL | `http://localhost:3002` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> **狀態說明**:
|
||||||
|
> - ✅ **已實作**: 健康檢查、搜尋、影片管理端點
|
||||||
|
> - ⚠️ **規劃中**: 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
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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" \
|
||||||
|
-d '{"path": "/path/to/video.mp4"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
**回應範例**:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"uuid": "a1b2c3d4e5f6g7h8",
|
||||||
|
"file_path": "/path/to/video.mp4",
|
||||||
|
"file_name": "video.mp4",
|
||||||
|
"duration": 120.5,
|
||||||
|
"width": 1920,
|
||||||
|
"height": 1080
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 列出所有影片 ✅
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:3002/api/v1/videos
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 查詢影片 ✅
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 依 UUID 查詢
|
||||||
|
curl "http://localhost:3002/api/v1/lookup?uuid=a1b2c3d4e5f6g7h8"
|
||||||
|
|
||||||
|
# 依路徑查詢
|
||||||
|
curl "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 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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 查詢與搜索
|
||||||
|
|
||||||
|
### 4.1 語意搜尋 ✅
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:3002/api/v1/search \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-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" \
|
||||||
|
-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,
|
||||||
|
"media_url": "https://wp.momentry.ddns.net/video.mp4"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 混合搜尋 ✅
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:3002/api/v1/search/hybrid \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-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/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" \
|
||||||
|
-d '{"query": "test", "limit": 3}' | jq .
|
||||||
|
|
||||||
|
# 列出影片
|
||||||
|
echo -e "\n=== Videos ==="
|
||||||
|
curl -s "$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 使用範例
|
||||||
@@ -16,9 +16,34 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 認證
|
||||||
|
|
||||||
|
除健康檢查端點外,所有 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
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 端點列表
|
## 端點列表
|
||||||
|
|
||||||
### 健康檢查
|
### 健康檢查(公開)
|
||||||
|
|
||||||
| 方法 | 端點 | 說明 |
|
| 方法 | 端點 | 說明 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
@@ -213,5 +238,7 @@ sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
|
|||||||
## 相關文件
|
## 相關文件
|
||||||
|
|
||||||
- [API_INDEX.md](./API_INDEX.md) - 文件總覽(起點)
|
- [API_INDEX.md](./API_INDEX.md) - 文件總覽(起點)
|
||||||
- [API_N8N_GUIDE.md](./API_N8N_GUIDE.md) - n8n 使用範例
|
- [API_EXAMPLES.md](./API_EXAMPLES.md) - **完整範例總覽(curl / n8n / WordPress)**
|
||||||
- [API_WORDPRESS_GUIDE.md](./API_WORDPRESS_GUIDE.md) - 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 範例
|
||||||
|
|||||||
726
docs/API_EXAMPLES.md
Normal file
726
docs/API_EXAMPLES.md
Normal file
@@ -0,0 +1,726 @@
|
|||||||
|
# Momentry Core API 使用範例總覽
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 版本 | V2.0 |
|
||||||
|
| 日期 | 2026-03-25 |
|
||||||
|
| Base URL (本地) | `http://localhost:3002` |
|
||||||
|
| Base URL (外部) | `https://api.momentry.ddns.net` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 快速參考
|
||||||
|
|
||||||
|
### 環境 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" \
|
||||||
|
-d '{"query": "charade", "limit": 5}'
|
||||||
|
|
||||||
|
# n8n 格式搜尋(推薦)
|
||||||
|
curl -X POST http://localhost:3002/api/v1/n8n/search \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"query": "charade", "limit": 5}'
|
||||||
|
|
||||||
|
# 混合搜尋
|
||||||
|
curl -X POST http://localhost:3002/api/v1/search/hybrid \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-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,
|
||||||
|
"media_url": "https://wp.momentry.ddns.net/video.mp4"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.3 影片管理
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 列出所有影片
|
||||||
|
curl http://localhost:3002/api/v1/videos
|
||||||
|
|
||||||
|
# 查詢特定影片(依 UUID)
|
||||||
|
curl "http://localhost:3002/api/v1/lookup?uuid=a1b10138a6bbb0cd"
|
||||||
|
|
||||||
|
# 查詢特定影片(依路徑)
|
||||||
|
curl "http://localhost:3002/api/v1/lookup?path=/path/to/video.mp4"
|
||||||
|
|
||||||
|
# 取得處理進度
|
||||||
|
curl http://localhost:3002/api/v1/progress/a1b10138a6bbb0cd
|
||||||
|
|
||||||
|
# 探測影片(不註冊)
|
||||||
|
curl -X POST http://localhost:3002/api/v1/probe \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"path": "/path/to/video.mp4"}'
|
||||||
|
|
||||||
|
# 註冊影片
|
||||||
|
curl -X POST http://localhost:3002/api/v1/register \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"path": "/path/to/video.mp4", "file_name": "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" \
|
||||||
|
-d '{"query": "charade", "limit": 3}' | jq .
|
||||||
|
|
||||||
|
echo -e "\n=== 影片列表 ==="
|
||||||
|
curl -s "$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" \
|
||||||
|
-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 }}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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: JSON,Body 為有效 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
|
||||||
|
// 註冊短碼
|
||||||
|
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'],
|
||||||
|
'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) {
|
||||||
|
$output .= sprintf(
|
||||||
|
'<li>%s <a href="%s?start=%s">播放</a></li>',
|
||||||
|
esc_html($hit['text']),
|
||||||
|
$hit['media_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,
|
||||||
|
"media_url": "https://wp.momentry.ddns.net/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 詳細指南
|
||||||
102
docs/API_INDEX.md
Normal file
102
docs/API_INDEX.md
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
# Momentry Core API 文件總覽
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 版本 | V2.1 |
|
||||||
|
| 日期 | 2026-03-25 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 文件架構
|
||||||
|
|
||||||
|
```
|
||||||
|
docs/
|
||||||
|
├── API_INDEX.md ← 本文件:總覽與入口
|
||||||
|
├── API_ENDPOINTS.md ← API 端點完整說明
|
||||||
|
├── API_EXAMPLES.md ← 完整範例總覽(curl / n8n / WordPress)
|
||||||
|
├── DEMO_MANUAL.md ← ⭐ 示範手冊(含 Demo API Key)
|
||||||
|
├── API_N8N_GUIDE.md ← n8n 詳細指南
|
||||||
|
├── API_WORDPRESS_GUIDE.md ← WordPress 詳細指南
|
||||||
|
├── API_CURL_EXAMPLES.md ← curl 快速範例
|
||||||
|
└── API_REFERENCE.md ← 詳細技術參考
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 快速選擇指南
|
||||||
|
|
||||||
|
| 需求 | 閱讀文件 |
|
||||||
|
|------|----------|
|
||||||
|
| **我要快速開始測試** | ⭐ [DEMO_MANUAL.md](./DEMO_MANUAL.md) |
|
||||||
|
| **我要查看所有範例** | [API_EXAMPLES.md](./API_EXAMPLES.md) |
|
||||||
|
| 我想了解有哪些 API 端點 | [API_ENDPOINTS.md](./API_ENDPOINTS.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) - 待解決問題追蹤
|
||||||
195
docs/API_KEY_ARCHITECTURE.md
Normal file
195
docs/API_KEY_ARCHITECTURE.md
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
# 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 │ │
|
||||||
|
│ └─────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
236
docs/API_KEY_INTEGRATION_TESTS.md
Normal file
236
docs/API_KEY_INTEGRATION_TESTS.md
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
# 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
|
||||||
|
```
|
||||||
699
docs/API_KEY_MANAGEMENT.md
Normal file
699
docs/API_KEY_MANAGEMENT.md
Normal file
@@ -0,0 +1,699 @@
|
|||||||
|
# Momentry API Key 管理系統設計
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 版本 | V1.2 |
|
||||||
|
| 日期 | 2026-03-21 |
|
||||||
|
| 狀態 | 開發中 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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/)
|
||||||
399
docs/API_KEY_OPTIMIZATION.md
Normal file
399
docs/API_KEY_OPTIMIZATION.md
Normal file
@@ -0,0 +1,399 @@
|
|||||||
|
# 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 模組
|
||||||
193
docs/API_N8N_GUIDE.md
Normal file
193
docs/API_N8N_GUIDE.md
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
# n8n 呼叫 Momentry API 指南
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 版本 | V1.0 |
|
||||||
|
| 日期 | 2026-03-23 |
|
||||||
|
| 用途 | 在 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` | 處理進度 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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 }}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 測試用(固定關鍵字)
|
||||||
|
|
||||||
|
```
|
||||||
|
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
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 完整 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:
|
||||||
|
|
||||||
|
```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":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 詳細設定
|
||||||
447
docs/API_REFERENCE.md
Normal file
447
docs/API_REFERENCE.md
Normal file
@@ -0,0 +1,447 @@
|
|||||||
|
# Momentry Core API 安裝指南
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 建立者 | Warren |
|
||||||
|
| 建立時間 | 2026-03-18 |
|
||||||
|
| 文件版本 | V1.0 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版本歷史
|
||||||
|
|
||||||
|
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||||
|
|------|------|------|--------|-----------|
|
||||||
|
| V1.0 | 2026-03-18 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||||
|
| V1.1 | 2026-03-23 | 更新端點與實際一致 | 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
|
||||||
|
|
||||||
|
Currently no authentication is required.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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,
|
||||||
|
"file_name": "video.mp4",
|
||||||
|
"duration": 120.5,
|
||||||
|
"width": 1920,
|
||||||
|
"height": 1080
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:3002/api/v1/register \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-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 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" \
|
||||||
|
-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,
|
||||||
|
"media_url": "https://wp.momentry.ddns.net/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[].media_url` | string | Full media URL (optional) |
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:3002/api/v1/n8n/search \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-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 "http://localhost:3002/api/v1/lookup?path=/path/to/video.mp4"
|
||||||
|
|
||||||
|
# Lookup by UUID
|
||||||
|
curl "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 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 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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>` |
|
||||||
270
docs/API_WORDPRESS_GUIDE.md
Normal file
270
docs/API_WORDPRESS_GUIDE.md
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
# WordPress 呼叫 Momentry API 指南
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 版本 | V1.0 |
|
||||||
|
| 日期 | 2026-03-23 |
|
||||||
|
| 用途 | 在 WordPress 中呼叫 Momentry API |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API URL
|
||||||
|
|
||||||
|
在 WordPress 中呼叫 API,**請使用外部 URL**:
|
||||||
|
|
||||||
|
```
|
||||||
|
https://api.momentry.ddns.net
|
||||||
|
```
|
||||||
|
|
||||||
|
> ⚠️ WordPress 運行於瀏覽器端,無法直接訪問 `localhost`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 常用端點
|
||||||
|
|
||||||
|
| 方法 | 端點 | 說明 |
|
||||||
|
|------|------|------|
|
||||||
|
| 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'],
|
||||||
|
'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, ['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, ['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' },
|
||||||
|
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
|
||||||
|
// 註冊短碼
|
||||||
|
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'],
|
||||||
|
'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) {
|
||||||
|
$output .= sprintf(
|
||||||
|
'<li>%s <a href="%s?start=%s">播放</a></li>',
|
||||||
|
esc_html($hit['text']),
|
||||||
|
$hit['media_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'],
|
||||||
|
'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 使用範例
|
||||||
331
docs/ARCHITECTURE_EVALUATION.md
Normal file
331
docs/ARCHITECTURE_EVALUATION.md
Normal file
@@ -0,0 +1,331 @@
|
|||||||
|
# 架構優化待評估事項
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 建立者 | 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導致B,B導致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 的一部分)
|
||||||
667
docs/BUILD_VERSION_RECORD.md
Normal file
667
docs/BUILD_VERSION_RECORD.md
Normal file
@@ -0,0 +1,667 @@
|
|||||||
|
# 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')*
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
```
|
||||||
1106
docs/CACHE_ARCHITECTURE_PLAN.md
Normal file
1106
docs/CACHE_ARCHITECTURE_PLAN.md
Normal file
File diff suppressed because it is too large
Load Diff
534
docs/CHUNK_DESIGN.md
Normal file
534
docs/CHUNK_DESIGN.md
Normal file
@@ -0,0 +1,534 @@
|
|||||||
|
# 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;
|
||||||
|
```
|
||||||
@@ -1,5 +1,21 @@
|
|||||||
# Video Chunk 切分規範
|
# Video Chunk 切分規範
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 建立者 | Warren |
|
||||||
|
| 建立時間 | 2026-03-16 |
|
||||||
|
| 文件版本 | V1.0 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版本歷史
|
||||||
|
|
||||||
|
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||||
|
|------|------|------|--------|-----------|
|
||||||
|
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
本文檔定義 Momentry Core 系統中影片 chunks 的切分原則與資料結構。
|
本文檔定義 Momentry Core 系統中影片 chunks 的切分原則與資料結構。
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -579,7 +595,518 @@ TimeBased Chunks (4 個, 重疊 2秒):
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 10. 相關文件
|
## 10. 資料庫儲存
|
||||||
|
|
||||||
|
### 10.1 PostgreSQL 儲存
|
||||||
|
|
||||||
|
#### Table Schema
|
||||||
|
|
||||||
|
```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,
|
||||||
|
start_frame BIGINT NOT NULL,
|
||||||
|
end_time DOUBLE PRECISION NOT NULL,
|
||||||
|
end_frame BIGINT NOT NULL,
|
||||||
|
fps VARCHAR(16) NOT NULL,
|
||||||
|
fps_value DOUBLE PRECISION NOT NULL,
|
||||||
|
content JSONB NOT NULL,
|
||||||
|
metadata JSONB,
|
||||||
|
vector_id VARCHAR(64),
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||||
|
UNIQUE(uuid, chunk_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 索引
|
||||||
|
CREATE INDEX idx_chunks_uuid ON chunks(uuid);
|
||||||
|
CREATE INDEX idx_chunks_type ON chunks(chunk_type);
|
||||||
|
CREATE INDEX idx_chunks_time ON chunks(start_time, end_time);
|
||||||
|
CREATE INDEX idx_chunks_uuid_type ON chunks(uuid, chunk_type);
|
||||||
|
CREATE INDEX idx_chunks_vector_id ON chunks(vector_id);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 儲存範例
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub async fn store_chunk_to_postgres(db: &PostgresDb, chunk: &Chunk) -> Result<()> {
|
||||||
|
sqlx::query!(
|
||||||
|
r#"
|
||||||
|
INSERT INTO chunks (
|
||||||
|
uuid, chunk_id, chunk_index, chunk_type,
|
||||||
|
start_time, start_frame, end_time, end_frame,
|
||||||
|
fps, fps_value, content, metadata, vector_id
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
ON CONFLICT (uuid, chunk_id) DO UPDATE SET
|
||||||
|
content = EXCLUDED.content,
|
||||||
|
metadata = EXCLUDED.metadata,
|
||||||
|
vector_id = EXCLUDED.vector_id,
|
||||||
|
updated_at = NOW()
|
||||||
|
"#,
|
||||||
|
chunk.uuid,
|
||||||
|
chunk.chunk_id,
|
||||||
|
chunk.chunk_index as i32,
|
||||||
|
chunk.chunk_type.as_str(),
|
||||||
|
chunk.start_time,
|
||||||
|
chunk.start_frame,
|
||||||
|
chunk.end_time,
|
||||||
|
chunk.end_frame,
|
||||||
|
chunk.fps,
|
||||||
|
chunk.fps_value,
|
||||||
|
serde_json::to_value(&chunk.content)?,
|
||||||
|
serde_json::to_value(&chunk.metadata)?,
|
||||||
|
chunk.vector_id,
|
||||||
|
)
|
||||||
|
.execute(&db.pool)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 10.2 MongoDB 儲存
|
||||||
|
|
||||||
|
#### Collection Schema
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// chunks collection
|
||||||
|
{
|
||||||
|
_id: ObjectId,
|
||||||
|
uuid: "1636719dc31f78ac",
|
||||||
|
chunk_id: "sentence_0001",
|
||||||
|
chunk_index: 1,
|
||||||
|
chunk_type: "sentence",
|
||||||
|
start_time: 10.5,
|
||||||
|
start_frame: 252,
|
||||||
|
end_time: 15.75,
|
||||||
|
end_frame: 378,
|
||||||
|
fps: "24/1",
|
||||||
|
fps_value: 24.0,
|
||||||
|
content: {
|
||||||
|
text: "Hello world, this is a test",
|
||||||
|
text_normalized: "hello world this is a test",
|
||||||
|
word_count: 7,
|
||||||
|
char_count: 34
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
source: "asr",
|
||||||
|
confidence: 0.95,
|
||||||
|
language: "en"
|
||||||
|
},
|
||||||
|
vector_id: "vec_sentence_0001",
|
||||||
|
created_at: ISODate("2026-03-16T10:00:00Z"),
|
||||||
|
updated_at: ISODate("2026-03-16T10:00:00Z")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 索引
|
||||||
|
db.chunks.createIndex({ uuid: 1 })
|
||||||
|
db.chunks.createIndex({ chunk_type: 1 })
|
||||||
|
db.chunks.createIndex({ start_time: 1, end_time: 1 })
|
||||||
|
db.chunks.createIndex({ vector_id: 1 })
|
||||||
|
db.chunks.createIndex({ uuid: 1, chunk_type: 1 })
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 儲存範例
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub async fn store_chunk_to_mongodb(db: &MongoDb, chunk: &Chunk) -> Result<()> {
|
||||||
|
let doc = bson::doc! {
|
||||||
|
"uuid": chunk.uuid,
|
||||||
|
"chunk_id": chunk.chunk_id,
|
||||||
|
"chunk_index": chunk.chunk_index,
|
||||||
|
"chunk_type": chunk.chunk_type.as_str(),
|
||||||
|
"start_time": chunk.start_time,
|
||||||
|
"start_frame": chunk.start_frame,
|
||||||
|
"end_time": chunk.end_time,
|
||||||
|
"end_frame": chunk.end_frame,
|
||||||
|
"fps": chunk.fps,
|
||||||
|
"fps_value": chunk.fps_value,
|
||||||
|
"content": serde_json::to_value(&chunk.content)?,
|
||||||
|
"metadata": serde_json::to_value(&chunk.metadata)?,
|
||||||
|
"vector_id": chunk.vector_id,
|
||||||
|
"created_at": chrono::Utc::now(),
|
||||||
|
"updated_at": chrono::Utc::now()
|
||||||
|
};
|
||||||
|
|
||||||
|
let collection = db.database("momentry").collection("chunks");
|
||||||
|
collection.update_one(
|
||||||
|
doc! { "uuid": &chunk.uuid, "chunk_id": &chunk.chunk_id },
|
||||||
|
doc! { "$set": doc },
|
||||||
|
UpdateOptions::builder().upsert(true).build(),
|
||||||
|
).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. 向量儲存設計
|
||||||
|
|
||||||
|
### 11.1 設計原則
|
||||||
|
|
||||||
|
**統一向量 ID 格式**,確保 Qdrant 與 PostgreSQL 相容:
|
||||||
|
|
||||||
|
```
|
||||||
|
{chunk_type}_{chunk_index:04}
|
||||||
|
|
||||||
|
範例:
|
||||||
|
sentence_0001
|
||||||
|
cut_0002
|
||||||
|
time_based_0015
|
||||||
|
```
|
||||||
|
|
||||||
|
### 11.2 Qdrant Collection
|
||||||
|
|
||||||
|
#### 建立 Collection
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 使用 Qdrant client 建立 collection
|
||||||
|
curl -X PUT http://localhost:6333/collections/chunks \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "api-key: Test3200Test3200Test3200" \
|
||||||
|
-d '{
|
||||||
|
"vectors": {
|
||||||
|
"size": 768,
|
||||||
|
"distance": "Cosine"
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Point 結構
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "sentence_0001",
|
||||||
|
"vector": [0.123, -0.456, ...],
|
||||||
|
"payload": {
|
||||||
|
"uuid": "1636719dc31f78ac",
|
||||||
|
"chunk_id": "sentence_0001",
|
||||||
|
"chunk_type": "sentence",
|
||||||
|
"chunk_index": 1,
|
||||||
|
"start_time": 10.5,
|
||||||
|
"end_time": 15.75,
|
||||||
|
"text": "Hello world, this is a test",
|
||||||
|
"metadata": {
|
||||||
|
"confidence": 0.95,
|
||||||
|
"language": "en"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Rust 結構
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct VectorPoint {
|
||||||
|
pub id: String,
|
||||||
|
pub vector: Vec<f32>,
|
||||||
|
pub payload: VectorPayload,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct VectorPayload {
|
||||||
|
pub uuid: String,
|
||||||
|
pub chunk_id: String,
|
||||||
|
pub chunk_type: String,
|
||||||
|
pub chunk_index: u32,
|
||||||
|
pub start_time: f64,
|
||||||
|
pub end_time: f64,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub text: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub scene_id: Option<i32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub segment_number: Option<i32>,
|
||||||
|
pub metadata: Option<serde_json::Value>,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 11.3 PostgreSQL Vector 儲存
|
||||||
|
|
||||||
|
#### Table Schema
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 使用 pgvector 擴展
|
||||||
|
CREATE EXTENSION IF NOT EXISTS vector;
|
||||||
|
|
||||||
|
CREATE TABLE chunk_vectors (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
vector_id VARCHAR(64) NOT NULL UNIQUE,
|
||||||
|
uuid VARCHAR(16) NOT NULL,
|
||||||
|
chunk_id VARCHAR(64) NOT NULL,
|
||||||
|
chunk_type VARCHAR(32) NOT NULL,
|
||||||
|
chunk_index INTEGER NOT NULL,
|
||||||
|
start_time DOUBLE PRECISION NOT NULL,
|
||||||
|
end_time DOUBLE PRECISION NOT NULL,
|
||||||
|
embedding vector(768) NOT NULL,
|
||||||
|
metadata JSONB,
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||||
|
|
||||||
|
FOREIGN KEY (uuid, chunk_id) REFERENCES chunks(uuid, chunk_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 向量檢索索引 (IVFFlat)
|
||||||
|
CREATE INDEX idx_chunk_vectors_embedding
|
||||||
|
ON chunk_vectors
|
||||||
|
USING ivfflat (embedding vector_cosine_ops)
|
||||||
|
WITH (lists = 100);
|
||||||
|
|
||||||
|
-- 查詢索引
|
||||||
|
CREATE INDEX idx_chunk_vectors_uuid ON chunk_vectors(uuid);
|
||||||
|
CREATE INDEX idx_chunk_vectors_type ON chunk_vectors(chunk_type);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 儲存範例
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub async fn store_vector_to_postgres(db: &PostgresDb, point: &VectorPoint) -> Result<()> {
|
||||||
|
sqlx::query!(
|
||||||
|
r#"
|
||||||
|
INSERT INTO chunk_vectors (
|
||||||
|
vector_id, uuid, chunk_id, chunk_type, chunk_index,
|
||||||
|
start_time, end_time, embedding, metadata
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
ON CONFLICT (vector_id) DO UPDATE SET
|
||||||
|
embedding = EXCLUDED.embedding,
|
||||||
|
metadata = EXCLUDED.metadata
|
||||||
|
"#,
|
||||||
|
point.id,
|
||||||
|
point.payload.uuid,
|
||||||
|
point.payload.chunk_id,
|
||||||
|
point.payload.chunk_type,
|
||||||
|
point.payload.chunk_index as i32,
|
||||||
|
point.payload.start_time,
|
||||||
|
point.payload.end_time,
|
||||||
|
point.vector,
|
||||||
|
serde_json::to_value(&point.payload.metadata)?,
|
||||||
|
)
|
||||||
|
.execute(&db.pool)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. 查詢範例
|
||||||
|
|
||||||
|
### 12.1 語義搜尋 (Semantic Search)
|
||||||
|
|
||||||
|
#### 查詢類型 1: 相似文字搜尋
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// 搜尋與問句相似的 chunks
|
||||||
|
pub async fn semantic_search(
|
||||||
|
qdrant: &QdrantDb,
|
||||||
|
query: &str,
|
||||||
|
limit: usize,
|
||||||
|
) -> Result<Vec<SearchResult>> {
|
||||||
|
// 1. 將問句向量化
|
||||||
|
let query_vector = embed_text(query).await?;
|
||||||
|
|
||||||
|
// 2. 搜尋 Qdrant
|
||||||
|
let results = qdrant.search(
|
||||||
|
"chunks",
|
||||||
|
&query_vector,
|
||||||
|
limit,
|
||||||
|
Some(&Filter::must([
|
||||||
|
Condition::Match("chunk_type", "sentence"),
|
||||||
|
])),
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
Ok(results)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用範例
|
||||||
|
let results = semantic_search(&qdrant, "找出有人在說話的片段", 10).await?;
|
||||||
|
for r in results {
|
||||||
|
println!("{}: {:.3}", r.payload.chunk_id, r.score);
|
||||||
|
println!(" Time: {}s - {}s", r.payload.start_time, r.payload.end_time);
|
||||||
|
println!(" Text: {:?}", r.payload.text);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 查詢類型 2: 語音/文字混合搜尋
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- PostgreSQL: 搜尋特定文字的 chunks
|
||||||
|
SELECT
|
||||||
|
c.chunk_id,
|
||||||
|
c.chunk_type,
|
||||||
|
c.start_time,
|
||||||
|
c.end_time,
|
||||||
|
c.content->>'text' as text,
|
||||||
|
v.embedding <=> query_embedding('找出開車的場景') as similarity
|
||||||
|
FROM chunks c
|
||||||
|
LEFT JOIN chunk_vectors v ON c.chunk_id = v.chunk_id
|
||||||
|
WHERE c.chunk_type = 'sentence'
|
||||||
|
AND c.content->>'text' ILIKE '%car%'
|
||||||
|
ORDER BY v.embedding <=> query_embedding('找出開車的場景')
|
||||||
|
LIMIT 10;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 12.2 時間範圍搜尋
|
||||||
|
|
||||||
|
#### 查詢類型 3: 特定時間範圍
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// 找出 30-60 秒之間的所有 chunks
|
||||||
|
pub async fn search_by_time_range(
|
||||||
|
db: &PostgresDb,
|
||||||
|
uuid: &str,
|
||||||
|
start: f64,
|
||||||
|
end: f64,
|
||||||
|
) -> Result<Vec<Chunk>> {
|
||||||
|
let chunks = sqlx::query_as!(
|
||||||
|
Chunk,
|
||||||
|
r#"
|
||||||
|
SELECT * FROM chunks
|
||||||
|
WHERE uuid = $1
|
||||||
|
AND start_time < $3
|
||||||
|
AND end_time > $2
|
||||||
|
ORDER BY chunk_type, chunk_index
|
||||||
|
"#,
|
||||||
|
uuid, start, end
|
||||||
|
)
|
||||||
|
.fetch_all(&db.pool)
|
||||||
|
.await?;
|
||||||
|
Ok(chunks)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用範例
|
||||||
|
let chunks = search_by_time_range(&db, "1636719dc31f78ac", 30.0, 60.0).await?;
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// MongoDB: 時間範圍查詢
|
||||||
|
db.chunks.find({
|
||||||
|
uuid: "1636719dc31f78ac",
|
||||||
|
start_time: { $lt: 60 },
|
||||||
|
end_time: { $gt: 30 }
|
||||||
|
}).sort({ chunk_type: 1, chunk_index: 1 })
|
||||||
|
```
|
||||||
|
|
||||||
|
### 12.3 混合搜尋 (Hybrid Search)
|
||||||
|
|
||||||
|
#### 查詢類型 4: 文字關鍵詞 + 向量相似度
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// 結合關鍵詞匹配與向量相似度
|
||||||
|
pub async fn hybrid_search(
|
||||||
|
db: &PostgresDb,
|
||||||
|
qdrant: &QdrantDb,
|
||||||
|
query: &str,
|
||||||
|
keywords: &[&str],
|
||||||
|
limit: usize,
|
||||||
|
) -> Result<Vec<HybridResult>> {
|
||||||
|
// 1. 向量搜尋
|
||||||
|
let query_vector = embed_text(query).await?;
|
||||||
|
let vector_results = qdrant.search("chunks", &query_vector, limit * 2, None).await?;
|
||||||
|
|
||||||
|
// 2. 關鍵詞過濾
|
||||||
|
let keyword_filter: Vec<_> = keywords.iter()
|
||||||
|
.map(|k| format!("%{}%", k))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let filtered: Vec<_> = vector_results.into_iter()
|
||||||
|
.filter(|r| {
|
||||||
|
if let Some(text) = &r.payload.text {
|
||||||
|
keyword_filter.iter().any(|k| text.contains(k.as_str()))
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.take(limit)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(filtered)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 12.4 場景搜尋
|
||||||
|
|
||||||
|
#### 查詢類型 5: 找出特定場景
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- PostgreSQL: 找出特定場景 ID 的 chunks
|
||||||
|
SELECT * FROM chunks
|
||||||
|
WHERE uuid = '1636719dc31f78ac'
|
||||||
|
AND chunk_type = 'cut'
|
||||||
|
AND (content->>'scene_id')::int = 5;
|
||||||
|
|
||||||
|
-- 找出包含轉場效果的 chunks
|
||||||
|
SELECT * FROM chunks
|
||||||
|
WHERE uuid = '1636719dc31f78ac'
|
||||||
|
AND chunk_type = 'cut'
|
||||||
|
AND content->>'transition_type' = 'dissolve';
|
||||||
|
```
|
||||||
|
|
||||||
|
### 12.5 影片摘要
|
||||||
|
|
||||||
|
#### 查詢類型 6: 產生影片摘要
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 合併影片所有語句
|
||||||
|
SELECT
|
||||||
|
string_agg(content->>'text', ' ' ORDER BY start_time) as full_transcript
|
||||||
|
FROM chunks
|
||||||
|
WHERE uuid = '1636719dc31f78ac'
|
||||||
|
AND chunk_type = 'sentence'
|
||||||
|
AND content->>'text' IS NOT NULL;
|
||||||
|
|
||||||
|
-- 按場景聚合文字
|
||||||
|
SELECT
|
||||||
|
content->>'scene_id' as scene,
|
||||||
|
string_agg(content->>'text', ' ' ORDER BY start_time) as scene_text
|
||||||
|
FROM chunks
|
||||||
|
WHERE uuid = '1636719dc31f78ac'
|
||||||
|
AND chunk_type = 'cut'
|
||||||
|
GROUP BY content->>'scene_id'
|
||||||
|
ORDER BY MIN(start_time);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 12.6 常見查詢模式
|
||||||
|
|
||||||
|
| 查詢類型 | 描述 | 資料庫 | SQL/程式碼 |
|
||||||
|
|----------|------|--------|-------------|
|
||||||
|
| 語義搜尋 | 找相似內容 | Qdrant | `search(vector, limit)` |
|
||||||
|
| 關鍵詞搜尋 | 精確文字匹配 | PostgreSQL | `ILIKE '%keyword%'` |
|
||||||
|
| 時間範圍 | 特定時段 | Both | `start_time < end AND end_time > start` |
|
||||||
|
| 場景搜尋 | 特定鏡頭 | PostgreSQL | `scene_id = N` |
|
||||||
|
| 混合搜尋 | 向量+關鍵詞 | Both |結合以上兩種 |
|
||||||
|
| 摘要產生 | 合併文字 | PostgreSQL | `string_agg()` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. 資料庫選擇建議
|
||||||
|
|
||||||
|
### 13.1 儲存策略
|
||||||
|
|
||||||
|
| 資料類型 | 主要儲存 | 備份/查詢 | 說明 |
|
||||||
|
|----------|----------|-----------|------|
|
||||||
|
| **Chunk 元數據** | PostgreSQL | MongoDB | 結構化查詢為主 |
|
||||||
|
| **向量資料** | Qdrant | PostgreSQL | 向量搜尋為主 |
|
||||||
|
| **全文檢索** | PostgreSQL | - | 關鍵詞搜尋 |
|
||||||
|
| **日誌/歷史** | MongoDB | - | 靈活性為主 |
|
||||||
|
|
||||||
|
### 13.2 讀寫模式
|
||||||
|
|
||||||
|
| 場景 | 寫入 | 讀取 |
|
||||||
|
|------|------|------|
|
||||||
|
| **影片處理** | PostgreSQL + Qdrant | - |
|
||||||
|
| **語義搜尋** | - | Qdrant |
|
||||||
|
| **時間軸瀏覽** | - | PostgreSQL |
|
||||||
|
| **系統分析** | MongoDB | MongoDB |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. 相關文件
|
||||||
|
|
||||||
- [JSON_OUTPUT_SPEC.md](./JSON_OUTPUT_SPEC.md) - JSON 輸出規範
|
- [JSON_OUTPUT_SPEC.md](./JSON_OUTPUT_SPEC.md) - JSON 輸出規範
|
||||||
- [RUST_DEVELOPMENT.md](./RUST_DEVELOPMENT.md) - Rust 開發規範
|
- [RUST_DEVELOPMENT.md](./RUST_DEVELOPMENT.md) - Rust 開發規範
|
||||||
|
|||||||
674
docs/DEMO_MANUAL.md
Normal file
674
docs/DEMO_MANUAL.md
Normal file
@@ -0,0 +1,674 @@
|
|||||||
|
# Momentry Core API 示範手冊
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 版本 | V1.0 |
|
||||||
|
| 日期 | 2026-03-25 |
|
||||||
|
| 狀態 | 完成 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 快速開始
|
||||||
|
|
||||||
|
### 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
|
||||||
@@ -1,5 +1,21 @@
|
|||||||
# Momentry Core 開發日誌
|
# Momentry Core 開發日誌
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 建立者 | Warren |
|
||||||
|
| 建立時間 | 2026-03-18 |
|
||||||
|
| 文件版本 | V1.0 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版本歷史
|
||||||
|
|
||||||
|
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||||
|
|------|------|------|--------|-----------|
|
||||||
|
| V1.0 | 2026-03-18 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
> **文檔維護開始**:2026-03-18
|
> **文檔維護開始**:2026-03-18
|
||||||
> **⚠️ 補充說明**:事後補記(2026-03-18 以前),僅供參考。未來紀錄將即時記錄,參考價值較高。
|
> **⚠️ 補充說明**:事後補記(2026-03-18 以前),僅供參考。未來紀錄將即時記錄,參考價值較高。
|
||||||
|
|
||||||
@@ -422,3 +438,103 @@ cargo run --bin momentry -- process <uuid>
|
|||||||
# 查詢進度
|
# 查詢進度
|
||||||
curl http://127.0.0.1:3002/api/v1/progress/<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 實時推送
|
||||||
|
- [ ] 移動端響應式設計
|
||||||
|
|||||||
474
docs/DOCS_STANDARD.md
Normal file
474
docs/DOCS_STANDARD.md
Normal file
@@ -0,0 +1,474 @@
|
|||||||
|
# 文件創建規範
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 建立者 | 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` | 變更日誌 | `/` |
|
||||||
151
docs/DOCUMENT_EMBEDDING_STRATEGY.md
Normal file
151
docs/DOCUMENT_EMBEDDING_STRATEGY.md
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
# Document Embedding Strategy - Parent-Child Chunks
|
||||||
|
|
||||||
|
## 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
|
||||||
323
docs/FILE_CHANGE_MANAGEMENT.md
Normal file
323
docs/FILE_CHANGE_MANAGEMENT.md
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
# 文件修改管理規範 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 |
|
||||||
707
docs/FRESH_MAC_INSTALLATION.md
Normal file
707
docs/FRESH_MAC_INSTALLATION.md
Normal file
@@ -0,0 +1,707 @@
|
|||||||
|
# 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
|
||||||
|
```
|
||||||
@@ -1,5 +1,21 @@
|
|||||||
# Caddy 安裝指南 (本地部署)
|
# Caddy 安裝指南 (本地部署)
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 建立者 | Warren |
|
||||||
|
| 建立時間 | 2026-03-16 |
|
||||||
|
| 文件版本 | V1.0 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版本歷史
|
||||||
|
|
||||||
|
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||||
|
|------|------|------|--------|-----------|
|
||||||
|
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
本文檔說明如何在 macOS 上安裝 Caddy Web Server,配置為本地部署,作為反向代理伺服器。
|
本文檔說明如何在 macOS 上安裝 Caddy Web Server,配置為本地部署,作為反向代理伺服器。
|
||||||
|
|||||||
@@ -1,5 +1,21 @@
|
|||||||
# Gitea 安裝指南 (本地部署)
|
# Gitea 安裝指南 (本地部署)
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 建立者 | Warren |
|
||||||
|
| 建立時間 | 2026-03-15 |
|
||||||
|
| 文件版本 | V1.0 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版本歷史
|
||||||
|
|
||||||
|
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||||
|
|------|------|------|--------|-----------|
|
||||||
|
| V1.0 | 2026-03-15 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
本文檔說明如何在 macOS 上安裝 Gitea Git 服務,配置為本地部署。
|
本文檔說明如何在 macOS 上安裝 Gitea Git 服務,配置為本地部署。
|
||||||
|
|||||||
393
docs/INSTALL_GITEA_MCP.md
Normal file
393
docs/INSTALL_GITEA_MCP.md
Normal file
@@ -0,0 +1,393 @@
|
|||||||
|
# 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 安裝狀態追蹤
|
||||||
@@ -1,5 +1,21 @@
|
|||||||
# MariaDB 安裝指南 (本地部署)
|
# MariaDB 安裝指南 (本地部署)
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 建立者 | Warren |
|
||||||
|
| 建立時間 | 2026-03-16 |
|
||||||
|
| 文件版本 | V1.0 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版本歷史
|
||||||
|
|
||||||
|
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||||
|
|------|------|------|--------|-----------|
|
||||||
|
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
本文檔說明如何在 macOS 上安裝 MariaDB,配置為本地部署,支援遠端訪問。
|
本文檔說明如何在 macOS 上安裝 MariaDB,配置為本地部署,支援遠端訪問。
|
||||||
|
|||||||
464
docs/INSTALL_MOMENTRY_API.md
Normal file
464
docs/INSTALL_MOMENTRY_API.md
Normal file
@@ -0,0 +1,464 @@
|
|||||||
|
# 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` - 待解決問題
|
||||||
@@ -1,5 +1,21 @@
|
|||||||
# MongoDB 安裝指南 (本地部署)
|
# MongoDB 安裝指南 (本地部署)
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 建立者 | Warren |
|
||||||
|
| 建立時間 | 2026-03-15 |
|
||||||
|
| 文件版本 | V1.0 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版本歷史
|
||||||
|
|
||||||
|
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||||
|
|------|------|------|--------|-----------|
|
||||||
|
| V1.0 | 2026-03-15 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
本文檔說明如何在 macOS 上安裝 MongoDB Community Edition,配置為本地部署,支援遠端訪問。
|
本文檔說明如何在 macOS 上安裝 MongoDB Community Edition,配置為本地部署,支援遠端訪問。
|
||||||
@@ -11,8 +27,8 @@
|
|||||||
| 項目 | 狀態 |
|
| 項目 | 狀態 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| MongoDB (mongodb-community) | ✅ 已安裝 v8.2.6 |
|
| MongoDB (mongodb-community) | ✅ 已安裝 v8.2.6 |
|
||||||
| 數據目錄 | 保留 (/Users/accusys/momentry/var) - 共用 |
|
| 數據目錄 | /opt/homebrew/var/mongodb |
|
||||||
| 日誌目錄 | 保留 (/Users/accusys/momentry/log) - 共用 |
|
| 日誌目錄 | /Users/accusys/momentry/log |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -40,9 +56,9 @@ sudo launchctl list | grep mongo
|
|||||||
|
|
||||||
### Step 2: 數據目錄 (已存在 - 共用)
|
### Step 2: 數據目錄 (已存在 - 共用)
|
||||||
|
|
||||||
數據目錄已存在,無需建立:
|
數據目錄使用 homebrew 預設位置:
|
||||||
- 數據目錄: `/Users/accusys/momentry/var`
|
- 數據目錄: `/opt/homebrew/var/mongodb`
|
||||||
- 配置目錄: `/Users/accusys/momentry/etc/mongodb`
|
- 配置目錄: `/opt/homebrew/etc/mongod.conf`
|
||||||
- 日誌目錄: `/Users/accusys/momentry/log`
|
- 日誌目錄: `/Users/accusys/momentry/log`
|
||||||
|
|
||||||
**建立配置目錄和日誌文件**:
|
**建立配置目錄和日誌文件**:
|
||||||
@@ -61,15 +77,19 @@ chown -R accusys:staff /Users/accusys/momentry
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Step 3: 啟動 MongoDB (後台執行)
|
### Step 3: 使用 LaunchAgent 啟動 (開機自動)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
nohup /opt/homebrew/bin/mongod \
|
# 複製 plist 到 LaunchDaemons 目錄 (開機自動需要 root 權限)
|
||||||
--dbpath /Users/accusys/momentry/var \
|
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.mongodb.plist \
|
||||||
--logpath /Users/accusys/momentry/log/mongodb.log \
|
/Library/LaunchDaemons/
|
||||||
--port 27017 \
|
|
||||||
--bind_ip 0.0.0.0 \
|
# 載入並啟動
|
||||||
> /Users/accusys/momentry/log/mongodb.log 2>&1 &
|
sudo launchctl load /Library/LaunchDaemons/com.momentry.mongodb.plist
|
||||||
|
|
||||||
|
# 驗證
|
||||||
|
launchctl list | grep mongodb
|
||||||
|
pgrep -a mongod
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -90,14 +110,20 @@ db.createUser({
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Step 5: 使用 plist 開機自動啟動
|
### Step 4: 驗證安裝
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 複製 plist 到 LaunchDaemons 目錄
|
# 檢查進程
|
||||||
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.mongodb.plist /Library/LaunchDaemons/
|
pgrep -a mongod
|
||||||
|
|
||||||
# 載入並啟動
|
# 檢查端口
|
||||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.mongodb.plist
|
lsof -i :27017
|
||||||
|
|
||||||
|
# 測試連線
|
||||||
|
mongosh --eval "db.adminCommand('ping')"
|
||||||
|
|
||||||
|
# 檢查 LaunchAgent
|
||||||
|
launchctl list | grep mongodb
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -265,12 +291,11 @@ tail -20 /Users/accusys/momentry/log/mongodb.error.log
|
|||||||
### 啟動/停止
|
### 啟動/停止
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 停止
|
# 使用 LaunchAgent (開機自動 - LaunchDaemons 目錄)
|
||||||
pkill mongod
|
sudo launchctl load /Library/LaunchDaemons/com.momentry.mongodb.plist # 啟動
|
||||||
# 或
|
sudo launchctl unload /Library/LaunchDaemons/com.momentry.mongodb.plist # 停止
|
||||||
kill <PID>
|
|
||||||
|
|
||||||
# 啟動 (後台)
|
# 手動啟動 (僅除錯用)
|
||||||
nohup /opt/homebrew/bin/mongod \
|
nohup /opt/homebrew/bin/mongod \
|
||||||
--dbpath /Users/accusys/momentry/var \
|
--dbpath /Users/accusys/momentry/var \
|
||||||
--logpath /Users/accusys/momentry/log/mongodb.log \
|
--logpath /Users/accusys/momentry/log/mongodb.log \
|
||||||
@@ -278,8 +303,8 @@ nohup /opt/homebrew/bin/mongod \
|
|||||||
--bind_ip 0.0.0.0 \
|
--bind_ip 0.0.0.0 \
|
||||||
> /Users/accusys/momentry/log/mongodb.log 2>&1 &
|
> /Users/accusys/momentry/log/mongodb.log 2>&1 &
|
||||||
|
|
||||||
# 使用 plist (開機自動啟動)
|
# 強制停止
|
||||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.mongodb.plist
|
pkill mongod
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,5 +1,21 @@
|
|||||||
# n8n 安裝指南 (本地部署)
|
# n8n 安裝指南 (本地部署)
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 建立者 | Warren |
|
||||||
|
| 建立時間 | 2026-03-16 |
|
||||||
|
| 文件版本 | V1.0 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版本歷史
|
||||||
|
|
||||||
|
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||||
|
|------|------|------|--------|-----------|
|
||||||
|
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
本文檔說明如何在 macOS 上安裝 n8n 工作流自動化平台,配置為本地部署,使用 Queue 模式。
|
本文檔說明如何在 macOS 上安裝 n8n 工作流自動化平台,配置為本地部署,使用 Queue 模式。
|
||||||
@@ -10,13 +26,22 @@
|
|||||||
|
|
||||||
| 項目 | 狀態 |
|
| 項目 | 狀態 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| n8n | ✅ 已安裝 v2.3.5 |
|
| n8n | ✅ 已安裝 v2.12.3 |
|
||||||
| 數據目錄 | /Users/accusys/momentry/var/n8n/ |
|
| 數據目錄 | /Users/accusys/momentry/var/n8n/ |
|
||||||
| 日誌目錄 | /Users/accusys/momentry/log/ |
|
| 日誌目錄 | /Users/accusys/momentry/log/ |
|
||||||
| Main Plist | /Library/LaunchDaemons/com.momentry.n8n.main.plist |
|
| Main Plist | /Library/LaunchDaemons/com.momentry.n8n.main.plist |
|
||||||
| Worker Plist | /Library/LaunchDaemons/com.momentry.n8n.worker.plist |
|
| Worker Plist | /Library/LaunchDaemons/com.momentry.n8n.worker.plist |
|
||||||
| 數據庫 | PostgreSQL (n8n) |
|
| 數據庫 | PostgreSQL (n8n) |
|
||||||
| 隊列 | Redis |
|
| 隊列 | 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 端口
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -32,7 +57,7 @@ brew install n8n
|
|||||||
**驗證**:
|
**驗證**:
|
||||||
```bash
|
```bash
|
||||||
n8n --version
|
n8n --version
|
||||||
# 2.3.5
|
# 2.12.3
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -217,9 +242,9 @@ ps aux | grep "n8n.*worker" | grep -v grep && echo " ✗ 仍在運行" || echo
|
|||||||
echo "3. Port 8085:"
|
echo "3. Port 8085:"
|
||||||
lsof -i :8085 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
|
lsof -i :8085 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
|
||||||
|
|
||||||
# 3. Port 5690-5691
|
# 3. Port 5679 (Worker)
|
||||||
echo "4. Port 5690-5691:"
|
echo "4. Port 5679 (Worker):"
|
||||||
lsof -i :5690 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
|
lsof -i :5679 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
|
||||||
|
|
||||||
# 4. n8n 命令
|
# 4. n8n 命令
|
||||||
echo "5. n8n 命令:"
|
echo "5. n8n 命令:"
|
||||||
@@ -287,8 +312,7 @@ ps aux | grep n8n | grep -v grep
|
|||||||
|
|
||||||
# 2. 檢查 Port
|
# 2. 檢查 Port
|
||||||
lsof -i :5678
|
lsof -i :5678
|
||||||
lsof -i :5690
|
lsof -i :5679
|
||||||
lsof -i :5691
|
|
||||||
|
|
||||||
# 3. 測試連線
|
# 3. 測試連線
|
||||||
curl http://localhost:5678/
|
curl http://localhost:5678/
|
||||||
@@ -325,8 +349,7 @@ sudo launchctl list | grep n8n
|
|||||||
| 服務 | Port |
|
| 服務 | Port |
|
||||||
|------|------|
|
|------|------|
|
||||||
| Main | 5678 |
|
| Main | 5678 |
|
||||||
| Worker Broker | 5690 |
|
| Task Broker (Worker 連接) | 5679 |
|
||||||
| Worker Health Check | 5691 |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -459,7 +482,7 @@ sudo launchctl load /Library/LaunchDaemons/com.momentry.n8n.main.plist
|
|||||||
|
|
||||||
- 版本: 2.3.5
|
- 版本: 2.3.5
|
||||||
- Main Port: 5678
|
- Main Port: 5678
|
||||||
- Worker Ports: 5690-5691
|
- Task Broker (Worker): 5679
|
||||||
- 數據目錄: /Users/accusys/momentry/var/n8n/
|
- 數據目錄: /Users/accusys/momentry/var/n8n/
|
||||||
- 日誌目錄: /Users/accusys/momentry/log/
|
- 日誌目錄: /Users/accusys/momentry/log/
|
||||||
- 數據庫: PostgreSQL n8n
|
- 數據庫: PostgreSQL n8n
|
||||||
|
|||||||
@@ -1,5 +1,21 @@
|
|||||||
# Ollama 安裝指南 (本地部署)
|
# Ollama 安裝指南 (本地部署)
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 建立者 | Warren |
|
||||||
|
| 建立時間 | 2026-03-15 |
|
||||||
|
| 文件版本 | V1.0 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版本歷史
|
||||||
|
|
||||||
|
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||||
|
|------|------|------|--------|-----------|
|
||||||
|
| V1.0 | 2026-03-15 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
本文檔說明如何在 macOS 上安裝 Ollama,配置為本地部署,用於運行大型語言模型 (LLM)。
|
本文檔說明如何在 macOS 上安裝 Ollama,配置為本地部署,用於運行大型語言模型 (LLM)。
|
||||||
@@ -12,7 +28,7 @@
|
|||||||
|------|------|
|
|------|------|
|
||||||
| Ollama | ✅ 已安裝 v0.13.5 |
|
| Ollama | ✅ 已安裝 v0.13.5 |
|
||||||
| Port | 11434 |
|
| Port | 11434 |
|
||||||
| Models 目錄 | /Users/accusys/.ollama/models/ |
|
| Models 目錄 | /Users/accusys/momentry/var/ollama/models |
|
||||||
| 日誌目錄 | /Users/accusys/momentry/log/ |
|
| 日誌目錄 | /Users/accusys/momentry/log/ |
|
||||||
| Plist | /Library/LaunchDaemons/com.momentry.ollama.plist |
|
| Plist | /Library/LaunchDaemons/com.momentry.ollama.plist |
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,21 @@
|
|||||||
# PHP 安裝指南 (本地部署)
|
# PHP 安裝指南 (本地部署)
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 建立者 | Warren |
|
||||||
|
| 建立時間 | 2026-03-16 |
|
||||||
|
| 文件版本 | V1.0 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版本歷史
|
||||||
|
|
||||||
|
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||||
|
|------|------|------|--------|-----------|
|
||||||
|
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
本文檔說明如何在 macOS 上安裝 PHP 及 PHP-FPM,配置為本地部署。
|
本文檔說明如何在 macOS 上安裝 PHP 及 PHP-FPM,配置為本地部署。
|
||||||
|
|||||||
@@ -1,5 +1,21 @@
|
|||||||
# PostgreSQL 安裝指南 (本地部署)
|
# PostgreSQL 安裝指南 (本地部署)
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 建立者 | Warren |
|
||||||
|
| 建立時間 | 2026-03-15 |
|
||||||
|
| 文件版本 | V1.0 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版本歷史
|
||||||
|
|
||||||
|
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||||
|
|------|------|------|--------|-----------|
|
||||||
|
| V1.0 | 2026-03-15 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
本文檔說明如何在 macOS 上安裝 PostgreSQL,配置為本地部署,支援遠端訪問。
|
本文檔說明如何在 macOS 上安裝 PostgreSQL,配置為本地部署,支援遠端訪問。
|
||||||
@@ -11,9 +27,18 @@
|
|||||||
| 項目 | 狀態 |
|
| 項目 | 狀態 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| PostgreSQL | ✅ 已安裝 v18.1 |
|
| PostgreSQL | ✅ 已安裝 v18.1 |
|
||||||
| 數據目錄 | /Users/accusys/momentry/var/postgresql/ |
|
| 數據目錄 | /Users/accusys/momentry/var/postgresql |
|
||||||
| 日誌目錄 | /Users/accusys/momentry/log/ |
|
| 日誌目錄 | /Users/accusys/momentry/log/ |
|
||||||
| Plist | /Library/LaunchDaemons/com.momentry.postgresql.plist |
|
| 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 後使用舊資料目錄
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,21 @@
|
|||||||
# Qdrant 安裝指南 (本地部署)
|
# Qdrant 安裝指南 (本地部署)
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 建立者 | Warren |
|
||||||
|
| 建立時間 | 2026-03-16 |
|
||||||
|
| 文件版本 | V1.0 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版本歷史
|
||||||
|
|
||||||
|
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||||
|
|------|------|------|--------|-----------|
|
||||||
|
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
本文檔說明如何在 macOS 上安裝 Qdrant Vector Database,配置為本地部署,支援遠端訪問。
|
本文檔說明如何在 macOS 上安裝 Qdrant Vector Database,配置為本地部署,支援遠端訪問。
|
||||||
|
|||||||
@@ -1,5 +1,23 @@
|
|||||||
# Redis 安裝指南 (本地部署)
|
# 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,配置為本地部署,支援遠端訪問。
|
本文檔說明如何在 macOS 上安裝 Redis,配置為本地部署,支援遠端訪問。
|
||||||
@@ -11,7 +29,7 @@
|
|||||||
| 項目 | 狀態 |
|
| 項目 | 狀態 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| Redis | ✅ 已安裝 v8.4.0 |
|
| Redis | ✅ 已安裝 v8.4.0 |
|
||||||
| 數據目錄 | /Users/accusys/momentry/var/redis/ |
|
| 數據目錄 | /opt/homebrew/var/db/redis/ |
|
||||||
| 日誌目錄 | /Users/accusys/momentry/log/ |
|
| 日誌目錄 | /Users/accusys/momentry/log/ |
|
||||||
| Plist | /Library/LaunchDaemons/com.momentry.redis.plist |
|
| Plist | /Library/LaunchDaemons/com.momentry.redis.plist |
|
||||||
|
|
||||||
@@ -360,8 +378,104 @@ sudo launchctl load /Library/LaunchDaemons/com.momentry.redis.plist
|
|||||||
|
|
||||||
## 版本資訊
|
## 版本資訊
|
||||||
|
|
||||||
- 版本: 8.4.0
|
| 項目 | 值 |
|
||||||
- Port: 6379
|
|------|-----|
|
||||||
- Password: accusys
|
| Redis Server | 8.4.0 |
|
||||||
- 數據目錄: /Users/accusys/momentry/var/redis/
|
| Rust redis crate | 0.32.7 |
|
||||||
- 日誌目錄: /Users/accusys/momentry/log/
|
| 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 認證測試
|
||||||
|
|||||||
@@ -1,5 +1,21 @@
|
|||||||
# RustDesk 安裝指南 (本地部署)
|
# RustDesk 安裝指南 (本地部署)
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 建立者 | Warren |
|
||||||
|
| 建立時間 | 2026-03-15 |
|
||||||
|
| 文件版本 | V1.0 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版本歷史
|
||||||
|
|
||||||
|
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||||
|
|------|------|------|--------|-----------|
|
||||||
|
| V1.0 | 2026-03-15 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
本文檔說明如何在 macOS 上安裝 RustDesk 遠端桌面服務,配置為本地部署。
|
本文檔說明如何在 macOS 上安裝 RustDesk 遠端桌面服務,配置為本地部署。
|
||||||
|
|||||||
@@ -1,5 +1,32 @@
|
|||||||
# SFTPGo 安裝指南 (本地部署)
|
# SFTPGo 安裝指南 (本地部署)
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 建立者 | Warren |
|
||||||
|
| 建立時間 | 2026-03-18 |
|
||||||
|
| 文件版本 | V1.0 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版本歷史
|
||||||
|
|
||||||
|
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||||
|
|------|------|------|--------|-----------|
|
||||||
|
| V1.0 | 2026-03-18 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||||
|
| V1.1 | 2026-03-22 | 添加備份還原、API管理、Hook配置 | opencode | OpenCode |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 更新紀錄
|
||||||
|
|
||||||
|
- **2026-03-22**:
|
||||||
|
- 成功使用統一備份系統還原 SFTPGo 到 20260321_101928 時間點
|
||||||
|
- 重置 admin 密碼為 `Test3200Test3200`
|
||||||
|
- 修復 demo 用戶權限格式 (從 `["*"]` 改為 `{" /":["*"]}`)
|
||||||
|
- 通过 API 创建并配置 `demo` 用户和 `demo` 組
|
||||||
|
- 配置 SFTPGo Hook 實現自動化檔案註冊
|
||||||
|
- 確認 API 認證方式為 Basic Auth (GET `/api/v2/token`)
|
||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
本文檔說明如何在 macOS 上安裝 SFTPGo,配置為本地部署,用於 SFTP/FTP/WebDAV 檔案傳輸服務。
|
本文檔說明如何在 macOS 上安裝 SFTPGo,配置為本地部署,用於 SFTP/FTP/WebDAV 檔案傳輸服務。
|
||||||
@@ -15,6 +42,76 @@
|
|||||||
| 配置目錄 | /Users/accusys/momentry/etc/sftpgo/ |
|
| 配置目錄 | /Users/accusys/momentry/etc/sftpgo/ |
|
||||||
| 日誌目錄 | /Users/accusys/momentry/log/ |
|
| 日誌目錄 | /Users/accusys/momentry/log/ |
|
||||||
| Plist | /Library/LaunchDaemons/com.momentry.sftpgo.plist |
|
| Plist | /Library/LaunchDaemons/com.momentry.sftpgo.plist |
|
||||||
|
| API 狀態 | ✅ 已啟用 (`enable_rest_api: true`) |
|
||||||
|
| Hook 狀態 | ✅ 已配置 (執行於檔案上傳時) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 備份與還原機制
|
||||||
|
|
||||||
|
SFTPGo 使用 Momentry 統一備份系統進行完整的配置與數據庫備份。
|
||||||
|
|
||||||
|
### 備份內容
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 備份目錄
|
||||||
|
/Users/accusys/momentry/backup/daily/sftpgo/
|
||||||
|
|
||||||
|
# 備份文件範例
|
||||||
|
sftpgo_cfg_20260321_101928.tar.gz # 配置文件
|
||||||
|
sftpgo_db_20260321_101928.sql.gz # PostgreSQL 數據庫
|
||||||
|
sftpgo_20260321_101928.sha256 # 校驗和
|
||||||
|
```
|
||||||
|
|
||||||
|
### 列出可用備份時間點
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/Users/accusys/momentry/scripts/backup_all.sh list | grep sftpgo
|
||||||
|
```
|
||||||
|
|
||||||
|
### 執行備份
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 備份 SFTPGo (配置 + 數據庫)
|
||||||
|
/Users/accusys/momentry/scripts/backup_all.sh sftpgo full
|
||||||
|
|
||||||
|
# 手動備份單一服務
|
||||||
|
/Users/accusys/momentry/scripts/backup_all.sh sftpgo cfg
|
||||||
|
```
|
||||||
|
|
||||||
|
### 執行還原
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 備份當前狀態 (安全措施)
|
||||||
|
/Users/accusys/momentry/scripts/backup_all.sh sftpgo full
|
||||||
|
|
||||||
|
# 2. 還原到指定時間點
|
||||||
|
/Users/accusys/momentry/scripts/backup_all.sh restore sftpgo 20260321_101928
|
||||||
|
|
||||||
|
# 3. 驗證還原結果
|
||||||
|
curl http://localhost:8080/api/v2/version
|
||||||
|
psql -U sftpgo -d sftpgo -c "SELECT username, status FROM admins;"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 還原流程说明
|
||||||
|
|
||||||
|
`backup_all.sh` 的 `restore_sftpgo()` 函数会执行以下步骤 (第 517-546 行):
|
||||||
|
|
||||||
|
1. **停止 SFTPGo** - 使用 `pkill -f sftpgo`
|
||||||
|
2. **恢复配置文件** - 从 `sftpgo_cfg_*.tar.gz` 恢复到 `/Users/accusys/momentry/etc/sftpgo/`
|
||||||
|
3. **恢复 PostgreSQL 数据库**:
|
||||||
|
- 删除现有数据库 (如存在)
|
||||||
|
- 创建新数据库并设置所有者
|
||||||
|
- 从 `sftpgo_db_*.sql.gz` 导入数据
|
||||||
|
4. **重启 SFTPGo** - 手动启动服务
|
||||||
|
|
||||||
|
### 備份狀態檢查
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/Users/accusys/momentry/scripts/backup_all.sh status
|
||||||
|
```
|
||||||
|
|
||||||
|
此命令會顯示所有服務的最新備份狀態與大小。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -111,9 +208,20 @@ chown -R accusys:staff /Users/accusys/workspace/sftpgo
|
|||||||
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.sftpgo.plist /Library/LaunchDaemons/
|
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.sftpgo.plist /Library/LaunchDaemons/
|
||||||
|
|
||||||
# 載入並啟動
|
# 載入並啟動
|
||||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.sftpgo.plist
|
sudo launchctl bootstrap system /Library/LaunchDaemons/com.momentry.sftpgo.plist
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Plist 環境變量說明
|
||||||
|
|
||||||
|
plist 包含以下環境變量用於自動創建 admin 用戶:
|
||||||
|
|
||||||
|
| 環境變量 | 值 | 說明 |
|
||||||
|
|----------|------|------|
|
||||||
|
| `SFTPGO_DEFAULT_ADMIN_USERNAME` | `admin` | 默認管理員用戶名 |
|
||||||
|
| `SFTPGO_DEFAULT_ADMIN_PASSWORD` | `Test3200Test3200` | 默認管理員密碼 |
|
||||||
|
|
||||||
|
**注意**: 這些變量僅在首次啟動時用於創建 admin 用戶。如果 admin 已存在,則不會覆蓋。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 監控配置
|
## 監控配置
|
||||||
@@ -134,6 +242,20 @@ service:
|
|||||||
enabled: true
|
enabled: true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### API 監控端點
|
||||||
|
|
||||||
|
| 端點 | 說明 | 認證 |
|
||||||
|
|------|------|------|
|
||||||
|
| `/api/v2/info` | 伺服器資訊 | ❌ 不需要 |
|
||||||
|
| `/api/v2/healthz` | 健康檢查 | ❌ 不需要 |
|
||||||
|
| `/api/v2/version` | 版本資訊 | ❌ 不需要 |
|
||||||
|
| `/api/v2/token` | 獲取 Token | ✅ Basic Auth |
|
||||||
|
| `/api/v2/admins` | 管理員列表 | ✅ Bearer Token |
|
||||||
|
| `/api/v2/users` | 用戶列表 | ✅ Bearer Token |
|
||||||
|
| `/api/v2/groups` | 組列表 | ✅ Bearer Token |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 卸載步驟
|
## 卸載步驟
|
||||||
@@ -275,6 +397,581 @@ SFTPGO_DATA_DIR=/Users/accusys/workspace/sftpgo
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 管理員帳戶修復
|
||||||
|
|
||||||
|
### 透過 Web 管理面板重置密碼 (推薦)
|
||||||
|
|
||||||
|
1. 前往 **http://localhost:8080/web/admin/login**
|
||||||
|
2. 點擊 **Forgot password?**
|
||||||
|
3. 輸入 **Installation Code**: `Test3200Test3200`
|
||||||
|
4. 為 `admin` 設定新密碼
|
||||||
|
|
||||||
|
### 透過 SQL 直接重建管理員帳戶
|
||||||
|
|
||||||
|
如果 Web 面板無法使用,可直接操作 PostgreSQL 資料庫:
|
||||||
|
|
||||||
|
#### 連線到資料庫
|
||||||
|
|
||||||
|
```bash
|
||||||
|
psql -U accusys -d sftpgo
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 插入管理員帳戶
|
||||||
|
|
||||||
|
```sql
|
||||||
|
INSERT INTO admins (
|
||||||
|
username,
|
||||||
|
description,
|
||||||
|
password,
|
||||||
|
email,
|
||||||
|
status,
|
||||||
|
permissions,
|
||||||
|
filters,
|
||||||
|
additional_info,
|
||||||
|
last_login,
|
||||||
|
role_id,
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
|
) VALUES (
|
||||||
|
'admin',
|
||||||
|
'',
|
||||||
|
'$2a$10$3Boql8AHoxPXWWmgmJiN5.b8s1Z65gNp1yqKQvZ5SOEv7j8NRA58.',
|
||||||
|
'',
|
||||||
|
1,
|
||||||
|
'["*"]',
|
||||||
|
'{"require_two_factor":false,"totp_config":{"secret":{}},"preferences":{}}',
|
||||||
|
'',
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
EXTRACT(EPOCH FROM NOW())::bigint * 1000,
|
||||||
|
EXTRACT(EPOCH FROM NOW())::bigint * 1000
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
**預設密碼**: `Test3200Test3200`
|
||||||
|
|
||||||
|
#### 使用 bcrypt 生成密碼哈希
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -c "import bcrypt; print(bcrypt.hashpw(b'Test3200Test3200', bcrypt.gensalt(rounds=10)).decode())"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用 CLI 重置密碼
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 停止 SFTPGo (如果正在运行)
|
||||||
|
pkill -f sftpgo
|
||||||
|
|
||||||
|
# 重置密碼 (互動式)
|
||||||
|
sftpgo resetpwd --admin admin --config-file /Users/accusys/momentry/etc/sftpgo/sftpgo.json
|
||||||
|
|
||||||
|
# 重新啟動 SFTPGo
|
||||||
|
/opt/homebrew/opt/sftpgo/bin/sftpgo serve --config-file /Users/accusys/momentry/etc/sftpgo/sftpgo.json &
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用 Expect 自動化密碼重置
|
||||||
|
|
||||||
|
如果需要自動化重置,可以創建 expect 腳本:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# resetpwd.exp
|
||||||
|
#!/usr/bin/expect -f
|
||||||
|
set timeout 10
|
||||||
|
set admin_user [lindex $argv 0]
|
||||||
|
set new_password [lindex $argv 1]
|
||||||
|
|
||||||
|
spawn sftpgo resetpwd --admin $admin_user --config-file /Users/accusys/momentry/etc/sftpgo/sftpgo.json
|
||||||
|
expect "Enter Password:"
|
||||||
|
send "$new_password\r"
|
||||||
|
expect "Enter Password:"
|
||||||
|
send "$new_password\r"
|
||||||
|
expect eof
|
||||||
|
```
|
||||||
|
|
||||||
|
執行:
|
||||||
|
```bash
|
||||||
|
chmod +x resetpwd.exp
|
||||||
|
expect resetpwd.exp admin Test3200Test3200
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 透過 CLI 重置密碼
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 停止 SFTPGo
|
||||||
|
kill $(pgrep -f "sftpgo serve")
|
||||||
|
|
||||||
|
# 重置密碼 (互動式)
|
||||||
|
sftpgo resetpwd --admin admin --config-file /Users/accusys/momentry/etc/sftpgo/sftpgo.json
|
||||||
|
|
||||||
|
# 重新啟動 SFTPGo
|
||||||
|
sftpgo serve --config-file /Users/accusys/momentry/etc/sftpgo/sftpgo.json &
|
||||||
|
```
|
||||||
|
|
||||||
|
### 驗證管理員帳戶
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 檢查 admin 是否存在
|
||||||
|
SELECT id, username, status, last_login FROM admins;
|
||||||
|
|
||||||
|
-- 檢查使用者是否存在 (SFTP/WebDAV 登入)
|
||||||
|
SELECT id, username, status, home_dir FROM users;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API 管理用戶與組
|
||||||
|
|
||||||
|
SFTPGo 提供 RESTful API 用於管理用戶和組,支援自動化運維。
|
||||||
|
|
||||||
|
### API 認證方式
|
||||||
|
|
||||||
|
1. **獲取 Access Token** (使用 Basic Auth):
|
||||||
|
```bash
|
||||||
|
TOKEN=$(curl -s -X GET http://localhost:8080/api/v2/token \
|
||||||
|
-u "admin:Test3200Test3200" | jq -r '.access_token')
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **使用 Token 調用 API**:
|
||||||
|
```bash
|
||||||
|
curl -X GET http://localhost:8080/api/v2/admins \
|
||||||
|
-H "Authorization: Bearer $TOKEN"
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意**: `/api/v2/token` 端點使用 `GET` 方法,而非 `POST`。
|
||||||
|
|
||||||
|
### 列出所有用戶
|
||||||
|
|
||||||
|
```bash
|
||||||
|
TOKEN=$(curl -s -X GET http://localhost:8080/api/v2/token -u "admin:Test3200Test3200" | jq -r '.access_token')
|
||||||
|
curl -s -X GET "http://localhost:8080/api/v2/users?limit=100" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" | jq -r '.[] | "\(.username) status=\(.status) home=\(.home_dir)"'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 創建用戶
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -X POST http://localhost:8080/api/v2/users \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"username": "demo",
|
||||||
|
"password": "demopassword123",
|
||||||
|
"email": "demo@momentry.local",
|
||||||
|
"status": 1,
|
||||||
|
"home_dir": "/Users/accusys/sftpgo_test/demo",
|
||||||
|
"uid": 501,
|
||||||
|
"gid": 20,
|
||||||
|
"permissions": {
|
||||||
|
"/": ["*"]
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
**權限格式注意**: 必須為 map[string][]string 格式,例如:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"/": ["*"],
|
||||||
|
"/upload": ["read", "write"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 修復用戶權限格式
|
||||||
|
|
||||||
|
如果權限存儲為數組 `["*"]` 而非 map,API 會返回錯誤:
|
||||||
|
```
|
||||||
|
unable to deserialize permissions for user "demo": json: cannot unmarshal array into Go value of type map[string][]string
|
||||||
|
```
|
||||||
|
|
||||||
|
修復方法:
|
||||||
|
```bash
|
||||||
|
psql -h 127.0.0.1 -p 5432 -U sftpgo -d sftpgo -c \
|
||||||
|
"UPDATE users SET permissions = '{\" /\":[\"*\"]}'::jsonb WHERE username='demo';"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 創建組
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -X POST http://localhost:8080/api/v2/groups \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"name": "demo",
|
||||||
|
"description": "Demo group for SFTP uploads"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 將用戶加入組
|
||||||
|
|
||||||
|
查看 User Schema 中的 `groups` 欄位為 `GroupMapping` 數組:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"name": "demo",
|
||||||
|
"type": 1 // 1=主要組, 2=次要組, 3=僅成員身份
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
更新用戶組關聯:
|
||||||
|
```bash
|
||||||
|
curl -s -X PUT http://localhost:8080/api/v2/users/demo \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"name": "demo",
|
||||||
|
"type": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"permissions": {
|
||||||
|
"/": ["*"]
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
**重要**: 更新用戶時必須提供完整的必要欄位 (如 `permissions`),否則會驗證失敗。
|
||||||
|
|
||||||
|
### API 端點對照表
|
||||||
|
|
||||||
|
| 功能 | 端點 | 方法 |
|
||||||
|
|------|------|------|
|
||||||
|
| 獲取 Token | `/api/v2/token` | GET (Basic Auth) |
|
||||||
|
| 列出管理員 | `/api/v2/admins` | GET |
|
||||||
|
| 列出用戶 | `/api/v2/users` | GET |
|
||||||
|
| 創建用戶 | `/api/v2/users` | POST |
|
||||||
|
| 獲取用戶 | `/api/v2/users/{username}` | GET |
|
||||||
|
| 更新用戶 | `/api/v2/users/{username}` | PUT |
|
||||||
|
| 刪除用戶 | `/api/v2/users/{username}` | DELETE |
|
||||||
|
| 列出組 | `/api/v2/groups` | GET |
|
||||||
|
| 創建組 | `/api/v2/groups` | POST |
|
||||||
|
| 獲取組 | `/api/v2/groups/{name}` | GET |
|
||||||
|
| 更新組 | `/api/v2/groups/{name}` | PUT |
|
||||||
|
| 刪除組 | `/api/v2/groups/{name}` | DELETE |
|
||||||
|
|
||||||
|
完整的 API 文件請參考: http://localhost:8080/openapi/
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Hook 配置
|
||||||
|
|
||||||
|
SFTPGo Hook 用於在檔案操作時觸發外部腳本,實現自動化處理。
|
||||||
|
|
||||||
|
### 配置文件位置
|
||||||
|
|
||||||
|
`/Users/accusys/momentry/etc/sftpgo/sftpgo.json`
|
||||||
|
|
||||||
|
### 通用 Actions 配置
|
||||||
|
|
||||||
|
在 `common.actions` 中配置:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"common": {
|
||||||
|
"actions": {
|
||||||
|
"execute_on": ["add"], // 觸發時機: 檔案上傳完成後
|
||||||
|
"execute_sync": [], // 同步執行 ([])
|
||||||
|
"hook": "/path/to/hook.sh" // Hook 腳本路徑
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hook 腳本範例
|
||||||
|
|
||||||
|
位置: `/Users/accusys/sftpgo_test/register_hook.sh`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# SFTPGo Hook - 檔案上傳後自動註冊到 Momentry Core
|
||||||
|
|
||||||
|
# SFTPGo 傳遞的環境變數
|
||||||
|
# ${SFTPGO_ACTION} - 操作類型 (add, delete, etc.)
|
||||||
|
# ${SFTPGO_USERNAME} - 用戶名
|
||||||
|
# ${SFTPGO_FILEPATH} - 檔案路徑 (相對首頁目錄)
|
||||||
|
# ${SFTPGO_FILESIZE} - 檔案大小
|
||||||
|
# ${SFTPGO_TIMESTAMP} - 操作時間戳
|
||||||
|
|
||||||
|
# 絕對路徑轉換
|
||||||
|
UPLOAD_DIR="/Users/accusys/sftpgo_test/demo"
|
||||||
|
RELATIVE_PATH="${SFTPGO_FILEPATH#./}"
|
||||||
|
ABS_PATH="${UPLOAD_DIR}/${RELATIVE_PATH}"
|
||||||
|
|
||||||
|
# 日誌記錄
|
||||||
|
LOG_FILE="/Users/accusys/sftpgo_test/hook.log"
|
||||||
|
echo "$(date '+%Y-%m-%d %H:%M:%S') - User: ${SFTPGO_USERNAME}, File: ${ABS_PATH}, Size: ${SFTPGO_FILESIZE}" >> "$LOG_FILE"
|
||||||
|
|
||||||
|
# 調用 Momentry Core 註冊 API
|
||||||
|
API_URL="http://localhost:8080/api/v1/register"
|
||||||
|
RESPONSE=$(curl -s -X POST "$API_URL" \
|
||||||
|
-H "Content-Type: multipart/form-data" \
|
||||||
|
-F "file=@${ABS_PATH}" \
|
||||||
|
--connect-timeout 10 --max-time 300)
|
||||||
|
|
||||||
|
echo "$(date '+%Y-%m-%d %H:%M:%S') - API Response: ${RESPONSE}" >> "$LOG_FILE"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hook 執行權限
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x /Users/accusys/sftpgo_test/register_hook.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hook 執行時間點
|
||||||
|
|
||||||
|
`execute_on` 支援以下值:
|
||||||
|
| 值 | 觸發時機 |
|
||||||
|
|-----|----------|
|
||||||
|
| `add` | 檔案上傳完成後 |
|
||||||
|
| `delete` | 檔案刪除後 |
|
||||||
|
| `upload_complete` | 上傳完成 (multipart) |
|
||||||
|
| `download` | 檔案下載時 |
|
||||||
|
| `rename` | 檔案重命名後 |
|
||||||
|
| `mkdir` | 建立目錄後 |
|
||||||
|
| `rmdir` | 刪除目錄後 |
|
||||||
|
|
||||||
|
### Hook 執行模式
|
||||||
|
|
||||||
|
- **同步執行** (`execute_sync`): Hook 腳本在 SFTPGo 操作返回客戶端前執行
|
||||||
|
- **非同步執行** (預設): Hook 腳本在后台執行,不阻塞客戶端
|
||||||
|
|
||||||
|
### 重新載入配置
|
||||||
|
|
||||||
|
修改 Hook 配置後,需重新載入 SFTPGo:
|
||||||
|
```bash
|
||||||
|
# 如果是 plist 啟動的服務
|
||||||
|
sudo launchctl kickstart -k system/local.sftpgo
|
||||||
|
|
||||||
|
# 或手動重啟
|
||||||
|
pkill -f sftpgo
|
||||||
|
sftpgo serve --config-file /Users/accusys/momentry/etc/sftpgo/sftpgo.json &
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hook 故障排除
|
||||||
|
|
||||||
|
1. **檢查 Hook 日誌**:
|
||||||
|
```bash
|
||||||
|
tail -f /Users/accusys/sftpgo_test/hook.log
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **手動測試 Hook 腳本**:
|
||||||
|
```bash
|
||||||
|
export SFTPGO_USERNAME=demo
|
||||||
|
export SFTPGO_FILEPATH="./test.txt"
|
||||||
|
export SFTPGO_FILESIZE=1024
|
||||||
|
export SFTPGO_ACTION=add
|
||||||
|
/Users/accusys/sftpgo_test/register_hook.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **SFTPGo 錯誤日誌**:
|
||||||
|
```bash
|
||||||
|
tail -20 /Users/accusys/momentry/log/sftpgo.error.log
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
| 資料表 | 用途 | 登入 URL |
|
||||||
|
|--------|------|----------|
|
||||||
|
| `admins` | 管理員用戶 | `/web/admin/login` |
|
||||||
|
| `users` | 檔案傳輸用戶 | `/web/client/login` |
|
||||||
|
|
||||||
|
### Demo 用戶與組設置
|
||||||
|
|
||||||
|
#### 建立 Demo 目錄
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p /Users/accusys/sftpgo_test/demo
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 創建 Demo 組
|
||||||
|
|
||||||
|
```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": "demo",
|
||||||
|
"description": "Demo group for SFTP uploads"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 創建 Demo 用戶並加入組
|
||||||
|
|
||||||
|
如果 `demo` 用戶不存在:
|
||||||
|
```bash
|
||||||
|
curl -s -X POST http://localhost:8080/api/v2/users \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"username": "demo",
|
||||||
|
"password": "demopassword123",
|
||||||
|
"email": "demo@momentry.local",
|
||||||
|
"status": 1,
|
||||||
|
"home_dir": "/Users/accusys/sftpgo_test/demo",
|
||||||
|
"uid": 501,
|
||||||
|
"gid": 20,
|
||||||
|
"permissions": {
|
||||||
|
"/": ["*"]
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
如果用戶已存在但權限格式錯誤,需修復:
|
||||||
|
```bash
|
||||||
|
psql -h 127.0.0.1 -p 5432 -U sftpgo -d sftpgo -c \
|
||||||
|
"UPDATE users SET permissions = '{\" /\":[\"*\"]}'::jsonb WHERE username='demo';"
|
||||||
|
```
|
||||||
|
|
||||||
|
將用戶加入 `demo` 組 (主要組):
|
||||||
|
```bash
|
||||||
|
curl -s -X PUT http://localhost:8080/api/v2/users/demo \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"name": "demo",
|
||||||
|
"type": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"permissions": {
|
||||||
|
"/": ["*"]
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 驗證設置
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 檢查用戶
|
||||||
|
curl -s -X GET "http://localhost:8080/api/v2/users/demo" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" | jq .
|
||||||
|
|
||||||
|
# 檢查組
|
||||||
|
curl -s -X GET "http://localhost:8080/api/v2/groups/demo" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" | jq .
|
||||||
|
```
|
||||||
|
|
||||||
|
#### SFTP 連接測試
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 使用 SFTP 連接
|
||||||
|
sshpass -p "demopassword123" sftp -P 2022 demo@localhost
|
||||||
|
|
||||||
|
# 上傳測試文件
|
||||||
|
sftp> put test.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 預期行為
|
||||||
|
|
||||||
|
1. 檔案上傳完成後,SFTPGo 觸發 Hook
|
||||||
|
2. Hook 腳本執行並記錄到 `/Users/accusys/sftpgo_test/hook.log`
|
||||||
|
3. Hook 調用 Momentry Core API 完成註冊
|
||||||
|
4. 查看注册狀態: `tail -f /Users/accusys/sftpgo_test/hook.log`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 常見問題
|
||||||
|
|
||||||
|
#### "無效的憑證" 即使密碼正確
|
||||||
|
|
||||||
|
- PostgreSQL 中的密碼哈希可能不符合 SFTPGo 預期格式
|
||||||
|
- 使用 Web 面板的 **Forgot password** 功能而非直接 SQL 更新
|
||||||
|
|
||||||
|
#### CSRF Token 錯誤
|
||||||
|
|
||||||
|
- 清除瀏覽器中 `localhost:8080` 的 cookies
|
||||||
|
- 使用無痕/私密瀏覽視窗
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 端到端測試流程
|
||||||
|
|
||||||
|
### 1. 準備工作
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 確認 SFTPGo 運行中
|
||||||
|
curl http://localhost:8080/healthz
|
||||||
|
|
||||||
|
# 確認 Momentry Core API 運行中
|
||||||
|
curl http://localhost:8080/api/v1/health
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. SFTP 上傳測試
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 連接到 SFTPGo
|
||||||
|
sshpass -p "demopassword123" sftp -P 2022 demo@localhost
|
||||||
|
|
||||||
|
# 上傳測試文件
|
||||||
|
sftp> put /path/to/video.mp4
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 驗證 Hook 執行
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 查看 Hook 日誌
|
||||||
|
tail -f /Users/accusys/sftpgo_test/hook.log
|
||||||
|
|
||||||
|
# 預期看到類似輸出:
|
||||||
|
# 2026-03-22 01:30:00 - User: demo, File: /Users/accusys/sftpgo_test/demo/video.mp4, Size: 10485760
|
||||||
|
# 2026-03-22 01:30:05 - API Response: {"uuid":"abc123","status":"registered"}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 檢查 Momentry Core 註冊狀態
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 使用 Momentry CLI 檢查
|
||||||
|
/Users/accusys/momentry/target/debug/momentry list --api-key <your-api-key>
|
||||||
|
|
||||||
|
# 或直接查詢資料庫
|
||||||
|
psql -U accusys -d momentry -c "SELECT uuid, filename, status FROM videos WHERE filename='video.mp4';"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 檢查處理狀態
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 查看處理佇列
|
||||||
|
curl http://localhost:8080/api/v1/queue
|
||||||
|
|
||||||
|
# 查看 searchable 狀態
|
||||||
|
curl "http://localhost:8080/api/v1/searchable?filename=video.mp4&user=demo"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. 故障排除檢查清單
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. SFTPGo 狀態
|
||||||
|
ps aux | grep sftpgo
|
||||||
|
curl http://localhost:8080/healthz
|
||||||
|
|
||||||
|
# 2. Hook 腳本權限
|
||||||
|
ls -l /Users/accusys/sftpgo_test/register_hook.sh
|
||||||
|
|
||||||
|
# 3. Hook 日誌
|
||||||
|
tail -20 /Users/accusys/sftpgo_test/hook.log
|
||||||
|
|
||||||
|
# 4. Momentry Core 日誌
|
||||||
|
tail -20 /Users/accusys/momentry/log/momentry.log
|
||||||
|
|
||||||
|
# 5. SFTPGo 日誌
|
||||||
|
tail -20 /Users/accusys/momentry/log/sftpgo.log
|
||||||
|
|
||||||
|
# 6. PostgreSQL 連接
|
||||||
|
psql -U sftpgo -d sftpgo -c "SELECT 1;"
|
||||||
|
|
||||||
|
# 7. 端口可用性
|
||||||
|
lsof -i :8080 # SFTPGo HTTP
|
||||||
|
lsof -i :2022 # SFTP
|
||||||
|
lsof -i :5678 # Momentry Core (Primary)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 故障排除
|
## 故障排除
|
||||||
|
|
||||||
### SFTPGo 無法啟動
|
### SFTPGo 無法啟動
|
||||||
@@ -327,7 +1024,11 @@ sudo launchctl load /Library/LaunchDaemons/com.momentry.sftpgo.plist
|
|||||||
| 錯誤日誌 | `/Users/accusys/momentry/log/sftpgo.error.log` | 錯誤日誌 |
|
| 錯誤日誌 | `/Users/accusys/momentry/log/sftpgo.error.log` | 錯誤日誌 |
|
||||||
| 工作目錄 | `/Users/accusys/workspace/sftpgo/` | 上傳檔案目錄 |
|
| 工作目錄 | `/Users/accusys/workspace/sftpgo/` | 上傳檔案目錄 |
|
||||||
| plist | `/Library/LaunchDaemons/com.momentry.sftpgo.plist` | 開機啟動 |
|
| plist | `/Library/LaunchDaemons/com.momentry.sftpgo.plist` | 開機啟動 |
|
||||||
| 備份 | `/Users/accusys/momentry/var/sftpgo_backup/sftpgo.json` | 配置備份 |
|
| 備份 | `/Users/accusys/momentry/backup/daily/sftpgo/` | 配置與數據庫備份 |
|
||||||
|
| 備份配置 | `/Users/accusys/momentry/var/sftpgo_backup/` | 實時配置備份 (含 sftpgo.json, sftpgo.db) |
|
||||||
|
| Hook 腳本 | `/Users/accusys/sftpgo_test/register_hook.sh` | 自動註冊腳本 |
|
||||||
|
| Hook 日誌 | `/Users/accusys/sftpgo_test/hook.log` | Hook 執行記錄 |
|
||||||
|
| Demo 目錄 | `/Users/accusys/sftpgo_test/demo` | Demo 用戶首頁目錄 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
332
docs/INSTALL_WORDPRESS.md
Normal file
332
docs/INSTALL_WORDPRESS.md
Normal file
@@ -0,0 +1,332 @@
|
|||||||
|
# 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 函數定義 |
|
||||||
@@ -1,5 +1,21 @@
|
|||||||
# Momentry JSON 輸出檔案規範
|
# Momentry JSON 輸出檔案規範
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 建立者 | Warren |
|
||||||
|
| 建立時間 | 2026-03-16 |
|
||||||
|
| 文件版本 | V1.0 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版本歷史
|
||||||
|
|
||||||
|
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||||
|
|------|------|------|--------|-----------|
|
||||||
|
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
本文檔定義 Momentry Core 系統中所有 JSON 輸出檔案的結構、命名規範與儲存位置。
|
本文檔定義 Momentry Core 系統中所有 JSON 輸出檔案的結構、命名規範與儲存位置。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
782
docs/MAC_INSTALLATION_PLAN.md
Normal file
782
docs/MAC_INSTALLATION_PLAN.md
Normal file
@@ -0,0 +1,782 @@
|
|||||||
|
# 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
|
||||||
322
docs/MOMENTRY_RAG_PRESENTATION.md
Normal file
322
docs/MOMENTRY_RAG_PRESENTATION.md
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
# Momentry Core 影片 RAG 系統說明稿
|
||||||
|
|
||||||
|
## 系統架構
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ 使用者 │
|
||||||
|
│ (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"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**回應:**
|
||||||
|
```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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 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,
|
||||||
|
"media_url": "https://wp.momentry.ddns.net/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 實作範例
|
||||||
|
|
||||||
|
### 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
|
||||||
|
```
|
||||||
|
|
||||||
|
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) + "%",
|
||||||
|
url: hit.media_url + "#t=" + hit.start + "," + hit.end
|
||||||
|
}));
|
||||||
|
|
||||||
|
return { json: { results } };
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 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" \
|
||||||
|
-d '{"query": "charade", "limit": 3}'
|
||||||
|
|
||||||
|
# n8n 格式
|
||||||
|
curl -X POST http://localhost:3002/api/v1/n8n/search \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"query": "charade", "limit": 3}'
|
||||||
|
|
||||||
|
# 影片列表
|
||||||
|
curl http://localhost:3002/api/v1/videos
|
||||||
|
|
||||||
|
# 特定影片區塊
|
||||||
|
curl 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
|
||||||
|
```
|
||||||
127
docs/Momentry_Core_API.postman_collection.json
Normal file
127
docs/Momentry_Core_API.postman_collection.json
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
106
docs/N8N_API_FIX_SUMMARY.md
Normal file
106
docs/N8N_API_FIX_SUMMARY.md
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
# 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/
|
||||||
220
docs/N8N_DEMO.md
Normal file
220
docs/N8N_DEMO.md
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
# n8n 整合範例
|
||||||
|
|
||||||
|
## 基本設定
|
||||||
|
|
||||||
|
### API 端點
|
||||||
|
- **Base URL:** `http://localhost:3002/api/v1`
|
||||||
|
- **Method:** `POST`
|
||||||
|
- **Content-Type:** `application/json`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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` 端點(已包含 media_url)
|
||||||
|
|
||||||
|
### HTTP Request
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"url": "http://localhost:3002/api/v1/n8n/search",
|
||||||
|
"method": "POST",
|
||||||
|
"body": {
|
||||||
|
"query": "={{ $json.searchTerm }}",
|
||||||
|
"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,
|
||||||
|
"media_url": "https://wp.momentry.ddns.net/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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" \
|
||||||
|
-d '{"query": "charade", "limit": 3}'
|
||||||
|
|
||||||
|
# n8n 格式
|
||||||
|
curl -X POST http://localhost:3002/api/v1/n8n/search \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"query": "charade", "limit": 3}'
|
||||||
|
|
||||||
|
# 取得影片列表
|
||||||
|
curl http://localhost:3002/api/v1/videos
|
||||||
|
|
||||||
|
# 取得特定影片的區塊
|
||||||
|
curl http://localhost:3002/api/v1/videos/a1b10138a6bbb0cd/chunks
|
||||||
|
```
|
||||||
341
docs/N8N_DEMO_EXECUTION_LOG.md
Normal file
341
docs/N8N_DEMO_EXECUTION_LOG.md
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
# n8n Video RAG Demo - API 執行記錄
|
||||||
|
|
||||||
|
> 建立時間: 2026-03-22
|
||||||
|
> 目標: 完整執行 n8n Video RAG Workflow 並記錄所有 API 呼叫
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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 搜尋 (含 media_url)
|
||||||
|
|
||||||
|
**API 呼叫:**
|
||||||
|
```bash
|
||||||
|
curl -X POST "http://localhost:3002/api/v1/n8n/search" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-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) |
|
||||||
677
docs/N8N_DEMO_WORKFLOW.md
Normal file
677
docs/N8N_DEMO_WORKFLOW.md
Normal file
@@ -0,0 +1,677 @@
|
|||||||
|
# n8n Video RAG Workflow - Node 設計
|
||||||
|
|
||||||
|
> 建立時間: 2026-03-22
|
||||||
|
> 目標: 讓 marcom 團隊能夠複製、貼上、修改使用的完整操作指南
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 完整 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 Media URL (含 media_url) │ │
|
||||||
|
│ │ ↓ │ │
|
||||||
|
│ │ ⑭ 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": ""}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意**: 只有 asr、asrx、story 三個模組
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 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,
|
||||||
|
"media_url": "https://wp.momentry.ddns.net/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 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` |
|
||||||
|
| 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` |
|
||||||
|
|
||||||
|
### 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 模組 |
|
||||||
245
docs/N8N_HTTP_REQUEST_GUIDE.md
Normal file
245
docs/N8N_HTTP_REQUEST_GUIDE.md
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
# n8n HTTP Request Node 設定指南
|
||||||
|
|
||||||
|
> **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 }}"
|
||||||
|
│ }
|
||||||
|
└── Options: (empty)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方法 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
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方法 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
|
||||||
|
curl -X POST https://api.momentry.ddns.net/api/v1/n8n/search \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-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) - 端點完整說明
|
||||||
562
docs/N8N_INTEGRATION_GUIDE.md
Normal file
562
docs/N8N_INTEGRATION_GUIDE.md
Normal file
@@ -0,0 +1,562 @@
|
|||||||
|
# Momentry n8n 整合使用手冊
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 版本 | V1.1 |
|
||||||
|
| 日期 | 2026-03-23 |
|
||||||
|
| 目標讀者 | 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 已建立"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 範例 3:API 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` | 整合測試文件 |
|
||||||
211
docs/N8N_MCP_SETUP.md
Normal file
211
docs/N8N_MCP_SETUP.md
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
# OpenCode n8n MCP 整合設定
|
||||||
|
|
||||||
|
> 建立時間: 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 範例
|
||||||
178
docs/N8N_MCP_TEST_REPORT.md
Normal file
178
docs/N8N_MCP_TEST_REPORT.md
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
# n8n MCP 整合測試報告
|
||||||
|
|
||||||
|
## 測試日期
|
||||||
|
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 整合工作流程
|
||||||
|
- 設定自動化監控與告警
|
||||||
|
- 建立工作流程模板庫
|
||||||
152
docs/N8N_SETUP_COMPLETE.md
Normal file
152
docs/N8N_SETUP_COMPLETE.md
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
# 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 了!
|
||||||
279
docs/N8N_VIDEO_SEARCH_SUCCESS.md
Normal file
279
docs/N8N_VIDEO_SEARCH_SUCCESS.md
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
# n8n Video Search 工作流程 - 成功設定指南
|
||||||
|
|
||||||
|
> 建立時間: 2026-03-22
|
||||||
|
> 適用版本: n8n 2.3.5
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 成功案例
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| **工作流程名稱** | 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": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 關鍵設定說明
|
||||||
|
|
||||||
|
| 設定項目 | 正確值 | 錯誤值 | 說明 |
|
||||||
|
|---------|--------|--------|------|
|
||||||
|
| **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": {}
|
||||||
|
},
|
||||||
|
"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,
|
||||||
|
"media_url": "https://wp.momentry.ddns.net/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" \
|
||||||
|
-d '{"query":"charade","limit":3}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 驗證服務狀態
|
||||||
|
```bash
|
||||||
|
# 檢查 Momentry Core
|
||||||
|
curl 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`)
|
||||||
141
docs/N8N_VIEW_OUTPUT_GUIDE.md
Normal file
141
docs/N8N_VIEW_OUTPUT_GUIDE.md
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
# 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. 有沒有紅色錯誤訊息?
|
||||||
169
docs/N8N_WORKFLOW_VIDEO_RAG_MCP.md
Normal file
169
docs/N8N_WORKFLOW_VIDEO_RAG_MCP.md
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
# 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`
|
||||||
@@ -1,5 +1,21 @@
|
|||||||
# Node.js 開發指南
|
# Node.js 開發指南
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 建立者 | Warren |
|
||||||
|
| 建立時間 | 2026-03-16 |
|
||||||
|
| 文件版本 | V1.0 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版本歷史
|
||||||
|
|
||||||
|
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||||
|
|------|------|------|--------|-----------|
|
||||||
|
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
本文檔說明 Momentry 專案中 Node.js 環境的配置、管理與監控。
|
本文檔說明 Momentry 專案中 Node.js 環境的配置、管理與監控。
|
||||||
@@ -175,7 +191,8 @@ psql -U accusys -h localhost -d momentry -c "SELECT * FROM node_version_baseline
|
|||||||
|
|
||||||
| 應用 | Node.js 版本 | 執行路徑 | Port | 狀態 | 說明 |
|
| 應用 | Node.js 版本 | 執行路徑 | Port | 狀態 | 說明 |
|
||||||
|------|-------------|----------|------|------|------|
|
|------|-------------|----------|------|------|------|
|
||||||
| n8n | 22.22.1 | /opt/homebrew/opt/node@22/bin/node | 5678/5690 | ✅ 執行中 | 工作流自動化平台 |
|
| n8n | 22.22.1 | /opt/homebrew/opt/node@22/bin/node | 5678/5679 | ✅ 執行中 | 工作流自動化平台 |
|
||||||
|
| markdownlint-cli | 25.x | /opt/homebrew/bin/npm | - | ✅ 已安裝 | Markdown lint 工具 |
|
||||||
| - | - | - | - | - | 新增應用請填入此表 |
|
| - | - | - | - | - | 新增應用請填入此表 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
430
docs/OPENCODE_GUIDE.md
Normal file
430
docs/OPENCODE_GUIDE.md
Normal file
@@ -0,0 +1,430 @@
|
|||||||
|
# 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 | 遠端 URL(remote 必要) |
|
||||||
|
|
||||||
|
**可選欄位**:
|
||||||
|
| 欄位 | 類型 | 說明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `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 安裝指南
|
||||||
535
docs/OPENCODE_MCP_INSTALL.md
Normal file
535
docs/OPENCODE_MCP_INSTALL.md
Normal file
@@ -0,0 +1,535 @@
|
|||||||
|
# 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) - 所有服務狀態總覽
|
||||||
813
docs/PENDING_ISSUES.md
Normal file
813
docs/PENDING_ISSUES.md
Normal file
@@ -0,0 +1,813 @@
|
|||||||
|
# 待解決問題追蹤
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 建立者 | 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 用戶設定
|
||||||
271
docs/PROCESSING_PIPELINE.md
Normal file
271
docs/PROCESSING_PIPELINE.md
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
# Video Processing Pipeline - 處理流程
|
||||||
|
|
||||||
|
> 建立時間: 2026-03-22
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 處理流程架構
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ 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] │ │
|
||||||
|
│ │ ↓ │ │
|
||||||
|
│ │ 返回結果含 media_url │ │
|
||||||
|
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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
|
||||||
|
cargo run --bin momentry -- vectorize <uuid>
|
||||||
|
|
||||||
|
# 指定模型
|
||||||
|
cargo run --bin momentry -- vectorize <uuid> --model sentence-transformers/all-MiniLM-L6-v2
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 處理模式選項
|
||||||
|
|
||||||
|
### --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 | 人體姿態 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 向量化模型選擇
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 預設模型
|
||||||
|
--model sentence-transformers/all-MiniLM-L6-v2
|
||||||
|
|
||||||
|
# 高精度模型
|
||||||
|
--model sentence-transformers/all-mpnet-base-v2
|
||||||
|
|
||||||
|
# 多語言模型
|
||||||
|
--model sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
|
||||||
|
|
||||||
|
# 中文模型
|
||||||
|
--model sentence-transformers/paraphrase-multilingual-mpnet-base-v2
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 資料庫儲存
|
||||||
|
|
||||||
|
### 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. **模型管理** - 新增、選擇、預覽模型
|
||||||
|
|
||||||
@@ -1,5 +1,22 @@
|
|||||||
# Python 開發規範
|
# 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 程式碼的開發標準與最佳實踐。
|
本文檔定義 Momentry 專案中 Python 程式碼的開發標準與最佳實踐。
|
||||||
@@ -229,6 +246,63 @@ Pillow>=10.0.0
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 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 排序
|
### Import 排序
|
||||||
|
|||||||
@@ -1,5 +1,22 @@
|
|||||||
# Rust 開發規範 - Momentry Core
|
# 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 開發標準,確保程式碼品質與一致性。
|
本規範定義 Momentry Core 專案的 Rust 開發標準,確保程式碼品質與一致性。
|
||||||
|
|
||||||
## 1. 專案結構
|
## 1. 專案結構
|
||||||
@@ -27,6 +44,7 @@ src/
|
|||||||
│ │ └── qdrant_db.rs
|
│ │ └── qdrant_db.rs
|
||||||
│ ├── processor/ # 影片處理器
|
│ ├── processor/ # 影片處理器
|
||||||
│ │ ├── mod.rs
|
│ │ ├── mod.rs
|
||||||
|
│ │ ├── executor.rs # Python 腳本統一執行器 (含超時控制)
|
||||||
│ │ ├── asr.rs # 語音識別
|
│ │ ├── asr.rs # 語音識別
|
||||||
│ │ ├── asrx.rs # 說話者分離
|
│ │ ├── asrx.rs # 說話者分離
|
||||||
│ │ ├── ocr.rs # 文字辨識
|
│ │ ├── ocr.rs # 文字辨識
|
||||||
@@ -273,6 +291,47 @@ for line in stderr.lines() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 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 混用規範
|
## 6. Python 與 Node.js 混用規範
|
||||||
@@ -379,7 +438,7 @@ let output = Command::new(venv_python)
|
|||||||
| **獨立路徑** | Python 用 venv 路徑,Node.js 用 node@22 路徑 |
|
| **獨立路徑** | Python 用 venv 路徑,Node.js 用 node@22 路徑 |
|
||||||
| **獨立環境** | n8n 服務使用 launchd plist,不與 Rust 共享環境 |
|
| **獨立環境** | n8n 服務使用 launchd plist,不與 Rust 共享環境 |
|
||||||
| **明確版本** | 所有腳本明確指定直譯器路徑 |
|
| **明確版本** | 所有腳本明確指定直譯器路徑 |
|
||||||
| **PORT 分配** | n8n: 5678/5690, API: 另行分配 |
|
| **PORT 分配** | n8n: 5678/5679, API: 另行分配 |
|
||||||
|
|
||||||
#### 6.4.2 環境變數隔離
|
#### 6.4.2 環境變數隔離
|
||||||
|
|
||||||
|
|||||||
121
docs/SEARCH_PROMPTS.md
Normal file
121
docs/SEARCH_PROMPTS.md
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
# 搜尋範例 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 搜尋
|
||||||
@@ -1,4 +1,22 @@
|
|||||||
# Momentry 服務添加規範 v2.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 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 一、概述
|
## 一、概述
|
||||||
|
|
||||||
@@ -265,24 +283,44 @@ EOF
|
|||||||
### 8.1 基本操作
|
### 8.1 基本操作
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 啟動服務
|
# 啟動服務 (使用 launchctl bootstrap)
|
||||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.{service}.plist
|
sudo launchctl bootstrap system /Library/LaunchDaemons/com.momentry.{service}.plist
|
||||||
|
|
||||||
# 停止服務
|
# 停止服務 (使用 launchctl bootout)
|
||||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.{service}.plist
|
sudo launchctl bootout system/com.momentry.{service}.plist
|
||||||
|
|
||||||
# 重啟服務
|
# 重新載入服務
|
||||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.{service}.plist
|
sudo launchctl bootout system/com.momentry.{service}.plist
|
||||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.{service}.plist
|
sudo launchctl bootstrap system /Library/LaunchDaemons/com.momentry.{service}.plist
|
||||||
|
|
||||||
# 查看服務狀態
|
# 查看服務狀態
|
||||||
launchctl list | grep momentry
|
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}.log
|
||||||
tail -f /Users/accusys/momentry/log/{service}.error.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 故障排除
|
### 8.2 故障排除
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -657,3 +695,4 @@ EOF
|
|||||||
| 1.0 | 2026-03-15 | 初始版本 |
|
| 1.0 | 2026-03-15 | 初始版本 |
|
||||||
| 2.0 | 2026-03-15 | 統一 Plist 位置、移除 root/用戶區分、加入運行方式分類 |
|
| 2.0 | 2026-03-15 | 統一 Plist 位置、移除 root/用戶區分、加入運行方式分類 |
|
||||||
| 2.1 | 2026-03-15 | 新增服務備份作業、服務完整刪除作業 |
|
| 2.1 | 2026-03-15 | 新增服務備份作業、服務完整刪除作業 |
|
||||||
|
| 2.1 | 2026-03-24 | 更新 launchctl 命令,使用 `bootstrap`/`bootout` 替代 `load`/`unload` | |
|
||||||
|
|||||||
504
docs/SFTPGO_DEMO_USER.md
Normal file
504
docs/SFTPGO_DEMO_USER.md
Normal file
@@ -0,0 +1,504 @@
|
|||||||
|
# SFTPGo Demo 用戶指南
|
||||||
|
|
||||||
|
## Web 管理介面
|
||||||
|
|
||||||
|
**URL**: https://sftpgo.momentry.ddns.net
|
||||||
|
|
||||||
|
### 登入方式
|
||||||
|
|
||||||
|
| 角色 | 用戶名 | 密碼 |
|
||||||
|
|------|--------|------|
|
||||||
|
| **Demo 用戶** | `demo` | `demopassword123` |
|
||||||
|
|
||||||
|
### 可用功能
|
||||||
|
|
||||||
|
- 瀏覽個人目錄結構
|
||||||
|
- 上傳、下載檔案
|
||||||
|
- 查看上傳記錄
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 快速連線資訊
|
||||||
|
|
||||||
|
| 項目 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| **主機** | `momentry.ddns.net` |
|
||||||
|
| **SFTP 連接埠** | `2022` |
|
||||||
|
| **用戶名** | `demo` |
|
||||||
|
| **密碼** | `demopassword123` |
|
||||||
|
| **主目錄** | `/demo` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 連線方式
|
||||||
|
|
||||||
|
### 1. 命令列 SFTP
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 使用密碼連線
|
||||||
|
sshpass -p "demopassword123" sftp -P 2022 demo@momentry.ddns.net
|
||||||
|
|
||||||
|
# 使用金鑰連線 (需先設定)
|
||||||
|
sftp -P 2022 -i ~/.ssh/id_rsa demo@momentry.ddns.net
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. FileZilla
|
||||||
|
|
||||||
|
1. **主機**: `sftp://momentry.ddns.net`
|
||||||
|
2. **連接埠**: `2022`
|
||||||
|
3. **協定**: `SFTP`
|
||||||
|
4. **登入類型**: `一般`
|
||||||
|
5. **用戶名**: `demo`
|
||||||
|
6. **密碼**: `demopassword123`
|
||||||
|
|
||||||
|
### 3. Cyberduck (macOS)
|
||||||
|
|
||||||
|
1. 選擇 **連線 > 新連線**
|
||||||
|
2. 協定選擇 **SFTP (SSH File Transfer Protocol)**
|
||||||
|
3. 伺服器: `momentry.ddns.net`
|
||||||
|
4. 連接埠: `2022`
|
||||||
|
5. 使用者名稱: `demo`
|
||||||
|
6. 密碼: `demopassword123`
|
||||||
|
|
||||||
|
### 4. curl 上傳
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -u demo:demopassword123 \
|
||||||
|
-T /path/to/video.mp4 \
|
||||||
|
sftp://momentry.ddns.net:2022/demo/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SFTP 基本操作
|
||||||
|
|
||||||
|
### 連線後常用指令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 進入互動式模式
|
||||||
|
sftp demo@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@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@momentry.ddns.net <<EOF
|
||||||
|
mput /path/to/videos/*.mp4
|
||||||
|
bye
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 自動上傳腳本
|
||||||
|
|
||||||
|
### Bash 腳本
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# upload.sh - 上傳視頻到 Momentry
|
||||||
|
|
||||||
|
HOST="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 = "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@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` 目錄
|
||||||
|
- **監控**: 所有上傳都有日誌記錄
|
||||||
|
- **生產環境**: 正式環境應使用更強的密碼和金鑰認證
|
||||||
1185
docs/TEST_AND_BENCHMARK_PLAN.md
Normal file
1185
docs/TEST_AND_BENCHMARK_PLAN.md
Normal file
File diff suppressed because it is too large
Load Diff
425
docs/USER_MANAGEMENT_PLAN.md
Normal file
425
docs/USER_MANAGEMENT_PLAN.md
Normal file
@@ -0,0 +1,425 @@
|
|||||||
|
# 統一會員系統 + 影片歸屬追蹤實作計畫
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 建立者 | 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 |
|
||||||
469
docs/USER_MANUAL.md
Normal file
469
docs/USER_MANUAL.md
Normal file
@@ -0,0 +1,469 @@
|
|||||||
|
# Momentry 使用手冊
|
||||||
|
|
||||||
|
| 項目 | 內容 |
|
||||||
|
|------|------|
|
||||||
|
| 版本 | V1.0 |
|
||||||
|
| 日期 | 2026-03-21 |
|
||||||
|
| 目標讀者 | 系統管理員、開發者 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 目錄
|
||||||
|
|
||||||
|
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` | 版本記錄 |
|
||||||
245
docs/VERSION_MANAGEMENT.md
Normal file
245
docs/VERSION_MANAGEMENT.md
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
# Momentry Core 版本管理規範
|
||||||
|
|
||||||
|
## 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
|
||||||
|
```
|
||||||
1347
docs/VIDEO_PROCESSING_SPEC.md
Normal file
1347
docs/VIDEO_PROCESSING_SPEC.md
Normal file
File diff suppressed because it is too large
Load Diff
102
docs/YOLO_RESUME_INTEGRATION.md
Normal file
102
docs/YOLO_RESUME_INTEGRATION.md
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
# 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 參數正確傳遞
|
||||||
110
docs/n8n_workflow_simple.json
Normal file
110
docs/n8n_workflow_simple.json
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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 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 videoUrl: hit.media_url,\n videoLink: hit.media_url + '#t=' + hit.start + ',' + hit.end\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"
|
||||||
|
}
|
||||||
89
docs/n8n_workflow_simple_test.json
Normal file
89
docs/n8n_workflow_simple_test.json
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
94
docs/n8n_workflow_video_rag_mcp.json
Normal file
94
docs/n8n_workflow_video_rag_mcp.json
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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 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 mediaUrl: hit.media_url || '',\n deepLink: hit.media_url ? `${hit.media_url}#t=${hit.start_time || hit.start},${hit.end_time || hit.end}` : ''\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
|
||||||
|
}
|
||||||
125
docs/n8n_workflow_video_search.json
Normal file
125
docs/n8n_workflow_video_search.json
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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 number: index + 1,\n text: hit.text,\n start: hit.start,\n end: hit.end,\n score: Math.round(hit.score * 100) + '%',\n url: hit.media_url + '#t=' + hit.start + ',' + hit.end,\n video_title: hit.title\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"
|
||||||
|
}
|
||||||
100
docs/test_all.sh
Executable file
100
docs/test_all.sh
Executable file
@@ -0,0 +1,100 @@
|
|||||||
|
#!/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"
|
||||||
33
docs/test_momentry_api.sh
Executable file
33
docs/test_momentry_api.sh
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/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 "=========================================="
|
||||||
104
docs/test_workflow.sh
Executable file
104
docs/test_workflow.sh
Executable file
@@ -0,0 +1,104 @@
|
|||||||
|
#!/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 "=========================================="
|
||||||
1
id_ecdsa.pub
Normal file
1
id_ecdsa.pub
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKEMn20j9Eql4KMUBppJ58JpWr0F0HH856KTvpIGcWXYbauJA5eopQaffnziWNIE/HPN5lXaHZ8Q07JChfjhwUk=
|
||||||
1
id_ed25519.pub
Normal file
1
id_ed25519.pub
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN7goG9Dsorv8if/23gxjb7PkPM9rTbl5A+3sX8avcqu
|
||||||
1
id_rsa.pub
Normal file
1
id_rsa.pub
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDHH/RCUu3Bs92nAZENbU70O7rObhH1bc+Neb7+LAWGjZa0lcrQNTHucM45DhtDjUObc2A7MqWJfTg244Ny5SV3sfTKK9mCXfPLFt5W7aXU6w4p7QRHm/JIJ3sbg2qqt14JPTQOkUWahjMYTe5NWgTvJKp7mckWdqQ/y6Ltlsn4wSQs4rJlMJeTZuoTRxsPtQkBUq0UYK6E+EseCnmNLxEM+gpLZpJ9eNAcDIZPNLsbyghUc+wjCyPUGnAKp/XreMBPdATp9yYKrlIjCPewfr6hesN+A4/CpMBy/aX3X/Ru4v2SvEo3xotcemuvgBpEaPuQZuxtQaiHMJy8I7S8aulLUM/GVMCdDlVkamDBBDx2h4V8dRLWcQeg8lbtC+ZP+4CcQp/2IqddZs2E+6FmjVas8IntzfjDyx+Pq8UKk3zHL2/fIm3hNwY/44TDVdpi5EsPSn/wX1zz7TXfm4eOLgS9gODZKrRwaskWp/vv6WjT2my6imoOg530znZ1pABDSJU=
|
||||||
94
migrations/001_api_key_management.sql
Normal file
94
migrations/001_api_key_management.sql
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
-- API Key Management Migration
|
||||||
|
-- Version: 001
|
||||||
|
-- Date: 2026-03-21
|
||||||
|
-- Description: Add API key management tables for secure API access
|
||||||
|
|
||||||
|
-- API Keys Table
|
||||||
|
CREATE TABLE IF NOT EXISTS api_keys (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
key_id VARCHAR(48) UNIQUE NOT NULL,
|
||||||
|
key_hash VARCHAR(64) NOT NULL,
|
||||||
|
key_prefix VARCHAR(8) NOT NULL,
|
||||||
|
name VARCHAR(128) NOT NULL,
|
||||||
|
key_type VARCHAR(20) NOT NULL DEFAULT 'user',
|
||||||
|
user_id BIGINT,
|
||||||
|
service_name VARCHAR(64),
|
||||||
|
permissions JSONB DEFAULT '["read", "write"]',
|
||||||
|
expires_at TIMESTAMP,
|
||||||
|
last_used_at TIMESTAMP,
|
||||||
|
last_used_ip VARCHAR(45),
|
||||||
|
usage_count BIGINT DEFAULT 0,
|
||||||
|
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
||||||
|
rotation_required BOOLEAN DEFAULT FALSE,
|
||||||
|
rotation_reason TEXT,
|
||||||
|
grace_period_end TIMESTAMP,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Indexes for api_keys
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_api_keys_key_id ON api_keys(key_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_api_keys_hash ON api_keys(key_hash);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_api_keys_type ON api_keys(key_type);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_api_keys_status ON api_keys(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_api_keys_user_id ON api_keys(user_id);
|
||||||
|
|
||||||
|
-- API Key Audit Log Table
|
||||||
|
CREATE TABLE IF NOT EXISTS api_key_audit_log (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
key_id VARCHAR(32) NOT NULL,
|
||||||
|
action VARCHAR(50) NOT NULL,
|
||||||
|
actor VARCHAR(128),
|
||||||
|
ip_address VARCHAR(45),
|
||||||
|
user_agent TEXT,
|
||||||
|
request_path TEXT,
|
||||||
|
response_code INT,
|
||||||
|
anomaly_type VARCHAR(30),
|
||||||
|
details JSONB,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Indexes for audit log
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_audit_key_id ON api_key_audit_log(key_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_audit_action ON api_key_audit_log(action);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_audit_created_at ON api_key_audit_log(created_at);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_audit_ip ON api_key_audit_log(ip_address);
|
||||||
|
|
||||||
|
-- API Key Anomalies Table
|
||||||
|
CREATE TABLE IF NOT EXISTS api_key_anomalies (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
key_id VARCHAR(32) NOT NULL,
|
||||||
|
anomaly_type VARCHAR(30) NOT NULL,
|
||||||
|
severity VARCHAR(10) NOT NULL,
|
||||||
|
ip_address VARCHAR(45),
|
||||||
|
request_count INT,
|
||||||
|
error_count INT,
|
||||||
|
error_rate DOUBLE PRECISION,
|
||||||
|
unique_ips INT,
|
||||||
|
details JSONB,
|
||||||
|
resolved BOOLEAN DEFAULT FALSE,
|
||||||
|
resolved_at TIMESTAMP,
|
||||||
|
resolved_by VARCHAR(128),
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Indexes for anomalies
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_anomalies_key_id ON api_key_anomalies(key_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_anomalies_resolved ON api_key_anomalies(resolved);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_anomalies_severity ON api_key_anomalies(severity);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_anomalies_created_at ON api_key_anomalies(created_at);
|
||||||
|
|
||||||
|
-- API Key Types Reference
|
||||||
|
-- System (msys_): Internal system use, 365 days TTL, 72h grace
|
||||||
|
-- User (muser_): User authentication, 90 days TTL, 24h grace
|
||||||
|
-- Service (msvc_): Service-to-service, 180 days TTL, 48h grace
|
||||||
|
-- Integration (mint_): Third-party integrations, 30 days TTL, 24h grace
|
||||||
|
-- Emergency (memg_): Emergency access, 1 day TTL, 0h grace (immediate)
|
||||||
|
|
||||||
|
-- Anomaly Types Reference
|
||||||
|
-- high_request_rate: Requests exceed threshold per minute
|
||||||
|
-- high_error_rate: Error rate exceeds threshold
|
||||||
|
-- multiple_ips: Unusual number of unique IPs
|
||||||
|
-- unusual_time: Activity at unusual hours
|
||||||
|
-- brute_force: Potential brute force attack
|
||||||
|
-- data_exfiltration: Unusual data access patterns
|
||||||
140
migrations/003_job_worker.sql
Normal file
140
migrations/003_job_worker.sql
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
-- ================================================================
|
||||||
|
-- Migration 003: Job Worker System
|
||||||
|
-- Version: 003
|
||||||
|
-- Date: 2026-03-24
|
||||||
|
-- Description: Add job worker system tables and columns for
|
||||||
|
-- automatic video processing after registration
|
||||||
|
-- ================================================================
|
||||||
|
|
||||||
|
-- 3.1.1: Update videos table - add status, user_id, job_id columns
|
||||||
|
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;
|
||||||
|
|
||||||
|
COMMENT ON COLUMN videos.status IS 'Video processing status: pending, processing, completed, failed';
|
||||||
|
COMMENT ON COLUMN videos.user_id IS 'WordPress user ID (for user association tracking)';
|
||||||
|
COMMENT ON COLUMN videos.job_id IS 'Associated monitor_jobs ID';
|
||||||
|
|
||||||
|
-- 3.1.2: Add foreign key for job_id after ensuring monitor_jobs table exists
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1 FROM information_schema.tables
|
||||||
|
WHERE table_name = 'monitor_jobs'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE videos ADD CONSTRAINT fk_videos_job_id
|
||||||
|
FOREIGN KEY (job_id) REFERENCES monitor_jobs(id) ON DELETE SET NULL;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- 3.1.3: Update monitor_jobs table - add video_id, user_id, processors columns
|
||||||
|
ALTER TABLE monitor_jobs ADD COLUMN IF NOT EXISTS video_id BIGINT;
|
||||||
|
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.video_id IS 'Foreign key to videos.id';
|
||||||
|
COMMENT ON COLUMN monitor_jobs.user_id IS 'WordPress user ID';
|
||||||
|
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.4: Add foreign key for video_id in monitor_jobs
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1 FROM information_schema.columns
|
||||||
|
WHERE table_name = 'monitor_jobs' AND column_name = 'video_id'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE monitor_jobs ADD CONSTRAINT fk_monitor_jobs_video_id
|
||||||
|
FOREIGN KEY (video_id) REFERENCES videos(id) ON DELETE CASCADE;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- 3.1.5: Create processor_results table
|
||||||
|
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,
|
||||||
|
|
||||||
|
CONSTRAINT unique_job_processor UNIQUE(job_id, processor)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 3.1.6: Create indexes for processor_results
|
||||||
|
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.7: Add function to update updated_at timestamp
|
||||||
|
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
BEGIN
|
||||||
|
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ language 'plpgsql';
|
||||||
|
|
||||||
|
-- 3.1.8: Create triggers for updated_at
|
||||||
|
DROP TRIGGER IF EXISTS update_videos_updated_at ON videos;
|
||||||
|
CREATE TRIGGER update_videos_updated_at
|
||||||
|
BEFORE UPDATE ON videos
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION update_updated_at_column();
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS update_monitor_jobs_updated_at ON monitor_jobs;
|
||||||
|
CREATE TRIGGER update_monitor_jobs_updated_at
|
||||||
|
BEFORE UPDATE ON monitor_jobs
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION update_updated_at_column();
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS update_processor_results_updated_at ON processor_results;
|
||||||
|
CREATE TRIGGER update_processor_results_updated_at
|
||||||
|
BEFORE UPDATE ON processor_results
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION update_updated_at_column();
|
||||||
|
|
||||||
|
-- 3.1.9: Add check constraint for videos.status
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1 FROM information_schema.columns
|
||||||
|
WHERE table_name = 'videos' AND column_name = 'status'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE videos ADD CONSTRAINT chk_videos_status
|
||||||
|
CHECK (status IN ('pending', 'processing', 'completed', 'failed'));
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- 3.1.10: Add check constraint for monitor_jobs.status (update existing if needed)
|
||||||
|
ALTER TABLE monitor_jobs DROP CONSTRAINT IF EXISTS chk_monitor_jobs_status;
|
||||||
|
ALTER TABLE monitor_jobs ADD CONSTRAINT chk_monitor_jobs_status
|
||||||
|
CHECK (status IN ('pending', 'running', 'completed', 'failed', 'cancelled'));
|
||||||
|
|
||||||
|
-- 3.1.11: Add check constraint for processor_results.status
|
||||||
|
ALTER TABLE processor_results DROP CONSTRAINT IF EXISTS chk_processor_results_status;
|
||||||
|
ALTER TABLE processor_results ADD CONSTRAINT chk_processor_results_status
|
||||||
|
CHECK (status IN ('pending', 'running', 'completed', 'failed', 'skipped'));
|
||||||
|
|
||||||
|
-- 3.1.12: Create index on videos.status for efficient queries
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_videos_status ON videos(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_videos_job_id ON videos(job_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_videos_user_id ON videos(user_id);
|
||||||
|
|
||||||
|
-- 3.1.13: Create index on monitor_jobs.video_id
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_monitor_jobs_video_id ON monitor_jobs(video_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_monitor_jobs_status_created ON monitor_jobs(status, created_at);
|
||||||
58
momentry_runtime/plist/com.momentry.api.plist
Normal file
58
momentry_runtime/plist/com.momentry.api.plist
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<?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>
|
||||||
30
momentry_runtime/plist/com.momentry.gitea-mcp-server.plist
Normal file
30
momentry_runtime/plist/com.momentry.gitea-mcp-server.plist
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?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>
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
<array>
|
<array>
|
||||||
<string>/opt/homebrew/bin/mongod</string>
|
<string>/opt/homebrew/bin/mongod</string>
|
||||||
<string>--dbpath</string>
|
<string>--dbpath</string>
|
||||||
<string>/Users/accusys/momentry/var</string>
|
<string>/opt/homebrew/var/mongodb</string>
|
||||||
<string>--logpath</string>
|
<string>--logpath</string>
|
||||||
<string>/Users/accusys/momentry/log/mongodb.log</string>
|
<string>/Users/accusys/momentry/log/mongodb.log</string>
|
||||||
<string>--port</string>
|
<string>--port</string>
|
||||||
@@ -24,9 +24,6 @@
|
|||||||
<key>KeepAlive</key>
|
<key>KeepAlive</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
|
||||||
<key>UserName</key>
|
|
||||||
<string>accusys</string>
|
|
||||||
|
|
||||||
<key>StandardErrorPath</key>
|
<key>StandardErrorPath</key>
|
||||||
<string>/Users/accusys/momentry/log/mongodb.error.log</string>
|
<string>/Users/accusys/momentry/log/mongodb.error.log</string>
|
||||||
|
|
||||||
@@ -34,6 +31,6 @@
|
|||||||
<string>/Users/accusys/momentry/log/mongodb.log</string>
|
<string>/Users/accusys/momentry/log/mongodb.log</string>
|
||||||
|
|
||||||
<key>WorkingDirectory</key>
|
<key>WorkingDirectory</key>
|
||||||
<string>/Users/accusys/momentry/var/mongodb/</string>
|
<string>/opt/homebrew/var/mongodb</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -35,12 +35,18 @@
|
|||||||
<key>N8N_PORT</key>
|
<key>N8N_PORT</key>
|
||||||
<string>5678</string>
|
<string>5678</string>
|
||||||
|
|
||||||
|
<key>N8N_HOST</key>
|
||||||
|
<string>127.0.0.1</string>
|
||||||
|
|
||||||
<key>N8N_LISTEN_ADDRESS</key>
|
<key>N8N_LISTEN_ADDRESS</key>
|
||||||
<string>0.0.0.0</string>
|
<string>0.0.0.0</string>
|
||||||
|
|
||||||
<key>N8N_USER_MANAGEMENT_JWT_DURATION</key>
|
<key>N8N_USER_MANAGEMENT_JWT_DURATION</key>
|
||||||
<string>1h</string>
|
<string>1h</string>
|
||||||
|
|
||||||
|
<key>N8N_RUNNERS_ENABLED</key>
|
||||||
|
<string>false</string>
|
||||||
|
|
||||||
<key>GENERIC_TIMEZONE</key>
|
<key>GENERIC_TIMEZONE</key>
|
||||||
<string>Asia/Taipei</string>
|
<string>Asia/Taipei</string>
|
||||||
|
|
||||||
@@ -85,6 +91,9 @@
|
|||||||
|
|
||||||
<key>N8N_METRICS</key>
|
<key>N8N_METRICS</key>
|
||||||
<string>true</string>
|
<string>true</string>
|
||||||
|
|
||||||
|
<key>N8N_PUBLIC_API_ENABLED</key>
|
||||||
|
<string>true</string>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
||||||
<key>RunAtLoad</key>
|
<key>RunAtLoad</key>
|
||||||
|
|||||||
@@ -29,14 +29,14 @@
|
|||||||
<key>N8N_ENCRYPTION_KEY</key>
|
<key>N8N_ENCRYPTION_KEY</key>
|
||||||
<string>Test3200Test3200Test3200</string>
|
<string>Test3200Test3200Test3200</string>
|
||||||
|
|
||||||
<key>DOMAIN_NAME</key>
|
<key>N8N_PORT</key>
|
||||||
<string>n8n.momentry.ddns.net</string>
|
<string>5681</string>
|
||||||
|
|
||||||
<key>WEBHOOK_URL</key>
|
<key>QUEUE_HEALTH_CHECK_PORT</key>
|
||||||
<string>https://n8n.momentry.ddns.net/</string>
|
<string>5682</string>
|
||||||
|
|
||||||
<key>N8N_LISTEN_ADDRESS</key>
|
<key>N8N_HOST</key>
|
||||||
<string>0.0.0.0</string>
|
<string>127.0.0.1</string>
|
||||||
|
|
||||||
<key>GENERIC_TIMEZONE</key>
|
<key>GENERIC_TIMEZONE</key>
|
||||||
<string>Asia/Taipei</string>
|
<string>Asia/Taipei</string>
|
||||||
@@ -80,14 +80,14 @@
|
|||||||
<key>N8N_USER_FOLDER</key>
|
<key>N8N_USER_FOLDER</key>
|
||||||
<string>/Users/accusys/momentry/var/n8n</string>
|
<string>/Users/accusys/momentry/var/n8n</string>
|
||||||
|
|
||||||
|
<key>N8N_METRICS</key>
|
||||||
|
<string>true</string>
|
||||||
|
|
||||||
<key>N8N_RUNNERS_BROKER_PORT</key>
|
<key>N8N_RUNNERS_BROKER_PORT</key>
|
||||||
<string>5690</string>
|
<string>5690</string>
|
||||||
|
|
||||||
<key>N8N_RUNNERS_LAUNCHER_HEALTH_CHECK_PORT</key>
|
<key>N8N_RUNNERS_LAUNCHER_HEALTH_CHECK_PORT</key>
|
||||||
<string>5691</string>
|
<string>5691</string>
|
||||||
|
|
||||||
<key>N8N_METRICS</key>
|
|
||||||
<string>true</string>
|
|
||||||
</dict>
|
</dict>
|
||||||
|
|
||||||
<key>RunAtLoad</key>
|
<key>RunAtLoad</key>
|
||||||
|
|||||||
@@ -25,6 +25,10 @@
|
|||||||
<string>/opt/homebrew/bin:/opt/homebrew/sbin:/usr/bin:/bin</string>
|
<string>/opt/homebrew/bin:/opt/homebrew/sbin:/usr/bin:/bin</string>
|
||||||
<key>HOME</key>
|
<key>HOME</key>
|
||||||
<string>/Users/accusys</string>
|
<string>/Users/accusys</string>
|
||||||
|
<key>SFTPGO_DEFAULT_ADMIN_USERNAME</key>
|
||||||
|
<string>admin</string>
|
||||||
|
<key>SFTPGO_DEFAULT_ADMIN_PASSWORD</key>
|
||||||
|
<string>Test3200Test3200</string>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
||||||
<key>RunAtLoad</key>
|
<key>RunAtLoad</key>
|
||||||
|
|||||||
58
momentry_runtime/plist/com.momentry.worker.plist
Normal file
58
momentry_runtime/plist/com.momentry.worker.plist
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<?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.worker</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>worker</string>
|
||||||
|
<string>--max-concurrent</string>
|
||||||
|
<string>2</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_worker.log</string>
|
||||||
|
|
||||||
|
<key>StandardErrorPath</key>
|
||||||
|
<string>/Users/accusys/momentry/log/momentry_worker.error.log</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
35
monitor/common/load_credentials.sh
Normal file
35
monitor/common/load_credentials.sh
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Momentry Monitor 密碼管理腳本
|
||||||
|
# 路徑: /Users/accusys/momentry_core_0.1/monitor/common/load_credentials.sh
|
||||||
|
#
|
||||||
|
# 使用方式:
|
||||||
|
# source /path/to/load_credentials.sh
|
||||||
|
|
||||||
|
# 載入環境變數(如果存在)
|
||||||
|
if [ -f "$HOME/.momentry/credentials" ]; then
|
||||||
|
set -a
|
||||||
|
source "$HOME/.momentry/credentials"
|
||||||
|
set +a
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 預設值
|
||||||
|
export PG_USER="${PG_USER:-accusys}"
|
||||||
|
export PG_PASSWORD="${PG_PASSWORD:-accusys}"
|
||||||
|
export PG_HOST="${PG_HOST:-localhost}"
|
||||||
|
export PG_PORT="${PG_PORT:-5432}"
|
||||||
|
|
||||||
|
export REDIS_PASSWORD="${REDIS_PASSWORD:-accusys}"
|
||||||
|
|
||||||
|
export MONGO_USER="${MONGO_USER:-accusys}"
|
||||||
|
export MONGO_PASSWORD="${MONGO_PASSWORD:-Test3200Test3200}"
|
||||||
|
export MONGO_PORT="${MONGO_PORT:-27017}"
|
||||||
|
|
||||||
|
export QDRANT_API_KEY="${QDRANT_API_KEY:-Test3200Test3200Test3200}"
|
||||||
|
|
||||||
|
export SFTPGO_PASSWORD="${SFTPGO_PASSWORD:-sftpgo_pass_2026}"
|
||||||
|
export SFTPGO_USER="${SFTPGO_USER:-sftpgo}"
|
||||||
|
|
||||||
|
export N8N_PASSWORD="${N8N_PASSWORD:-accusys}"
|
||||||
|
|
||||||
|
export MARIADB_USER="${MARIADB_USER:-accusys}"
|
||||||
|
export MARIADB_PASSWORD="${MARIADB_PASSWORD:-Test3200Test3200Test3200}"
|
||||||
@@ -405,14 +405,6 @@ backup:
|
|||||||
retention:
|
retention:
|
||||||
weekly: 4
|
weekly: 4
|
||||||
|
|
||||||
- name: "mongodb"
|
|
||||||
enabled: true
|
|
||||||
backup_type: "config"
|
|
||||||
method: "file"
|
|
||||||
schedule: "weekly"
|
|
||||||
retention:
|
|
||||||
weekly: 4
|
|
||||||
|
|
||||||
- name: "php"
|
- name: "php"
|
||||||
enabled: true
|
enabled: true
|
||||||
backup_type: "config"
|
backup_type: "config"
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user