cleanup: remove dead code and duplicate docs
- Remove session-ses_2f27.md (161KB raw session log) - Remove 49 ROOT_* duplicate files across REFERENCE/ - Remove 14 duplicate files between REFERENCE/ root and history/ - Remove asr_legacy.rs (dead code, replaced by asr.rs) - Remove src/core/worker/ (duplicate JobWorker) - Remove src/core/layers/ (empty directory) - Remove 4 .bak files in src/ - Remove 7 dead private methods in worker/processor.rs - Remove backup directory from git tracking
This commit is contained in:
@@ -10,7 +10,7 @@ MOMENTRY_REDIS_PREFIX=momentry_dev:
|
||||
|
||||
# Worker Configuration (enabled for development)
|
||||
MOMENTRY_WORKER_ENABLED=true
|
||||
MOMENTRY_MAX_CONCURRENT=1
|
||||
MOMENTRY_MAX_CONCURRENT=6
|
||||
MOMENTRY_POLL_INTERVAL=10
|
||||
MOMENTRY_WORKER_BATCH_SIZE=5
|
||||
|
||||
|
||||
157
.gitignore
vendored
157
.gitignore
vendored
@@ -1,92 +1,79 @@
|
||||
# Environment - Local configs (NEVER commit these)
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Build artifacts
|
||||
target/
|
||||
venv/
|
||||
|
||||
# Generated files
|
||||
thumbnails/
|
||||
*.asr.json
|
||||
*.probe.json
|
||||
test_asr.json
|
||||
|
||||
# Local output (machine learning results)
|
||||
output/
|
||||
*.pt
|
||||
|
||||
# Cache
|
||||
.ruff_cache/
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
# SSH keys (NEVER commit)
|
||||
id_*
|
||||
!id_*.pub
|
||||
|
||||
# IDE and editor
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Documentation backups
|
||||
# docs_v1.0/ (Moved to active tracking)
|
||||
|
||||
# Frontend dependencies
|
||||
node_modules/
|
||||
portal/src-tauri/target/
|
||||
|
||||
# Python cache
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
|
||||
# Test artifacts
|
||||
test_output/
|
||||
test_output_simple/
|
||||
test_output_v2/
|
||||
*.mp4
|
||||
*.pt
|
||||
server.pid
|
||||
server.pid.*
|
||||
|
||||
# Backup files
|
||||
*.bak
|
||||
*.backup
|
||||
*.bak[0-9]
|
||||
|
||||
# Model files
|
||||
models/
|
||||
model_checkpoints/
|
||||
pretrained_models/
|
||||
|
||||
# Desktop app
|
||||
momentry_desktop/
|
||||
|
||||
# Release artifacts (track docs, ignore binaries)
|
||||
release/*.zip
|
||||
release/momentry_v*
|
||||
release/*.sql
|
||||
release/dev_data_*.sql
|
||||
release/public_schema_*.sql
|
||||
release/migrate_*.sql
|
||||
|
||||
# But track release documentation
|
||||
!id_*.pub
|
||||
!release/*.md
|
||||
!release/*.txt
|
||||
|
||||
.DS_Store
|
||||
.env
|
||||
.env.*.local
|
||||
.env.local
|
||||
.idea/
|
||||
.ruff_cache/
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
.vscode/
|
||||
*.asr.json
|
||||
*.backup
|
||||
*.bak
|
||||
*.bak[0-9]
|
||||
*.log
|
||||
*.mp4
|
||||
*.probe.json
|
||||
*.pt
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.swo
|
||||
*.swp
|
||||
*~
|
||||
# Backup files
|
||||
# Build artifacts
|
||||
# But track release documentation
|
||||
# Cache
|
||||
# Data directories
|
||||
data/
|
||||
|
||||
# Desktop app
|
||||
# docs_v1.0/ (Moved to active tracking)
|
||||
# Documentation backups
|
||||
# Environment - Local configs (NEVER commit these)
|
||||
# Frontend dependencies
|
||||
# Generated files
|
||||
# IDE and editor
|
||||
# Local output (machine learning results)
|
||||
# Logs
|
||||
# Model files
|
||||
# OS files
|
||||
# Portal build artifacts
|
||||
# Python cache
|
||||
# Release and output directories
|
||||
# Release artifacts (track docs, ignore binaries)
|
||||
# SSH keys (NEVER commit)
|
||||
# System status
|
||||
# Test artifacts
|
||||
data/
|
||||
id_*
|
||||
model_checkpoints/
|
||||
models/
|
||||
momentry_desktop/
|
||||
momentry_runtime/
|
||||
node_modules/
|
||||
output/
|
||||
portal/dist/
|
||||
portal/node_modules/
|
||||
portal/src-tauri/target/
|
||||
pretrained_models/
|
||||
release/
|
||||
release/*.sql
|
||||
release/*.zip
|
||||
release/dev_data_*.sql
|
||||
release/migrate_*.sql
|
||||
release/momentry_v*
|
||||
release/public_schema_*.sql
|
||||
server.pid
|
||||
server.pid.*
|
||||
system_status_*.md
|
||||
target/
|
||||
test_asr.json
|
||||
test_output_simple/
|
||||
test_output_v2/
|
||||
test_output/
|
||||
thumbnails/
|
||||
venv/
|
||||
|
||||
@@ -29,10 +29,16 @@ Rust-based digital asset management system with video analysis and RAG capabilit
|
||||
| Production | 3002 | ❌ 禁止修改 | `cargo run -- server` (僅 release 時) |
|
||||
| Portal (Tauri) | 1420 | 前端開發 | `npm run tauri dev` |
|
||||
|
||||
### ⛔ 嚴格測試隔離規則 (Strict Test Isolation)
|
||||
- **所有測試 (Test) 必須在 Dev (3003) 進行**。
|
||||
- **絕對禁止 (ABSOLUTELY FORBIDDEN)** 在任何測試指令、Demo 流程或 API 檢查中使用 `localhost:3002`。
|
||||
- 即使是「測試 Unregister」或「檢查版本」,若未明確標示為 "Production Deployment",一律視為違規。
|
||||
- **預設行為**: 所有 curl, CLI, 或程式碼測試指令,預設 URL 必須為 `http://localhost:3003`。
|
||||
|
||||
### 違反後果
|
||||
- 修改 WordPress/n8n 可能影響 marcom 團隊工作與生產環境
|
||||
- 修改 WordPress/n8n 資料庫 table 可能破壞自動化流程與資料完整性
|
||||
- 修改 port 3002 可能中斷正在使用的服務
|
||||
- 修改 port 3002 可能中斷正在使用的服務 (這是非常嚴重的錯誤)
|
||||
- 所有 dev 測試必須在 playground (3003) 進行
|
||||
|
||||
---
|
||||
|
||||
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -2369,6 +2369,7 @@ dependencies = [
|
||||
"qdrant-client",
|
||||
"ratatui",
|
||||
"redis",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"sdl2",
|
||||
"serde",
|
||||
|
||||
@@ -26,6 +26,7 @@ futures-util = "0.3"
|
||||
# Serialization
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
regex = "1"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
|
||||
# UUID
|
||||
@@ -98,6 +99,10 @@ optional = true
|
||||
name = "momentry"
|
||||
path = "src/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "momentry-cli"
|
||||
path = "src/bin/cli.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "momentry_player"
|
||||
path = "src/player/main.rs"
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
/Users/accusys/momentry_core_0.1/output/a1b10138a6bbb0cd.cut.json
|
||||
@@ -1 +0,0 @@
|
||||
/Users/accusys/momentry_core_0.1/output/a1b10138a6bbb0cd.face.json
|
||||
@@ -1 +0,0 @@
|
||||
/Users/accusys/momentry_core_0.1/output/a1b10138a6bbb0cd.ocr.json
|
||||
@@ -1 +0,0 @@
|
||||
/Users/accusys/momentry_core_0.1/output/a1b10138a6bbb0cd.pose.json
|
||||
@@ -1 +0,0 @@
|
||||
/Users/accusys/momentry_core_0.1/output/a1b10138a6bbb0cd.story.json
|
||||
@@ -1 +0,0 @@
|
||||
/Users/accusys/momentry_core_0.1/output/a1b10138a6bbb0cd.yolo.json
|
||||
161
benchmark_asr.py
161
benchmark_asr.py
@@ -1,161 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Benchmark ASR processor direct vs chunked transcription overhead."""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import json
|
||||
import tempfile
|
||||
import time
|
||||
import shutil
|
||||
import statistics
|
||||
|
||||
# Use a small video clip for consistent benchmarking
|
||||
VIDEO_SOURCE = "../test_video/BigBuckBunny_320x180.mp4" # 10 minutes, 62MB
|
||||
if not os.path.exists(VIDEO_SOURCE):
|
||||
print(f"Video not found: {VIDEO_SOURCE}")
|
||||
sys.exit(1)
|
||||
|
||||
# Create temporary directory for all test runs
|
||||
temp_dir = tempfile.mkdtemp(prefix="asr_bench_")
|
||||
print(f"Benchmark directory: {temp_dir}")
|
||||
|
||||
|
||||
def run_asr_mode(mode_name, max_direct_duration, chunk_duration=600):
|
||||
"""Run ASR processor with given parameters, return timing and resource stats."""
|
||||
clip_path = os.path.join(temp_dir, f"clip_{mode_name}.mp4")
|
||||
output_path = os.path.join(temp_dir, f"output_{mode_name}.json")
|
||||
|
||||
# Copy source video to clip path (no transcoding)
|
||||
shutil.copy2(VIDEO_SOURCE, clip_path)
|
||||
|
||||
env = os.environ.copy()
|
||||
env["MOMENTRY_ASR_MAX_DIRECT_DURATION"] = str(max_direct_duration)
|
||||
env["MOMENTRY_ASR_CHUNK_DURATION"] = str(chunk_duration)
|
||||
env["MOMENTRY_ASR_MODEL_SIZE"] = "tiny"
|
||||
env["MOMENTRY_ASR_COMPUTE_TYPE"] = "int8"
|
||||
|
||||
cmd = [
|
||||
"/opt/homebrew/bin/python3.11",
|
||||
"scripts/asr_processor.py",
|
||||
clip_path,
|
||||
output_path,
|
||||
"--uuid",
|
||||
f"bench_{mode_name}",
|
||||
]
|
||||
|
||||
# Start monitoring (external)
|
||||
import psutil
|
||||
|
||||
start_time = time.time()
|
||||
proc = subprocess.Popen(
|
||||
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env
|
||||
)
|
||||
|
||||
# Monitor CPU and memory of child process
|
||||
cpu_percents = []
|
||||
memory_mbs = []
|
||||
|
||||
while True:
|
||||
try:
|
||||
p = psutil.Process(proc.pid)
|
||||
cpu = p.cpu_percent(interval=0.1)
|
||||
mem = p.memory_info().rss / (1024 * 1024)
|
||||
cpu_percents.append(cpu)
|
||||
memory_mbs.append(mem)
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||
break
|
||||
if proc.poll() is not None:
|
||||
# Process ended, wait a bit for final stats
|
||||
time.sleep(0.1)
|
||||
break
|
||||
|
||||
stdout, stderr = proc.communicate(timeout=1)
|
||||
elapsed = time.time() - start_time
|
||||
returncode = proc.returncode
|
||||
|
||||
# Read output
|
||||
segments = []
|
||||
if os.path.exists(output_path):
|
||||
with open(output_path, "r") as f:
|
||||
data = json.load(f)
|
||||
segments = data.get("segments", [])
|
||||
|
||||
# Clean up temporary files
|
||||
try:
|
||||
os.unlink(clip_path)
|
||||
os.unlink(output_path)
|
||||
except:
|
||||
pass
|
||||
|
||||
return {
|
||||
"mode": mode_name,
|
||||
"elapsed": elapsed,
|
||||
"returncode": returncode,
|
||||
"segments": len(segments),
|
||||
"cpu_avg": statistics.mean(cpu_percents) if cpu_percents else 0,
|
||||
"cpu_max": max(cpu_percents) if cpu_percents else 0,
|
||||
"memory_avg": statistics.mean(memory_mbs) if memory_mbs else 0,
|
||||
"memory_max": max(memory_mbs) if memory_mbs else 0,
|
||||
"stderr": stderr.decode() if stderr else "",
|
||||
}
|
||||
|
||||
|
||||
try:
|
||||
# Run direct transcription (clip duration ~600s, max_direct=1800)
|
||||
print("Running direct transcription benchmark...")
|
||||
direct = run_asr_mode("direct", max_direct_duration=1800, chunk_duration=600)
|
||||
|
||||
# Run chunked transcription (force chunked with max_direct=300, chunk=120)
|
||||
print("Running chunked transcription benchmark...")
|
||||
chunked = run_asr_mode("chunked", max_direct_duration=300, chunk_duration=120)
|
||||
|
||||
# Calculate overhead
|
||||
overhead = (chunked["elapsed"] - direct["elapsed"]) / direct["elapsed"] * 100
|
||||
|
||||
# Print results
|
||||
print("\n" + "=" * 60)
|
||||
print("ASR PROCESSOR BENCHMARK RESULTS")
|
||||
print("=" * 60)
|
||||
print(f"Test video: {VIDEO_SOURCE}")
|
||||
print(f"Video duration: ~10 minutes (600 seconds)")
|
||||
print()
|
||||
print("Direct Transcription:")
|
||||
print(f" Time: {direct['elapsed']:.1f}s")
|
||||
print(f" Segments: {direct['segments']}")
|
||||
print(f" CPU avg/max: {direct['cpu_avg']:.1f}% / {direct['cpu_max']:.1f}%")
|
||||
print(
|
||||
f" Memory avg/max: {direct['memory_avg']:.1f} MB / {direct['memory_max']:.1f} MB"
|
||||
)
|
||||
print()
|
||||
print("Chunked Transcription:")
|
||||
print(f" Time: {chunked['elapsed']:.1f}s")
|
||||
print(f" Segments: {chunked['segments']}")
|
||||
print(f" CPU avg/max: {chunked['cpu_avg']:.1f}% / {chunked['cpu_max']:.1f}%")
|
||||
print(
|
||||
f" Memory avg/max: {chunked['memory_avg']:.1f} MB / {chunked['memory_max']:.1f} MB"
|
||||
)
|
||||
print()
|
||||
print("OVERHEAD ANALYSIS:")
|
||||
print(f" Time overhead: {overhead:.2f}%")
|
||||
if overhead <= 5:
|
||||
print(f" ✅ PASS: Overhead ≤5% requirement")
|
||||
else:
|
||||
print(f" ❌ FAIL: Overhead exceeds 5% limit")
|
||||
print()
|
||||
|
||||
# Check for errors
|
||||
if direct["returncode"] != 0:
|
||||
print(f"WARNING: Direct transcription returned {direct['returncode']}")
|
||||
if chunked["returncode"] != 0:
|
||||
print(f"WARNING: Chunked transcription returned {chunked['returncode']}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Benchmark failed: {e}")
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
# Clean up directory
|
||||
shutil.rmtree(temp_dir, ignore_errors=True)
|
||||
print(f"Cleaned up {temp_dir}")
|
||||
@@ -1,151 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Benchmark ASR with realistic chunk sizes."""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import json
|
||||
import tempfile
|
||||
import time
|
||||
import shutil
|
||||
import statistics
|
||||
|
||||
VIDEO_SOURCE = "../test_video/BigBuckBunny_320x180.mp4" # 10 minutes, 62MB
|
||||
if not os.path.exists(VIDEO_SOURCE):
|
||||
print(f"Video not found: {VIDEO_SOURCE}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def run_asr_mode(mode_name, max_direct_duration, chunk_duration, description):
|
||||
"""Run ASR processor with given parameters, return timing."""
|
||||
clip_path = os.path.join(temp_dir, f"clip_{mode_name}.mp4")
|
||||
output_path = os.path.join(temp_dir, f"output_{mode_name}.json")
|
||||
|
||||
# Copy source video to clip path
|
||||
shutil.copy2(VIDEO_SOURCE, clip_path)
|
||||
|
||||
env = os.environ.copy()
|
||||
env["MOMENTRY_ASR_MAX_DIRECT_DURATION"] = str(max_direct_duration)
|
||||
env["MOMENTRY_ASR_CHUNK_DURATION"] = str(chunk_duration)
|
||||
env["MOMENTRY_ASR_MODEL_SIZE"] = "tiny"
|
||||
env["MOMENTRY_ASR_COMPUTE_TYPE"] = "int8"
|
||||
|
||||
cmd = [
|
||||
"/opt/homebrew/bin/python3.11",
|
||||
"scripts/asr_processor.py",
|
||||
clip_path,
|
||||
output_path,
|
||||
"--uuid",
|
||||
f"bench_{mode_name}",
|
||||
]
|
||||
|
||||
start_time = time.time()
|
||||
proc = subprocess.run(cmd, capture_output=True, env=env, text=True)
|
||||
elapsed = time.time() - start_time
|
||||
returncode = proc.returncode
|
||||
|
||||
# Read output
|
||||
segments = []
|
||||
language = ""
|
||||
if os.path.exists(output_path):
|
||||
with open(output_path, "r") as f:
|
||||
data = json.load(f)
|
||||
segments = data.get("segments", [])
|
||||
language = data.get("language", "")
|
||||
|
||||
# Clean up
|
||||
try:
|
||||
os.unlink(clip_path)
|
||||
os.unlink(output_path)
|
||||
except:
|
||||
pass
|
||||
|
||||
return {
|
||||
"mode": mode_name,
|
||||
"description": description,
|
||||
"elapsed": elapsed,
|
||||
"returncode": returncode,
|
||||
"segments": len(segments),
|
||||
"language": language,
|
||||
"stderr": proc.stderr[:200] if proc.stderr else "",
|
||||
}
|
||||
|
||||
|
||||
# Create temporary directory
|
||||
temp_dir = tempfile.mkdtemp(prefix="asr_bench_real_")
|
||||
print(f"Benchmark directory: {temp_dir}")
|
||||
|
||||
try:
|
||||
# Test 1: Direct transcription (video is 10 min, max_direct=30 min)
|
||||
print("\n1. Direct transcription (max_direct=1800s, chunk=600s):")
|
||||
direct = run_asr_mode(
|
||||
"direct",
|
||||
max_direct_duration=1800,
|
||||
chunk_duration=600,
|
||||
description="Direct (video < 30min threshold)",
|
||||
)
|
||||
print(f" Time: {direct['elapsed']:.1f}s, Segments: {direct['segments']}")
|
||||
|
||||
# Test 2: Chunked with 1 chunk (force chunked but chunk size = video duration)
|
||||
print("\n2. Chunked with 1 chunk (max_direct=300s, chunk=600s):")
|
||||
chunked1 = run_asr_mode(
|
||||
"chunked1",
|
||||
max_direct_duration=300,
|
||||
chunk_duration=600,
|
||||
description="Chunked with 1 chunk (10 min)",
|
||||
)
|
||||
print(f" Time: {chunked1['elapsed']:.1f}s, Segments: {chunked1['segments']}")
|
||||
|
||||
# Test 3: Chunked with 2 chunks (5 min each)
|
||||
print("\n3. Chunked with 2 chunks (max_direct=300s, chunk=300s):")
|
||||
chunked2 = run_asr_mode(
|
||||
"chunked2",
|
||||
max_direct_duration=300,
|
||||
chunk_duration=300,
|
||||
description="Chunked with 2 chunks (5 min each)",
|
||||
)
|
||||
print(f" Time: {chunked2['elapsed']:.1f}s, Segments: {chunked2['segments']}")
|
||||
|
||||
# Test 4: Chunked with 5 chunks (2 min each) - worst case
|
||||
print("\n4. Chunked with 5 chunks (max_direct=300s, chunk=120s):")
|
||||
chunked5 = run_asr_mode(
|
||||
"chunked5",
|
||||
max_direct_duration=300,
|
||||
chunk_duration=120,
|
||||
description="Chunked with 5 chunks (2 min each)",
|
||||
)
|
||||
print(f" Time: {chunked5['elapsed']:.1f}s, Segments: {chunked5['segments']}")
|
||||
|
||||
# Calculate overheads
|
||||
print("\n" + "=" * 60)
|
||||
print("OVERHEAD ANALYSIS (compared to direct transcription)")
|
||||
print("=" * 60)
|
||||
|
||||
for test in [chunked1, chunked2, chunked5]:
|
||||
if direct["elapsed"] > 0:
|
||||
overhead = (test["elapsed"] - direct["elapsed"]) / direct["elapsed"] * 100
|
||||
status = "✅ ≤5%" if overhead <= 5 else "❌ >5%"
|
||||
print(f"\n{test['description']}:")
|
||||
print(f" Time: {test['elapsed']:.1f}s (direct: {direct['elapsed']:.1f}s)")
|
||||
print(f" Overhead: {overhead:.2f}% {status}")
|
||||
print(f" Segments: {test['segments']} (direct: {direct['segments']})")
|
||||
if test["segments"] != direct["segments"]:
|
||||
print(f" ⚠️ Segment count mismatch!")
|
||||
|
||||
# Summary
|
||||
print("\n" + "=" * 60)
|
||||
print("SUMMARY")
|
||||
print("=" * 60)
|
||||
print(f"Video: {os.path.basename(VIDEO_SOURCE)} (~10 minutes)")
|
||||
print(f"\nKey finding: Overhead depends heavily on chunk count.")
|
||||
print(f"With realistic chunk sizes (10 min), overhead should be minimal.")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Benchmark failed: {e}")
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
# Clean up directory
|
||||
shutil.rmtree(temp_dir, ignore_errors=True)
|
||||
print(f"\nCleaned up {temp_dir}")
|
||||
19
build.rs
19
build.rs
@@ -1,19 +1,4 @@
|
||||
use chrono::Local;
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
let now = Local::now();
|
||||
let build_time = now.format("%Y-%m-%d %H:%M:%S").to_string();
|
||||
|
||||
// Get version from Cargo.toml
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
let full_version = format!("{} (build: {})", version, build_time);
|
||||
|
||||
// Set build-time environment variables
|
||||
println!("cargo:rustc-env=BUILD_VERSION={}", full_version);
|
||||
println!("cargo:rustc-env=BUILD_TIME={}", build_time);
|
||||
println!("cargo:rustc-env=VERSION={}", version);
|
||||
|
||||
// Also print for debugging
|
||||
println!("cargo:warning=Building version: {}", full_version);
|
||||
let version = std::env::var("CARGO_PKG_VERSION").unwrap_or_else(|_| "unknown".to_string());
|
||||
println!("cargo:rustc-env=BUILD_VERSION={}", version);
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/opt/homebrew/bin/python3.11
|
||||
try:
|
||||
import whisper
|
||||
|
||||
print("whisper available")
|
||||
except ImportError as e:
|
||||
print(f"whisper not available: {e}")
|
||||
@@ -1,200 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Chunked transcription to handle large audio files.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import time
|
||||
import tempfile
|
||||
import json
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
import numpy as np
|
||||
|
||||
|
||||
def split_audio(input_path, chunk_duration=1800, output_dir=None):
|
||||
"""Split audio into chunks using ffmpeg."""
|
||||
if output_dir is None:
|
||||
output_dir = Path(tempfile.mkdtemp(prefix="audio_chunks_"))
|
||||
else:
|
||||
output_dir = Path(output_dir)
|
||||
output_dir.mkdir(exist_ok=True, parents=True)
|
||||
|
||||
# Get total duration
|
||||
cmd = [
|
||||
"ffprobe",
|
||||
"-v",
|
||||
"error",
|
||||
"-show_entries",
|
||||
"format=duration",
|
||||
"-of",
|
||||
"csv=p=0",
|
||||
str(input_path),
|
||||
]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
total_duration = float(result.stdout.strip())
|
||||
|
||||
print(
|
||||
f"Total audio duration: {total_duration:.1f}s ({total_duration / 3600:.1f} hrs)"
|
||||
)
|
||||
print(f"Splitting into {chunk_duration}s chunks...")
|
||||
|
||||
chunks = []
|
||||
start = 0
|
||||
chunk_idx = 0
|
||||
while start < total_duration:
|
||||
chunk_path = output_dir / f"chunk_{chunk_idx:04d}.wav"
|
||||
cmd = [
|
||||
"ffmpeg",
|
||||
"-i",
|
||||
str(input_path),
|
||||
"-ss",
|
||||
str(start),
|
||||
"-t",
|
||||
str(chunk_duration),
|
||||
"-acodec",
|
||||
"pcm_s16le",
|
||||
"-ar",
|
||||
"16000",
|
||||
"-ac",
|
||||
"1",
|
||||
"-y",
|
||||
str(chunk_path),
|
||||
]
|
||||
subprocess.run(cmd, capture_output=True)
|
||||
if chunk_path.exists() and chunk_path.stat().st_size > 0:
|
||||
chunks.append(
|
||||
{
|
||||
"path": chunk_path,
|
||||
"start_time": start,
|
||||
"end_time": min(start + chunk_duration, total_duration),
|
||||
}
|
||||
)
|
||||
else:
|
||||
print(f"Warning: Chunk {chunk_idx} may be empty")
|
||||
start += chunk_duration
|
||||
chunk_idx += 1
|
||||
|
||||
print(f"Created {len(chunks)} chunks in {output_dir}")
|
||||
return chunks, output_dir
|
||||
|
||||
|
||||
def transcribe_chunk(chunk_info, model, chunk_idx, total_chunks):
|
||||
"""Transcribe a single chunk."""
|
||||
print(
|
||||
f"[{chunk_idx + 1}/{total_chunks}] Transcribing chunk {chunk_info['start_time']:.1f}-{chunk_info['end_time']:.1f}"
|
||||
)
|
||||
start_time = time.time()
|
||||
|
||||
segments, info = model.transcribe(str(chunk_info["path"]), beam_size=5)
|
||||
results = []
|
||||
for segment in segments:
|
||||
# Adjust timestamps by chunk start time
|
||||
results.append(
|
||||
{
|
||||
"start": segment.start + chunk_info["start_time"],
|
||||
"end": segment.end + chunk_info["start_time"],
|
||||
"text": segment.text.strip(),
|
||||
}
|
||||
)
|
||||
|
||||
elapsed = time.time() - start_time
|
||||
print(f" → {len(results)} segments in {elapsed:.1f}s")
|
||||
return results, info
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="Chunked transcription")
|
||||
parser.add_argument("audio_path", help="Audio file path")
|
||||
parser.add_argument(
|
||||
"--chunk-duration",
|
||||
type=int,
|
||||
default=1800,
|
||||
help="Chunk duration in seconds (default: 1800 = 30 min)",
|
||||
)
|
||||
parser.add_argument("--model-size", default="tiny", help="Whisper model size")
|
||||
parser.add_argument("--compute-type", default="int8", help="Compute type")
|
||||
parser.add_argument(
|
||||
"--output", "-o", default="chunked_transcription.json", help="Output JSON path"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
audio_path = Path(args.audio_path)
|
||||
if not audio_path.exists():
|
||||
print(f"Error: File not found: {audio_path}")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Chunked Transcription for {audio_path}")
|
||||
print(f"Model: {args.model_size}, Compute: {args.compute_type}")
|
||||
print(
|
||||
f"Chunk duration: {args.chunk_duration}s ({args.chunk_duration / 60:.1f} min)"
|
||||
)
|
||||
|
||||
# Split audio
|
||||
chunks, temp_dir = split_audio(audio_path, chunk_duration=args.chunk_duration)
|
||||
if not chunks:
|
||||
print("No chunks created")
|
||||
sys.exit(1)
|
||||
|
||||
# Load model once
|
||||
print("Loading Whisper model...")
|
||||
from faster_whisper import WhisperModel
|
||||
|
||||
model_start = time.time()
|
||||
model = WhisperModel(args.model_size, device="cpu", compute_type=args.compute_type)
|
||||
print(f"Model loaded in {time.time() - model_start:.1f}s")
|
||||
|
||||
# Process each chunk
|
||||
all_segments = []
|
||||
language = None
|
||||
language_prob = None
|
||||
|
||||
for i, chunk in enumerate(chunks):
|
||||
try:
|
||||
segments, info = transcribe_chunk(chunk, model, i, len(chunks))
|
||||
all_segments.extend(segments)
|
||||
if language is None:
|
||||
language = info.language
|
||||
language_prob = info.language_probability
|
||||
except Exception as e:
|
||||
print(f"Error transcribing chunk {i}: {e}")
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
# Continue with next chunk
|
||||
|
||||
# Sort segments by start time
|
||||
all_segments.sort(key=lambda x: x["start"])
|
||||
|
||||
# Save results
|
||||
output = {
|
||||
"language": language or "unknown",
|
||||
"language_probability": language_prob or 0.0,
|
||||
"segments": all_segments,
|
||||
"chunk_count": len(chunks),
|
||||
"chunk_duration": args.chunk_duration,
|
||||
"total_segments": len(all_segments),
|
||||
}
|
||||
|
||||
output_path = Path(args.output)
|
||||
output_path.parent.mkdir(exist_ok=True, parents=True)
|
||||
with open(output_path, "w") as f:
|
||||
json.dump(output, f, indent=2)
|
||||
|
||||
print(f"\nTranscription completed:")
|
||||
print(f" Total segments: {len(all_segments)}")
|
||||
print(
|
||||
f" Language: {output['language']} (prob {output['language_probability']:.2f})"
|
||||
)
|
||||
print(f" Results saved to: {output_path}")
|
||||
|
||||
# Cleanup temp directory
|
||||
import shutil
|
||||
|
||||
shutil.rmtree(temp_dir, ignore_errors=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,64 +0,0 @@
|
||||
<?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>DB_MAX_CONNECTIONS</key>
|
||||
<string>50</string>
|
||||
|
||||
<key>DB_ACQUIRE_TIMEOUT</key>
|
||||
<string>30</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://127.0.0.1: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>
|
||||
@@ -1,98 +0,0 @@
|
||||
use anyhow::Result;
|
||||
use sqlx::postgres::PgPoolOptions;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
// Database connection
|
||||
let pool = PgPoolOptions::new()
|
||||
.max_connections(5)
|
||||
.connect("postgres://accusys@localhost:5432/momentry")
|
||||
.await?;
|
||||
|
||||
let video_uuid = "9760d0820f0cf9a7";
|
||||
let video_id = 28;
|
||||
let video_path = "/Users/accusys/momentry/var/sftpgo/data/demo/ExaSAN PCIe series - Director Ou Yu-Zhi Shares His Experience.mp4";
|
||||
|
||||
println!("Creating monitor job for video:");
|
||||
println!(" UUID: {}", video_uuid);
|
||||
println!(" ID: {}", video_id);
|
||||
println!(" Path: {}", video_path);
|
||||
|
||||
// 1. Create monitor job
|
||||
let job_row = sqlx::query(
|
||||
r#"
|
||||
INSERT INTO monitor_jobs (uuid, video_path, status)
|
||||
VALUES ($1, $2, 'pending')
|
||||
RETURNING id, uuid, video_path, status
|
||||
"#
|
||||
)
|
||||
.bind(video_uuid)
|
||||
.bind(video_path)
|
||||
.fetch_one(&pool)
|
||||
.await?;
|
||||
|
||||
let job_id: i32 = job_row.get(0);
|
||||
let job_uuid: String = job_row.get(1);
|
||||
let job_status: String = job_row.get(3);
|
||||
|
||||
println!("\nCreated monitor job:");
|
||||
println!(" Job ID: {}", job_id);
|
||||
println!(" Job UUID: {}", job_uuid);
|
||||
println!(" Status: {}", job_status);
|
||||
|
||||
// 2. Update video with job_id
|
||||
sqlx::query(
|
||||
r#"
|
||||
UPDATE videos
|
||||
SET job_id = $1, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $2
|
||||
"#
|
||||
)
|
||||
.bind(job_id)
|
||||
.bind(video_id)
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
|
||||
println!("Updated video {} with job_id {}", video_id, job_id);
|
||||
|
||||
// 3. Update monitor_jobs with video_id
|
||||
sqlx::query(
|
||||
r#"
|
||||
UPDATE monitor_jobs
|
||||
SET video_id = $1, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $2
|
||||
"#
|
||||
)
|
||||
.bind(video_id)
|
||||
.bind(job_id)
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
|
||||
println!("Updated monitor_jobs {} with video_id {}", job_id, video_id);
|
||||
|
||||
// 4. Create processor results for this job
|
||||
let processors = vec!["asr", "cut", "yolo", "ocr", "face", "pose", "asrx"];
|
||||
|
||||
for processor in processors {
|
||||
sqlx::query(
|
||||
r#"
|
||||
INSERT INTO processor_results (job_id, video_id, processor, status)
|
||||
VALUES ($1, $2, $3, 'pending')
|
||||
ON CONFLICT (job_id, processor) DO NOTHING
|
||||
"#
|
||||
)
|
||||
.bind(job_id)
|
||||
.bind(video_id)
|
||||
.bind(processor)
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
|
||||
println!("Created processor result for {}: {}", processor, job_id);
|
||||
}
|
||||
|
||||
println!("\n✅ Job creation completed successfully!");
|
||||
println!("Job ID: {}", job_id);
|
||||
println!("The worker should now pick up this job and start processing.");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
-- 1. Create monitor job
|
||||
INSERT INTO monitor_jobs (uuid, video_path, status)
|
||||
VALUES ('9760d0820f0cf9a7', '/Users/accusys/momentry/var/sftpgo/data/demo/ExaSAN PCIe series - Director Ou Yu-Zhi Shares His Experience.mp4', 'pending')
|
||||
RETURNING id;
|
||||
|
||||
-- Note: The job_id will be returned. Let's assume it's 18 for now.
|
||||
-- We'll run these commands step by step.
|
||||
150
debug_asr.py
150
debug_asr.py
@@ -1,150 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Debug ASR processing stages for large video.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import subprocess
|
||||
import tempfile
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def run_ffmpeg_extract(video_path, audio_path):
|
||||
"""Extract audio using ffmpeg."""
|
||||
cmd = [
|
||||
"ffmpeg",
|
||||
"-i",
|
||||
str(video_path),
|
||||
"-vn",
|
||||
"-acodec",
|
||||
"pcm_s16le",
|
||||
"-ar",
|
||||
"16000",
|
||||
"-ac",
|
||||
"1",
|
||||
"-y",
|
||||
str(audio_path),
|
||||
]
|
||||
print(f"Running ffmpeg: {' '.join(cmd)}")
|
||||
start = time.time()
|
||||
proc = subprocess.run(cmd, capture_output=True, text=True)
|
||||
elapsed = time.time() - start
|
||||
print(f"ffmpeg completed in {elapsed:.1f}s, return code: {proc.returncode}")
|
||||
if proc.returncode != 0:
|
||||
print(f"stderr: {proc.stderr[:500]}")
|
||||
return proc.returncode == 0, elapsed
|
||||
|
||||
|
||||
def test_asr_stages(video_path):
|
||||
"""Test ASR stages step by step."""
|
||||
video_path = Path(video_path)
|
||||
print(f"Testing video: {video_path}")
|
||||
print(f"Size: {video_path.stat().st_size / 1024 / 1024:.1f} MB")
|
||||
|
||||
# Stage 1: Check audio streams
|
||||
print("\n=== Stage 1: Check audio streams ===")
|
||||
cmd = [
|
||||
"ffprobe",
|
||||
"-v",
|
||||
"error",
|
||||
"-select_streams",
|
||||
"a",
|
||||
"-show_entries",
|
||||
"stream=codec_name,channels,sample_rate,duration",
|
||||
"-of",
|
||||
"csv=p=0",
|
||||
str(video_path),
|
||||
]
|
||||
proc = subprocess.run(cmd, capture_output=True, text=True)
|
||||
print(f"Audio streams: {proc.stdout.strip()}")
|
||||
|
||||
# Stage 2: Extract audio
|
||||
print("\n=== Stage 2: Extract audio ===")
|
||||
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f:
|
||||
audio_path = f.name
|
||||
try:
|
||||
success, extract_time = run_ffmpeg_extract(video_path, audio_path)
|
||||
if success:
|
||||
print(f"Audio extracted to {audio_path}")
|
||||
print(f"Audio size: {Path(audio_path).stat().st_size / 1024 / 1024:.1f} MB")
|
||||
else:
|
||||
print("Audio extraction failed")
|
||||
os.unlink(audio_path)
|
||||
return
|
||||
except Exception as e:
|
||||
print(f"Error extracting audio: {e}")
|
||||
return
|
||||
|
||||
# Stage 3: Load faster_whisper model (just import)
|
||||
print("\n=== Stage 3: Test faster_whisper import ===")
|
||||
try:
|
||||
start = time.time()
|
||||
from faster_whisper import WhisperModel
|
||||
|
||||
elapsed = time.time() - start
|
||||
print(f"Import faster_whisper: {elapsed:.1f}s")
|
||||
except Exception as e:
|
||||
print(f"Import failed: {e}")
|
||||
os.unlink(audio_path)
|
||||
return
|
||||
|
||||
# Stage 4: Transcribe a small segment (first 30 seconds)
|
||||
print("\n=== Stage 4: Transcribe first 30 seconds ===")
|
||||
try:
|
||||
# Trim audio to first 30 seconds
|
||||
trim_path = audio_path + ".trim.wav"
|
||||
cmd = [
|
||||
"ffmpeg",
|
||||
"-i",
|
||||
audio_path,
|
||||
"-t",
|
||||
"30",
|
||||
"-acodec",
|
||||
"pcm_s16le",
|
||||
"-ar",
|
||||
"16000",
|
||||
"-ac",
|
||||
"1",
|
||||
"-y",
|
||||
trim_path,
|
||||
]
|
||||
subprocess.run(cmd, capture_output=True)
|
||||
|
||||
# Load model with small model
|
||||
start = time.time()
|
||||
model = WhisperModel("tiny", device="cpu", compute_type="int8")
|
||||
load_time = time.time() - start
|
||||
print(f"Model loaded in {load_time:.1f}s")
|
||||
|
||||
# Transcribe
|
||||
start = time.time()
|
||||
segments, info = model.transcribe(trim_path, beam_size=5)
|
||||
segments = list(segments) # Force processing
|
||||
transcribe_time = time.time() - start
|
||||
print(f"Transcription of 30s audio: {transcribe_time:.1f}s")
|
||||
print(
|
||||
f"Detected language: {info.language} with probability {info.language_probability}"
|
||||
)
|
||||
print(f"Segments found: {len(segments)}")
|
||||
|
||||
# Cleanup
|
||||
os.unlink(trim_path)
|
||||
except Exception as e:
|
||||
print(f"Transcription test failed: {e}")
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
os.unlink(audio_path)
|
||||
|
||||
print("\n=== Debug complete ===")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 2:
|
||||
print(f"Usage: {sys.argv[0]} <video_file>")
|
||||
sys.exit(1)
|
||||
test_asr_stages(sys.argv[1])
|
||||
@@ -1,85 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import time
|
||||
|
||||
print("Start")
|
||||
print("Importing faster_whisper...")
|
||||
try:
|
||||
from faster_whisper import WhisperModel
|
||||
|
||||
print("Import successful")
|
||||
except Exception as e:
|
||||
print(f"Import failed: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
print("Loading model...")
|
||||
try:
|
||||
model = WhisperModel("tiny", device="cpu", compute_type="int8")
|
||||
print("Model loaded")
|
||||
except Exception as e:
|
||||
print(f"Model load failed: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
import subprocess
|
||||
|
||||
print("Getting duration...")
|
||||
cmd = [
|
||||
"ffprobe",
|
||||
"-v",
|
||||
"error",
|
||||
"-show_entries",
|
||||
"format=duration",
|
||||
"-of",
|
||||
"csv=p=0",
|
||||
"/tmp/test_audio.wav",
|
||||
]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
print(f"ffprobe output: {result.stdout}")
|
||||
duration = float(result.stdout.strip())
|
||||
print(f"Duration: {duration}")
|
||||
|
||||
# Extract first chunk
|
||||
print("Extracting first chunk...")
|
||||
chunk_path = "/tmp/debug_chunk.wav"
|
||||
cmd = [
|
||||
"ffmpeg",
|
||||
"-i",
|
||||
"/tmp/test_audio.wav",
|
||||
"-t",
|
||||
"60",
|
||||
"-acodec",
|
||||
"pcm_s16le",
|
||||
"-ar",
|
||||
"16000",
|
||||
"-ac",
|
||||
"1",
|
||||
"-y",
|
||||
chunk_path,
|
||||
]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
print(f"ffmpeg return code: {result.returncode}")
|
||||
if result.returncode != 0:
|
||||
print(f"stderr: {result.stderr[:200]}")
|
||||
|
||||
import os
|
||||
|
||||
print(f"Chunk exists: {os.path.exists(chunk_path)}")
|
||||
if os.path.exists(chunk_path):
|
||||
print(f"Chunk size: {os.path.getsize(chunk_path)}")
|
||||
|
||||
print("Transcribing chunk...")
|
||||
start = time.time()
|
||||
try:
|
||||
segments, info = model.transcribe(chunk_path, beam_size=5)
|
||||
segments = list(segments)
|
||||
elapsed = time.time() - start
|
||||
print(f"Transcription succeeded in {elapsed}s, segments: {len(segments)}")
|
||||
except Exception as e:
|
||||
print(f"Transcription failed: {e}")
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
else:
|
||||
print("Chunk not created")
|
||||
|
||||
print("Script finished")
|
||||
@@ -1,442 +0,0 @@
|
||||
# People API 设计方案 (marcom 需求等效映射)
|
||||
|
||||
**日期**: 2026-04-28
|
||||
**状态**: 设计阶段
|
||||
**目的**: 根据 marcom 团队需求,在符合现有架构的前提下提供等效 API
|
||||
|
||||
---
|
||||
|
||||
## 设计原则
|
||||
|
||||
1. **遵循 RESTful 规范**: 使用标准 HTTP 方法 (GET, POST, PATCH, DELETE)
|
||||
2. **统一路径前缀**: `/api/v1/people`
|
||||
3. **响应格式统一**: `{ success: bool, message: string, data: any }`
|
||||
4. **向后兼容**: 现有 API 保持不变,新 API 扩展功能
|
||||
5. **符合 Identity 系统**: 与 `identities` 表和 `identity_bindings` 表集成
|
||||
|
||||
---
|
||||
|
||||
## API 对照表
|
||||
|
||||
### 1. GET /people/candidates (候选人物)
|
||||
|
||||
**marcom 需求**: 获取待确认的人物候选列表
|
||||
|
||||
**等效 API**:
|
||||
```
|
||||
GET /api/v1/people/candidates?file_uuid={uuid}&limit={n}
|
||||
```
|
||||
|
||||
**功能**:
|
||||
- 返回待确认的人物身份候选
|
||||
- 包含 face cluster、speaker cluster 的匹配建议
|
||||
- 状态: `pending`, `suggested`, `unmatched`
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Found 15 candidates",
|
||||
"data": {
|
||||
"candidates": [
|
||||
{
|
||||
"candidate_id": "face_cluster_1",
|
||||
"type": "face",
|
||||
"suggested_identity": {
|
||||
"id": 123,
|
||||
"name": "张曼玉",
|
||||
"confidence": 0.92
|
||||
},
|
||||
"appearance_count": 45,
|
||||
"status": "pending"
|
||||
}
|
||||
],
|
||||
"total": 15
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**实现**: 扩展现有 `/api/v1/people/suggest`
|
||||
|
||||
---
|
||||
|
||||
### 2. GET /people (人物列表)
|
||||
|
||||
**marcom 需求**: 获取所有人物列表
|
||||
|
||||
**等效 API**:
|
||||
```
|
||||
GET /api/v1/people?file_uuid={uuid}&limit={n}&offset={n}&status={status}
|
||||
```
|
||||
|
||||
**功能**:
|
||||
- 返回人物身份列表
|
||||
- 支持按 file_uuid 筛选
|
||||
- 支持分页
|
||||
- 支持按状态筛选 (confirmed, pending, all)
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Found 8 persons",
|
||||
"data": {
|
||||
"persons": [
|
||||
{
|
||||
"identity_id": "Person_17",
|
||||
"name": "张曼玉",
|
||||
"appearance_count": 45,
|
||||
"total_duration": 350.2,
|
||||
"is_confirmed": true
|
||||
}
|
||||
],
|
||||
"total": 8
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**实现**: 现有 `/api/v1/people/list` 已支持
|
||||
|
||||
---
|
||||
|
||||
### 3. GET /people/{identity_id} (人物详情)
|
||||
|
||||
**marcom 需求**: 获取人物详情
|
||||
|
||||
**等效 API**:
|
||||
```
|
||||
GET /api/v1/people/{identity_id}?file_uuid={uuid}
|
||||
```
|
||||
|
||||
**功能**:
|
||||
- 返回人物详细信息
|
||||
- 包含出场时间线
|
||||
- 包含关联的 face/speaker
|
||||
- 包含缩略图
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"identity_id": "Person_17",
|
||||
"name": "张曼玉",
|
||||
"face_identity_id": 123,
|
||||
"speaker_id": "SPEAKER_00",
|
||||
"appearance_count": 45,
|
||||
"total_duration": 350.2,
|
||||
"first_appearance_time": 10.5,
|
||||
"last_appearance_time": 360.2,
|
||||
"timeline": [...],
|
||||
"thumbnails": [...]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**实现**: 现有 `/api/v1/people/:person_id` 已支持
|
||||
|
||||
---
|
||||
|
||||
### 4. POST /people (创建人物)
|
||||
|
||||
**marcom 需求**: 手动创建新人物
|
||||
|
||||
**等效 API**:
|
||||
```
|
||||
POST /api/v1/people
|
||||
Body: { "name": "张曼玉", "file_uuid": "xxx", "metadata": {...} }
|
||||
```
|
||||
|
||||
**功能**:
|
||||
- 创建新人物身份
|
||||
- 关联到指定视频
|
||||
- 支持添加 metadata (角色名、演员名等)
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Person created",
|
||||
"data": {
|
||||
"identity_id": "Person_99",
|
||||
"name": "张曼玉",
|
||||
"file_uuid": "xxx"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**实现**: 需新增,参考 `CreatePersonIdentityRequest`
|
||||
|
||||
---
|
||||
|
||||
### 5. PATCH /people/{identity_id} (更新人物)
|
||||
|
||||
**marcom 需求**: 更新人物信息
|
||||
|
||||
**等效 API**:
|
||||
```
|
||||
PATCH /api/v1/people/{identity_id}
|
||||
Body: { "name": "新名字", "is_confirmed": true, "metadata": {...} }
|
||||
```
|
||||
|
||||
**功能**:
|
||||
- 更新人物名称
|
||||
- 确认人物身份
|
||||
- 更新 metadata
|
||||
|
||||
**实现**: 现有 `/api/v1/people/:person_id` (PATCH) 已支持
|
||||
|
||||
---
|
||||
|
||||
### 6. POST /people/merge (合并人物)
|
||||
|
||||
**marcom 需求**: 合并多个人物为一个
|
||||
|
||||
**等效 API**:
|
||||
```
|
||||
POST /api/v1/people/merge
|
||||
Body: {
|
||||
"target_identity_id": "Person_17",
|
||||
"source_identity_ids": ["Person_18", "Person_19"]
|
||||
}
|
||||
```
|
||||
|
||||
**功能**:
|
||||
- 合并多个人物身份
|
||||
- 转移所有出场记录
|
||||
- 更新统计数据
|
||||
|
||||
**实现**: 现有 `/api/v1/people/merge` 已支持
|
||||
|
||||
---
|
||||
|
||||
### 7. POST /people/skip (跳过人物)
|
||||
|
||||
**marcom 需求**: 跳过某个候选人物(不处理)
|
||||
|
||||
**等效 API**:
|
||||
```
|
||||
POST /api/v1/people/skip
|
||||
Body: { "candidate_id": "face_cluster_2", "reason": "非人物" }
|
||||
```
|
||||
|
||||
**功能**:
|
||||
- 标记候选为"已跳过"
|
||||
- 记录跳过原因
|
||||
- 不创建人物身份
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Candidate skipped",
|
||||
"data": {
|
||||
"candidate_id": "face_cluster_2",
|
||||
"status": "skipped",
|
||||
"reason": "非人物"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**实现**: 需新增,扩展候选管理功能
|
||||
|
||||
---
|
||||
|
||||
### 8. POST /people/{identity_id}/remove-face (移除人脸)
|
||||
|
||||
**marcom 需求**: 从人物身份中移除特定人脸绑定
|
||||
|
||||
**等效 API**:
|
||||
```
|
||||
POST /api/v1/people/{identity_id}/unbind
|
||||
Body: { "binding_type": "face", "binding_value": "face_123" }
|
||||
```
|
||||
|
||||
**功能**:
|
||||
- 解绑人脸与人物身份的关联
|
||||
- 人脸回到候选状态
|
||||
- 更新人物出场统计
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Face unbound",
|
||||
"data": {
|
||||
"identity_id": "Person_17",
|
||||
"unbound_face": "face_123",
|
||||
"updated_appearance_count": 42
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**实现**: 需新增,参考现有 `UnbindIdentityRequest`
|
||||
|
||||
---
|
||||
|
||||
### 9. POST /people/split-face (分离人脸)
|
||||
|
||||
**marcom 需求**: 将人脸从现有人物分离为新人物
|
||||
|
||||
**等效 API**:
|
||||
```
|
||||
POST /api/v1/people/split
|
||||
Body: {
|
||||
"source_identity_id": "Person_17",
|
||||
"face_ids": ["face_123", "face_124"],
|
||||
"new_identity_name": "新人物"
|
||||
}
|
||||
```
|
||||
|
||||
**功能**:
|
||||
- 从现有人物分离指定人脸
|
||||
- 创建新人物身份
|
||||
- 转移出场记录
|
||||
|
||||
**实现**: 现有 `/api/v1/people/:person_id/split` 部分支持
|
||||
|
||||
---
|
||||
|
||||
### 10. GET /people/{identity_id}/resolve (解决冲突)
|
||||
|
||||
**marcom 需求**: 获取人物的冲突/歧义信息
|
||||
|
||||
**等效 API**:
|
||||
```
|
||||
GET /api/v1/people/{identity_id}/conflicts
|
||||
```
|
||||
|
||||
**功能**:
|
||||
- 返回人物身份的潜在冲突
|
||||
- 显示相似人脸/声音的匹配
|
||||
- 提供解决方案建议
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"identity_id": "Person_17",
|
||||
"conflicts": [
|
||||
{
|
||||
"type": "similar_face",
|
||||
"conflicting_identity": "Person_18",
|
||||
"similarity": 0.85,
|
||||
"suggestion": "merge"
|
||||
}
|
||||
],
|
||||
"resolution_options": ["merge", "keep_separate", "skip"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**实现**: 需新增
|
||||
|
||||
---
|
||||
|
||||
### 11. POST /search (搜索)
|
||||
|
||||
**marcom 需求**: 搜索人物
|
||||
|
||||
**等效 API**:
|
||||
```
|
||||
POST /api/v1/people/search
|
||||
Body: {
|
||||
"query": "张",
|
||||
"filters": { "type": "people", "file_uuid": "xxx" },
|
||||
"limit": 20
|
||||
}
|
||||
```
|
||||
|
||||
**功能**:
|
||||
- 搜索人物身份
|
||||
- 支持按名称、类型、视频筛选
|
||||
- 返回匹配结果
|
||||
|
||||
**实现**: 现有 `/api/v1/identities/search` 已支持,建议扩展
|
||||
|
||||
---
|
||||
|
||||
### 12. GET /people/status (人物状态)
|
||||
|
||||
**marcom 需求**: 获取人物处理状态统计
|
||||
|
||||
**等效 API**:
|
||||
```
|
||||
GET /api/v1/people/status?file_uuid={uuid}
|
||||
```
|
||||
|
||||
**功能**:
|
||||
- 返回人物处理统计
|
||||
- 待确认数量、已确认数量、跳过数量
|
||||
- 合并历史
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"file_uuid": "xxx",
|
||||
"total_candidates": 15,
|
||||
"confirmed": 8,
|
||||
"pending": 5,
|
||||
"skipped": 2,
|
||||
"merge_count": 3,
|
||||
"split_count": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**实现**: 需新增
|
||||
|
||||
---
|
||||
|
||||
## 实现优先级
|
||||
|
||||
| 优先级 | API | 状态 | 预估工时 |
|
||||
|--------|-----|------|----------|
|
||||
| **P0** | GET /people | ✅ 已有 | 0h |
|
||||
| **P0** | GET /people/{identity_id} | ✅ 已有 | 0h |
|
||||
| **P0** | PATCH /people/{identity_id} | ✅ 已有 | 0h |
|
||||
| **P0** | POST /people/merge | ✅ 已有 | 0h |
|
||||
| **P1** | GET /people/candidates | ⚠️ 扩展 | 2h |
|
||||
| **P1** | POST /people | ❌ 新增 | 2h |
|
||||
| **P1** | POST /people/search | ⚠️ 扩展 | 1h |
|
||||
| **P2** | POST /people/skip | ❌ 新增 | 2h |
|
||||
| **P2** | POST /people/{identity_id}/unbind | ❌ 新增 | 2h |
|
||||
| **P2** | POST /people/split | ⚠️ 扩展 | 1h |
|
||||
| **P2** | GET /people/{identity_id}/conflicts | ❌ 新增 | 3h |
|
||||
| **P2** | GET /people/status | ❌ 新增 | 2h |
|
||||
|
||||
**总预估**: ~13h (P1+P2)
|
||||
|
||||
---
|
||||
|
||||
## 数据库表需求
|
||||
|
||||
现有表结构支持大部分需求,可能需要扩展:
|
||||
|
||||
```sql
|
||||
-- 建议新增: candidates 表 (候选管理)
|
||||
CREATE TABLE person_candidates (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
file_uuid VARCHAR(36) NOT NULL,
|
||||
candidate_type VARCHAR(20), -- 'face', 'speaker'
|
||||
candidate_id VARCHAR(50), -- 'face_cluster_1', 'speaker_2'
|
||||
suggested_identity_id BIGINT,
|
||||
confidence FLOAT,
|
||||
status VARCHAR(20), -- 'pending', 'confirmed', 'skipped'
|
||||
skip_reason TEXT,
|
||||
created_at TIMESTAMP,
|
||||
updated_at TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 参考文档
|
||||
|
||||
- `docs_v1.0/ARCHITECTURE/MOMENTRY_CORE_ARCHITECTURE_V2.md` - Identity 系统设计
|
||||
- `docs_v1.0/ARCHITECTURE/PERSON_IDENTITY_INTEGRATION.md` - Person Identity 整合
|
||||
- `src/api/person_identity.rs` - 现有 API 实现
|
||||
- `src/api/identity_binding.rs` - 身份绑定 API
|
||||
@@ -1,699 +0,0 @@
|
||||
# Momentry Core API Documentation v1.0.0
|
||||
|
||||
## Overview
|
||||
Momentry Core is a digital asset management system with video analysis, RAG, and face recognition capabilities. This document covers all API endpoints available in v1.0.0.
|
||||
|
||||
**Base URL**: `http://<host>:<port>`
|
||||
- Production: Port 3002
|
||||
- Development (Playground): Port 3003
|
||||
|
||||
**Authentication**: All protected routes require API key validation via `X-API-Key` header.
|
||||
|
||||
---
|
||||
|
||||
## API Classification
|
||||
|
||||
The API is organized into 7 categories:
|
||||
|
||||
| Category | Prefix | Description |
|
||||
|----------|--------|-------------|
|
||||
| **Health & Auth** | `/health`, `/api/v1/auth` | System health, authentication |
|
||||
| **Asset Management** | `/api/v1/register`, `/api/v1/files`, `/api/v1/assets` | File registration, probing, processing |
|
||||
| **Search** | `/api/v1/search`, `/api/v1/n8n` | Text, hybrid, visual, and n8n search |
|
||||
| **Video Details** | `/api/v1/videos`, `/api/v1/progress` | Video listing, details, chunks |
|
||||
| **Identity & Binding** | `/api/v1/identities`, `/api/v1/signals` | Face/speaker identity management |
|
||||
| **Jobs & Rules** | `/api/v1/jobs`, `/api/v1/rules` | Processing job monitoring |
|
||||
| **Stats & Config** | `/api/v1/stats`, `/api/v1/config` | System statistics, configuration |
|
||||
|
||||
---
|
||||
|
||||
## 1. Health & Authentication
|
||||
|
||||
### `GET /health`
|
||||
Basic health check.
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"version": "v1.0.0",
|
||||
"uptime_ms": 12345
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /health/detailed`
|
||||
Detailed health check with service status (PostgreSQL, Redis, Qdrant, MongoDB).
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"version": "v1.0.0",
|
||||
"uptime_ms": 12345,
|
||||
"services": {
|
||||
"postgres": { "status": "ok", "latency_ms": 5 },
|
||||
"redis": { "status": "ok", "latency_ms": 2 },
|
||||
"qdrant": { "status": "ok", "latency_ms": 10 },
|
||||
"mongodb": { "status": "ok", "latency_ms": 8 }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/auth/login`
|
||||
Authenticate and obtain API key.
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"username": "demo",
|
||||
"password": "demo"
|
||||
}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Login successful",
|
||||
"api_key": "muser_test_001",
|
||||
"user": { "username": "demo" }
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/auth/logout`
|
||||
Logout session.
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{ "success": true }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Asset Management
|
||||
|
||||
### `POST /api/v1/register`
|
||||
Register a video file (legacy path-based).
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{ "path": "./demo/video.mp4" }
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"file_uuid": "384b0ff44aaaa1f1",
|
||||
"file_id": 1,
|
||||
"job_id": 1,
|
||||
"file_name": "video.mp4",
|
||||
"duration": 120.5,
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"already_exists": false
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/files/register`
|
||||
Register a file with full metadata (recommended). Supports move detection.
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4",
|
||||
"user_id": null
|
||||
}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"file_uuid": "384b0ff44aaaa1f1",
|
||||
"file_name": "video.mp4",
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4",
|
||||
"file_type": "video",
|
||||
"duration": 120.5,
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"fps": 30.0,
|
||||
"total_frames": 3615,
|
||||
"registration_time": null,
|
||||
"already_exists": false,
|
||||
"message": "File registered successfully"
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/files/scan`
|
||||
Scan filesystem for unregistered files.
|
||||
|
||||
### `POST /api/v1/unregister`
|
||||
Unregister a video file.
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{ "uuid": "384b0ff44aaaa1f1" }
|
||||
```
|
||||
|
||||
### `POST /api/v1/probe`
|
||||
Probe a video file for metadata.
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{ "path": "./demo/video.mp4" }
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"uuid": "384b0ff44aaaa1f1",
|
||||
"file_name": "video.mp4",
|
||||
"duration": 120.5,
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"fps": 30.0,
|
||||
"cached": true,
|
||||
"format": { ... },
|
||||
"streams": [ ... ]
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/assets/:uuid/probe`
|
||||
Probe a video by UUID.
|
||||
|
||||
### `POST /api/v1/assets/:uuid/process`
|
||||
Trigger processing pipeline for an asset.
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"processors": ["asr", "cut", "yolo", "ocr", "face", "pose", "asrx", "visual_chunk"]
|
||||
}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"job_id": 1,
|
||||
"asset_uuid": "384b0ff44aaaa1f1",
|
||||
"status": "PENDING",
|
||||
"message": "Processing triggered for video.mp4"
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/assets/:uuid/status`
|
||||
Get asset processing status with frame progress.
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"uuid": "384b0ff44aaaa1f1",
|
||||
"file_name": "video.mp4",
|
||||
"registration_time": "2026-04-30T10:00:00Z",
|
||||
"processing_status": "processing",
|
||||
"current_job_id": "abc-123",
|
||||
"frame_progress": {
|
||||
"total_frames": 3615,
|
||||
"processed_frames": 1200,
|
||||
"progress_percent": 33.2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Search
|
||||
|
||||
### `POST /api/v1/search`
|
||||
Vector/smart search across chunks.
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"query": "person talking about AI",
|
||||
"mode": "smart",
|
||||
"uuid": "384b0ff44aaaa1f1",
|
||||
"limit": 10
|
||||
}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uuid": "384b0ff44aaaa1f1",
|
||||
"chunk_id": "chunk_1",
|
||||
"chunk_type": "sentence",
|
||||
"start_time": 10.5,
|
||||
"end_time": 15.2,
|
||||
"text": "AI is transforming...",
|
||||
"score": 0.85
|
||||
}
|
||||
],
|
||||
"query": "person talking about AI"
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/search/hybrid`
|
||||
Hybrid search (vector + BM25).
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"query": "search term",
|
||||
"limit": 10,
|
||||
"uuid": "384b0ff44aaaa1f1",
|
||||
"vector_weight": 0.7,
|
||||
"bm25_weight": 0.3
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/search/bm25`
|
||||
BM25 full-text search.
|
||||
|
||||
### `POST /api/v1/search/visual`
|
||||
Search visual chunks by criteria.
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"uuid": "384b0ff44aaaa1f1",
|
||||
"criteria": {
|
||||
"object_class": "person",
|
||||
"min_count": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/search/visual/class`
|
||||
Search by object class.
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"uuid": "384b0ff44aaaa1f1",
|
||||
"object_class": "person",
|
||||
"min_count": 1,
|
||||
"max_count": null
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/search/visual/density`
|
||||
Search by object density.
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"uuid": "384b0ff44aaaa1f1",
|
||||
"min_density": 0.5,
|
||||
"max_density": null
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/search/visual/combination`
|
||||
Search by object combination.
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"uuid": "384b0ff44aaaa1f1",
|
||||
"combination": [["person", 2], ["car", 1]]
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/search/visual/stats`
|
||||
Get visual chunk statistics.
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{ "uuid": "384b0ff44aaaa1f1" }
|
||||
```
|
||||
|
||||
### `POST /api/v1/n8n/search`
|
||||
Search via n8n integration.
|
||||
|
||||
### `POST /api/v1/n8n/search/bm25`
|
||||
BM25 search via n8n.
|
||||
|
||||
### `POST /api/v1/n8n/search/hybrid`
|
||||
Hybrid search via n8n.
|
||||
|
||||
### `POST /api/v1/n8n/search/smart`
|
||||
Smart search via n8n.
|
||||
|
||||
---
|
||||
|
||||
## 4. Video Details
|
||||
|
||||
### `GET /api/v1/videos`
|
||||
List all registered videos with pagination.
|
||||
|
||||
**Query Parameters**:
|
||||
- `page`: Page number (default: 1)
|
||||
- `page_size`: Items per page (default: 20)
|
||||
- `status`: Filter by status
|
||||
- `q`: Search query
|
||||
- `uuid`: Filter by UUID
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"file_uuid": "384b0ff44aaaa1f1",
|
||||
"file_path": "/path/to/video.mp4",
|
||||
"file_name": "video.mp4",
|
||||
"file_type": "video",
|
||||
"duration": 120.5,
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"status": "completed",
|
||||
"created_at": "2026-04-30T10:00:00Z",
|
||||
"file_size": 52428800,
|
||||
"total_frames": 3615
|
||||
}
|
||||
],
|
||||
"count": 1,
|
||||
"page": 1,
|
||||
"page_size": 20
|
||||
}
|
||||
```
|
||||
|
||||
### `DELETE /api/v1/videos/:uuid`
|
||||
Delete a video and all associated data (faces, chunks, processor results).
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "File 384b0ff44aaaa1f1 unregistered successfully...",
|
||||
"file_uuid": "384b0ff44aaaa1f1",
|
||||
"deleted_face_detections": 150,
|
||||
"deleted_processor_results": 8,
|
||||
"deleted_chunks": 45
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/videos/:uuid/details`
|
||||
Get detailed chunk information.
|
||||
|
||||
**Query Parameters**:
|
||||
- `chunk_id`: Specific chunk ID (required)
|
||||
- `parent_id`: Parent chunk ID
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"uuid": "384b0ff44aaaa1f1",
|
||||
"chunk_id": "chunk_1",
|
||||
"chunk_type": "sentence",
|
||||
"frame_range": {
|
||||
"start_frame": 315,
|
||||
"end_frame": 456,
|
||||
"duration_frames": 141,
|
||||
"fps": 30.0
|
||||
},
|
||||
"reference_time": {
|
||||
"start": 10.5,
|
||||
"end": 15.2
|
||||
},
|
||||
"text_content": "AI is transforming...",
|
||||
"summary_text": "Discussion about AI impact",
|
||||
"speaker_ids": ["SPEAKER_0"],
|
||||
"person_ids": ["face_100"]
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/videos/:uuid/pre_chunks`
|
||||
List pre-processor chunks.
|
||||
|
||||
**Query Parameters**:
|
||||
- `processor_type`: Filter by processor (asr, yolo, face, etc.)
|
||||
- `page`: Page number
|
||||
- `page_size`: Items per page
|
||||
|
||||
### `GET /api/v1/progress/:uuid`
|
||||
Get processing progress for a video.
|
||||
|
||||
---
|
||||
|
||||
## 5. Identity & Binding
|
||||
|
||||
### `POST /api/v1/identities/from-face`
|
||||
Register a global identity from face.json with multi-angle reference vectors.
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"face_json_path": "/path/to/face.json",
|
||||
"identity_name": "John Doe",
|
||||
"schema": "dev"
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/identities/from-person`
|
||||
Register identity from a person in a video.
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"file_uuid": "384b0ff44aaaa1f1",
|
||||
"person_id": "person_1",
|
||||
"identity_name": "John Doe"
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/identities`
|
||||
List all global identities.
|
||||
|
||||
**Query Parameters**:
|
||||
- `page`: Page number
|
||||
- `page_size`: Items per page
|
||||
|
||||
### `GET /api/v1/faces/candidates`
|
||||
List unbound face candidates.
|
||||
|
||||
**Query Parameters**:
|
||||
- `file_uuid`: Filter by file
|
||||
- `min_confidence`: Minimum confidence (default: 0.5)
|
||||
- `page`, `page_size`: Pagination
|
||||
|
||||
### `GET /api/v1/identities/:identity_id/faces`
|
||||
Get all faces for an identity.
|
||||
|
||||
### `GET /api/v1/faces/:face_id/thumbnail`
|
||||
Get face thumbnail image (JPEG).
|
||||
|
||||
### `POST /api/v1/identities/bind`
|
||||
Bind a face/speaker to an identity.
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"identity_id": 1,
|
||||
"binding_type": "face",
|
||||
"binding_value": "face_100",
|
||||
"source": "manual"
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/identities/unbind`
|
||||
Unbind an identity.
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"binding_type": "face",
|
||||
"binding_value": "face_100"
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/identity/:binding_type/:binding_value`
|
||||
Get identity info by binding.
|
||||
|
||||
### `GET /api/v1/signals/unbound`
|
||||
List unbound signals.
|
||||
|
||||
**Query Parameters**:
|
||||
- `uuid`: File UUID
|
||||
- `binding_type`: "face" or "speaker"
|
||||
|
||||
### `GET /api/v1/signals/:uuid/:binding_type/:binding_value/timeline`
|
||||
Get signal timeline (all chunks for a face/speaker).
|
||||
|
||||
### `POST /api/v1/identities/suggest-av`
|
||||
Suggest audio-visual bindings based on temporal overlap.
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"file_uuid": "384b0ff44aaaa1f1",
|
||||
"overlap_threshold": 0.6
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Jobs & Rules
|
||||
|
||||
### `GET /api/v1/jobs`
|
||||
List all monitor jobs.
|
||||
|
||||
**Query Parameters**:
|
||||
- `page`, `page_size`: Pagination
|
||||
- `status`: Filter by status
|
||||
|
||||
### `GET /api/v1/jobs/:job_id`
|
||||
Get job details with processor information.
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"job_id": "1",
|
||||
"asset_uuid": "384b0ff44aaaa1f1",
|
||||
"rule": "default",
|
||||
"status": "RUNNING",
|
||||
"current_processor_id": "asr",
|
||||
"frame_progress": {
|
||||
"total_frames": 3615,
|
||||
"processed_frames": 1200,
|
||||
"progress_percent": 33.2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/rules/:rule/status`
|
||||
Get rule status with active jobs.
|
||||
|
||||
---
|
||||
|
||||
## 7. Stats & Configuration
|
||||
|
||||
### `GET /api/v1/stats/ingest`
|
||||
Get ingestion statistics.
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"total_videos": 50,
|
||||
"total_chunks": 1200,
|
||||
"sentence_chunks": 800,
|
||||
"cut_chunks": 300,
|
||||
"time_chunks": 100,
|
||||
"searchable_chunks": 1150,
|
||||
"chunks_with_visual": 450,
|
||||
"chunks_with_summary": 200,
|
||||
"pending_videos": 5
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/stats/sftpgo`
|
||||
Get SFTPGo status and registered videos.
|
||||
|
||||
### `GET /api/v1/stats/inference`
|
||||
Check inference engine health (Ollama, llama-server).
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"ollama": {
|
||||
"engine": "Ollama",
|
||||
"model": "nomic-embed-text",
|
||||
"status": "ok",
|
||||
"latency_ms": 15
|
||||
},
|
||||
"llama_server": {
|
||||
"engine": "llama-server",
|
||||
"model": "gemma4_e4b_q5",
|
||||
"status": "ok",
|
||||
"latency_ms": 25
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/config/cache`
|
||||
Toggle MongoDB cache.
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{ "enabled": false }
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"cache_enabled": false,
|
||||
"message": "Cache disabled"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Usage Patterns
|
||||
|
||||
### 1. List Pattern
|
||||
```
|
||||
GET /api/v1/videos?page=1&page_size=20
|
||||
```
|
||||
- Supports pagination
|
||||
- Optional filters via query parameters
|
||||
- Returns `{ items: [...], count, page, page_size }`
|
||||
|
||||
### 2. Detail Pattern
|
||||
```
|
||||
GET /api/v1/videos/:uuid/details?chunk_id=chunk_1
|
||||
```
|
||||
- Path parameter for resource identifier
|
||||
- Query parameters for sub-resource selection
|
||||
- Returns detailed object with nested structures
|
||||
|
||||
### 3. Operation Pattern
|
||||
```
|
||||
POST /api/v1/assets/:uuid/process
|
||||
```
|
||||
- Action-oriented endpoint
|
||||
- Request body contains operation parameters
|
||||
- Returns operation status and job ID
|
||||
|
||||
### 4. Application Pattern
|
||||
```
|
||||
POST /api/v1/identities/bind
|
||||
POST /api/v1/identities/suggest-av
|
||||
```
|
||||
- Complex workflows with multiple steps
|
||||
- Often involve external services (Python scripts, FFmpeg)
|
||||
- Return comprehensive results with metadata
|
||||
|
||||
---
|
||||
|
||||
## Error Responses
|
||||
|
||||
| Status Code | Description |
|
||||
|-------------|-------------|
|
||||
| `400` | Bad Request - Invalid parameters |
|
||||
| `404` | Not Found - Resource doesn't exist |
|
||||
| `500` | Internal Server Error - Database/service failure |
|
||||
|
||||
---
|
||||
|
||||
## V4.0 Architecture Notes
|
||||
|
||||
### Key Changes from V3.x
|
||||
- `video_uuid` → `file_uuid` (terminology update)
|
||||
- `person_identities` table **removed**
|
||||
- Face → Identity direct binding (no intermediate person_id)
|
||||
- 28 person_id APIs removed (except register/bind)
|
||||
- Chunk binding auto via time alignment
|
||||
|
||||
### Identity Model
|
||||
```
|
||||
Face Detection → Identity (direct binding)
|
||||
Speaker Detection → Identity (direct binding)
|
||||
```
|
||||
|
||||
### Processing Pipeline
|
||||
```
|
||||
Register → Probe → ASR → CUT → YOLO → OCR → Face → Pose → ASRX → Visual Chunk
|
||||
```
|
||||
183
docs_v1.0/API_V1.0.0/INTERNAL/API_DICTIONARY_V1.0.0.md
Normal file
183
docs_v1.0/API_V1.0.0/INTERNAL/API_DICTIONARY_V1.0.0.md
Normal file
@@ -0,0 +1,183 @@
|
||||
---
|
||||
document_type: "reference_doc"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Momentry Core API 字典 V1.0.0"
|
||||
date: "2026-05-01"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "api"
|
||||
- "dictionary"
|
||||
- "v1.0.0"
|
||||
ai_query_hints:
|
||||
- "Momentry Core API 字典查詢"
|
||||
- "API 端點與參數說明"
|
||||
- "API 回應格式定義"
|
||||
- "查詢所有 Public/Internal/Admin API 端點列表"
|
||||
- "API 端點的 HTTP 方法與路徑結構"
|
||||
- "搜尋 API 有哪些端點(search/bm25/hybrid/visual)"
|
||||
- "API 端點的狀態分類(Public/Internal/Admin)"
|
||||
related_documents:
|
||||
- "API_V1.0.0/MOMENTRY_CORE_API_V1.0.0.md"
|
||||
- "API_V1.0.0/API_USAGE_DEMO_V1.0.0.md"
|
||||
- "API_V1.0.0/API_REFERENCE_v1.0.0.20260501md.md"
|
||||
- "API_V1.0.0/CHUNK_DEFINITION_V1.0.0.md"
|
||||
- "API_V1.0.0/VECTOR_SPEC_V1.0.0.md"
|
||||
---
|
||||
|
||||
# Momentry Core API 字典級全量文件 V1.0.0
|
||||
|
||||
## 關鍵術語定義
|
||||
|
||||
| 術語 | 定義 |
|
||||
|------|------|
|
||||
| Public API | 供前端與外部系統使用的標準介面(58 個端點) |
|
||||
| Internal API | 系統內部流程或狀態查詢用(5 個端點) |
|
||||
| Admin API | 管理員專用(5 個端點) |
|
||||
| file_uuid | 32 碼 SHA256 檔案識別碼 |
|
||||
| RESTful | 以資源為中心的 API 設計風格 |
|
||||
|
||||
## 📊 端點統計 (Endpoint Statistics)
|
||||
|
||||
| 分類 | 數量 | 說明 |
|
||||
|---|---|---|
|
||||
| ✅ **Public** | 58 | 供前端與外部系統使用的標準介面 |
|
||||
| ⚠️ **Internal** | 5 | 系統內部流程或狀態查詢 (如 Probe, SFTPGo) |
|
||||
| 🔒 **Admin** | 5 | 管理員專用 (如 Resources, Config Cache) |
|
||||
| **總計** | **67** | 所有已註冊路由 (`gen-traces` 已移除) |
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-05-01 |
|
||||
| 端點總數 | **68** |
|
||||
| 文件版本 | V1.1 (Route Fixes + Arch Notes) |
|
||||
|
||||
---
|
||||
|
||||
## 🚀 設計原則 (Design Principles)
|
||||
|
||||
### 1. Clear API (介面清晰化)
|
||||
* **去蕪存菁**: 嚴格區分 **Public** (公開) 與 **Internal** (內部) 端點。舊版冗餘路徑(如 `/api/v1/videos`, `/api/v1/probe`)已全面移除或合併。
|
||||
* **標準化回應**: 所有列表型 API 均回傳統一結構 `{ "success": true, "data": [...], "total": N }`。
|
||||
* **命名規範**: 採用 RESTful 風格,資源以複數名詞或明確動作命名(如 `files`, `identities`)。
|
||||
|
||||
### 2. File-Centric (以檔案為核心)
|
||||
* **唯一識別**: 每個媒體檔案(影片/圖片/音訊)均由 **32 碼 UUID** (`file_uuid`) 唯一標識。
|
||||
* **生命週期**: `File` 是所有資料的根節點。所有的 `Chunk` (片段), `Snapshot` (快照), `Jobs` (任務) 皆隸屬於特定的 `File`。
|
||||
* **操作模式**: 前端應優先呼叫 `GET /api/v1/files` 取得清單,再透過 `POST /api/v1/files/:uuid/snapshots/migrate` 載入詳細資源。
|
||||
|
||||
### 4. Trace Aggregation (軌跡聚合獨立化)
|
||||
* **架構**: `trace_face` 聚合由獨立 Python 腳本 `scripts/trace_face_aggregator.py` 處理,**不**內嵌於 Rust DB 層。
|
||||
* **流程**: Face Processor (Python) 輸出離散幀級資料到 `face_detections` 表 → Rust Worker 排程 `trace_face_aggregator.py` → 該腳本讀取 DB、按 `face_id` 分組聚合、寫入 `pre_chunks` (source_type=`trace_face`)。
|
||||
* **設計理由**: 保持 Rust 排程層輕量化,軌跡聚合邏輯留在 Python 層統一維護,便於未來調整聚合演算法 (如 IOU 門檻、時間間隔合併等) 而無需重新編譯 Rust。
|
||||
|
||||
### 5. Global Identity (全域身份識別)
|
||||
* **跨檔案關聯**: `Identity` 代表一個獨立的人物或角色,不受單一檔案限制。
|
||||
* **綁定機制 (Binding)**: 透過 `POST /api/v1/identities/bind`,我們可以將多個檔案中偵測到的臉部 (`face`) 或聲音 (`speaker`) 聚合到同一個 `Identity` 下。
|
||||
* **資料聚合**: 查詢某個 `Identity` 即可看到該人物在所有歷史檔案中的軌跡 (`/api/v1/identities/:uuid/files`)。
|
||||
|
||||
---
|
||||
|
||||
## 1. 系統與認證 (System & Auth)
|
||||
| 方法 | 路徑 | 狀態 |
|
||||
|---|---|---|
|
||||
| `GET` | `/health` | ✅ Public |
|
||||
| `GET` | `/health/detailed` | ✅ Public |
|
||||
| `POST` | `/api/v1/auth/login` | ✅ Public |
|
||||
| `POST` | `/api/v1/auth/logout` | ✅ Public |
|
||||
|
||||
## 2. 檔案管理 (Files & Assets)
|
||||
| 方法 | 路徑 | 狀態 |
|
||||
|---|---|---|
|
||||
| `GET` | `/api/v1/files` | ✅ Public |
|
||||
| `GET` | `/api/v1/files/scan` | ✅ Public |
|
||||
| `POST` | `/api/v1/files/register` | ✅ Public |
|
||||
| `POST` | `/api/v1/unregister` | ✅ Public |
|
||||
| `GET` | `/api/v1/files/:file_uuid` | ✅ Public |
|
||||
| `GET` | `/api/v1/files/:file_uuid/identities` | ✅ Public |
|
||||
| `GET` | `/api/v1/files/:file_uuid/snapshots` | ✅ Public |
|
||||
| `GET` | `/api/v1/files/:file_uuid/snapshots/status` | ✅ Public |
|
||||
| `POST` | `/api/v1/files/:file_uuid/snapshots/migrate` | ✅ Public |
|
||||
| `POST` | `/api/v1/files/:file_uuid/snapshots/teardown` | ✅ Public |
|
||||
|
||||
## 3. 影片與任務 (Videos & Jobs)
|
||||
| 方法 | 路徑 | 狀態 |
|
||||
|---|---|---|
|
||||
| `DELETE` | `/api/v1/videos/:file_uuid` | ✅ Public |
|
||||
| `GET` | `/api/v1/videos/:file_uuid/details` | ✅ Public |
|
||||
| `GET` | `/api/v1/videos/:file_uuid/pre_chunks` | ✅ Public |
|
||||
| `GET` | `/api/v1/progress/:file_uuid` | ✅ Public |
|
||||
| `GET` | `/api/v1/jobs` | ✅ Public |
|
||||
| `GET` | `/api/v1/jobs/:job_id` | ✅ Public |
|
||||
| `GET` | `/api/v1/rules/:rule/status` | ✅ Public |
|
||||
| `GET` | `/api/v1/files/:file_uuid/probe` | ✅ Public |
|
||||
| `POST` | `/api/v1/files/:file_uuid/process` | ✅ Public |
|
||||
| `GET` | `/api/v1/assets/:uuid/status` | ⚠️ Internal |
|
||||
| `POST` | `/api/v1/resources/register` | 🔒 Internal |
|
||||
| `POST` | `/api/v1/resources/heartbeat` | 🔒 Internal |
|
||||
| `GET` | `/api/v1/resources` | 🔒 Internal |
|
||||
|
||||
## 4. 搜尋 (Search)
|
||||
| 方法 | 路徑 | 狀態 |
|
||||
|---|---|---|
|
||||
| `POST` | `/api/v1/search` | ✅ Public |
|
||||
| `POST` | `/api/v1/search/bm25` | ✅ Public |
|
||||
| `POST` | `/api/v1/search/hybrid` | ✅ Public |
|
||||
| `POST` | `/api/v1/search/visual` | ✅ Public |
|
||||
| `POST` | `/api/v1/search/visual/class` | ✅ Public |
|
||||
| `POST` | `/api/v1/search/visual/density` | ✅ Public |
|
||||
| `POST` | `/api/v1/search/visual/combination` | ✅ Public |
|
||||
| `POST` | `/api/v1/search/visual/stats` | ✅ Public |
|
||||
|
||||
## 5. 身份與綁定 (Identity & Binding)
|
||||
| 方法 | 路徑 | 狀態 |
|
||||
|---|---|---|
|
||||
| `GET` | `/api/v1/identities` | ✅ Public |
|
||||
| `GET` | `/api/v1/identities/:uuid` | ✅ Public |
|
||||
| `GET` | `/api/v1/identities/:uuid/files` | ✅ Public |
|
||||
| `GET` | `/api/v1/identities/:uuid/chunks` | ✅ Public |
|
||||
| `GET` | `/api/v1/identities/:identity_id/faces` | ✅ Public |
|
||||
| `POST` | `/api/v1/identities/from-person` | ✅ Public |
|
||||
| `POST` | `/api/v1/identities/from-face` | ✅ Public |
|
||||
| `POST` | `/api/v1/identities/bind` | ✅ Public |
|
||||
| `POST` | `/api/v1/identities/unbind` | ✅ Public |
|
||||
|
||||
## 6. 臉部 (Face)
|
||||
| 方法 | 路徑 | 狀態 |
|
||||
|---|---|---|
|
||||
| `GET` | `/api/v1/face/list` | ✅ Public |
|
||||
| `GET` | `/api/v1/face/:face_id` | ✅ Public |
|
||||
| `DELETE` | `/api/v1/face/:face_id` | ✅ Public |
|
||||
| `POST` | `/api/v1/face/recognize` | ✅ Public |
|
||||
| `POST` | `/api/v1/face/register` | ✅ Public |
|
||||
| `POST` | `/api/v1/face/search` | ✅ Public |
|
||||
| `GET` | `/api/v1/faces/candidates` | ✅ Public |
|
||||
| `GET` | `/api/v1/files/:file_uuid/faces/:face_id/thumbnail` | ✅ Public |
|
||||
| `GET` | `/api/v1/signals/unbound` | ✅ Public |
|
||||
| `GET` | `/api/v1/signals/:uuid/:binding_type/:binding_value/timeline` | ✅ Public |
|
||||
|
||||
## 7. 代理人 (Agents)
|
||||
| 方法 | 路徑 | 狀態 |
|
||||
|---|---|---|
|
||||
| `POST` | `/api/v1/agents/translate` | ✅ Public |
|
||||
| `POST` | `/api/v1/agents/5w1h/analyze` | ✅ Public |
|
||||
| `POST` | `/api/v1/agents/5w1h/batch` | ✅ Public |
|
||||
| `GET` | `/api/v1/agents/5w1h/status` | ✅ Public |
|
||||
| `POST` | `/api/v1/agents/identity/analyze` | ✅ Public |
|
||||
| `POST` | `/api/v1/agents/identity/suggest` | ✅ Public |
|
||||
| `GET` | `/api/v1/agents/identity/status` | ✅ Public |
|
||||
| `POST` | `/api/v1/agents/suggest/merge` | ✅ Public |
|
||||
|
||||
## 8. 狀態與統計 (Stats)
|
||||
| 方法 | 路徑 | 狀態 |
|
||||
|---|---|---|
|
||||
| `GET` | `/api/v1/stats/ingest` | ✅ Public |
|
||||
| `GET` | `/api/v1/stats/sftpgo` | ⚠️ Internal |
|
||||
| `GET` | `/api/v1/stats/inference` | ⚠️ Internal |
|
||||
| `POST` | `/api/v1/config/cache` | 🔒 Internal |
|
||||
| `GET` | `/api/v1/lookup` | ✅ Public |
|
||||
310
docs_v1.0/API_V1.0.0/INTERNAL/API_REFERENCE_v1.0.0.20260501md.md
Normal file
310
docs_v1.0/API_V1.0.0/INTERNAL/API_REFERENCE_v1.0.0.20260501md.md
Normal file
@@ -0,0 +1,310 @@
|
||||
---
|
||||
document_type: "reference_doc"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Momentry Core API 參考文件 V1.0.0 (Demo 完整指南)"
|
||||
date: "2026-05-01"
|
||||
version: "V3.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "api"
|
||||
- "reference"
|
||||
- "v1.0.0"
|
||||
- "demo"
|
||||
- "marcom"
|
||||
ai_query_hints:
|
||||
- "查詢 V1.0.0 Demo 所需 API 列表"
|
||||
- "Momentry Core Demo 流程如何使用 API?"
|
||||
- "API 的檔案註冊、處理、臉部綁定流程"
|
||||
- "Demo 流程中 Scan → Unregister → Register → Probe → Process → Faces → Bind 的完整步驟"
|
||||
- "API 的 curl 範例與回應格式"
|
||||
- "Process 回傳 400 Bad Request 的常見原因與解決方法"
|
||||
- "臉部查詢回傳空結果的疑難排解步驟"
|
||||
related_documents:
|
||||
- "STANDARDS/DOCS_STANDARD.md"
|
||||
- "API_V1.0.0/MOMENTRY_CORE_API_V1.0.0.md"
|
||||
- "TEST_REPORT_CLI.md"
|
||||
---
|
||||
|
||||
# Momentry Core API 參考文件 V1.0.0 (Demo 完整指南)
|
||||
|
||||
## 關鍵術語定義
|
||||
|
||||
| 術語 | 定義 |
|
||||
|------|------|
|
||||
| file_uuid | 32 碼 SHA256 檔案識別碼 |
|
||||
| X-API-Key | API 認證方式,透過 HTTP Header 傳遞 |
|
||||
| Scan | 掃描檔案系統,列出所有檔案及當前狀態 |
|
||||
| Register | 將檔案加入資料庫系統 |
|
||||
| Probe | 讀取檔案 metadata(時長、解析度、幀率) |
|
||||
| Bind | 將臉部綁定到指定身份 |
|
||||
| Progress | 獲取處理進度與目前階段 |
|
||||
|
||||
## 📊 文件統計 (Document Statistics)
|
||||
|
||||
| 項目 | 數值 |
|
||||
|---|---|
|
||||
| **收錄端點** | 15+ (Demo 核心流程) |
|
||||
| **涵蓋率** | Demo 流程 100% |
|
||||
| **測試狀態** | ✅ CLI Verified |
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-05-01 |
|
||||
| 文件版本 | V3.0 |
|
||||
|
||||
---
|
||||
|
||||
## 1. Demo 流程總覽 (Demo Workflow)
|
||||
|
||||
本文件專注於 **Demo 測試計畫** 所需的 API。以下是完整流程與對應 API:
|
||||
|
||||
```
|
||||
1. 掃描狀態 (Scan) → GET /api/v1/files/scan
|
||||
2. 檔案重置 (Unregister) → POST /api/v1/unregister
|
||||
3. 檔案註冊 (Register) → POST /api/v1/files/register
|
||||
4. 檔案探測 (Probe) → GET /api/v1/files/:file_uuid/probe
|
||||
5. 開始處理 (Process) → POST /api/v1/files/:file_uuid/process
|
||||
6. 監控進度 (Progress) → GET /api/v1/progress/:file_uuid**
|
||||
7. 查詢臉部 (Faces) → GET /api/v1/faces/candidates
|
||||
8. 綁定身份 (Bind) → POST /api/v1/identities/bind
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 快速資訊
|
||||
|
||||
- **Base URL (Dev)**: `http://localhost:3003`
|
||||
- **Base URL (Prod)**: `http://localhost:3002`
|
||||
- **認證方式**: Header `X-API-Key: muser_test_001`
|
||||
- **測試 Key**: `muser_test_001`
|
||||
|
||||
---
|
||||
|
||||
## 3. API 詳細說明 (依 Demo 順序)
|
||||
|
||||
### 3.1 掃描檔案系統 (Scan Files)
|
||||
**路徑**: `GET /api/v1/files/scan`
|
||||
|
||||
**用途**: 列出檔案系統中所有檔案及當前狀態,**是 Demo 流程的第一步**。
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"file_name": "A12T3-Share-User Experience of Thunderbolt 3 Shareable Storage.mp4",
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/A12T3-Share-User Experience of Thunderbolt 3 Shareable Storage.mp4",
|
||||
"file_uuid": "7ab7e25f48b58675e33aca44d15c1ecc",
|
||||
"is_registered": true,
|
||||
"status": "processing"
|
||||
}
|
||||
],
|
||||
"total": 20,
|
||||
"registered_count": 20,
|
||||
"unregistered_count": 0
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.2 取消註冊 (Unregister File)
|
||||
**路徑**: `POST /api/v1/unregister`
|
||||
|
||||
**用途**: 從 Scan 結果中選取 `file_uuid`,對該檔案執行取消註冊。
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"uuid": "53e3a229bf68878b7a799e811e097f9c"
|
||||
}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"uuid": "53e3a229bf68878b7a799e811e097f9c",
|
||||
"message": "File unregistered successfully"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.3 註冊檔案 (Register File)
|
||||
**路徑**: `POST /api/v1/files/register`
|
||||
|
||||
**用途**: 從 Scan 結果中選取 `file_path`,將檔案加入資料庫系統。
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/view15.mp4"
|
||||
}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"file_uuid": "53e3a229bf68878b7a799e811e097f9c",
|
||||
"file_name": "view15.mp4",
|
||||
"file_path": "/Users/.../demo/view15.mp4",
|
||||
"already_exists": false
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.4 檔案探測 (Probe File)
|
||||
**路徑**: `GET /api/v1/files/:file_uuid/probe`
|
||||
|
||||
**用途**: 讀取檔案的 metadata (時長、解析度、幀率)。**必須在 Process 前執行**。
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"file_uuid": "7ab7e25f48b58675e33aca44d15c1ecc",
|
||||
"file_name": "A12T3-Share-User Experience of Thunderbolt 3 Shareable Storage.mp4",
|
||||
"duration": 621.55,
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"fps": 29.97,
|
||||
"cached": true
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.5 觸發處理 (Process File)
|
||||
**路徑**: `POST /api/v1/files/:file_uuid/process`
|
||||
|
||||
**用途**: 啟動後端 Worker 進行分析 (ASR, Face, YOLO, 等)。
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Processing started"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.6 查詢進度 (Progress)
|
||||
**路徑**: `GET /api/v1/progress/:file_uuid`
|
||||
|
||||
**用途**: 獲取處理進度與目前階段。
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"file_uuid": "53e3a229bf68878b7a799e811e097f9c",
|
||||
"overall_progress": 65,
|
||||
"current_processor": "face",
|
||||
"status": "running",
|
||||
"processors": [
|
||||
{ "name": "probe", "status": "completed" },
|
||||
{ "name": "asr", "status": "completed" },
|
||||
{ "name": "face", "status": "running" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.6 查詢未綁定臉部 (List Face Candidates)
|
||||
**路徑**: `GET /api/v1/faces/candidates`
|
||||
|
||||
**用途**: 列出檔案中尚未綁定身份的臉部。
|
||||
|
||||
**Query Parameters**:
|
||||
- `file_uuid` (必填): 檔案 UUID
|
||||
- `min_confidence` (選填): 最低信心值 (預設 0.5)
|
||||
- `page_size` (選填): 每頁數量 (預設 20)
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"candidates": [
|
||||
{
|
||||
"id": 123,
|
||||
"face_id": "123_RoleA",
|
||||
"file_uuid": "384b0ff44aaaa1f14cb2cd63b3fea966",
|
||||
"frame_number": 115,
|
||||
"confidence": 0.98,
|
||||
"bbox": { "x": 50, "y": 50, "w": 100, "h": 100 }
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
"page": 1,
|
||||
"page_size": 20
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.7 綁定身份 (Bind Identity)
|
||||
**路徑**: `POST /api/v1/identities/bind`
|
||||
|
||||
**用途**: 將臉部綁定到指定身份 (或建立新身份)。
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"identity_id": 22,
|
||||
"binding_type": "face",
|
||||
"binding_value": "123_RoleA"
|
||||
}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Bound face '123_RoleA' to Identity 'Cary Grant'"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 補充 API (Demo 選用)
|
||||
|
||||
### 4.1 列出身份 (List Identities)
|
||||
**路徑**: `GET /api/v1/identities`
|
||||
|
||||
**用途**: 列出系統中所有已建立的身份。
|
||||
|
||||
---
|
||||
|
||||
## 5. 常見問題 (FAQ)
|
||||
|
||||
### Q1: 為什麼 Process 回傳 400 Bad Request?
|
||||
**Ans**: 必須先執行 **Probe** (`GET /api/v1/files/:file_uuid/probe`),確保系統已知曉檔案的幀數資訊。
|
||||
|
||||
### Q2: 為什麼 Unregister 回傳 404?
|
||||
**Ans**: 確認伺服器是否已更新至最新版本。舊版可能尚未包含此路由。
|
||||
|
||||
### Q3: 臉部查詢回傳空結果?
|
||||
**Ans**:
|
||||
1. 確認檔案已**處理完成** (Progress = 100%)。
|
||||
2. 嘗試降低 `min_confidence` 參數 (例如設為 0.0)。
|
||||
3. 確認該檔案內容確實包含可辨識的臉部。
|
||||
|
||||
---
|
||||
|
||||
## 6. 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 |
|
||||
|------|------|------|--------|
|
||||
| V1.0 | 2026-04-30 | 初始 API 列表 | OpenCode |
|
||||
| V2.0 | 2026-05-01 | 基於 Production 測試結果補足文件 | OpenCode |
|
||||
| V3.0 | 2026-05-01 | 重構為 Demo 流程導向,補齊 Probe/Unregister 說明 | OpenCode |
|
||||
| V3.1 | 2026-05-01 | 修正 `:uuid`→`:file_uuid`,修正 port 3002→3003,移除重複 Scan 章節 | OpenCode |
|
||||
376
docs_v1.0/API_V1.0.0/INTERNAL/API_USAGE_DEMO_V1.0.0.md
Normal file
376
docs_v1.0/API_V1.0.0/INTERNAL/API_USAGE_DEMO_V1.0.0.md
Normal file
@@ -0,0 +1,376 @@
|
||||
---
|
||||
document_type: "develop_guide"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Momentry Core V1.0.0 API 示範與整合指南"
|
||||
date: "2026-05-01"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "api-usage"
|
||||
- "demo"
|
||||
- "n8n"
|
||||
- "wordpress"
|
||||
ai_query_hints:
|
||||
- "查詢 V1.0.0 API 示範與整合指南的內容"
|
||||
- "如何使用 n8n 呼叫 V1.0.0 API?"
|
||||
- "如何整合 V1.0.0 API 到 WordPress?"
|
||||
- "V1.0.0 API 的 curl 範例"
|
||||
- "PHP 整合 V1.0.0 API 的方式(wp_remote_request)"
|
||||
- "n8n 工作流如何串接 V1.0.0 API"
|
||||
- "Face 綁定錯誤修正的 API 操作步驟"
|
||||
- "前端 Face Interpolation 的實作方式"
|
||||
related_documents:
|
||||
- "API_V1.0.0/MOMENTRY_CORE_API_V1.0.0.md"
|
||||
- "API_V1.0.0/API_DICTIONARY_V1.0.0.md"
|
||||
- "API_V1.0.0/API_REFERENCE_v1.0.0.20260501md.md"
|
||||
- "API_V1.0.0/CHUNK_DEFINITION_V1.0.0.md"
|
||||
- "API_V1.0.0/PROCESSOR_SELECTION_V1.0.0.md"
|
||||
---
|
||||
|
||||
# Momentry Core V1.0.0 API 示範與整合指南
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-05-01 |
|
||||
| 文件版本 | V1.0 |
|
||||
| 適用版本 | Momentry Core V1.0.0+ |
|
||||
|
||||
---
|
||||
|
||||
## 關鍵術語定義
|
||||
|
||||
| 術語 | 定義 |
|
||||
|------|------|
|
||||
| file_uuid | 32 碼 SHA256 檔案識別碼 |
|
||||
| X-API-Key | API 認證方式,透過 HTTP Header 傳遞 |
|
||||
| face_id | 單一幀中的人臉偵測 ID,格式為 `<檢測ID>_<角色後綴>` |
|
||||
| Identity | 全域人物身份,跨檔案關聯同一人物 |
|
||||
| Face Interpolation | 前端線性插值,補足非逐幀臉部標記的顯示 |
|
||||
| Scan | 掃描檔案系統,列出所有檔案及當前狀態 |
|
||||
|
||||
## 1. 快速開始 (Quick Start)
|
||||
|
||||
### 1.1 環境 URL
|
||||
|
||||
| 環境 | URL | 用途 |
|
||||
|------|-----|------|
|
||||
| **對外 URL** | `https://api.momentry.ddns.net` | 外部存取 |
|
||||
| **Dev Server** | `http://localhost:3003` | **開發環境,所有測試用** |
|
||||
| **Local Server** | `http://localhost:3002` | Production,僅 release 用 |
|
||||
|
||||
### 1.2 測試連線
|
||||
|
||||
```bash
|
||||
curl http://localhost:3003/health
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"version": "1.0.0 (build: ...)",
|
||||
"uptime_ms": 64880
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心 API 工作流 (Workflows)
|
||||
|
||||
### 2.1 掃描檔案系統 (Scan Files)
|
||||
**入口 API**: `GET /api/v1/files/scan` — 所有 Demo 流程從這裡開始。
|
||||
|
||||
**掃描檔案**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/files/scan" \
|
||||
-H "X-API-Key: <your_api_key>"
|
||||
```
|
||||
|
||||
**列出檔案 (分頁)**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/files?page=1&page_size=10" \
|
||||
-H "X-API-Key: <your_api_key>"
|
||||
```
|
||||
|
||||
**取得單一檔案詳情**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/files/<file_uuid>" \
|
||||
-H "X-API-Key: <your_api_key>"
|
||||
```
|
||||
|
||||
### 2.2 搜尋 (Search)
|
||||
支援語意搜尋、混合搜尋與視覺搜尋。
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:3003/api/v1/search" \
|
||||
-H "X-API-Key: <your_api_key>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "尋找紅色信封", "uuid": "<file_uuid>"}'
|
||||
```
|
||||
|
||||
### 2.3 單獨 Face 綁定流程 (Single Face Binding Workflow)
|
||||
|
||||
此流程適用於手動將特定臉部關聯到已知人物或建立新人物的場景。系統支援**一人分飾多角**,透過 `face_id` 加上角色後綴來區分。
|
||||
|
||||
#### 步驟 1: 選定 Face (Input Format)
|
||||
使用者需提供一個 **`file_uuid`** 搭配 **`face_id`** 來鎖定目標。
|
||||
選定的意思是輸入 **`<file_uuid>:<face_id>`** 的組合。
|
||||
|
||||
* **命名規則**: `face_id` 格式通常為 `<原始檢測 ID>_<後綴>`,用於區分同一人的不同臉部實體或角色。
|
||||
* **有角色名稱**: 使用角色名 (如 `123_PeterJoshua`)。
|
||||
* **無角色名稱**: 使用通用代號 (如 `123_RoleA`, `123_RoleB`)。
|
||||
|
||||
#### 步驟 2: 列出 Identities 或新增 Identity
|
||||
使用者決定將該 Face 綁定到系統中已存在的全域人物 (Identity),或是建立一個新人物。
|
||||
* **Identity 特性**: 代表現實世界中的真實人物,具備**全域唯一性** (如 "Cary Grant")。
|
||||
|
||||
- **選項 A: 列出人物清單**
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/identities?page=1&page_size=20" \
|
||||
-H "X-API-Key: <your_api_key>"
|
||||
```
|
||||
|
||||
- **選項 B: 決定新增人物名稱**
|
||||
若列表中沒有對應人物,使用者需準備一個新名稱(如 "Cary Grant")。
|
||||
|
||||
#### 步驟 3: 確認綁定
|
||||
透過 `POST /api/v1/identities/bind` 完成綁定。
|
||||
* **若提供 `identity_id`**: 將帶有後綴的 `face_id` 綁定至該人物。
|
||||
* **若提供 `name`**: 系統自動建立新人物 (Identity),並將該臉部綁定上去。
|
||||
|
||||
- **綁定至現有身份 (範例)**:
|
||||
假設我們要綁定的目標是檔案 `file_uuid_abc` 中的臉部 `123_PeterJoshua`。
|
||||
```bash
|
||||
curl -X POST "http://localhost:3003/api/v1/identities/bind" \
|
||||
-H "X-API-Key: <your_api_key>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"identity_id": 101,
|
||||
"binding_type": "face",
|
||||
"binding_value": "123_PeterJoshua"
|
||||
}'
|
||||
```
|
||||
*註: 雖然 API 接收的是 `binding_value`,但系統內部會根據選定的 `file_uuid` 與 `face_id` 組合來精確鎖定目標。*
|
||||
|
||||
#### 步驟 4: 循環
|
||||
完成綁定後,返回列表處理下一個未綁定的 Face。
|
||||
|
||||
---
|
||||
|
||||
### 2.4 取得 Face 截圖 (Retrieve Face Snapshots)
|
||||
|
||||
在確認綁定前,通常需要檢視臉部截圖。根據使用場景,取得截圖有兩種方式:
|
||||
|
||||
#### 1. Local Path / Filename (本地路徑)
|
||||
* **適用**: Tauri 桌面應用、本機腳本。
|
||||
* **說明**: 直接從硬碟讀取圖片檔案,速度最快,無需經過網路層。
|
||||
* **路徑**: `<MOMENTRY_OUTPUT_DIR>/<file_uuid>/snapshots/faces/<face_id>.jpg`
|
||||
|
||||
#### 2. URL (網路存取)
|
||||
* **適用**: Web 前端、外部系統。
|
||||
* **說明**: 透過 HTTP GET 請求取得影像串流。
|
||||
* **API Endpoint**: `GET /api/v1/files/<file_uuid>/faces/<face_id>/thumbnail`
|
||||
* **範例**:
|
||||
```bash
|
||||
curl -s -o face.jpg \
|
||||
"http://localhost:3003/api/v1/files/<file_uuid>/faces/<face_id>/thumbnail" \
|
||||
-H "X-API-Key: <your_api_key>"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.4.1 前端動態辨識與插值 (Face Interpolation Logic)
|
||||
|
||||
由於系統對臉部標記並非逐幀 (Frame-by-Frame) 進行(為節省運算資源或受限於取樣率),在 Client 端進行**逐幀播放**或**時間軸拖曳**時,若直接顯示會導致臉部框選忽閃忽滅。
|
||||
|
||||
#### 運作邏輯
|
||||
前端需實作**線性插值 (Linear Interpolation)** 機制:
|
||||
|
||||
1. **取得資料**:從 API 取得該 `face_id` 在所有 `frame_number` 的座標列表(例如:Frame 10, Frame 15 有資料)。
|
||||
2. **插值計算**:
|
||||
* 當使用者停在 **Frame 12** 時,系統無直接資料。
|
||||
* 前端應找出前後最近的有資料幀(Frame 10 與 Frame 15)。
|
||||
* 根據時間差比例,動態計算出 Frame 12 的座標 `x, y, w, h`。
|
||||
|
||||
#### 實作範例 (JavaScript/TypeScript)
|
||||
|
||||
```typescript
|
||||
// 假設 API 回傳該 Face 的軌跡點
|
||||
const detections = [
|
||||
{ frame: 10, bbox: { x: 100, y: 100, w: 50, h: 60 } },
|
||||
{ frame: 15, bbox: { x: 110, y: 105, w: 50, h: 60 } },
|
||||
];
|
||||
|
||||
// 計算 Frame 12 的預測框選
|
||||
function getInterpolatedBBox(frameIndex: number, detections) {
|
||||
// 找到前一幀與後一幀
|
||||
const prev = detections.find(d => d.frame <= frameIndex); // Frame 10
|
||||
const next = detections.find(d => d.frame > frameIndex); // Frame 15
|
||||
|
||||
if (!prev) return null; // 還沒開始出現
|
||||
if (!next) return prev.bbox; // 結束了,維持最後位置
|
||||
|
||||
// 計算比例 (0.0 - 1.0)
|
||||
const ratio = (frameIndex - prev.frame) / (next.frame - prev.frame);
|
||||
|
||||
return {
|
||||
x: prev.bbox.x + (next.bbox.x - prev.bbox.x) * ratio,
|
||||
y: prev.bbox.y + (next.bbox.y - prev.bbox.y) * ratio,
|
||||
// w, h 亦可依此邏輯進行縮放插值
|
||||
w: prev.bbox.w,
|
||||
h: prev.bbox.h,
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.5 Face 綁定錯誤修正 (Face Binding Error Correction)
|
||||
|
||||
此流程適用於移除錯誤綁定的臉部資料,使其恢復為未綁定狀態。
|
||||
|
||||
1. **選定 Face**: 確認需要解除綁定的臉部 `face_id` 以及所屬的 `file_uuid`。
|
||||
2. **解除綁定 (Unbind)**:
|
||||
```bash
|
||||
curl -X POST "http://localhost:3003/api/v1/identities/unbind" \
|
||||
-H "X-API-Key: <your_api_key>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"binding_type": "face",
|
||||
"binding_value": "<selected_face_id>"
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. n8n 整合範例
|
||||
|
||||
### 3.1 HTTP Request 設定
|
||||
|
||||
| 欄位 | 值 |
|
||||
|---|---|
|
||||
| Method | `GET` 或 `POST` |
|
||||
| URL | `http://localhost:3003/api/v1/files` (Dev) 或 `https://<your-domain>` (Prod) |
|
||||
| Header `X-API-Key` | `<your_api_key>` |
|
||||
|
||||
### 3.2 列出檔案 Workflow (JSON)
|
||||
使用 `GET /api/v1/files/scan` 作為入口。
|
||||
|
||||
```json
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"name": "Get Files",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "http://localhost:3003/api/v1/files/scan",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [{ "name": "X-API-Key", "value": "{{ $env.API_KEY }}" }]
|
||||
},
|
||||
"options": { "qs": { "page": 1, "page_size": 10 } }
|
||||
},
|
||||
"position": [450, 300]
|
||||
},
|
||||
{
|
||||
"name": "Extract List",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"parameters": {
|
||||
"jsCode": "return $input.first().json.data.map(f => ({\n json: {\n uuid: f.file_uuid,\n name: f.file_name,\n status: f.status\n }\n}));"
|
||||
},
|
||||
"position": [650, 300]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. WordPress / PHP 整合範例
|
||||
|
||||
### 4.1 PHP Client Library (V1.0.0 相容)
|
||||
|
||||
```php
|
||||
<?php
|
||||
class Momentry_API {
|
||||
private const API_URL = 'http://localhost:3003'; // Dev environment
|
||||
private const API_KEY = '<your_api_key>';
|
||||
|
||||
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 scan_files(): array {
|
||||
return $this->request('/api/v1/files/scan');
|
||||
}
|
||||
|
||||
// 列出檔案
|
||||
public function list_files(): array {
|
||||
return $this->request('/api/v1/files');
|
||||
}
|
||||
|
||||
// 搜尋
|
||||
public function search(string $query): array {
|
||||
return $this->request('/api/v1/search', ['query' => $query], 'POST');
|
||||
}
|
||||
}
|
||||
?>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 疑難排解
|
||||
|
||||
| 錯誤 | 原因 | 解決方案 |
|
||||
|------|------|----------|
|
||||
| `401 Unauthorized` | API Key 無效 | 檢查 Key 格式與權限 |
|
||||
| `404 Not Found` | 端點不存在 | 確認是否使用了舊版 `/api/v1/videos`,應改為 `/api/v1/files` |
|
||||
| `400 Bad Request on Process` | 缺少 Probe 資料 | 先執行 `GET /api/v1/files/:file_uuid/probe` |
|
||||
| `500 Error` | 伺服器錯誤 | 檢查資料庫連線與 Schema 版本 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-05-01 | 初始版本 | OpenCode | deepseek-chat |
|
||||
| V1.1 | 2026-05-01 | 修正 port 為 Dev(3003),更新 API 路徑與掃描入口 | OpenCode | deepseek-chat |
|
||||
|
||||
---
|
||||
|
||||
## 7. 附錄:UUID 格式說明
|
||||
|
||||
V1.0.0 使用 **32 碼 SHA256** 作為 `file_uuid`。
|
||||
|
||||
```
|
||||
/Users/.../demo/video.mp4
|
||||
↓
|
||||
SHA256 Hash (前 32 字元)
|
||||
↓
|
||||
53e3a229bf68878b7a799e811e097f9c
|
||||
```
|
||||
198
docs_v1.0/API_V1.0.0/INTERNAL/CHUNK_DEFINITION_V1.0.0.md
Normal file
198
docs_v1.0/API_V1.0.0/INTERNAL/CHUNK_DEFINITION_V1.0.0.md
Normal file
@@ -0,0 +1,198 @@
|
||||
---
|
||||
document_type: "spec"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Chunk 定義 V1.0.0"
|
||||
date: "2026-05-01"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "chunk"
|
||||
- "v1.0.0"
|
||||
- "chunk-type"
|
||||
- "pre-chunk"
|
||||
- "parent-child"
|
||||
- "data-structure"
|
||||
ai_query_hints:
|
||||
- "chunk 的定義與結構"
|
||||
- "pre_chunk 與 chunk 的關係"
|
||||
- "parent_chunk 與 child_chunk 的關係"
|
||||
- "ChunkType 包含哪些類型(Sentence/Cut/Visual/Trace/Story)"
|
||||
- "chunk 的巢狀結構與 Rule 組合規則"
|
||||
- "chunk 如何對應到 file_uuid 與幀區間"
|
||||
- "chunk 的搜尋用途與向量儲存方式"
|
||||
- "chunk 與 pre_chunk 的雙層資料架構"
|
||||
related_documents:
|
||||
- "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
- "VECTOR_SPEC_V1.0.0.md"
|
||||
- "PROCESSORS/ASR_V1.0.0.md"
|
||||
- "PROCESSORS/CUT_V1.0.0.md"
|
||||
- "PROCESSORS/FACE_V1.0.0.md"
|
||||
---
|
||||
|
||||
# Chunk 定義 V1.0.0
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-05-01 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
## 名詞定義
|
||||
|
||||
| 名詞 | 定義 | 範例 |
|
||||
|------|------|------|
|
||||
| **Processor JSON** | Processor 腳本的第一層產出檔案 | `384b0ff44aaaa1f14cb2cd63b3fea966.face.json` |
|
||||
| **pre_chunk** | 從 Processor JSON 匯入 DB 的最低層元件(`pre_chunks` 表) | 單幀 face detection、單句 ASR text |
|
||||
| **chunk** | 可搜尋單位(`chunks` 表),由 Rule 組合 pre_chunks 產出,`start_frame` ~ `end_frame` 定義區間 | sentence chunk, visual chunk, scene chunk |
|
||||
| **parent_chunk** | chunk 的一種,包含 `child_chunk_ids`,其區間涵蓋多個 child_chunks,由 Summary Agent 產出統整描述 | scene chunk, story chunk |
|
||||
| **child_chunk** | chunk 的一種,被 parent_chunk 參照為子元素 | sentence chunk, visual chunk |
|
||||
|
||||
---
|
||||
|
||||
## Chunk 結構
|
||||
|
||||
```rust
|
||||
Chunk {
|
||||
uuid: String, // file_uuid (32-char hex)
|
||||
chunk_id: String, // "{uuid}_{chunk_index}"
|
||||
chunk_index: u32, // 0-based 序號
|
||||
chunk_type: ChunkType, // Sentence | Cut | Visual | Trace | Story
|
||||
rule: ChunkRule, // Rule1 (直接組合) | Rule2 (聚合)
|
||||
start_frame: i64, // 起始幀(0-based,唯一時間參考)
|
||||
end_frame: i64, // 結束幀(exclusive)
|
||||
fps: f64, // 該區間的 fps
|
||||
content: JSON, // 主要內容
|
||||
text_content: Option<String>, // 純文字內容(供搜尋用)
|
||||
metadata: Option<JSON>, // speaker, face_ids, yolo_objects 等
|
||||
pre_chunk_ids: Vec<i32>, // 來源 pre_chunks(原始元件追溯)
|
||||
parent_chunk_id: Option<String>, // 父 chunk ID(如存在)
|
||||
child_chunk_ids: Vec<String>, // 子 chunk IDs(如為 parent_chunk)
|
||||
vector_id: Option<String>, // 向量儲存參考
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ChunkType
|
||||
|
||||
| 類型 | 說明 | 範例 |
|
||||
|------|------|------|
|
||||
| `Sentence` | ASR 句子 chunk | 一句話對應一個 chunk |
|
||||
| `Cut` | 場景切換 chunk | PySceneDetect 輸出的場景邊界 |
|
||||
| `Visual` | 視覺物件 chunk | YOLO/OCR/Face/Pose 聚合 |
|
||||
| `Trace` | 追蹤 chunk | face_trace / yolo_trace |
|
||||
| `Story` | 敘事 chunk(parent) | 5W1H Agent 產出的統整描述 |
|
||||
|
||||
---
|
||||
|
||||
## Chunk 特性
|
||||
|
||||
- **區間定義**: `start_frame` / `end_frame`(frames 為唯一時間座標)
|
||||
- **可重疊**: 不同類型的 chunk 可以覆蓋相同區間
|
||||
- **可不連續**: chunk 之間不需要連續
|
||||
- **巢狀**: parent_chunk 包含 child_chunk_ids,子區間不須填滿父區間
|
||||
- **單幀 chunk**: `start_frame == end_frame`(如 frame-level detection)
|
||||
|
||||
---
|
||||
|
||||
## 資料流
|
||||
|
||||
```
|
||||
Processor JSON ({file_uuid}.{type}.json)
|
||||
│
|
||||
▼ 匯入
|
||||
pre_chunks (原始元件, start_frame / end_frame / data)
|
||||
│
|
||||
▼ Rule 組合 (Rule1 / Rule2 / Rule3)
|
||||
chunks (可搜尋單位)
|
||||
├── child_chunk (基礎搜尋單位)
|
||||
│ └── 5W1H: 該 chunk 的摘要描述(3~5 句話)
|
||||
│
|
||||
└── parent_chunk (較大區間, Summary Agent 產出)
|
||||
├── child_chunk_ids: [內含的所有 child_chunks]
|
||||
└── summary: (child_chunks 的 5W1H + parent_chunk 補充描述)
|
||||
via Summary Agent (如 5W1H Agent)
|
||||
summary 為 3~5 句話,統整區間內所有內容
|
||||
用於 embedding 成向量,確保搜尋時涵蓋足夠語意
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 與 pre_chunk 的關係
|
||||
|
||||
| 層級 | 產生方式 | 目的 |
|
||||
|------|----------|------|
|
||||
| pre_chunk | 直接從 Processor JSON 匯入 | 保留原始資料,供 Rule 加工 |
|
||||
| chunk | Rule 組合 pre_chunks | 成為可搜尋單位 |
|
||||
| child_chunk | chunk 的一種 | 基礎搜尋目標 |
|
||||
| parent_chunk | Summary Agent 產出 | 補足單一 child_chunk 資訊量不足 |
|
||||
|
||||
---
|
||||
|
||||
## 範例
|
||||
|
||||
### Sentence Chunk (child_chunk)
|
||||
|
||||
```json
|
||||
{
|
||||
"chunk_id": "384b0ff44aaaa1f14cb2cd63b3fea966_42",
|
||||
"chunk_index": 42,
|
||||
"chunk_type": "sentence",
|
||||
"rule": "rule_1",
|
||||
"start_frame": 1260,
|
||||
"end_frame": 1350,
|
||||
"fps": 29.97,
|
||||
"content": {
|
||||
"text": "今天天氣很好,我們決定去公園走走。",
|
||||
"speaker": "SPEAKER_00"
|
||||
},
|
||||
"text_content": "今天天氣很好,我們決定去公園走走。",
|
||||
"metadata": {
|
||||
"speaker": "SPEAKER_00",
|
||||
"face_ids": ["face_42", "face_43"],
|
||||
"5w1h": "講者 SPEAKER_00 在室內提到今天天氣很好。他建議大家一起到公園散步。同伴們同意這個提議。大家開始準備出發。整個對話顯示團隊氣氛融洽。"
|
||||
},
|
||||
"pre_chunk_ids": [101, 102, 103],
|
||||
"parent_chunk_id": "384b0ff44aaaa1f14cb2cd63b3fea966_scene_3"
|
||||
}
|
||||
```
|
||||
|
||||
### Scene Chunk (parent_chunk)
|
||||
|
||||
```json
|
||||
{
|
||||
"chunk_id": "384b0ff44aaaa1f14cb2cd63b3fea966_scene_3",
|
||||
"chunk_index": 3,
|
||||
"chunk_type": "cut",
|
||||
"rule": "rule_3",
|
||||
"start_frame": 1200,
|
||||
"end_frame": 1800,
|
||||
"fps": 29.97,
|
||||
"content": {
|
||||
"scene_number": 3,
|
||||
"scene_type": "dialogue"
|
||||
},
|
||||
"text_content": "今天天氣很好,我們決定去公園走走。之後我們在公園裡散步,看到很多花。",
|
||||
"metadata": {
|
||||
"summary": "講者和同伴在室內討論天氣狀況,提到今天陽光明媚。他們決定到附近的公園散步享受好天氣。抵達公園後,他們沿著步道行走,觀察到許多盛開的花朵。其中一人用手機拍攝了花朵的照片。整個對話氣氛輕鬆愉快。"
|
||||
},
|
||||
"pre_chunk_ids": [98, 99, 100],
|
||||
"child_chunk_ids": [
|
||||
"384b0ff44aaaa1f14cb2cd63b3fea966_42",
|
||||
"384b0ff44aaaa1f14cb2cd63b3fea966_43",
|
||||
"384b0ff44aaaa1f14cb2cd63b3fea966_44"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-05-01 | 初始版本 | OpenCode | deepseek-chat |
|
||||
240
docs_v1.0/API_V1.0.0/INTERNAL/MOMENTRY_CORE_API_V1.0.0.md
Normal file
240
docs_v1.0/API_V1.0.0/INTERNAL/MOMENTRY_CORE_API_V1.0.0.md
Normal file
@@ -0,0 +1,240 @@
|
||||
---
|
||||
document_type: "reference_doc"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Momentry Core V1.0.0 API 參考文件"
|
||||
date: "2026-04-30"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "api"
|
||||
- "reference"
|
||||
- "v1.0.0"
|
||||
- "marcom"
|
||||
- "restful"
|
||||
- "endpoint"
|
||||
- "file-centric"
|
||||
ai_query_hints:
|
||||
- "Momentry Core V1.0.0 API 參考文件的主要內容是什麼?"
|
||||
- "查詢 V1.0.0 API 列表包含哪些端點?"
|
||||
- "Marcom 團隊如何使用 API Reference?"
|
||||
- "API 的 Progressive Workflow 範例"
|
||||
- "Momentry API 的檔案管理與搜尋功能"
|
||||
- "API 的 Progressive Workflow 操作步驟"
|
||||
- "API 的檔案管理與搜尋功能"
|
||||
related_documents:
|
||||
- "STANDARDS/DOCS_STANDARD.md"
|
||||
- "DEV_API_V1.0/API_REFERENCE_v1.0.0.md"
|
||||
- "API_DICTIONARY_V1.0.0.md"
|
||||
- "API_USAGE_DEMO_V1.0.0.md"
|
||||
- "PRODUCTION_VERIFICATION_V1.0.0.md"
|
||||
---
|
||||
|
||||
# Momentry Core V1.0.0 API 參考文件
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-04-30 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-04-30 | 創建 V1.0.0 API 列表,移除過時端點 | OpenCode | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## 關鍵術語定義
|
||||
|
||||
| 術語 | 定義 |
|
||||
|------|------|
|
||||
| file_uuid | 媒體檔案(影片/圖片/音訊)的唯一 32 碼 SHA256 識別碼 |
|
||||
| identity_uuid | 全域人物身份識別碼,跨檔案關聯同一人物 |
|
||||
| Chunk | 可搜尋單位,由 Rule 組合 pre_chunks 產出 |
|
||||
| Snapshot | 臉部或場景的快取快照,需 migrate 後供 UI 使用 |
|
||||
| API Key | 認證方式,透過 Header `X-API-Key` 傳遞 |
|
||||
|
||||
## 概述
|
||||
|
||||
本文檔定義 Momentry Core **V1.0.0** 版本供 **Marcom 團隊** 使用的 API 列表與開發範例。此列表已移除舊版、冗餘及內部使用的端點,確保前端開發使用的是標準且穩定的介面。
|
||||
|
||||
---
|
||||
|
||||
## 🚀 設計原則 (Design Principles)
|
||||
|
||||
### 1. Clear API (介面清晰化)
|
||||
* **去蕪存菁**: 嚴格區分 **Public** (公開) 與 **Internal** (內部) 端點。舊版冗餘路徑(如 `/api/v1/videos`, `/api/v1/probe`)已全面移除或合併。
|
||||
* **標準化回應**: 所有列表型 API 均回傳統一結構 `{ "success": true, "data": [...], "total": N }`。
|
||||
* **命名規範**: 採用 RESTful 風格,資源以複數名詞或明確動作命名(如 `files`, `identities`)。
|
||||
|
||||
### 2. File-Centric (以檔案為核心)
|
||||
* **唯一識別**: 每個媒體檔案(影片/圖片/音訊)均由 **32 碼 UUID** (`file_uuid`) 唯一標識。
|
||||
* **生命週期**: `File` 是所有資料的根節點。所有的 `Chunk` (片段), `Snapshot` (快照), `Jobs` (任務) 皆隸屬於特定的 `File`。
|
||||
* **操作模式**: 前端應優先呼叫 `GET /api/v1/files` 取得清單,再透過 `POST /api/v1/files/:uuid/snapshots/migrate` 載入詳細資源。
|
||||
|
||||
### 3. Global Identity (全域身份識別)
|
||||
* **跨檔案關聯**: `Identity` 代表一個獨立的人物或角色,不受單一檔案限制。
|
||||
* **綁定機制 (Binding)**: 透過 `POST /api/v1/identities/bind`,我們可以將多個檔案中偵測到的臉部 (`face`) 或聲音 (`speaker`) 聚合到同一個 `Identity` 下。
|
||||
* **資料聚合**: 查詢某個 `Identity` 即可看到該人物在所有歷史檔案中的軌跡 (`/api/v1/identities/:uuid/files`)。
|
||||
|
||||
---
|
||||
|
||||
## 當前狀態
|
||||
|
||||
| 項目 | 狀態 |
|
||||
|------|------|
|
||||
| API 版本 | V1.0.0 |
|
||||
| 開發環境 Port | 3003 |
|
||||
| 正式環境 Port | 3002 |
|
||||
| 認證方式 | Header `X-API-Key` |
|
||||
|
||||
---
|
||||
|
||||
## 1. API Dictionary (端點清單)
|
||||
|
||||
### 1.1 系統與認證 (System & Auth)
|
||||
| Method | Endpoint | 說明 |
|
||||
| :--- | :--- | :--- |
|
||||
| `GET` | `/health` | 基本健康檢查 |
|
||||
| `POST` | `/api/v1/auth/login` | 登入以取得 API Key |
|
||||
|
||||
### 1.2 檔案管理 (File Management)
|
||||
*主要入口:瀏覽與管理資產*
|
||||
| Method | Endpoint | 說明 |
|
||||
| :--- | :--- | :--- |
|
||||
| `GET` | `/api/v1/files` | **列出所有檔案** (支援分頁) |
|
||||
| `GET` | `/api/v1/files/:uuid` | 取得檔案詳情 (包含 probe_json, metadata) |
|
||||
| `POST` | `/api/v1/files/register` | 從磁碟註冊新檔案 |
|
||||
| `DELETE`| `/api/v1/videos/:uuid` | **刪除影片** 及其關聯資料 |
|
||||
|
||||
### 1.3 搜尋與檢索 (Search & Retrieval)
|
||||
| Method | Endpoint | 說明 |
|
||||
| :--- | :--- | :--- |
|
||||
| `POST` | `/api/v1/search` | **語意搜尋** (Text-based, 使用 Embedding) |
|
||||
| `POST` | `/api/v1/search/hybrid` | 混合搜尋 (Vector + BM25 關鍵字) |
|
||||
| `POST` | `/api/v1/search/visual` | 視覺搜尋 (尋找物件/形狀) |
|
||||
| `POST` | `/api/v1/search/visual/class`| 依物件類別過濾 (如 "person", "car") |
|
||||
|
||||
### 1.4 身份與人物管理 (Identity Management)
|
||||
*跨影片的人物/角色關聯*
|
||||
| Method | Endpoint | 說明 |
|
||||
| :--- | :--- | :--- |
|
||||
| `GET` | `/api/v1/identities` | **列出所有身份** (人物/角色) |
|
||||
| `GET` | `/api/v1/identities/:uuid` | 取得身份詳情 (名稱, 品質, 來源) |
|
||||
| `GET` | `/api/v1/identities/:uuid/files`| 列出該身份出現的所有檔案 |
|
||||
| `GET` | `/api/v1/identities/:uuid/chunks`| 列出特定的時間軸片段 (Chunks) |
|
||||
| `POST` | `/api/v1/identities/bind` | 將臉部/聲音訊號綁定至身份 |
|
||||
|
||||
### 1.5 臉部與快照 (Face & Snapshots)
|
||||
| Method | Endpoint | 說明 |
|
||||
| :--- | :--- | :--- |
|
||||
| `GET` | `/api/v1/face/list` | 列出特定影片中偵測到的所有臉部 |
|
||||
| `POST` | `/api/v1/face/recognize` | 對指定影片觸發臉部辨識流程 |
|
||||
| `GET` | `/api/v1/files/:uuid/snapshots` | 檢查快照快取狀態 (Hot/Cold) |
|
||||
| `POST` | `/api/v1/files/:uuid/snapshots/migrate`| **載入快照至記憶體** (UI 顯示快圖前需呼叫) |
|
||||
|
||||
### 1.6 任務與代理人 (Jobs & Agents)
|
||||
| Method | Endpoint | 說明 |
|
||||
| :--- | :--- | :--- |
|
||||
| `GET` | `/api/v1/progress/:uuid` | 檢查即時處理進度 |
|
||||
| `POST` | `/api/v1/assets/:uuid/process` | 觸發處理流程 (ASR, YOLO, 等) |
|
||||
| `POST` | `/api/v1/agents/identity/analyze` | AI Agent: 分析身份重複情況 |
|
||||
|
||||
---
|
||||
|
||||
## 2. Progressive Workflow Examples (操作範例)
|
||||
|
||||
此章節展示典型的使用者操作情境:**尋找影片 → 處理 → 搜尋 → 人物綁定**。
|
||||
|
||||
### Phase 1: 瀏覽與檢視
|
||||
*使用者瀏覽檔案庫以尋找目標影片。*
|
||||
|
||||
**Step 1: 登入**
|
||||
```bash
|
||||
curl -s -X POST http://localhost:3003/api/v1/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username": "demo", "password": "demo"}'
|
||||
# 回應範例: { "api_key": "muser_test_001..." }
|
||||
```
|
||||
|
||||
**Step 2: 列出檔案**
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/files?page=1&page_size=5" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
# 回應範例: { "success": true, "data": [ { "file_uuid": "...", "file_name": "Demo.mp4" ... } ] }
|
||||
```
|
||||
|
||||
### Phase 2: 處理與監控
|
||||
*使用者決定分析該影片的臉部與語音內容。*
|
||||
|
||||
**Step 3: 觸發處理**
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3003/api/v1/assets/{file_uuid}/process" \
|
||||
-H "X-API-Key: muser_test_001" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
# 啟動 ASR, 臉部偵測等處理器
|
||||
```
|
||||
|
||||
**Step 4: 檢查進度**
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/progress/{file_uuid}" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
# 回應範例: { "overall_progress": 50, "processors": [...] }
|
||||
```
|
||||
|
||||
### Phase 3: 搜尋內容
|
||||
*使用者搜尋影片中的特定內容。*
|
||||
|
||||
**Step 5: 語意搜尋 (文字描述)**
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3003/api/v1/search" \
|
||||
-H "X-API-Key: muser_test_001" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "一個人拿著紅色的信封", "uuid": "{file_uuid}"}'
|
||||
# 回應範例: 符合文字描述的片段列表
|
||||
```
|
||||
|
||||
### Phase 4: 身份管理 (GUI 開發重點)
|
||||
*使用者發現了一張臉,確認該人物,並將其綁定到已知身份。*
|
||||
|
||||
**Step 6: 載入快照 (Migrate Snapshots)**
|
||||
*在 GUI 渲染大量臉部縮圖前,必須先將快取載入記憶體以加速讀取。*
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3003/api/v1/files/{file_uuid}/snapshots/migrate" \
|
||||
-H "X-API-Key: muser_test_001" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"parent_uuid": "{file_uuid}"}'
|
||||
# 回應範例: { "success": true, "migrated_types": ["faces", ...] }
|
||||
```
|
||||
|
||||
**Step 7: 綁定臉部到身份 (Bind Face)**
|
||||
*假設偵測到臉部 `face_123`,欲綁定至身份 `uuid_identity`。*
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3003/api/v1/identities/bind" \
|
||||
-H "X-API-Key: muser_test_001" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"identity_id": null,
|
||||
"name": "Cary Grant",
|
||||
"binding_type": "face",
|
||||
"binding_value": "face_123"
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 棄用聲明 (Deprecation Notices)
|
||||
|
||||
以下端點已在 V1.0.0 移除或棄用,**請勿**在新的開發中使用。
|
||||
|
||||
* `GET /api/v1/videos` (列表) → 已取代為 `GET /api/v1/files`
|
||||
* `POST /api/v1/register` → 已取代為 `POST /api/v1/files/register`
|
||||
* `POST /api/v1/probe` → 已取代為 `GET /api/v1/files/:uuid`
|
||||
* `GET /api/v1/people/...` → 已合併為 `GET /api/v1/identities/...`
|
||||
* `/api/v1/n8n/search/...` → 僅供內部 n8n 工作流使用 (請使用標準 `/api/v1/search`)
|
||||
102
docs_v1.0/API_V1.0.0/INTERNAL/PROCESSORS/ASRX_V1.0.0.md
Normal file
102
docs_v1.0/API_V1.0.0/INTERNAL/PROCESSORS/ASRX_V1.0.0.md
Normal file
@@ -0,0 +1,102 @@
|
||||
---
|
||||
document_type: "spec"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "ASRX Processor V1.0.0"
|
||||
date: "2026-05-02"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
parent: "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "processor"
|
||||
- "asrx"
|
||||
- "speaker-diarization"
|
||||
- "speechbrain"
|
||||
- "v1.0.0"
|
||||
ai_query_hints:
|
||||
- "ASRX 使用 SpeechBrain ECAPA-TDNN 進行說話者日誌化"
|
||||
- "ASRX 從 Pyannote 遷移至自定義 SpeechBrain,快 6 倍"
|
||||
- "ASRX 不需要 HuggingFace token(相較 Pyannote)"
|
||||
- "ASRX Charade 6879s 長片輸出 1118 segments, 8 說話人"
|
||||
- "ASRX 依賴 ASR processor 的轉錄結果"
|
||||
related_documents:
|
||||
- "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
- "../ASR_V1.0.0.md"
|
||||
- "../CUT_V1.0.0.md"
|
||||
- "../VOICE_EMBEDDING_FLOW_V1.0.0.md"
|
||||
- "../VECTOR_SPEC_V1.0.0.md"
|
||||
---
|
||||
|
||||
# ASRX Processor V1.0.0
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-05-02 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
**狀態**: ⚠️ 80% | **模型**: SpeechBrain ECAPA-TDNN | **GPU**: 否
|
||||
|
||||
## 關鍵術語定義
|
||||
|
||||
| 術語 | 定義 |
|
||||
|------|------|
|
||||
| ASRX | 進階語音處理,包含說話者日誌化(Speaker Diarization) |
|
||||
| Speaker Diarization | 說話者日誌化,區分「誰在什麼時候說話」 |
|
||||
| ECAPA-TDNN | SpeechBrain 提供的說話人辨識模型,產出 192-D embedding |
|
||||
| VAD | Voice Activity Detection,語音活動檢測(使用 Silero) |
|
||||
| Spectral Clustering | 頻譜聚類,將 embedding 分群以區分不同說話人 |
|
||||
|
||||
---
|
||||
|
||||
## 選型過程
|
||||
|
||||
| 指標 | Pyannote-based(原始) | Custom SpeechBrain(新) |
|
||||
|------|----------------------|------------------------|
|
||||
| Pipeline | VAD → Whisper → Align → Diarize | VAD (Silero) → ECAPA-TDNN → Spectral Clustering |
|
||||
| 處理時間 | 4.79s(輸出為空) | **1.66s** (96.25x) |
|
||||
| 比 Pyannote 快 | 基準 | **6x 更快** |
|
||||
| HuggingFace token | ✅ **需要** | ❌ **不需要** |
|
||||
| 重疊語音 | ✅ 支援 | ❌ 不支援 |
|
||||
|
||||
**決策**: 因 pyannote.audio 需要 HuggingFace token、import 錯誤頻繁、輸出為空,已改為自定義 SpeechBrain 實作。
|
||||
|
||||
---
|
||||
|
||||
## 處理時間分解(Custom SpeechBrain)
|
||||
|
||||
| 步驟 | 時間 | 佔比 |
|
||||
|------|------|------|
|
||||
| VAD (Silero) | 0.41s | 24.7% |
|
||||
| Speaker embedding (ECAPA-TDNN) | 1.15s | 69.3% |
|
||||
| Spectral clustering | 0.10s | 6.0% |
|
||||
|
||||
---
|
||||
|
||||
## Charade 長片(6879s)
|
||||
|
||||
| 指標 | 值 |
|
||||
|------|-----|
|
||||
| Segments | 1118 |
|
||||
| 說話人數 | 8 |
|
||||
| 匹配率 | 99.82% |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-05-02 | 初始版本 | OpenCode | deepseek-chat |
|
||||
|
||||
## 資源預估
|
||||
|
||||
| 資源 | 值 |
|
||||
|------|-----|
|
||||
| CPU | 0.8 |
|
||||
| 記憶體 | 2048 MB |
|
||||
| GPU | 不使用 |
|
||||
| 依賴 | ASR |
|
||||
117
docs_v1.0/API_V1.0.0/INTERNAL/PROCESSORS/ASR_V1.0.0.md
Normal file
117
docs_v1.0/API_V1.0.0/INTERNAL/PROCESSORS/ASR_V1.0.0.md
Normal file
@@ -0,0 +1,117 @@
|
||||
---
|
||||
document_type: "spec"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "ASR Processor V1.0.0"
|
||||
date: "2026-05-02"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
parent: "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "processor"
|
||||
- "asr"
|
||||
- "whisper"
|
||||
- "speech-recognition"
|
||||
- "v1.0.0"
|
||||
ai_query_hints:
|
||||
- "ASR 使用 faster-whisper/small 模型及 INT8 CPU 量化"
|
||||
- "ASR 以 CUT 場景邊界為基礎分段處理長片"
|
||||
- "ASR 每個 segment 記錄 scene_number 對應 CUT 場景序號"
|
||||
- "ASR 處理 159.6s 影片約 12.68s,即時倍率 12.6x"
|
||||
- "ASR 依賴 CUT processor 的場景邊界輸出"
|
||||
related_documents:
|
||||
- "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
- "../CUT_V1.0.0.md"
|
||||
- "../ASRX_V1.0.0.md"
|
||||
- "../STORY_V1.0.0.md"
|
||||
- "../CHUNK_DEFINITION_V1.0.0.md"
|
||||
---
|
||||
|
||||
# ASR Processor V1.0.0
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-05-02 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
**狀態**: ✅ 100% | **模型**: faster-whisper/small | **GPU**: 否
|
||||
|
||||
## 關鍵術語定義
|
||||
|
||||
| 術語 | 定義 |
|
||||
|------|------|
|
||||
| ASR | Automatic Speech Recognition,自動語音辨識 |
|
||||
| faster-whisper | 基於 OpenAI Whisper 的優化版本,支援 INT8 CPU 量化 |
|
||||
| segment | Whisper 輸出的語音片段,包含 start/end/time/text |
|
||||
| scene_number | CUT 場景序號(1-based),標示 segment 所屬場景 |
|
||||
| real-time factor | 即時倍率,處理時間與影片時長的比值 |
|
||||
|
||||
---
|
||||
|
||||
## 選型過程
|
||||
|
||||
| 模型 | 參數 | 大小 | English WER | Chinese CER | 速度 |
|
||||
|------|------|------|-------------|-------------|------|
|
||||
| tiny | 39M | ~40MB | 9.5% | 15.0% | ~1x RT |
|
||||
| base | 74M | ~75MB | 7.3% | 11.2% | ~1.5x RT |
|
||||
| **small** | **244M** | **~250MB** | **5.5%** | **8.4%** | **~2x RT** |
|
||||
| medium | 769M | ~800MB | 4.3% | 6.4% | ~3x RT |
|
||||
| large-v3 | 1.5B | ~1.5GB | 3.5% | 4.9% | ~5x RT |
|
||||
|
||||
**決策**: small 在準確率與速度間取得最佳平衡,經實驗驗證最少要使用 small 才能較好處理多語種及台灣腔國語。
|
||||
|
||||
---
|
||||
|
||||
## 效能實測(ExaSAN 159.6s 影片)
|
||||
|
||||
| 指標 | 值 |
|
||||
|------|-----|
|
||||
| 處理時間 | 12.68s |
|
||||
| 即時倍率 | 12.6x |
|
||||
| 輸出 | 78~79 segments, ~15KB |
|
||||
|
||||
---
|
||||
|
||||
## 長片分段處理
|
||||
|
||||
對於長片(如 Charade 6879s),ASR 以 CUT processor 產出的場景邊界為基礎分段處理:
|
||||
|
||||
1. CUT 先產出 `{file_uuid}.cut.json`(含 `scenes[]`,每個有 `start_time`/`end_time`)
|
||||
2. ASR 讀取 CUT JSON,依 `scene_number` 順序對每個場景萃取音訊
|
||||
3. 每個場景分別用 Whisper 轉錄
|
||||
4. 合併結果,每個 segment 記錄所屬的 `scene_number`
|
||||
|
||||
每個 segment 的 JSON 格式:
|
||||
```json
|
||||
{
|
||||
"start": 12.5,
|
||||
"end": 15.3,
|
||||
"text": "Hello world",
|
||||
"scene_number": 42
|
||||
}
|
||||
```
|
||||
|
||||
`scene_number` 是在該 `file_uuid` 下的 CUT 場景序號(1-based)。
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-05-02 | 初始版本 | OpenCode | deepseek-chat |
|
||||
|
||||
---
|
||||
|
||||
## 資源預估
|
||||
|
||||
| 資源 | 值 |
|
||||
|------|-----|
|
||||
| CPU | 1.0(一個完整核心) |
|
||||
| 記憶體 | 2048 MB(長片因分段處理,實際低於此值) |
|
||||
| GPU | 不使用(INT8 CPU 量化) |
|
||||
| 依賴 | 無 |
|
||||
80
docs_v1.0/API_V1.0.0/INTERNAL/PROCESSORS/CAPTION_V1.0.0.md
Normal file
80
docs_v1.0/API_V1.0.0/INTERNAL/PROCESSORS/CAPTION_V1.0.0.md
Normal file
@@ -0,0 +1,80 @@
|
||||
---
|
||||
document_type: "spec"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Caption Processor V1.0.0"
|
||||
date: "2026-05-02"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
parent: "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "processor"
|
||||
- "caption"
|
||||
- "moondream2"
|
||||
- "image-captioning"
|
||||
- "v1.0.0"
|
||||
ai_query_hints:
|
||||
- "Caption 使用 Moondream2 進行本地圖像描述生成"
|
||||
- "Caption 已從 GPT-4o 雲端 API 本地化為 Moondream2"
|
||||
- "Caption Moondream2 模型約 1.8GB,完全本地執行"
|
||||
- "Caption 處理速度約 5s/frame"
|
||||
- "Caption 備援方案為 YOLO + OCR + Scene 串接"
|
||||
related_documents:
|
||||
- "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
- "../SCENE_V1.0.0.md"
|
||||
- "../STORY_V1.0.0.md"
|
||||
- "../YOLO_V1.0.0.md"
|
||||
- "../OCR_V1.0.0.md"
|
||||
---
|
||||
|
||||
# Caption Processor V1.0.0
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-05-02 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
**狀態**: ✅ 100% | **模型**: Moondream2 | **GPU**: 否
|
||||
|
||||
## 關鍵術語定義
|
||||
|
||||
| 術語 | 定義 |
|
||||
|------|------|
|
||||
| Caption | 圖像描述生成,為每個場景產出文字敘述 |
|
||||
| Moondream2 | HuggingFace transformers 提供的本地圖像描述模型 |
|
||||
| GPT-4o | (已移除)先前使用的雲端 API 方案 |
|
||||
| local deployment | 完全本地執行,不依賴任何雲端 API |
|
||||
| fallback | 備援方案:YOLO + OCR + Scene 結果串接 |
|
||||
|
||||
---
|
||||
|
||||
## 選型過程
|
||||
|
||||
| 指標 | GPT-4o(已移除) | Moondream2(新) |
|
||||
|------|-----------------|-----------------|
|
||||
| 速度 | 2s/frame | 5s/frame |
|
||||
| 品質 | 高 | 良好 |
|
||||
| 依賴 | ✅ 雲端 API Key | ❌ 完全本地 |
|
||||
|
||||
**決策**: 已從 GPT-4o 雲端 API 本地化為 Moondream2(HuggingFace transformers, ~1.8GB)。備援方案為 YOLO + OCR + Scene 結果串接。
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-05-02 | 初始版本 | OpenCode | deepseek-chat |
|
||||
|
||||
## 資源預估
|
||||
|
||||
| 資源 | 值 |
|
||||
|------|-----|
|
||||
| CPU | - |
|
||||
| 記憶體 | ~1.8 GB(模型載入後) |
|
||||
| GPU | 不使用 |
|
||||
| 依賴 | Scene |
|
||||
135
docs_v1.0/API_V1.0.0/INTERNAL/PROCESSORS/CUT_V1.0.0.md
Normal file
135
docs_v1.0/API_V1.0.0/INTERNAL/PROCESSORS/CUT_V1.0.0.md
Normal file
@@ -0,0 +1,135 @@
|
||||
---
|
||||
document_type: "spec"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "CUT Processor (Scene Cut Detection) V1.0.0"
|
||||
date: "2026-05-03"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
parent: "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "processor"
|
||||
- "cut"
|
||||
- "scene-detection"
|
||||
- "pyscenedetect"
|
||||
- "v1.0.0"
|
||||
ai_query_hints:
|
||||
- "CUT 場景檢測的輸出結構與檔案後綴規則"
|
||||
- "CUT 的 cut_count 與 cut_max_duration 用途"
|
||||
- "長影片動態調度如何將 Face 移到 ASR 前"
|
||||
- "CUT 與 Scene 的執行階段(register 同步)"
|
||||
- "CUT 輸出 JSON 結構(start_time/end_time)"
|
||||
related_documents:
|
||||
- "PROCESSORS/SCENE_V1.0.0.md"
|
||||
- "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
- "PROCESSORS/ASR_V1.0.0.md"
|
||||
- "PROCESSORS/FACE_V1.0.0.md"
|
||||
- "CHUNK_DEFINITION_V1.0.0.md"
|
||||
---
|
||||
|
||||
# CUT Processor (Scene Cut Detection) V1.0.0
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-05-03 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
**狀態**: ✅ 100% | **模型**: PySceneDetect (ContentDetector) | **GPU**: 否
|
||||
|
||||
## 關鍵術語定義
|
||||
|
||||
| 術語 | 定義 |
|
||||
|------|------|
|
||||
| CUT | 場景切換檢測,使用 PySceneDetect ContentDetector |
|
||||
| scene boundary | 場景邊界,以 start_time/end_time 定義 |
|
||||
| cut_count | 場景數量,register 階段寫入 DB |
|
||||
| cut_max_duration | 最長場景秒數,用於長影片動態調度 |
|
||||
| ContentDetector | 基於幀差異的場景切換檢測演算法 |
|
||||
|
||||
---
|
||||
|
||||
## 選型過程
|
||||
|
||||
無 ML 模型,基於幀差異的場景切換檢測。門檻值 threshold=27 為實驗最佳值。
|
||||
|
||||
---
|
||||
|
||||
## 輸出結構
|
||||
|
||||
CUT 產出 `{file_uuid}.cut.json`,結構如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"scenes": [
|
||||
{ "start_time": 0.0, "end_time": 120.5 },
|
||||
{ "start_time": 120.5, "end_time": 245.0 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 執行階段
|
||||
|
||||
CUT 在 **register 階段同步執行**(`register_single_file`),不做 worker pipeline 排程。完成後寫入 DB 欄位:
|
||||
- `cut_done: bool` — 是否完成
|
||||
- `cut_count: i32` — 場景數量
|
||||
- `cut_max_duration: f64` — 最長場景秒數
|
||||
|
||||
---
|
||||
|
||||
## 狀態後綴
|
||||
|
||||
| 後綴 | 意義 | 行為 |
|
||||
|------|------|------|
|
||||
| `.cut.json` | 完成 | 直接載入使用 |
|
||||
| `.cut.json.tmp` | 執行中 | 跳過、等待 |
|
||||
| `.cut.json.err` | 失敗 | 跳過、不重試 |
|
||||
|
||||
---
|
||||
|
||||
## 長影片動態調度
|
||||
|
||||
當 `cut_count ≤ 3 && cut_max_duration > 600s`(如會議紀錄長鏡頭),Worker 自動調整 pipeline 順序:
|
||||
- **Face 移到 ASR 前面**,先用 face detection 找出人物進出點
|
||||
- 後續可用 face 分佈切分長 scene,輔助 ASR 分段
|
||||
|
||||
---
|
||||
|
||||
## 效能實測
|
||||
|
||||
**ExaSAN 159.6s 影片**:
|
||||
| 指標 | 值 |
|
||||
|------|-----|
|
||||
| 處理時間 | 0.08s |
|
||||
| 即時倍率 | 2036.5x(最快的 processor) |
|
||||
| 輸出 | 52 bytes |
|
||||
|
||||
**Charade 長片(6879s, 412343 幀)**:
|
||||
| 指標 | 值 |
|
||||
|------|-----|
|
||||
| 場景數 | 1331 |
|
||||
| 輸出 | 217 KB |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-05-03 | 初始版本 | OpenCode | deepseek-chat |
|
||||
|
||||
---
|
||||
|
||||
## 資源預估
|
||||
|
||||
| 資源 | 值 |
|
||||
|------|-----|
|
||||
| CPU | 0.5 |
|
||||
| 記憶體 | 512 MB |
|
||||
| GPU | 不使用 |
|
||||
| 依賴 | 無 |
|
||||
@@ -0,0 +1,133 @@
|
||||
---
|
||||
document_type: "spec"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Face Embedding 產出流程 V1.0.0"
|
||||
date: "2026-05-02"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "face"
|
||||
- "embedding"
|
||||
- "qdrant"
|
||||
- "v1.0.0"
|
||||
ai_query_hints:
|
||||
- "Face Embedding 的完整處理流程(Frame → InsightFace → Qdrant)"
|
||||
- "Face processor 的輸出結構與 embedding 欄位說明"
|
||||
- "Worker store_face_chunks 與 store_face_embeddings_to_qdrant 的步驟"
|
||||
- "Qdrant face collection 的 payload 結構與點位 ID 規則"
|
||||
- "Face embedding 的 512-D ArcFace w600k_r50 向量規格"
|
||||
- "Face embedding 使用 Cosine 距離計算"
|
||||
- "InsightFace buffalo_l 的資源預估與 GPU 加速資訊"
|
||||
- "face_detections 表與 Qdrant 的資料同步方式"
|
||||
related_documents:
|
||||
- "../VECTOR_SPEC_V1.0.0.md"
|
||||
- "../PROCESSORS/FACE_V1.0.0.md"
|
||||
- "../PROCESSOR_SELECTION_V1.0.0.md"
|
||||
- "../CHUNK_DEFINITION_V1.0.0.md"
|
||||
- "../MOMENTRY_CORE_API_V1.0.0.md"
|
||||
---
|
||||
|
||||
# Face Embedding 產出流程 V1.0.0
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-05-02 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
## 關鍵術語定義
|
||||
|
||||
| 術語 | 定義 |
|
||||
|------|------|
|
||||
| Face Embedding | 人臉向量嵌入,由 InsightFace ArcFace 產出 512-D 向量 |
|
||||
| SCRFD-10G | InsightFace 的人臉檢測模型 |
|
||||
| ArcFace w600k_r50 | InsightFace 的人臉辨識模型,產出 512-D embedding |
|
||||
| point_id | Qdrant 中向量的唯一 ID,使用幀編號 (frame number) |
|
||||
| Cosine distance | 餘弦距離,用於向量相似度計算 |
|
||||
| payload | Qdrant 向量的附帶 metadata 欄位 |
|
||||
|
||||
## 處理流程
|
||||
|
||||
```
|
||||
1. Video Frame (取樣)
|
||||
│
|
||||
▼
|
||||
2. Face Processor (face_processor.py)
|
||||
├── InsightFace buffalo_l
|
||||
│ ├── SCRFD-10G 人臉檢測
|
||||
│ ├── ArcFace w600k_r50 512-D embedding
|
||||
│ ├── 年齡/性別預測
|
||||
│ └── 2D106 landmarks
|
||||
│
|
||||
├── 輸出: job_{id}_face_{ts}.json → {file_uuid}.face.json
|
||||
│ └── FaceResult { frame_count, fps, frames: [FaceFrame] }
|
||||
│
|
||||
▼
|
||||
3. Worker store_face_chunks()
|
||||
├── 解析 FaceResult
|
||||
├── 寫入 pre_chunks 表 (file_uuid, processor_type='face', data)
|
||||
└── 寫入 face_detections 表
|
||||
│
|
||||
▼
|
||||
4. Worker store_face_embeddings_to_qdrant()
|
||||
├── 對每個 face frame 的每個 face
|
||||
│ └── 若有 embedding (512-D):
|
||||
│ ├── point_id = frame number (u64)
|
||||
│ ├── vector = 512-D float array
|
||||
│ └── payload (見下方)
|
||||
└── 寫入 Qdrant collection `momentry_dev_face`
|
||||
```
|
||||
|
||||
## Qdrant Payload 結構
|
||||
|
||||
```json
|
||||
{
|
||||
"file_uuid": "dd61fda85fee441fdd00ab5528213ff7",
|
||||
"face_id": null,
|
||||
"frame": 15,
|
||||
"timestamp": 0.68,
|
||||
"x": 328,
|
||||
"y": 88,
|
||||
"width": 63,
|
||||
"height": 75,
|
||||
"confidence": 0.83
|
||||
}
|
||||
```
|
||||
|
||||
| 欄位 | 型別 | 說明 |
|
||||
|------|------|------|
|
||||
| `file_uuid` | string | 來源影片識別碼 |
|
||||
| `face_id` | string|null | 臉部追蹤 ID(尚未分配時為 null) |
|
||||
| `frame` | integer | 幀編號 |
|
||||
| `timestamp` | float | 時間戳(秒) |
|
||||
| `x, y, width, height` | integer | 人臉邊界框 |
|
||||
| `confidence` | float | 檢測信心度 (0~1) |
|
||||
|
||||
## Vector 規格
|
||||
|
||||
| 屬性 | 值 |
|
||||
|------|-----|
|
||||
| 模型 | InsightFace ArcFace w600k_r50 |
|
||||
| 維度 | 512 |
|
||||
| 距離計算 | Cosine |
|
||||
| 歸一化 | 否 (raw output) |
|
||||
|
||||
## 來源 Processor 資源預估
|
||||
|
||||
| 資源 | 值 |
|
||||
|------|-----|
|
||||
| 模型 | InsightFace buffalo_l (~150MB) |
|
||||
| CPU | 0.6 |
|
||||
| 記憶體 | 1536 MB |
|
||||
| GPU | 支援(CoreML 50-80 FPS, CUDA 80-120 FPS) |
|
||||
| 處理速度 | 130.5x real-time (M4 Mac Mini) |
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-05-02 | 初始版本 | OpenCode | deepseek-chat |
|
||||
104
docs_v1.0/API_V1.0.0/INTERNAL/PROCESSORS/FACE_V1.0.0.md
Normal file
104
docs_v1.0/API_V1.0.0/INTERNAL/PROCESSORS/FACE_V1.0.0.md
Normal file
@@ -0,0 +1,104 @@
|
||||
---
|
||||
document_type: "spec"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Face Processor V1.0.0"
|
||||
date: "2026-05-02"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
parent: "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "processor"
|
||||
- "face"
|
||||
- "insightface"
|
||||
- "face-detection"
|
||||
- "v1.0.0"
|
||||
ai_query_hints:
|
||||
- "Face 使用 InsightFace buffalo_l 進行人臉偵測與辨識"
|
||||
- "Face 在 ExaSAN 159.6s 影片上僅需 1.22s,即時倍率 130.5x"
|
||||
- "Face 支援 GPU 加速,CoreML 可達 50~80 FPS"
|
||||
- "Face 輸出 512-D embedding 用於比對"
|
||||
- "Face 不再使用 Haar Cascade fallback,強制使用 InsightFace"
|
||||
related_documents:
|
||||
- "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
- "../FACE_EMBEDDING_FLOW_V1.0.0.md"
|
||||
- "../CUT_V1.0.0.md"
|
||||
- "../VECTOR_SPEC_V1.0.0.md"
|
||||
- "../CHUNK_DEFINITION_V1.0.0.md"
|
||||
---
|
||||
|
||||
# Face Processor V1.0.0
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-05-02 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
**狀態**: ✅ 100% | **模型**: InsightFace buffalo_l | **GPU**: 是
|
||||
|
||||
## 關鍵術語定義
|
||||
|
||||
| 術語 | 定義 |
|
||||
|------|------|
|
||||
| Face Detection | 人臉偵測,使用 InsightFace SCRFD-10G |
|
||||
| Face Recognition | 人臉辨識,使用 ArcFace w600k_r50 產出 512-D embedding |
|
||||
| embedding | 向量嵌入,用於人臉比對與搜尋 |
|
||||
| CoreML | Apple Silicon 上的 GPU 加速方案 |
|
||||
| LFW | Labeled Faces in the Wild,人臉辨識基準資料集 |
|
||||
|
||||
---
|
||||
|
||||
## 選型過程
|
||||
|
||||
| 模型 | 類型 | 大小 | 檢測率 | 辨識率 | Embedding |
|
||||
|------|------|------|--------|--------|-----------|
|
||||
| **InsightFace Buffalo_l** | **完整套件** | **~150MB** | **97.3% mAP** | **99.77% (LFW)** | **512-D ✅** |
|
||||
| MediaPipe BlazeFace | 輕量檢測 | 1~2MB | 95.2% mAP | 無 | ❌ |
|
||||
| OpenCV Haar Cascade | 傳統 ML | 900KB | 70~85% | 無 | ❌ |
|
||||
|
||||
**關鍵決策**: 舊版 Haar Cascade fallback 會產生全鏈路失敗(0 embeddings),已改為強制使用 InsightFace。
|
||||
|
||||
---
|
||||
|
||||
## 效能實測(ExaSAN 159.6s 影片)
|
||||
|
||||
| 指標 | 值 |
|
||||
|------|-----|
|
||||
| 處理時間 | 1.22s |
|
||||
| 即時倍率 | 130.5x |
|
||||
| 輸出 | 49 frames, 67 faces |
|
||||
|
||||
---
|
||||
|
||||
## GPU 加速
|
||||
|
||||
| 平台 | FPS |
|
||||
|------|-----|
|
||||
| CoreML (Apple Silicon) | 50~80 FPS |
|
||||
| CUDA (NVIDIA) | 80~120 FPS |
|
||||
| CPU | 15~20 FPS |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-05-02 | 初始版本 | OpenCode | deepseek-chat |
|
||||
|
||||
---
|
||||
|
||||
## 資源預估
|
||||
|
||||
| 資源 | 值 |
|
||||
|------|-----|
|
||||
| CPU | 0.6 |
|
||||
| 記憶體 | 1536 MB |
|
||||
| GPU | 支援(`uses_gpu = true`) |
|
||||
| 依賴 | 無 |
|
||||
|
||||
---
|
||||
87
docs_v1.0/API_V1.0.0/INTERNAL/PROCESSORS/OCR_V1.0.0.md
Normal file
87
docs_v1.0/API_V1.0.0/INTERNAL/PROCESSORS/OCR_V1.0.0.md
Normal file
@@ -0,0 +1,87 @@
|
||||
---
|
||||
document_type: "spec"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "OCR Processor V1.0.0"
|
||||
date: "2026-05-02"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
parent: "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "processor"
|
||||
- "ocr"
|
||||
- "paddleocr"
|
||||
- "optical-character-recognition"
|
||||
- "v1.0.0"
|
||||
ai_query_hints:
|
||||
- "OCR 使用 PaddleOCR PP-OCRv4 模型支援 80+ 語言"
|
||||
- "OCR 處理 159.6s 影片全幀約 36.87s,即時倍率 4.3x"
|
||||
- "OCR 輸出 102 frames, 234 texts, 65KB"
|
||||
- "OCR 不使用 GPU,CPU 使用率 0.8"
|
||||
- "OCR 精度 > 95%,支援繁體中文"
|
||||
related_documents:
|
||||
- "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
- "../YOLO_V1.0.0.md"
|
||||
- "../CAPTION_V1.0.0.md"
|
||||
- "../VISUAL_CHUNK_V1.0.0.md"
|
||||
- "../CHUNK_DEFINITION_V1.0.0.md"
|
||||
---
|
||||
|
||||
# OCR Processor V1.0.0
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-05-02 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
**狀態**: ✅ 100% | **模型**: PaddleOCR PP-OCRv4 | **GPU**: 否
|
||||
|
||||
## 關鍵術語定義
|
||||
|
||||
| 術語 | 定義 |
|
||||
|------|------|
|
||||
| OCR | Optical Character Recognition,光學字元辨識 |
|
||||
| PaddleOCR | 百度開發的 OCR 引擎,PP-OCRv4 為最新版本 |
|
||||
| PP-OCRv4 | PaddleOCR 第四代模型,支援 80+ 語言 |
|
||||
| real-time factor | 即時倍率,處理時間與影片時長的比值 |
|
||||
| full-frame processing | 全幀處理模式,對影片每一幀進行 OCR |
|
||||
|
||||
---
|
||||
|
||||
## 選型過程
|
||||
|
||||
選擇 PaddleOCR 原因:
|
||||
- 支援 80+ 語言(含繁體中文)
|
||||
- 精度 > 95%
|
||||
- EasyOCR 經測試不如 PaddleOCR
|
||||
|
||||
---
|
||||
|
||||
## 效能實測(ExaSAN 159.6s 影片, 全幀處理)
|
||||
|
||||
| 指標 | 值 |
|
||||
|------|-----|
|
||||
| 處理時間 | 36.87s |
|
||||
| 即時倍率 | 4.3x |
|
||||
| 輸出 | 102 frames, 234 texts, 65KB |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-05-02 | 初始版本 | OpenCode | deepseek-chat |
|
||||
|
||||
## 資源預估
|
||||
|
||||
| 資源 | 值 |
|
||||
|------|-----|
|
||||
| CPU | 0.8 |
|
||||
| 記憶體 | 1024 MB |
|
||||
| GPU | 不使用 |
|
||||
| 依賴 | 無 |
|
||||
84
docs_v1.0/API_V1.0.0/INTERNAL/PROCESSORS/POSE_V1.0.0.md
Normal file
84
docs_v1.0/API_V1.0.0/INTERNAL/PROCESSORS/POSE_V1.0.0.md
Normal file
@@ -0,0 +1,84 @@
|
||||
---
|
||||
document_type: "spec"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Pose Processor V1.0.0"
|
||||
date: "2026-05-02"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
parent: "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "processor"
|
||||
- "pose"
|
||||
- "mediapipe"
|
||||
- "pose-estimation"
|
||||
- "v1.0.0"
|
||||
ai_query_hints:
|
||||
- "Pose 使用 MediaPipe Pose (pose_landmarker_heavy, 33 keypoints)"
|
||||
- "Pose 處理 159.6s 影片全幀約 65.87s,即時倍率 2.4x"
|
||||
- "Pose 輸出 1853 frames, 2341 persons, 603KB"
|
||||
- "Pose 支援 GPU 加速(uses_gpu = true)"
|
||||
- "Pose 與 YOLO 同為處理瓶頸之一"
|
||||
related_documents:
|
||||
- "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
- "../YOLO_V1.0.0.md"
|
||||
- "../FACE_V1.0.0.md"
|
||||
- "../CUT_V1.0.0.md"
|
||||
- "../CHUNK_DEFINITION_V1.0.0.md"
|
||||
---
|
||||
|
||||
# Pose Processor V1.0.0
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-05-02 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
**狀態**: ✅ 100% | **模型**: MediaPipe Pose | **GPU**: 是
|
||||
|
||||
## 關鍵術語定義
|
||||
|
||||
| 術語 | 定義 |
|
||||
|------|------|
|
||||
| Pose Estimation | 姿態估計,偵測人體關鍵點位置 |
|
||||
| MediaPipe | Google 開發的跨平台 ML 解決方案 |
|
||||
| keypoint | 關鍵點,pose_landmarker_heavy 輸出 33 個關鍵點 |
|
||||
| landmarker_heavy | MediaPipe 的精確模式,準確度最高但速度較慢 |
|
||||
| bottleneck | 處理瓶頸,Pose 與 YOLO 同為最耗時的 processor |
|
||||
|
||||
---
|
||||
|
||||
## 選型過程
|
||||
|
||||
使用 MediaPipe Pose(pose_landmarker_heavy, 33 keypoints)。
|
||||
|
||||
---
|
||||
|
||||
## 效能實測(ExaSAN 159.6s 影片, 全幀處理)
|
||||
|
||||
| 指標 | 值 |
|
||||
|------|-----|
|
||||
| 處理時間 | 65.87s |
|
||||
| 即時倍率 | 2.4x(瓶頸之一,與 YOLO 相當) |
|
||||
| 輸出 | 1853 frames, 2341 persons, 603KB |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-05-02 | 初始版本 | OpenCode | deepseek-chat |
|
||||
|
||||
## 資源預估
|
||||
|
||||
| 資源 | 值 |
|
||||
|------|-----|
|
||||
| CPU | 0.4 |
|
||||
| 記憶體 | 1024 MB |
|
||||
| GPU | 支援(`uses_gpu = true`) |
|
||||
| 依賴 | 無 |
|
||||
95
docs_v1.0/API_V1.0.0/INTERNAL/PROCESSORS/SCENE_V1.0.0.md
Normal file
95
docs_v1.0/API_V1.0.0/INTERNAL/PROCESSORS/SCENE_V1.0.0.md
Normal file
@@ -0,0 +1,95 @@
|
||||
---
|
||||
document_type: "processor-spec"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Scene Processor (Scene Classification) V1.0.0"
|
||||
date: "2026-05-03"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
parent: "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "processor"
|
||||
- "scene"
|
||||
- "places365"
|
||||
- "scene-classification"
|
||||
- "v1.0.0"
|
||||
ai_query_hints:
|
||||
- "Scene 分類的模型選型與效能實測"
|
||||
- "Scene 的執行階段與檔案後綴檢查規則"
|
||||
- "Scene 與 CUT 的依賴關係(已移除 ASR)"
|
||||
- "Scene 輸出為 pre_chunks 供 Rule 3 parent chunk 使用"
|
||||
- "load_scene_from_file 直接載入 JSON 不入庫"
|
||||
related_documents:
|
||||
- "PROCESSORS/CUT_V1.0.0.md"
|
||||
- "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
- "PROCESSORS/CAPTION_V1.0.0.md"
|
||||
- "PROCESSORS/STORY_V1.0.0.md"
|
||||
- "CHUNK_DEFINITION_V1.0.0.md"
|
||||
---
|
||||
|
||||
# Scene Processor (Scene Classification) V1.0.0
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-05-03 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
**狀態**: ✅ 100% | **模型**: MIT Places365 (ResNet18) | **GPU**: 否
|
||||
|
||||
## 關鍵術語定義
|
||||
|
||||
| 術語 | 定義 |
|
||||
|------|------|
|
||||
| Scene Classification | 場景分類,辨識影片畫面的場景類型 |
|
||||
| Places365 | MIT 開發的場景辨識資料集與模型(365 個場景類別) |
|
||||
| ResNet18 | 殘差網路架構,輕量級分類模型 |
|
||||
| pre_chunks | 原始元件的資料表,Scene 輸出供 Rule 3 使用 |
|
||||
| parent chunk | 聚合多個 child chunks 的上層 chunk,由 Rule 3 產出 |
|
||||
|
||||
## 選型過程
|
||||
|
||||
初始使用 ImageNet(產生 scene_XXX 類別索引),後升級至 Places365 以獲得具名場景類別(如 living_room, beach, airport),準確率 85~90%。
|
||||
|
||||
## 執行階段
|
||||
|
||||
Scene 在 **register 階段同步執行**(`register_single_file`)。Worker 中重入時檢查後綴:
|
||||
- `.scene.json` → 從檔案載入(不入庫 pre_chunks)
|
||||
- `.scene.json.tmp` → 跳過(回傳空結果)
|
||||
- `.scene.json.err` → 跳過(回傳空結果)
|
||||
|
||||
載入函數:`load_scene_from_file(path: &str) -> SceneClassificationResult`
|
||||
|
||||
## 與 CUT 的關係
|
||||
|
||||
Scene 與 ASR 無關(純視覺分類),已移除對 ASR 的依賴。CUT 為 Scene 的唯一前置依賴。
|
||||
|
||||
## 輸出用途
|
||||
|
||||
Scene 為 **pre_chunks**(scene boundary),供 Rule 3 產生 parent chunk。Rule 3 需要 CUT + Scene 的 boundary 來產生複合 parent chunk。
|
||||
|
||||
## 效能實測(ExaSAN 159.6s 影片, 取樣間隔=2s)
|
||||
|
||||
| 指標 | 值 |
|
||||
|------|-----|
|
||||
| 處理時間 | 4.09s |
|
||||
| 即時倍率 | 39.0x |
|
||||
| 取樣數 | 79 samples |
|
||||
|
||||
## Charade 長片(6879s)
|
||||
|
||||
| 指標 | 值 |
|
||||
|------|-----|
|
||||
| 處理時間 | 313.3s(5.2 分鐘) |
|
||||
|
||||
## 資源預估
|
||||
|
||||
| 資源 | 值 |
|
||||
|------|-----|
|
||||
| CPU | 0.3 |
|
||||
| 記憶體 | 512 MB |
|
||||
| GPU | 不使用 |
|
||||
| 依賴 | CUT, ASR |
|
||||
80
docs_v1.0/API_V1.0.0/INTERNAL/PROCESSORS/STORY_V1.0.0.md
Normal file
80
docs_v1.0/API_V1.0.0/INTERNAL/PROCESSORS/STORY_V1.0.0.md
Normal file
@@ -0,0 +1,80 @@
|
||||
---
|
||||
document_type: "spec"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Story Processor V1.0.0"
|
||||
date: "2026-05-02"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
parent: "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "processor"
|
||||
- "story"
|
||||
- "template-aggregator"
|
||||
- "narrative"
|
||||
- "v1.0.0"
|
||||
ai_query_hints:
|
||||
- "Story 使用模板聚合從 ASR+YOLO+Scene 產生結構化敘述"
|
||||
- "Story 已從 GPT-4 雲端 API 本地化為模板聚合"
|
||||
- "Story 處理速度 <0.1s/chunk,極快"
|
||||
- "Story 完全不依賴雲端 API,完全本地執行"
|
||||
- "Story 依賴 Scene 和 Caption processor 的輸出"
|
||||
related_documents:
|
||||
- "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
- "../SCENE_V1.0.0.md"
|
||||
- "../CAPTION_V1.0.0.md"
|
||||
- "../ASR_V1.0.0.md"
|
||||
- "../CHUNK_DEFINITION_V1.0.0.md"
|
||||
---
|
||||
|
||||
# Story Processor V1.0.0
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-05-02 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
**狀態**: ✅ 100% | **模型**: 模板聚合 | **GPU**: 否
|
||||
|
||||
## 關鍵術語定義
|
||||
|
||||
| 術語 | 定義 |
|
||||
|------|------|
|
||||
| Story Processor | 從 ASR + YOLO + Scene 結果產生結構化敘述的處理器 |
|
||||
| Template Aggregation | 使用預定義模板組合資料,非 LLM 生成 |
|
||||
| GPT-4 | (已移除)先前使用的雲端 API 方案 |
|
||||
| local deployment | 完全本地執行,不依賴任何雲端 API |
|
||||
| structured narrative | 結構化敘述,以固定格式組織的故事描述 |
|
||||
|
||||
---
|
||||
|
||||
## 選型過程
|
||||
|
||||
| 指標 | GPT-4(已移除) | 模板(新) |
|
||||
|------|----------------|------------|
|
||||
| 速度 | 3s/chunk | **<0.1s/chunk** |
|
||||
| 品質 | 自然語言 | 結構化格式 |
|
||||
| 依賴 | ✅ 雲端 API Key | ❌ 完全本地 |
|
||||
|
||||
**決策**: 已從 GPT-4 雲端 API 本地化為模板聚合,從 ASR + YOLO + Scene 結果產生結構化敘述。
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-05-02 | 初始版本 | OpenCode | deepseek-chat |
|
||||
|
||||
## 資源預估
|
||||
|
||||
| 資源 | 值 |
|
||||
|------|-----|
|
||||
| CPU | - |
|
||||
| 記憶體 | - |
|
||||
| GPU | 不使用 |
|
||||
| 依賴 | Scene, Caption |
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
document_type: "spec"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "VisualChunk Processor V1.0.0"
|
||||
date: "2026-05-02"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
parent: "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "processor"
|
||||
- "visual-chunk"
|
||||
- "rule-aggregator"
|
||||
- "yolo"
|
||||
- "v1.0.0"
|
||||
ai_query_hints:
|
||||
- "VisualChunk 是規則驅動的聚合器,非 ML 模型"
|
||||
- "VisualChunk 將 YOLO 結果組合成視覺分片"
|
||||
- "VisualChunk 依賴 YOLO processor 的偵測結果"
|
||||
- "VisualChunk CPU 使用率低(0.3),記憶體 512 MB"
|
||||
- "VisualChunk 是 Scene 和 Story processor 的前置依賴"
|
||||
related_documents:
|
||||
- "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
- "../YOLO_V1.0.0.md"
|
||||
- "../SCENE_V1.0.0.md"
|
||||
- "../STORY_V1.0.0.md"
|
||||
- "../CHUNK_DEFINITION_V1.0.0.md"
|
||||
---
|
||||
|
||||
# VisualChunk Processor V1.0.0
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-05-02 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
**狀態**: ✅ 整合 | **模型**: 無(規則聚合) | **GPU**: 否
|
||||
|
||||
## 關鍵術語定義
|
||||
|
||||
| 術語 | 定義 |
|
||||
|------|------|
|
||||
| VisualChunk | 規則驅動的聚合器,將 YOLO 結果組合成視覺分片 |
|
||||
| Rule Aggregation | 使用預設規則而非 ML 模型進行資料組合 |
|
||||
| Visual Chunk | 視覺分片,包含 YOLO 偵測物件的時間區間 |
|
||||
| pre_chunks | 原始元件表,VisualChunk 的輸出會寫入此表 |
|
||||
| dependency chain | 依賴鏈:YOLO → VisualChunk → Scene → Story |
|
||||
|
||||
---
|
||||
|
||||
## 說明
|
||||
|
||||
非 ML 模型,是規則驅動的聚合器,將 YOLO 結果組合成視覺分片。
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-05-02 | 初始版本 | OpenCode | deepseek-chat |
|
||||
|
||||
## 資源預估
|
||||
|
||||
| 資源 | 值 |
|
||||
|------|-----|
|
||||
| CPU | 0.3 |
|
||||
| 記憶體 | 512 MB |
|
||||
| GPU | 不使用 |
|
||||
| 依賴 | YOLO |
|
||||
@@ -0,0 +1,139 @@
|
||||
---
|
||||
document_type: "spec"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Voice Embedding 產出流程 V1.0.0"
|
||||
date: "2026-05-02"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "voice"
|
||||
- "embedding"
|
||||
- "asrx"
|
||||
- "qdrant"
|
||||
- "v1.0.0"
|
||||
ai_query_hints:
|
||||
- "Voice Embedding 的完整處理流程(音軌 → ECAPA-TDNN → Qdrant)"
|
||||
- "ASRX Processor 的三階段處理:音軌預處理 → ASR segments 載入 → Speaker Diarization"
|
||||
- "Worker store_asrx_chunks 的步驟與 pre_chunks 寫入規則"
|
||||
- "Qdrant voice collection 的 payload 結構與欄位定義"
|
||||
- "Voice embedding 的 192-D ECAPA-TDNN 向量規格(L2 normalize)"
|
||||
- "Voice embedding 使用 Cosine 距離計算與 L2 歸一化"
|
||||
- "SpeechBrain ECAPA-TDNN 的資源預估與處理速度"
|
||||
- "Voice embedding 與 ASR 處理器的依賴關係"
|
||||
related_documents:
|
||||
- "../VECTOR_SPEC_V1.0.0.md"
|
||||
- "../PROCESSORS/ASRX_V1.0.0.md"
|
||||
- "../PROCESSORS/ASR_V1.0.0.md"
|
||||
- "../PROCESSOR_SELECTION_V1.0.0.md"
|
||||
- "../MOMENTRY_CORE_API_V1.0.0.md"
|
||||
---
|
||||
|
||||
# Voice Embedding 產出流程 V1.0.0
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-05-02 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
## 關鍵術語定義
|
||||
|
||||
| 術語 | 定義 |
|
||||
|------|------|
|
||||
| Voice Embedding | 語音向量嵌入,由 ECAPA-TDNN 產出 192-D 向量 |
|
||||
| ECAPA-TDNN | SpeechBrain 提供的說話人辨識模型 |
|
||||
| L2 normalize | 向量歸一化,確保所有向量單位長度 |
|
||||
| Spectral Clustering | 頻譜聚類,將語音 embedding 分群以區分說話人 |
|
||||
| segment_index | 在 asrx 輸出 segments 中的索引編號 |
|
||||
| speaker_id | 說話人標籤(如 SPEAKER_0, SPEAKER_1) |
|
||||
|
||||
## 處理流程
|
||||
|
||||
```
|
||||
1. Video → ffmpeg 萃取音軌 → 16kHz mono WAV
|
||||
│
|
||||
▼
|
||||
2. ASRX Processor (asrx_processor_custom.py)
|
||||
│
|
||||
├── Stage 1: 音軌預處理
|
||||
│ ├── ffprobe 列出所有音軌
|
||||
│ ├── 選擇最佳音軌(優先英語)
|
||||
│ └── ffmpeg 轉為 16kHz mono WAV
|
||||
│
|
||||
├── Stage 2: 載入 ASR segments
|
||||
│ └── 從 {file_uuid}.asr.json 讀取 segments
|
||||
│
|
||||
├── Stage 3: Speaker Diarization (SelfASRXFixed.process_with_segments)
|
||||
│ ├── 對每個 ASR segment 取出音訊片段
|
||||
│ ├── ECAPA-TDNN 產出 192-D embedding
|
||||
│ ├── 正規化 embeddings
|
||||
│ └── 譜聚類 → speaker label
|
||||
│
|
||||
├── 輸出: {file_uuid}.asrx.json
|
||||
│ ├── segments: [start_time, end_time, speaker_id]
|
||||
│ └── embeddings: [[192-D float array], ...]
|
||||
│
|
||||
▼
|
||||
3. Worker store_asrx_chunks()
|
||||
├── 解析 AsrxResult
|
||||
├── 寫入 pre_chunks 表
|
||||
└── 寫入 voice embeddings 到 Qdrant
|
||||
│
|
||||
▼
|
||||
4. Qdrant `momentry_dev_voice`
|
||||
└── 每個 segment 一個 vector
|
||||
```
|
||||
|
||||
## Qdrant Payload 結構
|
||||
|
||||
```json
|
||||
{
|
||||
"file_uuid": "dd61fda85fee441fdd00ab5528213ff7",
|
||||
"speaker_id": "SPEAKER_0",
|
||||
"segment_index": 0,
|
||||
"start_frame": 9,
|
||||
"end_frame": 441,
|
||||
"start_time": 0.3,
|
||||
"end_time": 14.7
|
||||
}
|
||||
```
|
||||
|
||||
| 欄位 | 型別 | 說明 |
|
||||
|------|------|------|
|
||||
| `file_uuid` | string | 來源影片識別碼 |
|
||||
| `speaker_id` | string | 說話人標籤(如 SPEAKER_0) |
|
||||
| `segment_index` | integer | 在 segments 中的索引 |
|
||||
| `start_frame` | integer | 起始幀 |
|
||||
| `end_frame` | integer | 結束幀 |
|
||||
| `start_time` | float | 起始時間(秒) |
|
||||
| `end_time` | float | 結束時間(秒) |
|
||||
|
||||
## Vector 規格
|
||||
|
||||
| 屬性 | 值 |
|
||||
|------|-----|
|
||||
| 模型 | SpeechBrain ECAPA-TDNN |
|
||||
| 維度 | 192 |
|
||||
| 距離計算 | Cosine |
|
||||
| 歸一化 | 是(L2 normalize) |
|
||||
|
||||
## 來源 Processor 資源預估
|
||||
|
||||
| 資源 | 值 |
|
||||
|------|-----|
|
||||
| 模型 | SpeechBrain ECAPA-TDNN (~80MB) |
|
||||
| CPU | 0.8 |
|
||||
| 記憶體 | 2048 MB |
|
||||
| GPU | 不使用 |
|
||||
| 處理速度 | 57x real-time (M4 Mac Mini) |
|
||||
| 依賴 | ASR(需 ASR JSON 完成後才能啟動) |
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-05-02 | 初始版本 | OpenCode | deepseek-chat |
|
||||
92
docs_v1.0/API_V1.0.0/INTERNAL/PROCESSORS/YOLO_V1.0.0.md
Normal file
92
docs_v1.0/API_V1.0.0/INTERNAL/PROCESSORS/YOLO_V1.0.0.md
Normal file
@@ -0,0 +1,92 @@
|
||||
---
|
||||
document_type: "spec"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "YOLO Processor V1.0.0"
|
||||
date: "2026-05-02"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
parent: "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "processor"
|
||||
- "yolo"
|
||||
- "object-detection"
|
||||
- "yolov8"
|
||||
- "v1.0.0"
|
||||
ai_query_hints:
|
||||
- "YOLO 使用 yolov8n (nano) 模型進行物件偵測"
|
||||
- "YOLO 在 M4 Mac Mini 上可達 100~200 FPS"
|
||||
- "YOLO 支援 GPU 加速(MPS),可快 2~5 倍"
|
||||
- "YOLO 輸出 4.3 MB 含偵測結果"
|
||||
- "YOLO 是 VisualChunk 和 Scene 的依賴"
|
||||
related_documents:
|
||||
- "PROCESSOR_SELECTION_V1.0.0.md"
|
||||
- "../VISUAL_CHUNK_V1.0.0.md"
|
||||
- "../POSE_V1.0.0.md"
|
||||
- "../OCR_V1.0.0.md"
|
||||
- "../CHUNK_DEFINITION_V1.0.0.md"
|
||||
---
|
||||
|
||||
# YOLO Processor V1.0.0
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-05-02 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
**狀態**: ✅ 100% | **模型**: YOLOv8n (nano) | **GPU**: 是
|
||||
|
||||
## 關鍵術語定義
|
||||
|
||||
| 術語 | 定義 |
|
||||
|------|------|
|
||||
| YOLO | You Only Look Once,即時物件偵測演算法 |
|
||||
| YOLOv8n | Ultralytics YOLO 第八代 nano 版本,最小最快 |
|
||||
| object detection | 物件偵測,辨識影像中的物體類別與位置 |
|
||||
| MPS | Metal Performance Shaders,Apple Silicon GPU 加速 |
|
||||
| bottleneck | 處理瓶頸,YOLO 與 Pose 同為最耗時的 processor |
|
||||
|
||||
---
|
||||
|
||||
## 選型過程
|
||||
|
||||
| 模型 | 參數 | 大小 | 速度 | 精度 |
|
||||
|------|------|------|------|------|
|
||||
| **yolov8n (nano)** | **3.2M** | **6.2MB** | **最快** | **較低** |
|
||||
| yolov8s (small) | 11.2M | - | 快 | 中等 |
|
||||
| yolov8m (medium) | 25.9M | - | 中 | 高 |
|
||||
| yolov8l (large) | 43.7M | - | 慢 | 很高 |
|
||||
| yolov8x (x-large) | 68.2M | - | 最慢 | 最高 |
|
||||
|
||||
**決策**: 預設使用 `yolov8n.pt`(nano),在 M4 Mac Mini 上可達 100~200 FPS。可透過配置檔切換至更大模型。
|
||||
|
||||
---
|
||||
|
||||
## 效能實測(ExaSAN 159.6s 影片, 全幀處理)
|
||||
|
||||
| 指標 | 值 |
|
||||
|------|-----|
|
||||
| 處理時間 | 65.72s |
|
||||
| 即時倍率 | 2.4x(瓶頸之一) |
|
||||
| 輸出 | 4.3 MB |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-05-02 | 初始版本 | OpenCode | deepseek-chat |
|
||||
|
||||
## 資源預估
|
||||
|
||||
| 資源 | 值 |
|
||||
|------|-----|
|
||||
| CPU | 0.3 |
|
||||
| 記憶體 | 1024 MB |
|
||||
| GPU | 支援(`yolo_processor_mps.py` 可使用 MPS,快 2~5 倍) |
|
||||
| 依賴 | 無 |
|
||||
108
docs_v1.0/API_V1.0.0/INTERNAL/PROCESSOR_SELECTION_V1.0.0.md
Normal file
108
docs_v1.0/API_V1.0.0/INTERNAL/PROCESSOR_SELECTION_V1.0.0.md
Normal file
@@ -0,0 +1,108 @@
|
||||
---
|
||||
document_type: "spec"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Processor 選型與資源預估 V1.0.0"
|
||||
date: "2026-05-02"
|
||||
version: "V1.1"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "processor"
|
||||
- "model-selection"
|
||||
- "resource-estimation"
|
||||
- "v1.0.0"
|
||||
ai_query_hints:
|
||||
- "processor 的選型原因與實驗報告"
|
||||
- "各 processor 的資源預估與模型資訊"
|
||||
- "processor 之間的依賴關係"
|
||||
- "模型選擇的比較與決策"
|
||||
- "processor 檔案狀態後綴規則(json/tmp/err)"
|
||||
- "Job 完成條件與必要 processor 定義"
|
||||
related_documents:
|
||||
- "PROCESSORS/ASR_V1.0.0.md"
|
||||
- "PROCESSORS/FACE_V1.0.0.md"
|
||||
- "PROCESSORS/YOLO_V1.0.0.md"
|
||||
- "PROCESSORS/CUT_V1.0.0.md"
|
||||
- "CHUNK_DEFINITION_V1.0.0.md"
|
||||
---
|
||||
|
||||
# Processor 選型與資源預估 V1.0.0
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-05-02 |
|
||||
| 文件版本 | V1.1 |
|
||||
|
||||
---
|
||||
|
||||
## 關鍵術語定義
|
||||
|
||||
| 術語 | 定義 |
|
||||
|------|------|
|
||||
| Processor | 處理器,負責特定類型媒體分析的 Python 腳本 |
|
||||
| Pipeline | 處理管線,定義 processor 的執行順序與依賴關係 |
|
||||
| PythonExecutor | 統一執行 Python 腳本的 Rust 封裝層 |
|
||||
| real-time factor | 即時倍率,處理時間與影片時長的比值 |
|
||||
| resource estimation | 資源預估,包含 CPU/記憶體/GPU 的使用量 |
|
||||
| Job | 處理任務,包含多個 processor 的執行與狀態管理 |
|
||||
|
||||
## 總覽
|
||||
|
||||
| Processor | 狀態 | 模型 | 依賴 | GPU | CPU | 記憶體 | 文件 |
|
||||
|-----------|------|------|------|-----|-----|--------|------|
|
||||
| ASR | ✅ 100% | faster-whisper (small) | 無 | 否 | 1.0 | 2048 MB | [詳細](./PROCESSORS/ASR_V1.0.0.md) |
|
||||
| CUT | ✅ 100% | PySceneDetect | 無 | 否 | 0.5 | 512 MB | [詳細](./PROCESSORS/CUT_V1.0.0.md) |
|
||||
| YOLO | ✅ 100% | YOLOv8n | 無 | 是 | 0.3 | 1024 MB | [詳細](./PROCESSORS/YOLO_V1.0.0.md) |
|
||||
| OCR | ✅ 100% | PaddleOCR PP-OCRv4 | 無 | 否 | 0.8 | 1024 MB | [詳細](./PROCESSORS/OCR_V1.0.0.md) |
|
||||
| Face | ✅ 100% | InsightFace buffalo_l | 無 | 是 | 0.6 | 1536 MB | [詳細](./PROCESSORS/FACE_V1.0.0.md) |
|
||||
| Pose | ✅ 100% | MediaPipe Pose | 無 | 是 | 0.4 | 1024 MB | [詳細](./PROCESSORS/POSE_V1.0.0.md) |
|
||||
| ASRX | ⚠️ 80% | SpeechBrain ECAPA-TDNN | ASR | 否 | 0.8 | 2048 MB | [詳細](./PROCESSORS/ASRX_V1.0.0.md) |
|
||||
| Scene | ✅ 100% | MIT Places365 | CUT | 否 | 0.3 | 512 MB | [詳細](./PROCESSORS/SCENE_V1.0.0.md) |
|
||||
| VisualChunk | ✅ 整合 | 規則聚合(無模型) | YOLO | 否 | 0.3 | 512 MB | [詳細](./PROCESSORS/VISUAL_CHUNK_V1.0.0.md) |
|
||||
| Caption | ✅ 100% (本地化) | Moondream2 | Scene | 否 | - | - | [詳細](./PROCESSORS/CAPTION_V1.0.0.md) |
|
||||
| Story | ✅ 100% (本地化) | 模板聚合 | Scene, Caption | 否 | - | - | [詳細](./PROCESSORS/STORY_V1.0.0.md) |
|
||||
|
||||
---
|
||||
|
||||
## Processor 依賴關係圖 (V4.1)
|
||||
|
||||
```
|
||||
CUT ───→ Scene
|
||||
│
|
||||
ASR ───→ ASRX
|
||||
│
|
||||
YOLO ─→ VisualChunk
|
||||
```
|
||||
|
||||
> **註(V4.1)**:CUT 和 Scene 在 register 階段同步執行,Worker pipeline 中 Scene 依賴僅 CUT(已移除 ASR)。長影片(scene ≤ 3, max > 600s)時 Face 動態移到 ASR 前。
|
||||
|
||||
## 檔案狀態後綴
|
||||
|
||||
所有 processor 輸出檔案使用統一的後綴規則:
|
||||
|
||||
| 後綴 | 意義 | 行為 |
|
||||
|------|------|------|
|
||||
| `.json` | 完成 | 直接載入使用 |
|
||||
| `.json.tmp` | 執行中 | 跳過、等待 |
|
||||
| `.json.err` | 失敗 | 跳過、不重試 |
|
||||
|
||||
此規則由 `PythonExecutor` 統一處理(`executor.rs:150-279`)。
|
||||
|
||||
## Job 完成條件(V4.1)
|
||||
|
||||
| 條件 | 結果 |
|
||||
|------|------|
|
||||
| 所有 processor 完成 | ✅ Job completed |
|
||||
| 必要 processor (cut/asr/yolo) 完成,其餘失敗 | ✅ Job completed(非必要失敗不卡住) |
|
||||
| 必要 processor 任一失敗 | ❌ Job failed |
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-05-02 | 初始版本,含選型實驗報告與資源預估 | OpenCode | deepseek-chat |
|
||||
| V1.1 | 2026-05-03 | CUT 新增 cut_count/cut_max_duration;Scene 移除 ASR 依賴;長影片 Face 動態調度;Job 完成條件放寬 | OpenCode | deepseek-chat |
|
||||
129
docs_v1.0/API_V1.0.0/INTERNAL/VECTOR_SPEC_V1.0.0.md
Normal file
129
docs_v1.0/API_V1.0.0/INTERNAL/VECTOR_SPEC_V1.0.0.md
Normal file
@@ -0,0 +1,129 @@
|
||||
---
|
||||
document_type: "spec"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "向量化規範 V1.0.0"
|
||||
date: "2026-05-02"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "vector-embedding"
|
||||
- "qdrant"
|
||||
- "v1.0.0"
|
||||
- "face-embedding"
|
||||
- "voice-embedding"
|
||||
- "text-embedding"
|
||||
ai_query_hints:
|
||||
- "向量化規範的向量類型與維度說明"
|
||||
- "Face/Voice/Text 三種 embedding 的處理流程"
|
||||
- "Qdrant collection 的名稱與 payload 結構"
|
||||
- "Face embedding 的 512-D 向量規格(InsightFace ArcFace)"
|
||||
- "Voice embedding 的 192-D 向量規格(ECAPA-TDNN)"
|
||||
- "Text embedding 的 768-D 向量規格(nomic-embed-text-v2-moe)"
|
||||
- "Qdrant Payload 中 face 與 voice 的欄位定義"
|
||||
- "向量化流程中 child chunk 與 parent chunk 的 collection 區別"
|
||||
related_documents:
|
||||
- "PROCESSORS/FACE_EMBEDDING_FLOW_V1.0.0.md"
|
||||
- "PROCESSORS/VOICE_EMBEDDING_FLOW_V1.0.0.md"
|
||||
- "CHUNK_DEFINITION_V1.0.0.md"
|
||||
- "PROCESSORS/FACE_V1.0.0.md"
|
||||
- "PROCESSORS/ASRX_V1.0.0.md"
|
||||
---
|
||||
|
||||
# 向量化規範 V1.0.0
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-05-02 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
## 關鍵術語定義
|
||||
|
||||
| 術語 | 定義 |
|
||||
|------|------|
|
||||
| embedding | 向量嵌入,將非結構化資料轉換為數值向量 |
|
||||
| Qdrant | 向量資料庫,用於儲存與檢索 embedding |
|
||||
| collection | Qdrant 中的向量集合,類似資料庫中的資料表 |
|
||||
| 768-D | Text embedding 的維度,由 nomic-embed-text-v2-moe 產出 |
|
||||
| 512-D | Face embedding 的維度,由 InsightFace ArcFace 產出 |
|
||||
| 192-D | Voice embedding 的維度,由 SpeechBrain ECAPA-TDNN 產出 |
|
||||
|
||||
## 向量類型
|
||||
|
||||
| 類型 | 來源 | 維度 | Collection | 用途 |
|
||||
|------|------|------|------------|------|
|
||||
| Text (child) | sentence chunk | 768-D | `momentry_dev_rule1` | 語意搜尋 |
|
||||
| Text (parent) | scene chunk summary | 768-D | `momentry_dev_chunk_summaries` | 場景語意搜尋 |
|
||||
| **Face** | Face processor (InsightFace) | **512-D** | `momentry_dev_face` | 人臉比對 |
|
||||
| **Voice** | ASRX processor (ECAPA-TDNN) | **192-D** | `momentry_dev_voice` | 說話人比對 |
|
||||
|
||||
## 向量化流程
|
||||
|
||||
### Text Embedding
|
||||
|
||||
```
|
||||
chunk (sentence / scene)
|
||||
→ text_content / summary_text
|
||||
→ nomic-embed-text-v2-moe (Ollama)
|
||||
→ 768-D vector
|
||||
→ Qdrant momentry_dev_rule1 / momentry_dev_chunk_summaries
|
||||
```
|
||||
|
||||
### Face Embedding
|
||||
|
||||
```
|
||||
Face processor (InsightFace buffalo_l)
|
||||
→ face_detections.embedding (512-D)
|
||||
→ Qdrant momentry_dev_face
|
||||
→ 用於 1:N 人臉比對
|
||||
```
|
||||
|
||||
### Voice Embedding
|
||||
|
||||
```
|
||||
ASRX processor (ECAPA-TDNN)
|
||||
→ speaker embedding (192-D)
|
||||
→ Qdrant momentry_dev_voice
|
||||
→ 用於跨影片說話人辨識
|
||||
```
|
||||
|
||||
## Qdrant Payload 結構
|
||||
|
||||
### Face Payload
|
||||
|
||||
```json
|
||||
{
|
||||
"file_uuid": "384b0ff44aaaa1f14cb2cd63b3fea966",
|
||||
"face_id": "face_42",
|
||||
"frame": 1260,
|
||||
"timestamp": 42.0,
|
||||
"x": 328,
|
||||
"y": 88,
|
||||
"width": 63,
|
||||
"height": 75,
|
||||
"confidence": 0.83
|
||||
}
|
||||
```
|
||||
|
||||
### Voice Payload
|
||||
|
||||
```json
|
||||
{
|
||||
"file_uuid": "384b0ff44aaaa1f14cb2cd63b3fea966",
|
||||
"speaker_id": "SPEAKER_0",
|
||||
"start_frame": 9,
|
||||
"end_frame": 441,
|
||||
"start_time": 0.3,
|
||||
"end_time": 14.7
|
||||
}
|
||||
```
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-05-02 | 初始版本 | OpenCode | deepseek-chat |
|
||||
@@ -1,167 +0,0 @@
|
||||
# Document Embedding Strategy - Parent-Child Chunks
|
||||
|
||||
| Item | Content |
|
||||
|------|---------|
|
||||
| Author | Warren |
|
||||
| Created | 2026-03-23 |
|
||||
| Document Version | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## Version History
|
||||
|
||||
| Version | Date | Purpose | Operator | Tool/Model |
|
||||
|---------|------|---------|----------|------------|
|
||||
| V1.0 | 2026-03-23 | Create document embedding strategy | Warren | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Momentry uses a **parent-child chunk hierarchy** for improved RAG retrieval. This document describes the embedding strategy for this hierarchy.
|
||||
|
||||
## Chunk Structure
|
||||
|
||||
### Parent Chunk
|
||||
- **Purpose**: Summarize multiple child chunks with narrative description
|
||||
- **Content**: High-level description of multiple scenes/segments
|
||||
- **Example**:
|
||||
```json
|
||||
{
|
||||
"chunk_id": "story_asr_0000",
|
||||
"chunk_type": "story",
|
||||
"text_content": "[0s-125s] A man enters a building. He walks down a hallway.",
|
||||
"child_chunk_ids": ["asr_0001", "asr_0002", "asr_0003", "asr_0004", "asr_0005"]
|
||||
}
|
||||
```
|
||||
|
||||
### Child Chunk
|
||||
- **Purpose**: Individual segments from ASR, scenes from CUT, etc.
|
||||
- **Content**: Raw transcription or detection results
|
||||
- **Example**:
|
||||
```json
|
||||
{
|
||||
"chunk_id": "asr_0001",
|
||||
"chunk_type": "sentence",
|
||||
"text_content": "Hello world",
|
||||
"parent_chunk_id": "story_asr_0000"
|
||||
}
|
||||
```
|
||||
|
||||
## Embedding Strategy
|
||||
|
||||
### For Vector Search
|
||||
|
||||
When embedding chunks for vector search, we combine **parent description + child content** to provide both context and detail.
|
||||
|
||||
#### Parent Chunk Embedding
|
||||
```
|
||||
embedding_text = f"Summary: {parent.text_content}
|
||||
Children: {child_text_1}. {child_text_2}. {child_text_3}..."
|
||||
```
|
||||
|
||||
**Prefix**: `search_document:` (for documents in Qdrant)
|
||||
|
||||
**Example**:
|
||||
```
|
||||
search_document: Summary: A man enters a building. He walks down a hallway.
|
||||
Children: Hello, how are you? I'm fine thank you. The weather is nice today.
|
||||
```
|
||||
|
||||
#### Child Chunk Embedding
|
||||
```
|
||||
embedding_text = f"[{child.chunk_type}] {child.text_content}
|
||||
Parent: {parent.description}"
|
||||
```
|
||||
|
||||
**Prefix**: `search_document:`
|
||||
|
||||
**Example**:
|
||||
```
|
||||
search_document: [sentence] Hello, how are you?
|
||||
Parent: A man enters a building. He walks down a hallway.
|
||||
```
|
||||
|
||||
### For BM25 Text Search
|
||||
|
||||
BM25 operates on raw text with PostgreSQL full-text search.
|
||||
|
||||
- **Index**: `search_vector` (TSVECTOR) on `chunks.text_content`
|
||||
- **Search**: Uses `ts_rank_cd()` for ranking
|
||||
|
||||
## Hybrid Search Ranking
|
||||
|
||||
Combined score = `(vector_score * 0.7) + (bm25_score * 0.3)`
|
||||
|
||||
### Why 0.7/0.3?
|
||||
|
||||
| Weight | Vector | BM25 |
|
||||
|--------|--------|------|
|
||||
| Pros | Semantic similarity | Exact keyword match |
|
||||
| Cons | May miss specific terms | No semantic understanding |
|
||||
| Best for | Thematic queries | Fact lookup |
|
||||
|
||||
## Query Patterns
|
||||
|
||||
### Thematic Query ("What are the main themes?")
|
||||
- Use higher `vector_weight` (0.8-0.9)
|
||||
- Vector search finds semantically similar content
|
||||
|
||||
### Fact Lookup ("Who said X?")
|
||||
- Use higher `bm25_weight` (0.5-0.7)
|
||||
- BM25 finds exact matches
|
||||
|
||||
### Balanced ("Tell me about scene 5")
|
||||
- Use default 0.7/0.3
|
||||
|
||||
## Implementation
|
||||
|
||||
### Embedding Generation
|
||||
```rust
|
||||
fn build_embedding_text(chunk: &Chunk, parent_text: Option<&str>) -> String {
|
||||
match chunk.chunk_type {
|
||||
ChunkType::Story => {
|
||||
format!(
|
||||
"Summary: {}\nChildren: {}",
|
||||
chunk.text_content,
|
||||
get_children_text(chunk)
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
format!(
|
||||
"[{}] {}\nParent: {}",
|
||||
chunk.chunk_type.as_str(),
|
||||
chunk.text_content,
|
||||
parent_text.unwrap_or("N/A")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Storage
|
||||
- Parent chunks stored with their `child_chunk_ids`
|
||||
- Child chunks reference `parent_chunk_id`
|
||||
- Both stored in PostgreSQL with full-text index
|
||||
- Vectors stored in Qdrant
|
||||
|
||||
## Example Flow
|
||||
|
||||
1. **Story Processing** generates parent-child hierarchy
|
||||
2. **Embedding** creates vector for each chunk
|
||||
3. **Storage** saves to PostgreSQL + Qdrant
|
||||
4. **Search** retrieves using hybrid search
|
||||
5. **Results** include both parent context and child details
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Chunk Size**: 5 child chunks per parent (configurable)
|
||||
2. **Text Length**: Keep embeddings under 512 tokens
|
||||
3. **Parent Description**: Include temporal markers (timestamps)
|
||||
4. **Child Content**: Preserve original transcription
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- [ ] GraphRAG integration for relationship traversal
|
||||
- [ ] Cross-chunk entity linking
|
||||
- [ ] Temporal graph building
|
||||
@@ -1,392 +0,0 @@
|
||||
# Playground Binary Implementation Plan
|
||||
|
||||
| Item | Content |
|
||||
|------|---------|
|
||||
| Author | Warren |
|
||||
| Created | 2026-03-23 |
|
||||
| Document Version | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## Version History
|
||||
|
||||
| Version | Date | Purpose | Operator | Tool/Model |
|
||||
|---------|------|---------|----------|------------|
|
||||
| V1.0 | 2026-03-23 | Create implementation plan | Warren | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Create separate `momentry_playground` binary with distinct configuration from `momentry` (production).
|
||||
|
||||
| Aspect | Production (`momentry`) | Development (`momentry_playground`) |
|
||||
|--------|------------------------|-------------------------------------|
|
||||
| **Port** | 3002 | 3003 |
|
||||
| **Redis Prefix** | `momentry:` | `momentry_dev:` |
|
||||
| **Worker** | Enabled | Disabled |
|
||||
| **Purpose** | Production deployment | Testing/Development |
|
||||
|
||||
---
|
||||
|
||||
## Files to Modify
|
||||
|
||||
```
|
||||
Files Changed: 6 files (+1 new)
|
||||
├── src/core/config.rs ← Add server_port(), redis_key_prefix()
|
||||
├── src/core/db/redis_client.rs ← Replace hardcoded prefixes
|
||||
├── src/core/cache/redis_cache.rs ← Use configurable prefix
|
||||
├── src/main.rs ← Update CLI defaults
|
||||
├── src/playground.rs ← NEW: Development binary
|
||||
├── Cargo.toml ← Add new binary
|
||||
└── .env.development ← NEW: Dev environment config
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### Step 1: Update `src/core/config.rs`
|
||||
|
||||
Add after line 51 (after `MEDIA_BASE_URL`):
|
||||
|
||||
```rust
|
||||
pub static SERVER_PORT: Lazy<u16> = Lazy::new(|| {
|
||||
env::var("MOMENTRY_SERVER_PORT")
|
||||
.unwrap_or_else(|_| "3002".to_string())
|
||||
.parse()
|
||||
.unwrap_or(3002)
|
||||
});
|
||||
|
||||
pub static REDIS_KEY_PREFIX: Lazy<String> = Lazy::new(|| {
|
||||
env::var("MOMENTRY_REDIS_PREFIX")
|
||||
.unwrap_or_else(|_| "momentry:".to_string())
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: Update `src/core/db/redis_client.rs`
|
||||
|
||||
Replace all hardcoded `momentry:` prefixes with configurable prefix.
|
||||
|
||||
**Import at top:**
|
||||
```rust
|
||||
use crate::core::config::REDIS_KEY_PREFIX;
|
||||
```
|
||||
|
||||
**Pattern for each method:**
|
||||
```rust
|
||||
let prefix = REDIS_KEY_PREFIX.as_str();
|
||||
let key = format!("{}job:{}", prefix, uuid);
|
||||
```
|
||||
|
||||
**Affected lines:**
|
||||
|
||||
| Line | Key Pattern |
|
||||
|------|-------------|
|
||||
| 47 | `job:{uuid}` |
|
||||
| 81, 109 | `job:{uuid}:processor:{processor}` |
|
||||
| 136, 146 | `progress:{uuid}` |
|
||||
| 172 | `jobs:active` |
|
||||
| 179 | `jobs:active` → `jobs:completed` |
|
||||
| 187 | `jobs:active` → `jobs:failed` |
|
||||
| 194 | `jobs:active` |
|
||||
| 201, 208 | `health:momentry_core` |
|
||||
| 214 | `monitor:job:{uuid}` |
|
||||
| 242, 300 | `errors:{uuid}` |
|
||||
| 258, 281 | `anomaly:alerts`, `anomaly:key:{key_id}` |
|
||||
| 317, 346, 364, 392, 397 | `worker:job:{uuid}...` |
|
||||
| 406, 410 | `worker:job:*` |
|
||||
|
||||
---
|
||||
|
||||
### Step 3: Update `src/core/cache/redis_cache.rs`
|
||||
|
||||
**Import:**
|
||||
```rust
|
||||
use crate::core::config::REDIS_KEY_PREFIX;
|
||||
```
|
||||
|
||||
**Replace line 10:**
|
||||
```rust
|
||||
// Remove: const KEY_PREFIX: &str = "momentry:cache:";
|
||||
```
|
||||
|
||||
**Update `prefixed_key` method (line 24):**
|
||||
```rust
|
||||
fn prefixed_key(&self, key: &str) -> String {
|
||||
format!("{}cache:{}", REDIS_KEY_PREFIX.as_str(), key)
|
||||
}
|
||||
```
|
||||
|
||||
**Update tests (lines 161-162):**
|
||||
```rust
|
||||
#[test]
|
||||
fn test_prefixed_key() {
|
||||
// Note: This test will use the configured prefix
|
||||
let cache = RedisCache::new().unwrap();
|
||||
// With default prefix "momentry:"
|
||||
assert_eq!(cache.prefixed_key("test"), "momentry:cache:test");
|
||||
assert_eq!(cache.prefixed_key("video:abc"), "momentry:cache:video:abc");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: Update `src/main.rs`
|
||||
|
||||
**Change CLI defaults (Lines 691-695):**
|
||||
|
||||
```rust
|
||||
// Before:
|
||||
#[arg(long, default_value = "3000")]
|
||||
port: u16,
|
||||
|
||||
// After:
|
||||
#[arg(long)]
|
||||
port: Option<u16>,
|
||||
```
|
||||
|
||||
**Update Server match arm (around line 2398):**
|
||||
|
||||
```rust
|
||||
Commands::Server { host, port } => {
|
||||
let port = port.unwrap_or_else(|| *crate::core::config::SERVER_PORT);
|
||||
momentry_core::api::start_server(&host, port).await?;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
**Update Redis key usage (Line 1098):**
|
||||
|
||||
```rust
|
||||
// Before:
|
||||
let key = format!("momentry:job:{}:processor:{}", uuid, processor);
|
||||
|
||||
// After:
|
||||
let key = format!(
|
||||
"{}job:{}:processor:{}",
|
||||
crate::core::config::REDIS_KEY_PREFIX.as_str(),
|
||||
uuid,
|
||||
processor
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 5: Create `src/playground.rs`
|
||||
|
||||
```rust
|
||||
use anyhow::{Context, Result};
|
||||
use clap::{Parser, Subcommand};
|
||||
// ... same imports as main.rs ...
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Load development environment first
|
||||
dotenv::from_filename(".env.development").ok();
|
||||
|
||||
tracing_subscriber::fmt::init();
|
||||
tracing::info!("Starting momentry_playground (development binary)");
|
||||
tracing::info!("Port: {}", *momentry_core::core::config::SERVER_PORT);
|
||||
tracing::info!("Redis prefix: {}", *momentry_core::core::config::REDIS_KEY_PREFIX);
|
||||
|
||||
let cli = Cli::parse();
|
||||
// ... rest identical to main.rs ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 6: Update `Cargo.toml`
|
||||
|
||||
**Add after line 90:**
|
||||
|
||||
```toml
|
||||
[[bin]]
|
||||
name = "momentry_playground"
|
||||
path = "src/playground.rs"
|
||||
```
|
||||
|
||||
**Add dependency (if not present):**
|
||||
|
||||
```toml
|
||||
dotenv = "0.15"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 7: Create `.env.development`
|
||||
|
||||
```bash
|
||||
# Development Environment Configuration
|
||||
# Used by: momentry_playground binary
|
||||
|
||||
# Server Configuration
|
||||
MOMENTRY_SERVER_PORT=3003
|
||||
MOMENTRY_REDIS_PREFIX=momentry_dev:
|
||||
|
||||
# Worker Configuration (disabled for development)
|
||||
MOMENTRY_WORKER_ENABLED=false
|
||||
MOMENTRY_MAX_CONCURRENT=1
|
||||
MOMENTRY_POLL_INTERVAL=10
|
||||
|
||||
# Database (can use separate dev database)
|
||||
DATABASE_URL=postgres://accusys@localhost:5432/momentry
|
||||
MONGODB_URL=mongodb://accusys:Test3200Test3200@localhost:27017/admin
|
||||
|
||||
# Redis
|
||||
REDIS_URL=redis://:accusys@localhost:6379
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 8: Update `.env` (Production)
|
||||
|
||||
Add these lines:
|
||||
|
||||
```bash
|
||||
# Production Environment Configuration
|
||||
# Used by: momentry binary
|
||||
|
||||
# Server Configuration
|
||||
MOMENTRY_SERVER_PORT=3002
|
||||
MOMENTRY_REDIS_PREFIX=momentry:
|
||||
|
||||
# Worker Configuration
|
||||
MOMENTRY_WORKER_ENABLED=true
|
||||
MOMENTRY_MAX_CONCURRENT=2
|
||||
MOMENTRY_POLL_INTERVAL=5
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
### 1. Build and Run Production Binary
|
||||
|
||||
```bash
|
||||
cargo build --release --bin momentry
|
||||
cargo run --bin momentry -- server
|
||||
# Expected: Listening on http://127.0.0.1:3002
|
||||
|
||||
cargo run --bin momentry -- worker
|
||||
# Expected: Worker started with momentry: prefix
|
||||
```
|
||||
|
||||
### 2. Build and Run Development Binary
|
||||
|
||||
```bash
|
||||
cargo build --bin momentry_playground
|
||||
cargo run --bin momentry_playground -- server
|
||||
# Expected: Listening on http://127.0.0.1:3003
|
||||
```
|
||||
|
||||
### 3. Verify Redis Key Isolation
|
||||
|
||||
```bash
|
||||
# Production data
|
||||
redis-cli KEYS "momentry:*"
|
||||
# Development data
|
||||
redis-cli KEYS "momentry_dev:*"
|
||||
# Should be separate
|
||||
```
|
||||
|
||||
### 4. Run Both Simultaneously
|
||||
|
||||
```bash
|
||||
# Terminal 1: Production
|
||||
cargo run --bin momentry -- server
|
||||
|
||||
# Terminal 2: Development
|
||||
cargo run --bin momentry_playground -- server
|
||||
|
||||
# Both should run without port conflicts
|
||||
```
|
||||
|
||||
### 5. Unit Tests
|
||||
|
||||
```bash
|
||||
cargo test --lib
|
||||
# All tests should pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Redis Key Structure
|
||||
|
||||
### Production (`momentry:`)
|
||||
|
||||
```
|
||||
momentry:job:{uuid} # Job status
|
||||
momentry:job:{uuid}:processor:{name} # Processor progress
|
||||
momentry:progress:{uuid} # Progress pub/sub
|
||||
momentry:jobs:active # Active job set
|
||||
momentry:jobs:completed # Completed job set
|
||||
momentry:jobs:failed # Failed job set
|
||||
momentry:health:momentry_core # Health status
|
||||
momentry:cache:{key} # Cache entries
|
||||
momentry:worker:job:{uuid} # Worker job
|
||||
momentry:worker:job:{uuid}:processor:{name}
|
||||
```
|
||||
|
||||
### Development (`momentry_dev:`)
|
||||
|
||||
```
|
||||
momentry_dev:job:{uuid}
|
||||
momentry_dev:job:{uuid}:processor:{name}
|
||||
momentry_dev:progress:{uuid}
|
||||
momentry_dev:jobs:active
|
||||
momentry_dev:jobs:completed
|
||||
momentry_dev:jobs:failed
|
||||
momentry_dev:health:momentry_core
|
||||
momentry_dev:cache:{key}
|
||||
momentry_dev:worker:job:{uuid}
|
||||
momentry_dev:worker:job:{uuid}:processor:{name}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Potential Issues & Solutions
|
||||
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| `dotenv` crate not in dependencies | Add to Cargo.toml |
|
||||
| Tests use hardcoded prefix | Update tests to use config, or use `#[cfg(test)]` defaults |
|
||||
| Worker starts in playground | Check `MOMENTRY_WORKER_ENABLED=false` in `.env.development` |
|
||||
| Port already in use | Graceful error message with suggestion to use `--port` flag |
|
||||
| Mixed data in Redis | Ensure prefix is loaded before any Redis operations |
|
||||
|
||||
---
|
||||
|
||||
## Files Summary
|
||||
|
||||
| File | Lines Changed | Purpose |
|
||||
|------|---------------|---------|
|
||||
| `src/core/config.rs` | +15 | Add SERVER_PORT and REDIS_KEY_PREFIX |
|
||||
| `src/core/db/redis_client.rs` | ~50 | Replace hardcoded prefixes |
|
||||
| `src/core/cache/redis_cache.rs` | ~10 | Use configurable prefix |
|
||||
| `src/main.rs` | ~15 | Update CLI defaults, Redis key usage |
|
||||
| `src/playground.rs` | NEW (~2800) | Development binary |
|
||||
| `Cargo.toml` | +4 | Add binary definition |
|
||||
| `.env.development` | NEW (~20) | Development environment |
|
||||
|
||||
**Total**: ~60 lines modified + ~2800 lines new file
|
||||
|
||||
---
|
||||
|
||||
## Reference Documents
|
||||
|
||||
| Document | Purpose |
|
||||
|----------|---------|
|
||||
| `docs/SERVICES.md` | Port allocations |
|
||||
| `docs/MOMENTRY_CORE_REDIS_KEYS.md` | Redis key design |
|
||||
| `AGENTS.md` | Code style and conventions |
|
||||
|
||||
---
|
||||
|
||||
## Version History
|
||||
|
||||
| Version | Date | Author | Changes |
|
||||
|---------|------|--------|---------|
|
||||
| 1.0 | 2025-03-25 | OpenCode | Initial implementation plan |
|
||||
@@ -1,195 +0,0 @@
|
||||
# API Key Management System Architecture
|
||||
|
||||
## System Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ API Key Management System │
|
||||
├─────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ CLI │ │ HTTP API │ │ Service │ │ External │ │
|
||||
│ │ Layer │────▶│ Layer │────▶│ Layer │────▶│ Services │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
│ │ │ │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ ▼ ▼ ▼ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Core Modules │ │
|
||||
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
|
||||
│ │ │ Service │ │Validator│ │ Anomaly │ │Rotation │ │ Cleanup │ │ │
|
||||
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
|
||||
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
|
||||
│ │ │ Webhook │ │Encrypt │ │Blacklist│ │ Report │ │ Error │ │ │
|
||||
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │ │ │ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ PostgreSQL │ │ Redis │ │ External │ │
|
||||
│ │ (Storage) │ │ (Cache) │ │ (Gitea/n8n)│ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Module Dependencies
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ models.rs │
|
||||
│ (Types) │
|
||||
└──────┬───────┘
|
||||
│
|
||||
┌──────────────────┼──────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
|
||||
│ service.rs │ │ error.rs │ │ validator.rs │
|
||||
│ (Core CRUD) │ │ (Errors) │ │ (Cache+Rate) │
|
||||
└───────┬───────┘ └───────────────┘ └───────────────┘
|
||||
│
|
||||
│ ┌───────────────────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
|
||||
│ anomaly.rs │ │ rotation.rs │ │ blacklist.rs │
|
||||
│ (Detection) │ │ (Rotation) │ │ (IP Block) │
|
||||
└───────────────┘ └───────────────┘ └───────────────┘
|
||||
```
|
||||
|
||||
## Request Flow
|
||||
|
||||
```
|
||||
Client Request
|
||||
│
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ CLI/API │
|
||||
└──────┬──────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────┐ ┌─────────────┐
|
||||
│ Rate Limit │────▶│ IP Blacklist│
|
||||
│ Check │ │ Check │
|
||||
└──────┬──────┘ └──────┬──────┘
|
||||
│ │
|
||||
└─────────┬─────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────┐
|
||||
│ Hash API Key │
|
||||
└───────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────┐ ┌───────────────┐
|
||||
│ Cache Lookup │────▶│ PostgreSQL │
|
||||
└───────┬───────┘ │ Lookup │
|
||||
│ └───────┬───────┘
|
||||
│ │
|
||||
└──────────┬──────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────┐
|
||||
│ Validate │
|
||||
│ (Status, │
|
||||
│ Expiry) │
|
||||
└───────┬───────┘
|
||||
│
|
||||
┌─────────────┼─────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||
│ Valid │ │ Invalid │ │ Error │
|
||||
│ Response│ │ Response │ │ Response │
|
||||
└──────────┘ └──────────┘ └──────────┘
|
||||
```
|
||||
|
||||
## Database Schema
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ PostgreSQL │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ │
|
||||
│ │ api_keys │ │ api_key_audit_ │ │
|
||||
│ ├─────────────────┤ │ log │ │
|
||||
│ │ id │ ├─────────────────┤ │
|
||||
│ │ key_id │─────▶│ id │ │
|
||||
│ │ key_hash │ │ key_id (FK) │ │
|
||||
│ │ name │ │ action │ │
|
||||
│ │ key_type │ │ ip_address │ │
|
||||
│ │ status │ │ details │ │
|
||||
│ │ expires_at │ └─────────────────┘ │
|
||||
│ │ ... │ │
|
||||
│ └─────────────────┘ ┌─────────────────┐ │
|
||||
│ │ api_key_anomalies│ │
|
||||
│ ┌─────────────────┐ ├─────────────────┤ │
|
||||
│ │ gitea_tokens │ │ id │ │
|
||||
│ ├─────────────────┤ │ key_id (FK) │ │
|
||||
│ │ id │ │ anomaly_type │ │
|
||||
│ │ gitea_token_id │ │ severity │ │
|
||||
│ │ token_name │ │ details │ │
|
||||
│ │ scopes │ └─────────────────┘ │
|
||||
│ └─────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────┐ │
|
||||
│ │ n8n_api_keys │ │
|
||||
│ ├─────────────────┤ │
|
||||
│ │ id │ │
|
||||
│ │ n8n_key_id │ │
|
||||
│ │ label │ │
|
||||
│ └─────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## External Integrations
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ External Integrations │
|
||||
├─────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
||||
│ │ Gitea │ │ n8n │ │ Webhook │ │
|
||||
│ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤ │
|
||||
│ │ • Create Token │ │ • Create API Key│ │ • Key Created │ │
|
||||
│ │ • List Tokens │ │ • List API Keys │ │ • Key Revoked │ │
|
||||
│ │ • Delete Token │ │ • Delete API Key│ │ • Anomaly │ │
|
||||
│ │ • Verify Token │ │ • Verify │ │ • Rate Limited │ │
|
||||
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Security Layers
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Security Layers │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Layer 1: Network │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ • IP Blacklist │ │
|
||||
│ │ • Rate Limiting │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Layer 2: Authentication │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ • API Key Hash (SHA256) │ │
|
||||
│ │ • Constant-time Comparison │ │
|
||||
│ │ • Key Validation (Status, Expiry) │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Layer 3: Monitoring │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ • Anomaly Detection │ │
|
||||
│ │ • Audit Logging (Encrypted) │ │
|
||||
│ │ • Webhook Notifications │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
@@ -1,461 +0,0 @@
|
||||
# Momentry API 使用流程
|
||||
|
||||
> **目標**: 從影片上傳到搜尋的完整流程
|
||||
> **適用**: WordPress / n8n 整合
|
||||
> **版本**: V1.0 | **日期**: 2026-03-25
|
||||
|
||||
---
|
||||
|
||||
## 流程總覽
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||
│ 1. 上傳 │ → │ 2. 註冊 │ → │ 3. 確認 │ → │ 4. 處理 │ → │ 5. 搜尋 │
|
||||
│ SFTPGo │ │ 自動完成 │ │ UUID │ │ 查詢進度 │ │ 測試 │
|
||||
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 1: 上傳影片
|
||||
|
||||
### 方式 A: SFTP 上傳(推薦)
|
||||
|
||||
```bash
|
||||
# 連線資訊
|
||||
主機: sftpgo.momentry.ddns.net
|
||||
連接埠: 2022
|
||||
用戶名: demo
|
||||
密碼: demopassword123
|
||||
```
|
||||
|
||||
使用 FileZilla 或 SFTP 客戶端上傳到 `/` 目錄
|
||||
|
||||
### 方式 B: SFTP 命令列
|
||||
|
||||
```bash
|
||||
sshpass -p "demopassword123" sftp -P 2022 demo@sftpgo.momentry.ddns.net
|
||||
```
|
||||
|
||||
上傳後確認檔案在 SFTPGo 中的位置
|
||||
|
||||
---
|
||||
|
||||
## Step 2: 自動註冊
|
||||
|
||||
上傳後,系統會自動:
|
||||
1. 偵測新檔案
|
||||
2. 計算 UUID(SHA256)
|
||||
3. 建立資料庫記錄
|
||||
|
||||
**無需手動操作**
|
||||
|
||||
---
|
||||
|
||||
## Step 3: 確認註冊成功
|
||||
|
||||
### 查詢所有影片
|
||||
|
||||
```bash
|
||||
curl -s -H "X-API-Key: YOUR_API_KEY" \
|
||||
"https://api.momentry.ddns.net/api/v1/videos" | jq '.videos | length'
|
||||
```
|
||||
|
||||
### 查詢特定檔案
|
||||
|
||||
```bash
|
||||
curl -s -H "X-API-Key: YOUR_API_KEY" \
|
||||
"https://api.momentry.ddns.net/api/v1/videos" | jq '.videos[] | select(.file_name | contains("你的檔案名"))'
|
||||
```
|
||||
|
||||
### 預期回應
|
||||
|
||||
```json
|
||||
{
|
||||
"uuid": "952f5854b9febad1",
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/你的檔案.mp4",
|
||||
"file_name": "你的檔案.mp4",
|
||||
"duration": 123.45,
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
}
|
||||
```
|
||||
|
||||
**確認要點**:
|
||||
- ✅ UUID 已產生(16位 hex)
|
||||
- ✅ `file_path` 正確
|
||||
- ✅ `duration` > 0
|
||||
|
||||
---
|
||||
|
||||
## Step 4: 查詢處理進度
|
||||
|
||||
### 取得任務 UUID
|
||||
|
||||
```bash
|
||||
# 從影片資訊取得 job_id
|
||||
curl -s -H "X-API-Key: YOUR_API_KEY" \
|
||||
"https://api.momentry.ddns.net/api/v1/videos" | \
|
||||
jq '.videos[] | select(.file_name == "你的檔案.mp4") | {uuid, job_id}'
|
||||
```
|
||||
|
||||
### 查詢任務狀態
|
||||
|
||||
```bash
|
||||
curl -s -H "X-API-Key: YOUR_API_KEY" \
|
||||
"https://api.momentry.ddns.net/api/v1/jobs/{uuid}"
|
||||
```
|
||||
|
||||
### 任務狀態說明
|
||||
|
||||
| status | 說明 | 動作 |
|
||||
|--------|------|------|
|
||||
| `pending` | 等待處理 | 等待中 |
|
||||
| `processing` | 處理中 | 繼續輪詢 |
|
||||
| `completed` | 已完成 | 可進入 Step 5 |
|
||||
| `failed` | 處理失敗 | 檢查錯誤 |
|
||||
|
||||
### n8n 輪詢範例
|
||||
|
||||
```javascript
|
||||
// n8n Workflow: 檢查處理狀態
|
||||
const jobUuid = $input.item.json.job_uuid;
|
||||
|
||||
const response = await fetch(
|
||||
`https://api.momentry.ddns.net/api/v1/jobs/${jobUuid}`,
|
||||
{
|
||||
headers: {
|
||||
"X-API-Key": "YOUR_API_KEY"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const job = await response.json();
|
||||
|
||||
// 狀態檢查
|
||||
if (job.status === 'completed') {
|
||||
return [{ json: { done: true, file_uuid: job.file_uuid } }];
|
||||
} else {
|
||||
return [{ json: { done: false, status: job.status } }];
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 5: 搜尋測試
|
||||
|
||||
處理完成後,資料會入庫到向量資料庫,可進行搜尋測試。
|
||||
|
||||
### 測試向量搜尋
|
||||
|
||||
```bash
|
||||
curl -s -X POST "https://api.momentry.ddns.net/api/v1/search" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"query": "測試關鍵字",
|
||||
"limit": 5
|
||||
}'
|
||||
```
|
||||
|
||||
### 取得分段(Chunk)內容
|
||||
|
||||
搜尋結果會返回影片分段(Chunk),包含可播放的時間軸資訊:
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uuid": "39567a0eb16f39fd",
|
||||
"chunk_id": "sentence_1471",
|
||||
"chunk_type": "sentence",
|
||||
"start_time": 5309.08,
|
||||
"end_time": 5311.08,
|
||||
"text": "influenced by a vital way,",
|
||||
"score": 0.68
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Chunk 欄位說明**:
|
||||
| 欄位 | 說明 |
|
||||
|------|------|
|
||||
| `uuid` | 影片 UUID(用於取得影片網址) |
|
||||
| `chunk_id` | 分段 ID |
|
||||
| `chunk_type` | 分段類型(sentence/cut/time/trace/story) |
|
||||
| `start_time` | 開始時間(秒) |
|
||||
| `end_time` | 結束時間(秒) |
|
||||
| `text` | 語音內容文字 |
|
||||
| `score` | 相似度分數(0-1) |
|
||||
|
||||
### 播放分段
|
||||
|
||||
取得 Chunk 後可組合成播放網址:
|
||||
|
||||
```
|
||||
影片網址?start={start_time}&end={end_time}
|
||||
```
|
||||
|
||||
範例:
|
||||
```
|
||||
https://wp.momentry.ddns.net/video.mp4?start=5309.08&end=5311.08
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 完整 n8n Workflow 範例
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ 觸發 (定時) │
|
||||
└──────┬───────┘
|
||||
▼
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ 查詢影片 │────►│ 比對新檔案 │
|
||||
│ /videos │ │ │
|
||||
└──────┬───────┘ └──────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ 等待處理 │◄────│ 輪詢任務狀態 │
|
||||
│ /jobs/:uuid │ │ /jobs/:uuid │
|
||||
└──────┬───────┘ └──────────────┘
|
||||
│
|
||||
▼ (completed)
|
||||
┌──────────────┐
|
||||
│ 搜尋測試 │
|
||||
│ /search │
|
||||
└──────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 快速參考
|
||||
|
||||
| 步驟 | API | 用途 |
|
||||
|------|-----|------|
|
||||
| 查詢影片 | `GET /api/v1/videos` | 確認上傳成功 |
|
||||
| 查詢任務 | `GET /api/v1/jobs/:uuid` | 查看處理進度 |
|
||||
| 搜尋內容 | `POST /api/v1/search` | 測試搜尋功能 |
|
||||
|
||||
---
|
||||
|
||||
## WordPress PHP 範例
|
||||
|
||||
### 基本設定
|
||||
|
||||
```php
|
||||
<?php
|
||||
class Momentry_API {
|
||||
private const API_URL = 'https://api.momentry.ddns.net';
|
||||
private const API_KEY = 'YOUR_API_KEY';
|
||||
|
||||
public static function request(string $method, string $endpoint, ?array $data = null): array {
|
||||
$url = self::API_URL . $endpoint;
|
||||
|
||||
$args = [
|
||||
'method' => $method,
|
||||
'headers' => [
|
||||
'X-API-Key' => self::API_KEY,
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
'timeout' => 30,
|
||||
];
|
||||
|
||||
if ($data !== null) {
|
||||
$args['body'] = json_encode($data);
|
||||
}
|
||||
|
||||
$response = wp_remote_request($url, $args);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
throw new Exception($response->get_error_message());
|
||||
}
|
||||
|
||||
return json_decode(wp_remote_retrieve_body($response), true);
|
||||
}
|
||||
|
||||
public static function getVideos(): array {
|
||||
return self::request('GET', '/api/v1/videos');
|
||||
}
|
||||
|
||||
public static function getVideo(string $uuid): array {
|
||||
return self::request('GET', "/api/v1/videos/{$uuid}");
|
||||
}
|
||||
|
||||
public static function getJob(string $uuid): array {
|
||||
return self::request('GET', "/api/v1/jobs/{$uuid}");
|
||||
}
|
||||
|
||||
public static function search(string $query, int $topK = 5): array {
|
||||
return self::request('POST', '/api/v1/search', [
|
||||
'query' => $query,
|
||||
'top_k' => $topK,
|
||||
]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: 確認註冊成功
|
||||
|
||||
```php
|
||||
<?php
|
||||
// 查詢所有影片
|
||||
$videos = Momentry_API::getVideos();
|
||||
|
||||
foreach ($videos['videos'] as $video) {
|
||||
echo "UUID: " . $video['uuid'] . "\n";
|
||||
echo "檔案: " . $video['file_name'] . "\n";
|
||||
echo "時長: " . $video['duration'] . " 秒\n";
|
||||
echo "---\n";
|
||||
}
|
||||
|
||||
// 查詢特定影片
|
||||
$video = Momentry_API::getVideo('952f5854b9febad1');
|
||||
print_r($video);
|
||||
```
|
||||
|
||||
### Step 4: 查詢處理進度
|
||||
|
||||
```php
|
||||
<?php
|
||||
// 取得任務狀態
|
||||
$job = Momentry_API::getJob('9760d0820f0cf9a7');
|
||||
|
||||
switch ($job['status']) {
|
||||
case 'pending':
|
||||
echo "等待處理中...\n";
|
||||
break;
|
||||
case 'processing':
|
||||
echo "處理中: " . $job['progress'] . "%\n";
|
||||
break;
|
||||
case 'completed':
|
||||
echo "處理完成!\n";
|
||||
break;
|
||||
case 'failed':
|
||||
echo "處理失敗: " . ($job['error'] ?? '未知錯誤') . "\n";
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: 搜尋內容並取得 Chunk
|
||||
|
||||
```php
|
||||
<?php
|
||||
// 搜尋相關片段
|
||||
$results = Momentry_API::search('測試關鍵字', 5);
|
||||
|
||||
foreach ($results['results'] as $result) {
|
||||
echo "影片 UUID: " . $result['uuid'] . "\n";
|
||||
echo "Chunk ID: " . $result['chunk_id'] . "\n";
|
||||
echo "類型: " . $result['chunk_type'] . "\n";
|
||||
echo "開始: " . $result['start_time'] . "s\n";
|
||||
echo "結束: " . $result['end_time'] . "s\n";
|
||||
echo "內容: " . ($result['text'] ?? '') . "\n";
|
||||
echo "相似度: " . $result['score'] . "\n";
|
||||
echo "---\n";
|
||||
}
|
||||
```
|
||||
|
||||
### WordPress Shortcode 範例(可點擊播放)
|
||||
|
||||
```php
|
||||
<?php
|
||||
// 在 functions.php 中加入
|
||||
add_shortcode('momentry_search', function($atts) {
|
||||
$atts = shortcode_atts([
|
||||
'query' => '',
|
||||
'limit' => 10,
|
||||
], $atts);
|
||||
|
||||
if (empty($atts['query'])) {
|
||||
return '<p>請輸入搜尋關鍵字</p>';
|
||||
}
|
||||
|
||||
try {
|
||||
$results = Momentry_API::search($atts['query'], $atts['limit']);
|
||||
|
||||
if (empty($results['results'])) {
|
||||
return '<p>找不到相關結果</p>';
|
||||
}
|
||||
|
||||
$html = '<div class="momentry-results">';
|
||||
$html .= '<h3>搜尋結果: ' . esc_html($atts['query']) . '</h3>';
|
||||
$html .= '<ul>';
|
||||
|
||||
foreach ($results['results'] as $result) {
|
||||
$file_uuid = $result['uuid'];
|
||||
$start = $result['start_time'] ?? 0;
|
||||
$end = $result['end_time'] ?? 0;
|
||||
$text = $result['text'] ?? '無文字描述';
|
||||
|
||||
$html .= '<li>';
|
||||
$html .= '<a href="/player?uuid=' . esc_attr($file_uuid) .
|
||||
'&start=' . esc_attr($start) .
|
||||
'&end=' . esc_attr($end) . '">';
|
||||
$html .= '播放 ' . $start . 's - ' . $end . 's';
|
||||
$html .= '</a>';
|
||||
$html .= '<br>';
|
||||
$html .= '<small>相似度: ' . round($result['score'] * 100) . '%</small>';
|
||||
$html .= '<br>';
|
||||
$html .= esc_html($text);
|
||||
$html .= '</li>';
|
||||
}
|
||||
|
||||
$html .= '</ul></div>';
|
||||
return $html;
|
||||
|
||||
} catch (Exception $e) {
|
||||
return '<p>搜尋服務暫時無法使用</p>';
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**使用方式**:
|
||||
```html
|
||||
[momentry_search query="關鍵字" limit="5"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 完整 n8n Workflow 範例
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ 觸發 (定時) │
|
||||
└──────┬───────┘
|
||||
▼
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ 查詢影片 │────►│ 比對新檔案 │
|
||||
│ /videos │ │ │
|
||||
└──────┬───────┘ └──────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ 等待處理 │◄────│ 輪詢任務狀態 │
|
||||
│ /jobs/:uuid │ │ /jobs/:uuid │
|
||||
└──────┬───────┘ └──────────────┘
|
||||
│
|
||||
▼ (completed)
|
||||
┌──────────────┐
|
||||
│ 搜尋測試 │
|
||||
│ /search │
|
||||
└──────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**注意**:
|
||||
- 處理時間視影片長度而定(1分鐘影片約需 2-5 分鐘處理)
|
||||
- 大量影片時建議分批上傳
|
||||
|
||||
---
|
||||
|
||||
## 附錄:版本歷史
|
||||
|
||||
| 版本 | 日期 | 內容 | 操作人 |
|
||||
|------|------|------|--------|
|
||||
| V1.0 | 2026-03-25 | 初版建立 | OpenCode |
|
||||
| V1.1 | 2026-03-25 | 新增 Chunk 取得與播放說明、Shortcode 範例 | OpenCode |
|
||||
| V1.2 | 2026-03-25 | 修正 SFTPGo 主機名稱為 sftpgo.momentry.ddns.net | OpenCode |
|
||||
@@ -1,331 +0,0 @@
|
||||
# 架構優化待評估事項
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-03-21 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 |
|
||||
|------|------|------|--------|
|
||||
| V1.0 | 2026-03-21 | 創建文件 | OpenCode |
|
||||
| V1.1 | 2026-03-22 | 新增 TigerGraph/GraphRAG 說故事評估 | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## 架構優化項目
|
||||
|
||||
### 1. PostgreSQL → Redis 故障轉移
|
||||
|
||||
**說明**: 當 PostgreSQL 不可用時,降級到 Redis 作為臨時存儲
|
||||
|
||||
**複雜度**: 中
|
||||
|
||||
**影響範圍**:
|
||||
- `src/core/db/postgres_db.rs`
|
||||
- `src/core/db/redis_client.rs`
|
||||
|
||||
**風險**:
|
||||
- 數據一致性問題
|
||||
- 需要定義轉移策略
|
||||
|
||||
**優先級**: 待評估
|
||||
|
||||
---
|
||||
|
||||
### 2. 連接池監控
|
||||
|
||||
**說明**: 添加 PostgreSQL 和 Redis 連接池指標到 Prometheus
|
||||
|
||||
**複雜度**: 低
|
||||
|
||||
**影響範圍**:
|
||||
- `src/core/db/postgres_db.rs`
|
||||
- `src/core/db/redis_client.rs`
|
||||
- `src/api/` (新增 metrics endpoint)
|
||||
|
||||
**風險**: 低
|
||||
|
||||
**優先級**: 待評估
|
||||
|
||||
---
|
||||
|
||||
### 3. Processor 重試機制
|
||||
|
||||
**說明**: 當 processor 失敗時自動重試
|
||||
|
||||
**複雜度**: 中
|
||||
|
||||
**影響範圍**:
|
||||
- `src/core/processor/executor.rs` (新增 `run_with_retry` 方法)
|
||||
- `src/core/processor/mod.rs` (導出 `RetryConfig`)
|
||||
|
||||
**風險**:
|
||||
- 無限重試風險 → 已通過 `max_attempts` 控制
|
||||
- 需要指數退避 → 已實現
|
||||
|
||||
**優先級**: ✅ 已完成 (2026-03-21)
|
||||
|
||||
**實作內容**:
|
||||
- `RetryConfig` 結構體 (可配置重試次數、初始延遲、最大延遲、退避倍數)
|
||||
- `run_with_retry()` 方法 (自動重試 + 指數退避)
|
||||
- 單元測試覆蓋
|
||||
|
||||
**使用範例**:
|
||||
```rust
|
||||
use crate::core::processor::{PythonExecutor, RetryConfig};
|
||||
|
||||
let executor = PythonExecutor::new()?;
|
||||
let config = RetryConfig::new(3).with_delay(1000).with_max_delay(30000);
|
||||
|
||||
executor.run_with_retry(
|
||||
"asr_processor.py",
|
||||
&["--input", "/path/to/video"],
|
||||
Some(&uuid),
|
||||
"asr",
|
||||
Some(Duration::from_secs(3600)),
|
||||
Some(config),
|
||||
).await?;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. PyO3 整合
|
||||
|
||||
**說明**: Python/Rust 直接調用,移除子進程調用
|
||||
|
||||
**複雜度**: 高
|
||||
|
||||
**影響範圍**:
|
||||
- `src/core/processor/executor.rs` (重寫)
|
||||
- Python 模組 (修改為可直接 import)
|
||||
|
||||
**風險**:
|
||||
- Python GIL 問題
|
||||
- 依賴版本兼容性
|
||||
- 需要大量重寫
|
||||
|
||||
**優先級**: 低 (長期目標)
|
||||
|
||||
---
|
||||
|
||||
### 5. HTTP 健康端點
|
||||
|
||||
**說明**: 添加 `/health` API 用於外部監控
|
||||
|
||||
**複雜度**: 低
|
||||
|
||||
**影響範圍**:
|
||||
- `src/api/server.rs` (新增路由)
|
||||
|
||||
**風險**: 低
|
||||
|
||||
**優先級**: ✅ 已完成 (2026-03-21)
|
||||
|
||||
**實作內容**:
|
||||
- `GET /health` - 基本健康檢查 (status, version, uptime)
|
||||
- `GET /health/detailed` - 詳細健康檢查 (PostgreSQL, Redis, Qdrant 狀態和延遲)
|
||||
|
||||
---
|
||||
|
||||
### 6. Gitea Actions CI/CD
|
||||
|
||||
**說明**: 配置 Gitea Actions 自動化 CI/CD,在合併前執行檢查
|
||||
|
||||
**複雜度**: 中
|
||||
|
||||
**影響範圍**:
|
||||
- `.gitea/workflows/` (新增 workflow 文件)
|
||||
|
||||
**優點**:
|
||||
- 強制執行檢查,無法跳過
|
||||
- 跨設備一致
|
||||
- PR 審查前自動檢查
|
||||
|
||||
**風險**: 低
|
||||
|
||||
**優先級**: 待評估
|
||||
|
||||
---
|
||||
|
||||
### 7. Commit Message Lint
|
||||
|
||||
**說明**: 規範化提交訊息格式 (Conventional Commits)
|
||||
|
||||
**複雜度**: 低
|
||||
|
||||
**影響範圍**:
|
||||
- `.git/hooks/commit-msg` (新增 hook)
|
||||
- `~/dotfiles/hooks/commit-msg`
|
||||
|
||||
**風險**: 低
|
||||
|
||||
**優先級**: ✅ 已完成 (2026-03-21)
|
||||
|
||||
**實作內容**:
|
||||
- 驗證格式: `<type>(<scope>): <description>`
|
||||
- 有效類型: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert
|
||||
- 警告: 第一行超過 72 字符
|
||||
|
||||
**範例**:
|
||||
```
|
||||
feat(api): add health check endpoint
|
||||
fix(db): resolve connection pool issue
|
||||
docs: update README
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 8. 自動化安裝腳本
|
||||
|
||||
**說明**: 創建腳本一次安裝所有開發工具
|
||||
|
||||
**複雜度**: 低
|
||||
|
||||
**影響範圍**:
|
||||
- `scripts/install-dev-tools.sh` (新增)
|
||||
|
||||
**風險**: 低
|
||||
|
||||
**優先級**: 待評估
|
||||
|
||||
---
|
||||
|
||||
## 評估標準
|
||||
|
||||
| 標準 | 說明 |
|
||||
|------|------|
|
||||
| 業務價值 | 對用戶有何幫助 |
|
||||
| 技術風險 | 實現難度和潛在問題 |
|
||||
| 維護成本 | 未來維護負擔 |
|
||||
| 依賴性 | 對其他系統的影響 |
|
||||
|
||||
---
|
||||
|
||||
## 評估記錄
|
||||
|
||||
| 項目 | 評估日期 | 決策 | 原因 |
|
||||
|------|----------|------|------|
|
||||
| PostgreSQL → Redis 故障轉移 | 待評估 | - | - |
|
||||
| 連接池監控 | 待評估 | - | - |
|
||||
| Processor 重試機制 | 2026-03-21 | 已完成 | - |
|
||||
| PyO3 整合 | 待評估 | - | - |
|
||||
| HTTP 健康端點 | 2026-03-21 | 已完成 | - |
|
||||
| Gitea Actions CI/CD | 待評估 | - | - |
|
||||
| Commit Message Lint | 2026-03-21 | 已完成 | - |
|
||||
| 自動化安裝腳本 | 待評估 | - | - |
|
||||
|
||||
---
|
||||
|
||||
## 9. TigerGraph / Knowledge Graph 圖譜說故事
|
||||
|
||||
**說明**: 使用知識圖譜 (Knowledge Graph) 增強視頻敘事 (Storytelling) 和 RAG 檢索
|
||||
|
||||
**複雜度**: 高
|
||||
|
||||
**研究來源**:
|
||||
- [TigerGraph Agentic GraphRAG](https://www.tigergraph.com/blog/agentic-graphrag-gives-ai-a-playbook-for-smarter-retrieval/) (2025-12-15)
|
||||
- [TigerGraph GraphRAG GitHub](https://github.com/tigergraph/graphrag) (v1.2.0, 2026-03-11)
|
||||
- [GraphRAG in 2026: Practitioner's Guide](https://medium.com/graph-praxis/graph-rag-in-2026-a-practitioners-guide-to-what-actually-works-dca4962e7517) (2026-02-22)
|
||||
- [GraphRAG Complete Guide](https://medium.com/@brian-curry-research/graphrag-the-complete-guide-to-graph-powered-retrieval-augmented-generation-eeb58a6bb4d1) (2026-02-11)
|
||||
|
||||
### 核心概念
|
||||
|
||||
| 概念 | 說明 |
|
||||
|------|------|
|
||||
| **GraphRAG** | 結合知識圖譜與 RAG,比傳統向量檢索更智能 |
|
||||
| **知識圖譜** | 實體 (Entity) + 關係 (Relationship) 的結構化表示 |
|
||||
| **多跳推理** | Multi-hop traversal,可連接多個相關節點 |
|
||||
| **混合檢索** | Graph traversal + Vector similarity 結合 |
|
||||
|
||||
### 對 Momentry 的潛在應用
|
||||
|
||||
```
|
||||
視頻場景 → 實體識別 → 關係建立 → 故事圖譜
|
||||
↓ ↓ ↓ ↓
|
||||
CUT [人物, 物品, 動作] [誰做了什麼, 什麼導致什麼] [敘事鏈]
|
||||
```
|
||||
|
||||
**1. 敘事圖譜構建 (Narrative Graph)**
|
||||
- 從 Story/Chunks 模組提取實體
|
||||
- 建立場景之間的因果關係
|
||||
- 追蹤角色互動和情節發展
|
||||
|
||||
**2. 故事檢索增強**
|
||||
```python
|
||||
# 現有: Parent-child chunks
|
||||
parent_chunk: "場景描述"
|
||||
child_chunks: [詳細內容]
|
||||
|
||||
# 加入圖譜:
|
||||
場景A --led_to--> 場景B
|
||||
角色X --interacted_with--> 角色Y
|
||||
主題Y --related_to--> 主題Z
|
||||
```
|
||||
|
||||
**3. 查詢模式**
|
||||
|
||||
| 查詢類型 | 傳統 RAG | GraphRAG |
|
||||
|----------|----------|----------|
|
||||
| 事實查找 | ✅ "這個場景在說什麼" | ✅ |
|
||||
| 主題推理 | ❌ "這個視頻的主要情節" | ✅ Global search |
|
||||
| 多跳關係 | ❌ | ✅ "A導致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 的一部分)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,682 +0,0 @@
|
||||
# Job Worker 實作計畫
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren / OpenCode |
|
||||
| 建立時間 | 2026-03-24 |
|
||||
| 文件版本 | V1.1 |
|
||||
| 狀態 | ✅ 已實作 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 |
|
||||
|------|------|------|--------|
|
||||
| V1.0 | 2026-03-24 | 建立實作計畫 | OpenCode |
|
||||
| V1.1 | 2026-03-25 | 實作完成,更新狀態 | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## 實作狀態
|
||||
|
||||
### ✅ 已完成
|
||||
|
||||
| 元件 | 檔案 | 狀態 |
|
||||
|------|------|------|
|
||||
| MonitorJob 結構 | `src/core/db/postgres_db.rs` | ✅ |
|
||||
| ProcessorResult 結構 | `src/core/db/postgres_db.rs` | ✅ |
|
||||
| Worker 配置 | `src/worker/config.rs` | ✅ |
|
||||
| Job Worker | `src/worker/job_worker.rs` | ✅ |
|
||||
| Processor Pool | `src/worker/processor.rs` | ✅ |
|
||||
| Worker 模組 | `src/worker/mod.rs` | ✅ |
|
||||
| PostgreSQL 表格 | `monitor_jobs`, `processor_results` | ✅ |
|
||||
| 類型修復 | `i32`, `NaiveDateTime` | ✅ |
|
||||
|
||||
### 待整合
|
||||
|
||||
| 項目 | 說明 |
|
||||
|------|------|
|
||||
| Worker 服務啟動 | 需要加入 launchd plist |
|
||||
| 監控整合 | 需要加入 MOMENTRY_CORE_MONITORING.md |
|
||||
| 備份涵蓋 | 需要確認備份包含新表格 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 設計決策
|
||||
|
||||
### 1.1 確認的設計決策
|
||||
|
||||
| 項目 | 決策 | 理由 |
|
||||
|------|------|------|
|
||||
| 觸發方式 | 輪詢(Job Worker) | 暫無可靠的 API 觸發機制 |
|
||||
| 並行處理 | 最多 2 個 | 可根據 CPU/GPU 能力調整 |
|
||||
| 失敗處理 | 獨立模組,部分完成可接續 | 任何模組失敗都產出狀態記錄 |
|
||||
| Worker 啟動 | 獨立進程 | 隔離、易管理 |
|
||||
| 並行上限調整 | 環境變數 + 預設值 | 靈活、可調整 |
|
||||
| 狀態同步 | PostgreSQL + Redis | 可靠 + 即時 |
|
||||
|
||||
### 1.2 環境變數
|
||||
|
||||
| 變數 | 預設值 | 說明 |
|
||||
|------|--------|------|
|
||||
| `MOMENTRY_MAX_CONCURRENT` | 2 | 最大並行 processor 數 |
|
||||
| `MOMENTRY_POLL_INTERVAL` | 5 | 輪詢間隔(秒) |
|
||||
| `MOMENTRY_WORKER_ENABLED` | true | 是否啟用 worker |
|
||||
|
||||
---
|
||||
|
||||
## 2. 系統架構
|
||||
|
||||
### 2.1 完整流程圖
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ 檔案註冊觸發處理流程 │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 1. SFTPGo 上傳 │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 2. Hook 呼叫 Register API │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 3. Register API │
|
||||
│ ├─► ffprobe 提取 metadata │
|
||||
│ ├─► 寫入 videos 表 │
|
||||
│ └─► 建立 monitor_jobs 記錄 (status=pending) │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 4. Job Worker (獨立進程,輪詢機制) │
|
||||
│ ├─► 輪詢 pending jobs │
|
||||
│ ├─► 檢查 videos 表 fs_json 決定需要處理什麼 │
|
||||
│ ├─► 並行執行 processors (最多 2 個) │
|
||||
│ └─► 更新 videos, monitor_jobs, processor_results 表 │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 5. 處理結果 │
|
||||
│ ├─► 更新 videos 表 (fs_json, psql_chunk, qvector_chunk) │
|
||||
│ ├─► 更新 monitor_jobs 表 (status, progress) │
|
||||
│ ├─► 更新 processor_results 表 (每個模組狀態) │
|
||||
│ └─► Redis Pub/Sub 即時進度 │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 Job Worker 架構
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Job Worker 架構 │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ PostgreSQL │ ───▶ │ Worker │ ───▶ │ Processor │ │
|
||||
│ │ Job Queue │ │ Loop │ │ Pool │ │
|
||||
│ └─────────────┘ └──────┬──────┘ └──────┬──────┘ │
|
||||
│ │ │ │
|
||||
│ ▼ ▼ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ Video State │ │ Processor 1 │ │
|
||||
│ │ Check │ │ (ASR/YOLO) │ │
|
||||
│ └─────────────┘ ├─────────────┤ │
|
||||
│ │ Processor 2 │ │
|
||||
│ │ (CUT/OCR) │ │
|
||||
│ └─────────────┘ │
|
||||
│ │
|
||||
│ Redis ──── Pub/Sub ──── 即時進度 │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 資料庫結構
|
||||
|
||||
### 3.1 Migration 檔案
|
||||
|
||||
**檔案**: `migrations/003_job_worker.sql`
|
||||
|
||||
```sql
|
||||
-- ================================================================
|
||||
-- Migration 003: Job Worker System
|
||||
-- ================================================================
|
||||
|
||||
-- 3.1.1 更新 videos 表
|
||||
ALTER TABLE videos ADD COLUMN IF NOT EXISTS status VARCHAR(20) DEFAULT 'pending';
|
||||
ALTER TABLE videos ADD COLUMN IF NOT EXISTS user_id BIGINT;
|
||||
ALTER TABLE videos ADD COLUMN IF NOT EXISTS job_id INTEGER REFERENCES monitor_jobs(id);
|
||||
|
||||
COMMENT ON COLUMN videos.status IS 'pending, processing, completed, failed';
|
||||
COMMENT ON COLUMN videos.user_id IS 'WordPress user ID';
|
||||
COMMENT ON COLUMN videos.job_id IS 'Associated monitor_jobs ID';
|
||||
|
||||
-- 3.1.2 更新 monitor_jobs 表
|
||||
ALTER TABLE monitor_jobs ADD COLUMN IF NOT EXISTS video_id BIGINT REFERENCES videos(id);
|
||||
ALTER TABLE monitor_jobs ADD COLUMN IF NOT EXISTS user_id BIGINT;
|
||||
ALTER TABLE monitor_jobs ADD COLUMN IF NOT EXISTS processors VARCHAR(20)[];
|
||||
ALTER TABLE monitor_jobs ADD COLUMN IF NOT EXISTS completed_processors VARCHAR(20)[];
|
||||
ALTER TABLE monitor_jobs ADD COLUMN IF NOT EXISTS failed_processors VARCHAR(20)[];
|
||||
|
||||
COMMENT ON COLUMN monitor_jobs.processors IS 'Processors to run: asr, cut, yolo, ocr, face, pose, asrx';
|
||||
COMMENT ON COLUMN monitor_jobs.completed_processors IS 'Successfully completed processors';
|
||||
COMMENT ON COLUMN monitor_jobs.failed_processors IS 'Failed processors';
|
||||
|
||||
-- 3.1.3 新增 processor_results 表
|
||||
CREATE TABLE IF NOT EXISTS processor_results (
|
||||
id SERIAL PRIMARY KEY,
|
||||
job_id INTEGER REFERENCES monitor_jobs(id) ON DELETE CASCADE,
|
||||
video_id BIGINT REFERENCES videos(id) ON DELETE CASCADE,
|
||||
processor VARCHAR(20) NOT NULL,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
||||
output_path TEXT,
|
||||
started_at TIMESTAMP,
|
||||
completed_at TIMESTAMP,
|
||||
error_message TEXT,
|
||||
progress_total INT DEFAULT 0,
|
||||
progress_current INT DEFAULT 0,
|
||||
last_checkpoint JSONB,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
UNIQUE(job_id, processor)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_processor_results_job ON processor_results(job_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_processor_results_video ON processor_results(video_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_processor_results_status ON processor_results(status);
|
||||
|
||||
COMMENT ON TABLE processor_results IS 'Tracks individual processor execution status';
|
||||
COMMENT ON COLUMN processor_results.status IS 'pending, running, completed, failed, skipped';
|
||||
|
||||
-- 3.1.4 更新 videos 表標記欄位用途
|
||||
COMMENT ON COLUMN videos.fs_video IS 'Video file exists on filesystem';
|
||||
COMMENT ON COLUMN videos.fs_json IS 'All processor JSON files generated';
|
||||
COMMENT ON COLUMN videos.fs_chunks IS 'Chunk files generated';
|
||||
COMMENT ON COLUMN videos.fs_vectors IS 'Vector files generated';
|
||||
COMMENT ON COLUMN videos.psql_chunk IS 'Chunks stored in PostgreSQL';
|
||||
COMMENT ON COLUMN videos.pvector_chunk IS 'Vectors stored in PostgreSQL';
|
||||
COMMENT ON COLUMN videos.qvector_chunk IS 'Vectors stored in Qdrant';
|
||||
```
|
||||
|
||||
### 3.2 表關係圖
|
||||
|
||||
```
|
||||
videos monitor_jobs
|
||||
┌──────────────────────┐ ┌──────────────────────┐
|
||||
│ id (PK) │◄────────│ video_id (FK) │
|
||||
│ uuid │ │ user_id │
|
||||
│ status │ │ processors[] │
|
||||
│ fs_video │ │ completed_processors[]│
|
||||
│ fs_json │ │ failed_processors[] │
|
||||
│ job_id (FK)─────────┼────────►│ status │
|
||||
│ user_id │ │ id (PK) │
|
||||
└──────────────────────┘ └──────────────────────┘
|
||||
│
|
||||
│
|
||||
processor_results
|
||||
┌──────────────────────┐
|
||||
│ job_id (FK) │
|
||||
│ video_id (FK) │
|
||||
│ processor │
|
||||
│ status │
|
||||
│ progress_current │
|
||||
│ last_checkpoint │
|
||||
│ id (PK) │
|
||||
└──────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 模組並行策略
|
||||
|
||||
### 4.1 模組分類
|
||||
|
||||
| 模組 | 資源需求 | 獨立性 | 建議並行 |
|
||||
|------|----------|--------|----------|
|
||||
| ASR | GPU/CPU | 高 | ✅ 可並行 |
|
||||
| CUT | CPU | 高 | ✅ 可並行 |
|
||||
| YOLO | GPU | 中 | ✅ 可並行 |
|
||||
| OCR | GPU/CPU | 高 | ✅ 可並行 |
|
||||
| Face | GPU | 中 | ✅ 可並行 |
|
||||
| Pose | GPU | 中 | ✅ 可並行 |
|
||||
| ASRX | GPU/CPU | 高 | ✅ 可並行 |
|
||||
|
||||
### 4.2 建議並行組合
|
||||
|
||||
| 組合 | 模組 1 | 模組 2 | 說明 |
|
||||
|------|---------|---------|------|
|
||||
| GPU+CPU | YOLO/Pose/Face | ASR/CUT/OCR | 平衡負載 |
|
||||
| 雙GPU | YOLO | Pose | 雙 GPU 卡片 |
|
||||
| 雙CPU | ASR | CUT/OCR | 無 GPU 時 |
|
||||
|
||||
### 4.3 Worker 配置
|
||||
|
||||
```rust
|
||||
// src/worker/config.rs
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WorkerConfig {
|
||||
pub max_concurrent: usize, // 預設 2
|
||||
pub poll_interval_secs: u64, // 預設 5
|
||||
pub enabled: bool, // 預設 true
|
||||
}
|
||||
|
||||
impl Default for WorkerConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
max_concurrent: 2,
|
||||
poll_interval_secs: 5,
|
||||
enabled: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WorkerConfig {
|
||||
pub fn from_env() -> Self {
|
||||
Self {
|
||||
max_concurrent: std::env::var("MOMENTRY_MAX_CONCURRENT")
|
||||
.ok()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.unwrap_or(2),
|
||||
poll_interval_secs: std::env::var("MOMENTRY_POLL_INTERVAL")
|
||||
.ok()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.unwrap_or(5),
|
||||
enabled: std::env::var("MOMENTRY_WORKER_ENABLED")
|
||||
.ok()
|
||||
.map(|v| v != "false")
|
||||
.unwrap_or(true),
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 失敗處理機制
|
||||
|
||||
### 5.1 設計原則
|
||||
|
||||
```
|
||||
每個模組獨立處理:
|
||||
- 成功 → 產出完整 .json,status=completed
|
||||
- 失敗 → 產出 .json 包含 error 狀態,status=failed
|
||||
- 部分完成 → 可從 checkpoint 繼續,status=running
|
||||
```
|
||||
|
||||
### 5.2 Processor 輸出格式
|
||||
|
||||
```json
|
||||
{
|
||||
"processor": "asr",
|
||||
"status": "completed|failed|partial",
|
||||
"completed_at": "2026-03-24T12:00:00Z",
|
||||
"result": { ... },
|
||||
"error": null,
|
||||
"last_checkpoint": {
|
||||
"frame": 5000,
|
||||
"timestamp": 180.5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 失敗處理流程
|
||||
|
||||
```rust
|
||||
async fn run_processor(&self, module: &str, video: &Video) -> Result<()> {
|
||||
let output_path = self.get_output_path(video, module);
|
||||
|
||||
match self.execute_processor(module, video, &output_path).await {
|
||||
Ok(result) => {
|
||||
// 成功:更新狀態
|
||||
self.db.update_processor_status(job_id, module, "completed").await?;
|
||||
self.publish_progress(job_id, module, 100).await?;
|
||||
}
|
||||
Err(e) => {
|
||||
// 失敗:仍然保存部分結果
|
||||
let partial_result = self.get_partial_result(&output_path);
|
||||
self.db.update_processor_status(job_id, module, "failed").await?;
|
||||
self.db.save_error_message(job_id, module, &e.to_string()).await?;
|
||||
|
||||
// 記錄錯誤但不中斷其他模組
|
||||
tracing::warn!("Processor {} failed: {}", module, e);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 實作結構
|
||||
|
||||
### 6.1 目錄結構
|
||||
|
||||
```
|
||||
src/
|
||||
├── worker/
|
||||
│ ├── mod.rs # Worker 模組導出
|
||||
│ ├── config.rs # Worker 配置
|
||||
│ ├── worker.rs # Worker 主邏輯
|
||||
│ ├── processor.rs # Processor 執行器
|
||||
│ ├── queue.rs # Job 佇列管理
|
||||
│ └── progress.rs # 進度追蹤
|
||||
├── api/
|
||||
│ └── server.rs # 更新 Register API
|
||||
└── main.rs # 新增 worker 命令
|
||||
```
|
||||
|
||||
### 6.2 核心模組
|
||||
|
||||
#### 6.2.1 Worker Config (`src/worker/config.rs`)
|
||||
|
||||
```rust
|
||||
pub struct WorkerConfig {
|
||||
pub max_concurrent: usize,
|
||||
pub poll_interval_secs: u64,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
impl WorkerConfig {
|
||||
pub fn from_env() -> Self { ... }
|
||||
}
|
||||
```
|
||||
|
||||
#### 6.2.2 Worker Loop (`src/worker/worker.rs`)
|
||||
|
||||
```rust
|
||||
pub struct JobWorker {
|
||||
db: PostgresDb,
|
||||
redis: RedisCache,
|
||||
config: WorkerConfig,
|
||||
semaphore: Arc<Semaphore>,
|
||||
}
|
||||
|
||||
impl JobWorker {
|
||||
pub async fn run(&self) -> Result<()> {
|
||||
loop {
|
||||
if self.config.enabled {
|
||||
self.process_pending_jobs().await?;
|
||||
}
|
||||
tokio::time::sleep(Duration::from_secs(self.config.poll_interval_secs)).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn process_pending_jobs(&self) -> Result<()> {
|
||||
// 1. 檢查並發數
|
||||
// 2. 取得 pending jobs
|
||||
// 3. 分配給 worker pool
|
||||
// 4. 並行執行 processors
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 6.2.3 Processor Pool (`src/worker/processor.rs`)
|
||||
|
||||
```rust
|
||||
pub struct ProcessorPool {
|
||||
max_concurrent: usize,
|
||||
}
|
||||
|
||||
impl ProcessorPool {
|
||||
pub async fn execute(&self, job: &Job, video: &Video) -> Result<ProcessorResult> {
|
||||
// 根據 videos 表決定需要執行哪些 processor
|
||||
// 並行執行最多 2 個
|
||||
// 處理失敗但不中斷其他 processor
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. API 端點設計
|
||||
|
||||
### 7.1 新增端點
|
||||
|
||||
| 端點 | 方法 | 說明 |
|
||||
|------|------|------|
|
||||
| `/api/v1/jobs` | GET | 列出所有 jobs |
|
||||
| `/api/v1/jobs/:uuid` | GET | 取得特定 job 詳細 |
|
||||
| `/api/v1/jobs/:uuid/retry` | POST | 重試失敗的 processor |
|
||||
| `/api/v1/jobs/:uuid/cancel` | POST | 取消 job |
|
||||
|
||||
### 7.2 端點詳情
|
||||
|
||||
#### GET /api/v1/jobs
|
||||
|
||||
```json
|
||||
Response:
|
||||
{
|
||||
"jobs": [
|
||||
{
|
||||
"id": 1,
|
||||
"uuid": "abc123def456",
|
||||
"status": "running",
|
||||
"progress": 60,
|
||||
"processors": ["asr", "cut", "yolo", "ocr", "face", "pose"],
|
||||
"completed": ["asr", "cut", "yolo"],
|
||||
"failed": []
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### GET /api/v1/jobs/:uuid
|
||||
|
||||
```json
|
||||
Response:
|
||||
{
|
||||
"id": 1,
|
||||
"uuid": "abc123def456",
|
||||
"video_id": 10,
|
||||
"status": "running",
|
||||
"processors": {
|
||||
"asr": {"status": "completed", "progress": 100},
|
||||
"cut": {"status": "completed", "progress": 100},
|
||||
"yolo": {"status": "running", "progress": 45, "current": 5000, "total": 11000},
|
||||
"ocr": {"status": "pending"},
|
||||
"face": {"status": "pending"},
|
||||
"pose": {"status": "pending"}
|
||||
},
|
||||
"created_at": "2026-03-24T12:00:00Z",
|
||||
"started_at": "2026-03-24T12:01:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Redis Key 設計
|
||||
|
||||
### 8.1 現有 Key 保持
|
||||
|
||||
```bash
|
||||
momentry:job:{uuid} # Job Hash
|
||||
momentry:job:{uuid}:processor:{name} # Processor Hash
|
||||
momentry:progress:{uuid} # Pub/Sub Channel
|
||||
momentry:jobs:active # Set: 運行中 UUIDs
|
||||
momentry:jobs:completed # Set: 完成 UUIDs
|
||||
momentry:jobs:failed # Set: 失敗 UUIDs
|
||||
```
|
||||
|
||||
### 8.2 進度更新時序
|
||||
|
||||
```
|
||||
Processor 執行
|
||||
│
|
||||
├─► 每秒更新 Redis Hash (即時)
|
||||
│
|
||||
├─► 每 10% 或完成時更新 PostgreSQL (持久)
|
||||
│
|
||||
└─► 失敗時立即更新 PostgreSQL (錯誤記錄)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 實作順序
|
||||
|
||||
### Phase 1: 資料庫遷移
|
||||
|
||||
| 任務 | 說明 |
|
||||
|------|------|
|
||||
| 1.1 | 建立 `migrations/003_job_worker.sql` |
|
||||
| 1.2 | 更新 `postgres_db.rs` 對應的 struct |
|
||||
| 1.3 | 執行 migration 驗證 |
|
||||
|
||||
### Phase 2: Worker 框架
|
||||
|
||||
| 任務 | 說明 |
|
||||
|------|------|
|
||||
| 2.1 | 建立 `src/worker/mod.rs` |
|
||||
| 2.2 | 建立 `src/worker/config.rs` |
|
||||
| 2.3 | 建立 `src/worker/worker.rs` |
|
||||
| 2.4 | 建立 `src/worker/processor.rs` |
|
||||
|
||||
### Phase 3: Register API 整合
|
||||
|
||||
| 任務 | 說明 |
|
||||
|------|------|
|
||||
| 3.1 | 修改 `src/api/server.rs` 的 register 函數 |
|
||||
| 3.2 | 加入建立 monitor_jobs 的邏輯 |
|
||||
| 3.3 | 更新 videos 表 status 欄位 |
|
||||
|
||||
### Phase 4: Processor 執行
|
||||
|
||||
| 任務 | 說明 |
|
||||
|------|------|
|
||||
| 4.1 | 實作 processor 並行執行(最多 2 個) |
|
||||
| 4.2 | 實作失敗處理(保存部分結果) |
|
||||
| 4.3 | 實作 checkpoint 恢復 |
|
||||
|
||||
### Phase 5: 進度追蹤
|
||||
|
||||
| 任務 | 說明 |
|
||||
|------|------|
|
||||
| 5.1 | Redis Pub/Sub 整合 |
|
||||
| 5.2 | PostgreSQL 定期同步 |
|
||||
| 5.3 | API 進度端點更新 |
|
||||
|
||||
### Phase 6: API 端點
|
||||
|
||||
| 任務 | 說明 |
|
||||
|------|------|
|
||||
| 6.1 | GET /api/v1/jobs |
|
||||
| 6.2 | GET /api/v1/jobs/:uuid |
|
||||
| 6.3 | POST /api/v1/jobs/:uuid/retry |
|
||||
| 6.4 | POST /api/v1/jobs/:uuid/cancel |
|
||||
|
||||
### Phase 7: CLI 命令
|
||||
|
||||
| 任務 | 說明 |
|
||||
|------|------|
|
||||
| 7.1 | `cargo run -- worker` 命令 |
|
||||
| 7.2 | Worker 啟動/停止/狀態顯示 |
|
||||
| 7.3 | launchd plist 設定 |
|
||||
|
||||
### Phase 8: 測試
|
||||
|
||||
| 任務 | 說明 |
|
||||
|------|------|
|
||||
| 8.1 | 單元測試 |
|
||||
| 8.2 | 端到端測試 |
|
||||
| 8.3 | 失敗處理測試 |
|
||||
| 8.4 | 並行執行測試 |
|
||||
|
||||
---
|
||||
|
||||
## 10. CLI 命令
|
||||
|
||||
### 10.1 Worker 命令
|
||||
|
||||
```bash
|
||||
# 啟動 worker
|
||||
cargo run -- worker
|
||||
|
||||
# 顯示 worker 幫助
|
||||
cargo run -- worker --help
|
||||
```
|
||||
|
||||
### 10.2 環境變數
|
||||
|
||||
```bash
|
||||
# Worker 配置
|
||||
export MOMENTRY_MAX_CONCURRENT=2
|
||||
export MOMENTRY_POLL_INTERVAL=5
|
||||
export MOMENTRY_WORKER_ENABLED=true
|
||||
|
||||
# 現有環境變數
|
||||
export DATABASE_URL=postgres://accusys@localhost:5432/momentry
|
||||
export REDIS_URL=redis://:accusys@localhost:6379
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. 預估工時
|
||||
|
||||
| Phase | 任務 | 預估工時 |
|
||||
|-------|------|----------|
|
||||
| 1 | 資料庫遷移 | 2h |
|
||||
| 2 | Worker 框架 | 4h |
|
||||
| 3 | Register API 整合 | 2h |
|
||||
| 4 | Processor 執行 | 4h |
|
||||
| 5 | 進度追蹤 | 2h |
|
||||
| 6 | API 端點 | 3h |
|
||||
| 7 | CLI 命令 | 2h |
|
||||
| 8 | 測試 | 4h |
|
||||
| **總計** | | **23h** |
|
||||
|
||||
---
|
||||
|
||||
## 12. 參考文件
|
||||
|
||||
| 文件 | 用途 |
|
||||
|------|------|
|
||||
| `docs/MOMENTRY_CORE_MONITORING.md` | 監控系統規範 |
|
||||
| `docs/MOMENTRY_CORE_REDIS_KEYS.md` | Redis Key 設計 |
|
||||
| `docs/PROCESSING_PIPELINE.md` | 處理流程 |
|
||||
| `docs/CHUNK_DESIGN.md` | 資料庫設計 |
|
||||
| `docs/API_REFERENCE.md` | API 參考 |
|
||||
|
||||
---
|
||||
|
||||
## 13. 附錄
|
||||
|
||||
### A. 狀態機
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ PENDING │
|
||||
└──────┬───────┘
|
||||
│ register 後
|
||||
▼
|
||||
┌──────────────┐
|
||||
┌─────▶│ PROCESSING │◀──────┐
|
||||
│ └──────┬───────┘ │
|
||||
│ │ │
|
||||
部分失敗 all completed 全部失敗
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||
│ PARTIAL │ │COMPLETED │ │ FAILED │
|
||||
└──────────┘ └──────────┘ └──────────┘
|
||||
```
|
||||
|
||||
### B. videos 表 status 欄位
|
||||
|
||||
| 值 | 說明 |
|
||||
|------|------|
|
||||
| `pending` | 已註冊,等待處理 |
|
||||
| `processing` | 處理中 |
|
||||
| `completed` | 所有處理完成 |
|
||||
| `failed` | 處理失敗 |
|
||||
|
||||
### C. processor_results 表 status 欄位
|
||||
|
||||
| 值 | 說明 |
|
||||
|------|------|
|
||||
| `pending` | 等待執行 |
|
||||
| `running` | 執行中 |
|
||||
| `completed` | 執行成功 |
|
||||
| `failed` | 執行失敗 |
|
||||
| `skipped` | 跳過(如檔案已存在) |
|
||||
@@ -1,782 +0,0 @@
|
||||
# Momentry 系統自動化安裝計劃
|
||||
|
||||
> **計劃階段** - 僅供討論,尚未執行
|
||||
> **建立時間**: 2026-03-23
|
||||
> **目標**: Thunderbolt NVMe 外開機完整安裝
|
||||
|
||||
---
|
||||
|
||||
## 系統概述
|
||||
|
||||
### 當前環境
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| **主控機** | Mac mini (M4, 16GB RAM) |
|
||||
| **作業系統** | macOS 26.3.1 (Tahoe) |
|
||||
| **儲存** | Thunderbolt NVMe (2TB) |
|
||||
| **用途** | 開機碟 + 完整 Momentry 系統 |
|
||||
|
||||
### 目標環境
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| **目標主機** | 其他 Mac (Intel 或 Apple Silicon) |
|
||||
| **安裝方式** | Thunderbolt NVMe 外接開機 |
|
||||
| **連接方式** | Thunderbolt 3/4 |
|
||||
| **控制方式** | SSH 遠端管理 |
|
||||
|
||||
---
|
||||
|
||||
## 系統架構
|
||||
|
||||
### 服務列表
|
||||
|
||||
| 服務 | 版本 | 用途 | Port |
|
||||
|------|------|------|------|
|
||||
| **PostgreSQL** | 18.1 | 主資料庫、n8n 資料庫 | 5432 |
|
||||
| **MongoDB** | 8.0 | 文件資料庫 | 27017 |
|
||||
| **MariaDB** | 11.4 | WordPress 資料庫 | 3306 |
|
||||
| **Redis** | 7.x | 快取、佇列 | 6379 |
|
||||
| **Qdrant** | 1.7.x | 向量資料庫 | 6333 |
|
||||
| **Ollama** | 0.13.5 | 本地 LLM | 11434 |
|
||||
| **Caddy** | 2.x | 反向代理 | 80/443 |
|
||||
| **Gitea** | 1.21 | Git 服務 | 3000 |
|
||||
| **PHP-FPM** | 8.5 | WordPress | 9000 |
|
||||
| **n8n** | 2.3.5 | 工作流程自動化 | 5678 |
|
||||
| **RustDesk** | hbbs/hbbr | 遠端桌面 | 21115-21119 |
|
||||
| **SFTPGo** | 2.x | SFTP 服務 | 2022 |
|
||||
| **Momentry Core** | 0.1.0 | 影片處理核心 | 3002 |
|
||||
| **Prometheus** | 3.9.1 | 監控 | 9090 |
|
||||
|
||||
### 目錄結構
|
||||
|
||||
```
|
||||
/Volumes/Momentry/
|
||||
├── System/
|
||||
│ └── macOS/ # macOS 系統
|
||||
├── Applications/
|
||||
│ └── Homebrew/ # Homebrew 應用程式
|
||||
├── momentry/
|
||||
│ ├── var/ # 資料目錄
|
||||
│ │ ├── postgresql/ # PostgreSQL 資料
|
||||
│ │ ├── mongodb/ # MongoDB 資料
|
||||
│ │ ├── mariadb/ # MariaDB 資料
|
||||
│ │ ├── redis/ # Redis 資料
|
||||
│ │ ├── qdrant/ # Qdrant 資料
|
||||
│ │ ├── n8n/ # n8n 資料
|
||||
│ │ ├── ollama/ # Ollama 模型
|
||||
│ │ └── ...
|
||||
│ ├── etc/ # 配置檔案
|
||||
│ │ ├── Caddyfile
|
||||
│ │ ├── gitea/
|
||||
│ │ ├── php/
|
||||
│ │ └── ...
|
||||
│ ├── log/ # 日誌
|
||||
│ ├── scripts/ # 管理腳本
|
||||
│ └── backup/ # 備份
|
||||
├── momentry_core/ # Rust 原始碼
|
||||
└── momentry_dashboard/ # Web Dashboard
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 階段一:前置準備
|
||||
|
||||
### 1.1 收集目標主機資訊
|
||||
|
||||
```bash
|
||||
# 需要收集的資訊
|
||||
- Mac 型號 (Intel/Apple Silicon)
|
||||
- macOS 版本
|
||||
- Thunderbolt 版本 (3/4)
|
||||
- 可用記憶體
|
||||
- 目標磁碟代號 (diskX)
|
||||
- 網路配置 (DHCP/固定 IP)
|
||||
```
|
||||
|
||||
### 1.2 準備 Thunderbolt NVMe
|
||||
|
||||
```bash
|
||||
# 檢查 Thunderbolt NVMe
|
||||
diskutil list external
|
||||
|
||||
# 預期輸出:
|
||||
# /dev/diskX (external, physical):
|
||||
# NAME TYPE SIZE
|
||||
# Thunderbolt NVMe ...
|
||||
```
|
||||
|
||||
### 1.3 準備主控機腳本
|
||||
|
||||
```bash
|
||||
# 主控機需要準備的腳本
|
||||
~/momentry/setup/
|
||||
├── 01_prepare_disk.sh
|
||||
├── 02_install_macos.sh
|
||||
├── 03_install_homebrew.sh
|
||||
├── 04_install_dependencies.sh
|
||||
├── 05_install_services.sh
|
||||
├── 06_install_momentry.sh
|
||||
├── 07_configure_network.sh
|
||||
├── 08_start_services.sh
|
||||
└── utils/
|
||||
├── common.sh
|
||||
├── backup.sh
|
||||
└── monitor.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 階段二:Thunderbolt NVMe 準備
|
||||
|
||||
### 2.1 分割磁碟方案 A(推薦)
|
||||
|
||||
```bash
|
||||
# 磁碟分割配置
|
||||
diskutil partitionDisk /dev/diskX \
|
||||
GPT \
|
||||
"APFS System" APFS "Momentry System" 200G \
|
||||
"APFS Data" APFS "Momentry Data" 1.8T
|
||||
```
|
||||
|
||||
### 2.2 分割磁碟方案 B(最小化)
|
||||
|
||||
```bash
|
||||
# 統一 APFS 容器
|
||||
diskutil partitionDisk /dev/diskX \
|
||||
GPT \
|
||||
APFS "Momentry" 100%
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 階段三:安裝 macOS
|
||||
|
||||
### 3.1 建立 macOS 安裝碟
|
||||
|
||||
```bash
|
||||
# 下載 macOS Sonoma (或最新版本)
|
||||
softwareupdate --fetch-full-installer --full-installer-version 14.0
|
||||
|
||||
# 建立可開機安裝碟
|
||||
sudo /Applications/Install\ macOS\ Sonoma.app/Contents/Resources/createinstallinstmedi \
|
||||
--volume /Volumes/Momentry \
|
||||
--nointeraction
|
||||
```
|
||||
|
||||
### 3.2 安裝 macOS 到 Thunderbolt NVMe
|
||||
|
||||
**兩種方法:**
|
||||
|
||||
#### 方法 A: 復原模式安裝
|
||||
1. 連接 Thunderbolt NVMe
|
||||
2. 重啟目標主機,按住Option鍵
|
||||
3. 選擇 Thunderbolt NVMe 開機
|
||||
4. 進入 Recovery Mode (Command+R)
|
||||
5. 使用 Disk Utility 格式化目標磁碟
|
||||
6. 安裝 macOS
|
||||
|
||||
#### 方法 B: ASR 複製(建議)
|
||||
```bash
|
||||
# 從主控機執行
|
||||
# 將現有系統複製到目標磁碟
|
||||
sudo asr restore \
|
||||
--source /Volumes/Macintosh\ HD \
|
||||
--target /Volumes/Momentry \
|
||||
--erase --noprompt
|
||||
```
|
||||
|
||||
### 3.3 設定 macOS
|
||||
|
||||
```bash
|
||||
# 自動化設定腳本
|
||||
./setup/scripts/03_install_homebrew.sh
|
||||
```
|
||||
|
||||
**設定項目:**
|
||||
- 電腦名稱:`momentry-<serial>`
|
||||
- 使用者帳號:`momentry` (管理員)
|
||||
- SSH 遠端登入:啟用
|
||||
- 螢幕鎖定:關閉
|
||||
- 節能設定:永不休眠
|
||||
|
||||
---
|
||||
|
||||
## 階段四:安裝 Homebrew
|
||||
|
||||
### 4.1 安裝 Homebrew
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 04_install_homebrew.sh
|
||||
|
||||
# 檢查架構
|
||||
ARCH=$(uname -m)
|
||||
|
||||
if [ "$ARCH" = "arm64" ]; then
|
||||
# Apple Silicon
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
|
||||
eval "$(/opt/homebrew/bin/brew shellenv)"
|
||||
elif [ "$ARCH" = "x86_64" ]; then
|
||||
# Intel
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
echo 'eval "$(/usr/local/bin/brew shellenv)"' >> ~/.zprofile
|
||||
eval "$(/usr/local/bin/brew shellenv)"
|
||||
fi
|
||||
|
||||
# 驗證
|
||||
brew --version
|
||||
```
|
||||
|
||||
### 4.2 安裝基礎工具
|
||||
|
||||
```bash
|
||||
# 基礎開發工具
|
||||
brew install \
|
||||
git \
|
||||
curl \
|
||||
wget \
|
||||
jq \
|
||||
yq \
|
||||
tree \
|
||||
htop \
|
||||
tmux \
|
||||
zsh \
|
||||
zsh-completions
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 階段五:安裝服務
|
||||
|
||||
### 5.1 安裝資料庫服務
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 05_install_services.sh
|
||||
|
||||
# PostgreSQL
|
||||
brew install postgresql@18
|
||||
brew services start postgresql@18
|
||||
|
||||
# MongoDB
|
||||
brew tap mongodb/brew
|
||||
brew install mongodb-community
|
||||
brew services start mongodb-community
|
||||
|
||||
# MariaDB
|
||||
brew install mariadb
|
||||
brew services start mariadb
|
||||
|
||||
# Redis
|
||||
brew install redis
|
||||
brew services start redis
|
||||
|
||||
# Qdrant (需要 Cargo)
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
cargo install qdrant
|
||||
```
|
||||
|
||||
### 5.2 安裝應用服務
|
||||
|
||||
```bash
|
||||
# Ollama
|
||||
brew install ollama
|
||||
brew services start ollama
|
||||
|
||||
# Caddy
|
||||
brew install caddy
|
||||
brew services start caddy
|
||||
|
||||
# Gitea
|
||||
brew install gitea
|
||||
brew services start gitea
|
||||
|
||||
# PHP
|
||||
brew install php
|
||||
brew services start php
|
||||
|
||||
# n8n
|
||||
brew install n8n
|
||||
brew services start n8n
|
||||
```
|
||||
|
||||
### 5.3 Launchd 服務配置
|
||||
|
||||
```xml
|
||||
<!-- /Library/LaunchDaemons/com.momentry.postgresql.plist -->
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.momentry.postgresql</string>
|
||||
<key>UserName</key>
|
||||
<string>momentry</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/opt/homebrew/opt/postgresql@18/bin/postgres</string>
|
||||
<string>-D</string>
|
||||
<string>/Volumes/Momentry/momentry/var/postgresql</string>
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/Volumes/Momentry/momentry/log/postgresql.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/Volumes/Momentry/momentry/log/postgresql.error.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 階段六:安裝 Momentry Core
|
||||
|
||||
### 6.1 複製原始碼
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 06_install_momentry.sh
|
||||
|
||||
# 建立 Momentry 目錄
|
||||
mkdir -p /Volumes/Momentry/momentry/{var,etc,log,scripts,backup}
|
||||
mkdir -p /Volumes/Momentry/momentry_core
|
||||
|
||||
# 複製原始碼
|
||||
rsync -av \
|
||||
--exclude 'target' \
|
||||
--exclude '.git' \
|
||||
--exclude 'node_modules' \
|
||||
/Users/accusys/momentry_core_0.1/ \
|
||||
/Volumes/Momentry/momentry_core/
|
||||
|
||||
# 編譯 Rust 專案
|
||||
cd /Volumes/Momentry/momentry_core
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
### 6.2 初始化資料庫
|
||||
|
||||
```bash
|
||||
# 建立 PostgreSQL 資料庫
|
||||
psql -U postgres <<EOF
|
||||
CREATE DATABASE momentry;
|
||||
CREATE DATABASE n8n;
|
||||
CREATE DATABASE video_register;
|
||||
CREATE USER momentry WITH PASSWORD 'momentry_password';
|
||||
CREATE USER n8n WITH PASSWORD 'n8n_password';
|
||||
GRANT ALL PRIVILEGES ON DATABASE momentry TO momentry;
|
||||
GRANT ALL PRIVILEGES ON DATABASE n8n TO n8n;
|
||||
EOF
|
||||
|
||||
# 執行 migration
|
||||
cd /Volumes/Momentry/momentry_core
|
||||
sqlx migrate run
|
||||
```
|
||||
|
||||
### 6.3 配置環境變數
|
||||
|
||||
```bash
|
||||
# ~/.zshrc 或 ~/.bash_profile
|
||||
export DATABASE_URL="postgres://momentry:momentry_password@localhost:5432/momentry"
|
||||
export REDIS_URL="redis://:momentry_password@localhost:6379"
|
||||
export QDRANT_URL="http://localhost:6333"
|
||||
export MONGODB_URI="mongodb://localhost:27017/momentry"
|
||||
export MOMENTRY_OUTPUT_DIR="/Volumes/Momentry/momentry/var/output"
|
||||
export MOMENTRY_LOG_LEVEL="info"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 階段七:網路配置
|
||||
|
||||
### 7.1 設定固定 IP(可選)
|
||||
|
||||
```bash
|
||||
# 網路配置腳本
|
||||
#!/bin/bash
|
||||
# 07_configure_network.sh
|
||||
|
||||
# 取得網路介面
|
||||
INTERFACE=$(networksetup -listallnetworkservices | grep "Thunderbolt")
|
||||
|
||||
# 設定固定 IP
|
||||
networksetup -setmanual "$INTERFACE" \
|
||||
192.168.1.100 \
|
||||
255.255.255.0 \
|
||||
192.168.1.1
|
||||
|
||||
# 設定 DNS
|
||||
networksetup -setdnsservers "$INTERFACE" \
|
||||
8.8.8.8 \
|
||||
8.8.4.4
|
||||
```
|
||||
|
||||
### 7.2 配置防火牆
|
||||
|
||||
```bash
|
||||
# 開放服務端口
|
||||
# 使用 macOS Firewall 或 pfctl
|
||||
```
|
||||
|
||||
### 7.3 設定 SSH 金鑰
|
||||
|
||||
```bash
|
||||
# 產生 SSH 金鑰對
|
||||
ssh-keygen -t ed25519 -C "momentry@$(hostname)"
|
||||
|
||||
# 複製公鑰到目標主機
|
||||
ssh-copy-id momentry@target-host
|
||||
|
||||
# 主控機 SSH 配置
|
||||
# ~/.ssh/config
|
||||
Host momentry-target
|
||||
HostName 192.168.1.100
|
||||
User momentry
|
||||
IdentityFile ~/.ssh/id_ed25519
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 階段八:啟動服務
|
||||
|
||||
### 8.1 啟動順序
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 08_start_services.sh
|
||||
|
||||
# 1. 基礎服務
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.postgresql.plist
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.mongodb.plist
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.mariadb.plist
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.redis.plist
|
||||
|
||||
sleep 10
|
||||
|
||||
# 2. 向量資料庫
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.qdrant.plist
|
||||
|
||||
sleep 5
|
||||
|
||||
# 3. 應用服務
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.ollama.plist
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.caddy.plist
|
||||
|
||||
sleep 5
|
||||
|
||||
# 4. 其他服務
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.gitea.plist
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.php.plist
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.n8n.main.plist
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.n8n.worker.plist
|
||||
|
||||
# 5. Momentry Core
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.sftpgo.plist
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.rustdesk.hbbs.plist
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.rustdesk.hbbr.plist
|
||||
```
|
||||
|
||||
### 8.2 驗證服務
|
||||
|
||||
```bash
|
||||
# 檢查所有服務狀態
|
||||
function check_services() {
|
||||
services=(
|
||||
"postgresql"
|
||||
"mongodb"
|
||||
"mariadb"
|
||||
"redis"
|
||||
"qdrant"
|
||||
"ollama"
|
||||
"caddy"
|
||||
"gitea"
|
||||
"php"
|
||||
"n8n"
|
||||
"sftpgo"
|
||||
)
|
||||
|
||||
for service in "${services[@]}"; do
|
||||
if launchctl list | grep "$service" | grep -q "running"; then
|
||||
echo "✅ $service: Running"
|
||||
else
|
||||
echo "❌ $service: Not running"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
check_services
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 階段九:備份與還原
|
||||
|
||||
### 9.1 備份策略
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 備份腳本
|
||||
|
||||
BACKUP_DIR="/Volumes/Momentry/backup/$(date +%Y%m%d)"
|
||||
|
||||
# 1. PostgreSQL 備份
|
||||
pg_dump -U momentry momentry > "$BACKUP_DIR/momentry.sql"
|
||||
pg_dump -U n8n n8n > "$BACKUP_DIR/n8n.sql"
|
||||
|
||||
# 2. MongoDB 備份
|
||||
mongodump --out "$BACKUP_DIR/mongodb"
|
||||
|
||||
# 3. Redis 備份
|
||||
redis-cli BGSAVE
|
||||
cp /Volumes/Momentry/var/redis/dump.rdb "$BACKUP_DIR/redis.rdb"
|
||||
|
||||
# 4. Qdrant 備份
|
||||
curl -X POST http://localhost:6333/collections/accusysdb/snapshots
|
||||
|
||||
# 5. 配置檔案備份
|
||||
tar -czf "$BACKUP_DIR/config.tar.gz" \
|
||||
/Volumes/Momentry/momentry/etc/
|
||||
```
|
||||
|
||||
### 9.2 自動備份 Cron
|
||||
|
||||
```bash
|
||||
# crontab -e
|
||||
0 2 * * * /Volumes/Momentry/scripts/backup.sh
|
||||
0 3 * * 0 /Volumes/Momentry/scripts/backup_full.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 階段十:監控與維護
|
||||
|
||||
### 10.1 健康檢查腳本
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# health_check.sh
|
||||
|
||||
# 檢查所有服務
|
||||
check_postgresql() {
|
||||
pg_isready -q && echo "✅ PostgreSQL" || echo "❌ PostgreSQL"
|
||||
}
|
||||
|
||||
check_mongodb() {
|
||||
mongosh --eval "db.stats()" > /dev/null 2>&1 && echo "✅ MongoDB" || echo "❌ MongoDB"
|
||||
}
|
||||
|
||||
check_redis() {
|
||||
redis-cli ping > /dev/null 2>&1 && echo "✅ Redis" || echo "❌ Redis"
|
||||
}
|
||||
|
||||
check_qdrant() {
|
||||
curl -s http://localhost:6333/health && echo "✅ Qdrant" || echo "❌ Qdrant"
|
||||
}
|
||||
|
||||
check_n8n() {
|
||||
curl -s http://localhost:5678/api/v1/workflows > /dev/null 2>&1 && echo "✅ n8n" || echo "❌ n8n"
|
||||
}
|
||||
|
||||
check_momentry() {
|
||||
curl -s http://localhost:3002/api/v1/videos > /dev/null 2>&1 && echo "✅ Momentry" || echo "❌ Momentry"
|
||||
}
|
||||
```
|
||||
|
||||
### 10.2 日誌輪替
|
||||
|
||||
```bash
|
||||
# 新聞日誌配置
|
||||
/Volumes/Momentry/momentry/log/*.log {
|
||||
daily
|
||||
rotate 7
|
||||
compress
|
||||
missingok
|
||||
notifempty
|
||||
create 644 momentry staff
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 自動化腳本架構
|
||||
|
||||
### 主控腳本:部署控制器
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# deploy_controller.sh
|
||||
# 用於從主控機部署到目標主機
|
||||
|
||||
set -e
|
||||
|
||||
# 配置
|
||||
TARGET_HOST="momentry@192.168.1.100"
|
||||
TARGET_DISK="/dev/disk2"
|
||||
|
||||
# 顏色定義
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
function log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
function log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
function log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 階段執行
|
||||
function run_stage() {
|
||||
local stage=$1
|
||||
local script=$2
|
||||
|
||||
log_info "執行階段: $stage..."
|
||||
ssh "$TARGET_HOST" "bash /Volumes/Momentry/scripts/$script"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_info "✅ 階段完成: $stage"
|
||||
else
|
||||
log_error "❌ 階段失敗: $stage"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 主程序
|
||||
log_info "開始 Momentry 系統部署..."
|
||||
|
||||
# 執行各階段
|
||||
run_stage "磁碟準備" "01_prepare_disk.sh"
|
||||
run_stage "macOS 安裝" "02_install_macos.sh"
|
||||
run_stage "Homebrew 安裝" "03_install_homebrew.sh"
|
||||
run_stage "依賴安裝" "04_install_dependencies.sh"
|
||||
run_stage "服務安裝" "05_install_services.sh"
|
||||
run_stage "Momentry 安裝" "06_install_momentry.sh"
|
||||
run_stage "網路配置" "07_configure_network.sh"
|
||||
run_stage "啟動服務" "08_start_services.sh"
|
||||
|
||||
log_info "✅ 部署完成!"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 待確認事項
|
||||
|
||||
### 需要與使用者確認
|
||||
|
||||
1. **目標主機型號**
|
||||
- Intel Mac 或 Apple Silicon?
|
||||
- Thunderbolt 版本 (3/4)?
|
||||
|
||||
2. **網路配置**
|
||||
- DHCP 或固定 IP?
|
||||
- 目標 IP 網段?
|
||||
|
||||
3. **磁碟配置**
|
||||
- 分割方案 A (200G 系統 + 1.8T 資料)?
|
||||
- 分割方案 B (統一磁碟區)?
|
||||
|
||||
4. **服務需求**
|
||||
- 需要安裝全部服務?
|
||||
- 還是選擇性安裝?
|
||||
|
||||
5. **備份策略**
|
||||
- 本地備份?
|
||||
- 遠端備份?
|
||||
- 備份頻率?
|
||||
|
||||
6. **監控需求**
|
||||
- Prometheus + Grafana?
|
||||
- 簡單腳本監控?
|
||||
|
||||
---
|
||||
|
||||
## 預估時間
|
||||
|
||||
| 階段 | 預估時間 | 備註 |
|
||||
|------|---------|------|
|
||||
| 前置準備 | 30 分鐘 | 收集資訊、準備腳本 |
|
||||
| 磁碟準備 | 10 分鐘 | 分割格式化 |
|
||||
| macOS 安裝 | 30-60 分鐘 | 視 USB 速度 |
|
||||
| Homebrew 安裝 | 15 分鐘 | 下載速度 |
|
||||
| 服務安裝 | 60-90 分鐘 | 多個服務 |
|
||||
| Momentry 安裝 | 20 分鐘 | 編譯 Rust |
|
||||
| 網路配置 | 10 分鐘 | 固定 IP |
|
||||
| 服務啟動 | 15 分鐘 | 依序啟動 |
|
||||
| 驗證測試 | 30 分鐘 | 完整測試 |
|
||||
| **總計** | **3-4 小時** | 自動化後可縮短 |
|
||||
|
||||
---
|
||||
|
||||
## 風險與應對
|
||||
|
||||
| 風險 | 機率 | 影響 | 應對措施 |
|
||||
|------|------|------|---------|
|
||||
| Thunderbolt 不相容 | 低 | 高 | 準備多種驅動 |
|
||||
| macOS 安裝失敗 | 低 | 高 | 準備還原方案 |
|
||||
| 服務啟動失敗 | 中 | 中 | 日誌診斷腳本 |
|
||||
| 網路連線問題 | 中 | 中 | 有線網路備援 |
|
||||
| 儲存空間不足 | 低 | 高 | 磁碟空間檢查 |
|
||||
|
||||
---
|
||||
|
||||
## 下一步行動
|
||||
|
||||
1. ✅ 確認目標主機規格
|
||||
2. ✅ 確認 Thunderbolt NVMe 容量
|
||||
3. ✅ 確認網路配置
|
||||
4. ✅ 選擇服務清單
|
||||
5. ✅ 準備安裝腳本
|
||||
6. ✅ 測試腳本執行
|
||||
7. ✅ 正式部署
|
||||
|
||||
---
|
||||
|
||||
## 附錄
|
||||
|
||||
### A. 服務端口對照表
|
||||
|
||||
| 服務 | Port | 協議 |
|
||||
|------|------|------|
|
||||
| PostgreSQL | 5432 | TCP |
|
||||
| MongoDB | 27017 | TCP |
|
||||
| MariaDB | 3306 | TCP |
|
||||
| Redis | 6379 | TCP |
|
||||
| Qdrant API | 6333 | HTTP |
|
||||
| Qdrant gRPC | 6334 | gRPC |
|
||||
| Ollama | 11434 | HTTP |
|
||||
| Caddy HTTP | 80 | HTTP |
|
||||
| Caddy HTTPS | 443 | HTTPS |
|
||||
| Gitea | 3000 | HTTP |
|
||||
| PHP-FPM | 9000 | FastCGI |
|
||||
| n8n | 5678 | HTTP |
|
||||
| SFTPGo | 2022 | SFTP |
|
||||
| RustDesk hbbs | 21115 | TCP |
|
||||
| RustDesk hbbr | 21117 | TCP |
|
||||
| Momentry | 3002 | HTTP |
|
||||
| Prometheus | 9090 | HTTP |
|
||||
|
||||
### B. 環境變數清單
|
||||
|
||||
見 `.env` 範例檔案或 `docs/MOMENTRY_CORE_MONITORING.md`
|
||||
|
||||
### C. 疑難排解
|
||||
|
||||
見 `docs/PENDING_ISSUES.md`
|
||||
|
||||
---
|
||||
|
||||
**計劃狀態**: 📝 草稿 - 等待使用者確認後執行
|
||||
|
||||
**負責人**: OpenCode AI Assistant
|
||||
|
||||
**最後更新**: 2026-03-23
|
||||
@@ -1,169 +0,0 @@
|
||||
# Momentry Video RAG MCP Workflow
|
||||
|
||||
## 工作流程資訊
|
||||
|
||||
- **名稱**: Momentry Video RAG MCP
|
||||
- **ID**: WlVvpX2OeKK83QOK
|
||||
- **Webhook Path**: `video-rag-mcp`
|
||||
- **狀態**: ✅ Active (已啟動)
|
||||
- **建立時間**: 2026-03-22
|
||||
|
||||
## 工作流程架構
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌──────────────────────┐ ┌───────────────────┐ ┌─────────────────┐
|
||||
│ Webhook │────▶│ Search Momentry │────▶│ Process RAG │────▶│ Respond to │
|
||||
│ Trigger │ │ Core │ │ Results │ │ Webhook │
|
||||
└─────────────────┘ └──────────────────────┘ └───────────────────┘ └─────────────────┘
|
||||
│
|
||||
│ POST http://localhost:5678/webhook/video-rag-mcp
|
||||
│
|
||||
▼
|
||||
{
|
||||
"query": "搜尋關鍵字",
|
||||
"limit": 5,
|
||||
"uuid": "可選的影片UUID"
|
||||
}
|
||||
```
|
||||
|
||||
## Node 說明
|
||||
|
||||
### 1. Webhook Trigger
|
||||
- **類型**: Webhook
|
||||
- **Method**: POST
|
||||
- **Path**: `video-rag-mcp`
|
||||
- **Response Mode**: Last Node (等待最後一個節點完成後回應)
|
||||
|
||||
### 2. Search Momentry Core
|
||||
- **類型**: HTTP Request
|
||||
- **URL**: `http://localhost:3002/api/v1/n8n/search`
|
||||
- **Method**: POST
|
||||
- **Body**:
|
||||
```json
|
||||
{
|
||||
"query": "搜尋關鍵字",
|
||||
"limit": 5,
|
||||
"uuid": "可選的影片UUID"
|
||||
}
|
||||
```
|
||||
- **Timeout**: 30秒
|
||||
|
||||
### 3. Process RAG Results
|
||||
- **類型**: Code (JavaScript)
|
||||
- **功能**:
|
||||
- 處理 Momentry Core 搜尋結果
|
||||
- 格式化 hits 為結構化資料
|
||||
- 建立 RAG context(用於 LLM 問答)
|
||||
- 計算相關度百分比
|
||||
|
||||
**輸出格式**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"query": "搜尋關鍵字",
|
||||
"totalFound": 5,
|
||||
"context": "[1] 文本內容... (Video: 影片標題, Time: 10s-20s)\n\n[2] ...",
|
||||
"results": [
|
||||
{
|
||||
"index": 1,
|
||||
"id": "chunk_id",
|
||||
"title": "影片標題",
|
||||
"text": "文本內容",
|
||||
"startTime": 10,
|
||||
"endTime": 20,
|
||||
"relevance": "85%",
|
||||
"videoUuid": "uuid",
|
||||
"mediaUrl": "影片URL",
|
||||
"deepLink": "影片URL#t=10,20"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Respond to Webhook
|
||||
- **類型**: Respond to Webhook
|
||||
- **Response**: JSON 格式結果
|
||||
- **Status Code**: 200
|
||||
|
||||
## 使用方式
|
||||
|
||||
### 直接呼叫 Webhook
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:5678/webhook/video-rag-mcp \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"query": "charade",
|
||||
"limit": 5
|
||||
}'
|
||||
```
|
||||
|
||||
### 指定特定影片搜尋
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:5678/webhook/video-rag-mcp \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"query": "audrey hepburn",
|
||||
"limit": 3,
|
||||
"uuid": "a1b10138a6bbb0cd"
|
||||
}'
|
||||
```
|
||||
|
||||
### 在 n8n 工作流程中使用
|
||||
|
||||
可以將此 Webhook 作為子工作流程觸發器,或使用 HTTP Request Node 呼叫:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Call Video RAG",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"url": "http://localhost:5678/webhook/video-rag-mcp",
|
||||
"method": "POST",
|
||||
"body": {
|
||||
"query": "={{ $json.searchTerm }}",
|
||||
"limit": 5
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## RAG Context 用途
|
||||
|
||||
工作流程產生的 `context` 欄位可直接用於 LLM 提示:
|
||||
|
||||
```javascript
|
||||
// Example: 使用 context 進行問答
|
||||
const prompt = `
|
||||
基於以下影片片段資訊回答問題:
|
||||
|
||||
${context}
|
||||
|
||||
問題:${userQuestion}
|
||||
|
||||
請根據上述內容提供準確的答案。
|
||||
`;
|
||||
```
|
||||
|
||||
## 相關文件
|
||||
|
||||
- [Momentry Core API 文件](./API_ACCESS.md)
|
||||
- [n8n MCP 測試報告](./N8N_MCP_TEST_REPORT.md)
|
||||
- [N8N_DEMO_WORKFLOW.md](./N8N_DEMO_WORKFLOW.md) - 完整工作流程設計
|
||||
|
||||
## MCP 建立指令
|
||||
|
||||
此工作流程是透過 MCP 工具建立的:
|
||||
|
||||
```bash
|
||||
# 使用 MCP 建立工作流程
|
||||
node create_workflow.js | mcp-n8n
|
||||
|
||||
# 使用 MCP 啟動工作流程
|
||||
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"n8n_activate_workflow","arguments":{"workflowId":"WlVvpX2OeKK83QOK"}}}' | mcp-n8n
|
||||
```
|
||||
|
||||
## 工作流程檔案
|
||||
|
||||
- 原始檔案: `docs/n8n_workflow_video_rag_mcp.json`
|
||||
@@ -1,292 +0,0 @@
|
||||
# Video Processing Pipeline - 處理流程
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-22 |
|
||||
| 文件版本 | V1.1 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-22 | 創建文件 | Warren | OpenCode |
|
||||
| V1.1 | 2026-03-26 | 更新流程圖文字 (media_url→file_path) | OpenCode | deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
## 處理流程架構
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Video Processing Pipeline │
|
||||
├─────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Stage 1: JSON 生成 (Process) │ │
|
||||
│ │ │ │
|
||||
│ │ video.mp4 ──→ [ASR] ──→ asr.json (語音辨識) │ │
|
||||
│ │ ──→ [CUT] ──→ cut.json (場景偵測) │ │
|
||||
│ │ ──→ [ASRX] ──→ asrx.json (說話者分離) │ │
|
||||
│ │ ──→ [YOLO] ──→ yolo.json (物體偵測) │ │
|
||||
│ │ ──→ [OCR] ──→ ocr.json (文字辨識) │ │
|
||||
│ │ ──→ [Face] ──→ face.json (人臉偵測) │ │
|
||||
│ │ ──→ [Pose] ──→ pose.json (姿態估計) │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ ↓ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Stage 2: 入庫 (Import) │ │
|
||||
│ │ │ │
|
||||
│ │ .json files ──→ PostgreSQL (fs_json = true) │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ pre_chunks 表 (from ASR, CUT) │ │
|
||||
│ │ frames 表 (from YOLO, OCR, Face, Pose) │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ ↓ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Stage 3: Chunk 生成 (Chunk) │ │
|
||||
│ │ │ │
|
||||
│ │ pre_chunks ──→ [Chunk Rule] ──→ chunks 表 │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ 清洗 → 純文字 │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ ↓ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Stage 4: 向量化 (Vectorize) │ │
|
||||
│ │ │ │
|
||||
│ │ chunks ──→ [Embedding Model] ──→ vectors │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ Qdrant (主要向量庫) │ │
|
||||
│ │ PGVector (備份向量庫) │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ ↓ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Stage 5: 搜尋 (Search) │ │
|
||||
│ │ │ │
|
||||
│ │ Natural Language Query ──→ [Embedding] ──→ [Qdrant Search] │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ 返回結果含 file_path │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CLI 命令
|
||||
|
||||
### Stage 1: JSON 生成 (Process)
|
||||
|
||||
```bash
|
||||
# 基本用法
|
||||
cargo run --bin momentry -- process <uuid_or_path>
|
||||
|
||||
# 只處理特定模組
|
||||
cargo run --bin momentry -- process <uuid> --modules asr,cut
|
||||
|
||||
# 強制重新處理(忽略完整性檢查)
|
||||
cargo run --bin momentry -- process <uuid> --force
|
||||
|
||||
# 從中斷點續傳
|
||||
cargo run --bin momentry -- process <uuid> --resume
|
||||
|
||||
# 模組使用雲端處理
|
||||
cargo run --bin momentry -- process <uuid> --modules yolo,face --cloud yolo
|
||||
|
||||
# 完整範例
|
||||
cargo run --bin momentry -- process /path/to/video.mp4 \
|
||||
--modules asr,cut,yolo,ocr \
|
||||
--cloud yolo
|
||||
```
|
||||
|
||||
### Stage 2: 入庫 (Import)
|
||||
|
||||
```bash
|
||||
# 目前入庫在 process 完成後自動執行
|
||||
# 計劃新增獨立的 import 命令
|
||||
# cargo run --bin momentry -- import <uuid>
|
||||
```
|
||||
|
||||
### Stage 3: Chunk 生成
|
||||
|
||||
```bash
|
||||
# 生成 chunks
|
||||
cargo run --bin momentry -- chunk <uuid>
|
||||
```
|
||||
|
||||
### Stage 4: 向量化
|
||||
|
||||
```bash
|
||||
# 向量化 chunks(使用預設模型 nomic-embed-text-v2-moe:latest)
|
||||
cargo run --bin momentry -- vectorize <uuid>
|
||||
|
||||
# 明確指定模型
|
||||
cargo run --bin momentry -- vectorize <uuid> --model nomic-embed-text-v2-moe:latest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 處理模式選項
|
||||
|
||||
### --force (強制重新處理)
|
||||
|
||||
- 刪除現有的 JSON 檔案
|
||||
- 從頭開始處理
|
||||
- 適用於:處理失敗、模型更新、需要重新處理
|
||||
|
||||
```bash
|
||||
# 強制重新處理 YOLO
|
||||
cargo run --bin momentry -- process <uuid> --modules yolo --force
|
||||
```
|
||||
|
||||
### --resume (續傳)
|
||||
|
||||
- 檢查現有 JSON 的進度
|
||||
- 從中斷點繼續處理
|
||||
- 適用於:處理中斷、系統崩潰後恢復
|
||||
|
||||
```bash
|
||||
# 從上次中斷點繼續
|
||||
cargo run --bin momentry -- process <uuid> --resume
|
||||
```
|
||||
|
||||
### 預設行為 (Smart Mode)
|
||||
|
||||
- 如果 JSON 完全:跳過
|
||||
- 如果 JSON 不完整:警告 + 跳過(需要 --resume 或 --force)
|
||||
- 如果 JSON 不存在:處理
|
||||
|
||||
```
|
||||
Output:
|
||||
ASR: ✓ Already complete, skipping
|
||||
|
||||
⚠️ Found incomplete JSON file: /path/to/yolo.json
|
||||
Progress: 73800/412343 (17.9%)
|
||||
Use --resume to continue from checkpoint
|
||||
Use --force to reprocess from scratch
|
||||
YOLO: ✓ Already complete, skipping
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 可用模組
|
||||
|
||||
| 模組 | 功能 | 輸出 | 用途 |
|
||||
|------|------|------|------|
|
||||
| asr | 自動語音辨識 | asr.json | 語音轉文字 |
|
||||
| cut | 場景偵測 | cut.json | 影片分段 |
|
||||
| asrx | 說話者分離 | asrx.json | 多人對話分析 |
|
||||
| yolo | 物體偵測 | yolo.json | 物體辨識 |
|
||||
| ocr | 文字辨識 | ocr.json | 畫面文字 |
|
||||
| face | 人臉偵測 | face.json | 人臉辨識 |
|
||||
| pose | 姿態估計 | pose.json | 人體姿態 |
|
||||
|
||||
---
|
||||
|
||||
## 向量化模型選擇
|
||||
|
||||
### 統一嵌入模型
|
||||
Momentry Core 統一使用 **`nomic-embed-text-v2-moe:latest`** 作為所有規則的嵌入模型:
|
||||
|
||||
```bash
|
||||
# 統一模型(所有 Rule 1/2/3 使用)
|
||||
--model nomic-embed-text-v2-moe:latest
|
||||
```
|
||||
|
||||
### 模型特性
|
||||
| 特性 | 說明 |
|
||||
|------|------|
|
||||
| **模型名稱** | `nomic-embed-text-v2-moe:latest` |
|
||||
| **向量維度** | 768 維 |
|
||||
| **多語言支持** | ✅ 完整支持(英語、中文、日語、韓語等) |
|
||||
| **模型架構** | Mixture of Experts (MoE) |
|
||||
| **推理速度** | 快速,適合實時應用 |
|
||||
|
||||
### 使用方式
|
||||
```bash
|
||||
# 向量化命令
|
||||
cargo run --bin momentry -- vectorize <uuid> --model nomic-embed-text-v2-moe:latest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 資料庫儲存
|
||||
|
||||
### PostgreSQL (主要關聯式資料庫)
|
||||
|
||||
- 影片資訊
|
||||
- Chunks 資料
|
||||
- Pre-chunks 資料
|
||||
- Frames 資料
|
||||
- 使用者資料
|
||||
|
||||
### Qdrant (主要向量資料庫)
|
||||
|
||||
- Chunk 向量
|
||||
- 相似度搜尋
|
||||
|
||||
### PGVector (備份向量資料庫)
|
||||
|
||||
- Chunk 向量副本
|
||||
- 備援機制
|
||||
|
||||
---
|
||||
|
||||
## Pipeline 狀態追蹤
|
||||
|
||||
### PostgreSQL 狀態欄位
|
||||
|
||||
```sql
|
||||
-- 影片處理狀態
|
||||
videos.status: 'pending' | 'processing' | 'completed' | 'failed'
|
||||
|
||||
-- 檔案處理狀態
|
||||
videos.fs_json: true/false
|
||||
videos.fs_chunks: true/false
|
||||
videos.fs_vectors: true/false
|
||||
|
||||
-- pre_chunks 狀態
|
||||
pre_chunks.imported: true/false
|
||||
|
||||
-- frames 狀態
|
||||
frames.imported: true/false
|
||||
|
||||
-- chunks 狀態
|
||||
chunks.cleaned: true/false
|
||||
chunks.vectorized: true/false
|
||||
```
|
||||
|
||||
### 進度查詢 API
|
||||
|
||||
```bash
|
||||
# 查詢處理進度
|
||||
curl http://localhost:3002/api/v1/progress/{uuid}
|
||||
|
||||
# 回應範例
|
||||
{
|
||||
"uuid": "a1b10138a6bbb0cd",
|
||||
"file_name": "video.mp4",
|
||||
"overall_progress": 65,
|
||||
"cpu_percent": 45.2,
|
||||
"gpu_percent": 98.5,
|
||||
"memory_mb": 8500,
|
||||
"processors": [
|
||||
{"name": "asr", "status": "complete", "progress": 100},
|
||||
{"name": "cut", "status": "complete", "progress": 100},
|
||||
{"name": "yolo", "status": "progress", "progress": 45},
|
||||
{"name": "ocr", "status": "pending", "progress": 0}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 下一步
|
||||
|
||||
1. **API 端點** - 支援 --modules 和 --cloud 參數
|
||||
2. **獨立 Import 命令** - 分離入庫流程
|
||||
3. **獨立 Chunk 命令** - 分離 chunk 生成
|
||||
4. **獨立 Vectorize 命令** - 分離向量化流程
|
||||
5. **模型管理** - 新增、選擇、預覽模型
|
||||
@@ -1,464 +0,0 @@
|
||||
# Momentry Core API Documentation v1.0.0
|
||||
|
||||
## 快速資訊
|
||||
- **Base URL**: `http://<host>:3003` (開發環境)
|
||||
- **Production**: `http://<host>:3002`
|
||||
- **認證方式**: Header `X-API-Key: <your_api_key>`
|
||||
- **測試 Key**: `muser_test_001`
|
||||
|
||||
---
|
||||
|
||||
## API 分類原則
|
||||
|
||||
| 分類 | 用途 | 代表端點 |
|
||||
|------|------|----------|
|
||||
| **健康檢查** | 系統狀態確認 | `/health` |
|
||||
| **檔案管理 (Files)** | 列出、查詢檔案 | `/api/v1/files` |
|
||||
| **人物管理 (People)** | Identity 搜尋、候選 | `/api/v1/people` |
|
||||
| **Identity 管理** | 人物詳細資訊 | `/api/v1/identities/:uuid` |
|
||||
| **搜尋 (Search)** | 文字、BM25、混合搜尋 | `/api/v1/search/*` |
|
||||
| **人臉 (Face)** | 人臉辨識、列表 | `/api/v1/face/*` |
|
||||
| **任務 (Jobs)** | 處理任務狀態 | `/api/v1/jobs` |
|
||||
| **統計 (Stats)** | 系統統計 | `/api/v1/stats/ingest` |
|
||||
|
||||
### 設計概念
|
||||
1. **Files 是核心資源**:所有影片/圖片都是 File,用 32 碼 `file_uuid` 識別
|
||||
2. **Identity 是跨檔案的人物**:一個 Identity 可出現在多個 Files
|
||||
3. **People = Identity 的別名路由**:`/api/v1/people` 和 `/api/v1/identities` 指向相同資料
|
||||
4. **所有列表 API 支援分頁**:`page`, `page_size` 參數
|
||||
5. **所有操作 API 回傳統一格式**:`{ success, data, total, page, page_size }`
|
||||
|
||||
---
|
||||
|
||||
## 1. 健康檢查
|
||||
|
||||
### `GET /health`
|
||||
檢查系統是否運作。
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"version": "1.0.0 (build: 2026-04-30 15:40:04)",
|
||||
"uptime_ms": 348240
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 檔案管理 (Files)
|
||||
|
||||
> **測試日期**: 2026-04-30 | **環境**: Playground (Port 3003) | **Schema**: dev
|
||||
|
||||
### `GET /api/v1/files` — 列出所有檔案
|
||||
|
||||
**參數**: `page` (預設 1), `page_size` (預設 20)
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/files?page=1&page_size=2" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**實際回應**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"total": 21,
|
||||
"page": 1,
|
||||
"page_size": 2,
|
||||
"data": [
|
||||
{
|
||||
"file_uuid": "384b0ff44aaaa1f14cb2cd63b3fea966",
|
||||
"file_name": "Old_Time_Movie_Show_-_Charade_1963.HD.mov",
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/Old_Time_Movie_Show_-_Charade_1963.HD.mov",
|
||||
"status": "ready"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/files/:uuid` — 取得檔案詳情
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/files/384b0ff44aaaa1f14cb2cd63b3fea966" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**實際回應**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"file_uuid": "384b0ff44aaaa1f14cb2cd63b3fea966",
|
||||
"file_name": "Old_Time_Movie_Show_-_Charade_1963.HD.mov",
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/Old_Time_Movie_Show_-_Charade_1963.HD.mov",
|
||||
"metadata": {
|
||||
"format": {
|
||||
"duration": "6879.329524",
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
},
|
||||
"streams": [
|
||||
{
|
||||
"codec_name": "h264",
|
||||
"codec_type": "video",
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"r_frame_rate": "60000/1001"
|
||||
},
|
||||
{
|
||||
"codec_name": "aac",
|
||||
"codec_type": "audio",
|
||||
"sample_rate": "44100",
|
||||
"channels": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/files/scan` — 掃描檔案系統
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/files/scan" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**實際回應**:
|
||||
```json
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"file_name": "Old_Time_Movie_Show_-_Charade_1963.HD.mov",
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/Old_Time_Movie_Show_-_Charade_1963.HD.mov",
|
||||
"file_size": 2361629896,
|
||||
"file_uuid": "384b0ff44aaaa1f14cb2cd63b3fea966",
|
||||
"status": "pending",
|
||||
"is_registered": true,
|
||||
"registration_time": "2026-04-28 18:25:14.010351+00"
|
||||
}
|
||||
],
|
||||
"total": 20,
|
||||
"registered_count": 20,
|
||||
"unregistered_count": 0
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/files/register` — 註冊新檔案
|
||||
|
||||
> **⚠️ 已知問題**: 此 endpoint 在 dev 環境有 SQLx 型別綁定問題 (probe_json text vs jsonb)。不影響 marcom 團隊的 GUI 設計。
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"file_path": "/path/to/video.mp4"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 人物管理 (People)
|
||||
|
||||
### `GET /api/v1/people` — 列出所有人物
|
||||
|
||||
**參數**: `page`, `page_size`
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/people?page=1&page_size=3" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"total": 100,
|
||||
"page": 1,
|
||||
"page_size": 3,
|
||||
"data": [
|
||||
{
|
||||
"identity_id": "a9a90105-6d6b-46ff-92da-0c3c1a57dff4",
|
||||
"name": "Trace 2 Fixed Format",
|
||||
"metadata": {},
|
||||
"created_at": "2026-04-28T06:10:14.582062Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/people/search` — 搜尋人物
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"query": "Trace",
|
||||
"limit": 3
|
||||
}
|
||||
```
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3003/api/v1/people/search" \
|
||||
-H "X-API-Key: muser_test_001" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query":"Trace","limit":3}'
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"total": 3,
|
||||
"page": 1,
|
||||
"page_size": 20,
|
||||
"data": [
|
||||
{
|
||||
"identity_id": "a9a90105-6d6b-46ff-92da-0c3c1a57dff4",
|
||||
"name": "Trace 2 Fixed Format",
|
||||
"metadata": {},
|
||||
"created_at": "2026-04-28T06:10:14.582062Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/people/candidates` — 列出未命名人物候選
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/people/candidates" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Identity 詳細資訊
|
||||
|
||||
### `GET /api/v1/identities/:uuid` — 取得人物詳情
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/identities/a9a90105-6d6b-46ff-92da-0c3c1a57dff4" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"uuid": "a9a90105-6d6b-46ff-92da-0c3c1a57dff4",
|
||||
"name": "Trace 2 Fixed Format",
|
||||
"identity_type": "people",
|
||||
"source": "auto_trace",
|
||||
"status": "active",
|
||||
"metadata": {},
|
||||
"reference_data": {
|
||||
"trace_id": 2,
|
||||
"quality_avg": 0.8748,
|
||||
"trace_stats": {
|
||||
"start_frame": 155,
|
||||
"end_frame": 297,
|
||||
"avg_confidence": 0.8624,
|
||||
"duration_seconds": 6.5,
|
||||
"total_appearances": 143
|
||||
},
|
||||
"angles_covered": ["profile_right", "three_quarter"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/identities/:uuid/files` — 取得人物出現的檔案列表
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/identities/a9a90105-6d6b-46ff-92da-0c3c1a57dff4/files" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 搜尋 (Search)
|
||||
|
||||
### `POST /api/v1/search` — 向量搜尋
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"query": "person talking",
|
||||
"limit": 5
|
||||
}
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"results": [],
|
||||
"query": "person talking"
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/search/bm25` — BM25 全文搜尋
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"query": "test",
|
||||
"limit": 3
|
||||
}
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uuid": "a1b10138a6bbb0cd",
|
||||
"chunk_id": "sentence_1006",
|
||||
"text": "...",
|
||||
"bm25_score": 5.23
|
||||
}
|
||||
],
|
||||
"query": "test"
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/search/hybrid` — 混合搜尋 (向量 + BM25)
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"query": "test",
|
||||
"limit": 3
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 人臉 (Face)
|
||||
|
||||
### `GET /api/v1/face/list` — 列出人臉
|
||||
|
||||
**必填參數**: `file_uuid`
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/face/list?file_uuid=384b0ff44aaaa1f14cb2cd63b3fea966&page=1&page_size=3" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Found 2 faces",
|
||||
"faces": [
|
||||
{
|
||||
"face_id": "identity_8d7c22e3a6794a8c93b4a3655f106944",
|
||||
"name": "Cary Grant",
|
||||
"created_at": "2026-04-18T10:44:48.571407+00:00",
|
||||
"is_active": true
|
||||
},
|
||||
{
|
||||
"face_id": "identity_9c5d1e8965eb49ae83d9b88db12fbb18",
|
||||
"name": "Audrey Hepburn",
|
||||
"created_at": "2026-04-18T10:44:31.536039+00:00",
|
||||
"is_active": true
|
||||
}
|
||||
],
|
||||
"count": 2,
|
||||
"page": 1,
|
||||
"page_size": 3
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 任務 (Jobs)
|
||||
|
||||
### `GET /api/v1/jobs` — 列出處理任務
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/jobs?page=1&page_size=2" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"jobs": [
|
||||
{
|
||||
"id": 32,
|
||||
"uuid": "942d0bdf5d6fb6ac18b47deb031e60c3",
|
||||
"status": "running",
|
||||
"current_processor": null,
|
||||
"progress_current": 0,
|
||||
"progress_total": 0,
|
||||
"created_at": "2026-04-28 15:55:47.911654",
|
||||
"started_at": null
|
||||
}
|
||||
],
|
||||
"count": 19,
|
||||
"page": 1,
|
||||
"page_size": 2
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/rules/:rule/status` — 取得規則狀態
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/rules/default/status" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"rule": "default",
|
||||
"supported_processor_ids": [],
|
||||
"active_jobs": []
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 統計 (Stats)
|
||||
|
||||
### `GET /api/v1/stats/ingest` — 取得匯入統計
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/stats/ingest" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"total_videos": 21,
|
||||
"total_chunks": 0,
|
||||
"sentence_chunks": 0,
|
||||
"cut_chunks": 0,
|
||||
"time_chunks": 0,
|
||||
"searchable_chunks": 0,
|
||||
"chunks_with_visual": 0,
|
||||
"chunks_with_summary": 0,
|
||||
"pending_videos": 12
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 錯誤回應
|
||||
|
||||
| HTTP Code | 說明 |
|
||||
|-----------|------|
|
||||
| `400` | 請求參數錯誤 (例如缺少必填欄位) |
|
||||
| `401` | API Key 無效或缺失 |
|
||||
| `404` | 資源不存在 |
|
||||
| `422` | 請求格式錯誤 (JSON 解析失敗) |
|
||||
| `500` | 伺服器內部錯誤 |
|
||||
@@ -1,904 +0,0 @@
|
||||
# Momentry Core API Documentation v1.0.0
|
||||
|
||||
## 快速資訊
|
||||
- **Base URL**: `http://<host>:3003` (開發環境)
|
||||
- **Production**: `http://<host>:3002`
|
||||
- **認證方式**: Header `X-API-Key: <your_api_key>`
|
||||
- **測試 Key**: `muser_test_001`
|
||||
|
||||
---
|
||||
|
||||
## API 分類原則
|
||||
|
||||
| 分類 | 用途 | 代表端點 |
|
||||
|------|------|----------|
|
||||
| **健康檢查** | 系統狀態確認 | `/health` |
|
||||
| **檔案管理 (Files)** | 列出、查詢檔案 | `/api/v1/files` |
|
||||
| **人物管理 (People)** | Identity 搜尋、候選 | `/api/v1/people` |
|
||||
| **Identity 管理** | 人物詳細資訊 | `/api/v1/identities/:uuid` |
|
||||
| **搜尋 (Search)** | 文字、BM25、混合搜尋 | `/api/v1/search/*` |
|
||||
| **人臉 (Face)** | 人臉辨識、列表 | `/api/v1/face/*` |
|
||||
| **任務 (Jobs)** | 處理任務狀態 | `/api/v1/jobs` |
|
||||
| **統計 (Stats)** | 系統統計 | `/api/v1/stats/ingest` |
|
||||
|
||||
### 設計概念
|
||||
1. **Files 是核心資源**:所有影片/圖片都是 File,用 32 碼 `file_uuid` 識別
|
||||
2. **Identity 是跨檔案的人物**:一個 Identity 可出現在多個 Files
|
||||
3. **People = Identity 的別名路由**:`/api/v1/people` 和 `/api/v1/identities` 指向相同資料
|
||||
4. **所有列表 API 支援分頁**:`page`, `page_size` 參數
|
||||
5. **所有操作 API 回傳統一格式**:`{ success, data, total, page, page_size }`
|
||||
|
||||
---
|
||||
|
||||
## 1. 健康檢查
|
||||
|
||||
### `GET /health`
|
||||
檢查系統是否運作。
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"version": "1.0.0 (build: 2026-04-30 15:40:04)",
|
||||
"uptime_ms": 348240
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 檔案管理 (Files)
|
||||
|
||||
> **測試日期**: 2026-04-30 | **環境**: Playground (Port 3003) | **Schema**: dev
|
||||
|
||||
### `GET /api/v1/files` — 列出所有檔案
|
||||
|
||||
**參數**: `page` (預設 1), `page_size` (預設 20)
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/files?page=1&page_size=2" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**實際回應**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"total": 21,
|
||||
"page": 1,
|
||||
"page_size": 2,
|
||||
"data": [
|
||||
{
|
||||
"file_uuid": "384b0ff44aaaa1f14cb2cd63b3fea966",
|
||||
"file_name": "Old_Time_Movie_Show_-_Charade_1963.HD.mov",
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/Old_Time_Movie_Show_-_Charade_1963.HD.mov",
|
||||
"status": "ready"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/files/:uuid` — 取得檔案詳情
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/files/384b0ff44aaaa1f14cb2cd63b3fea966" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**實際回應**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"file_uuid": "384b0ff44aaaa1f14cb2cd63b3fea966",
|
||||
"file_name": "Old_Time_Movie_Show_-_Charade_1963.HD.mov",
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/Old_Time_Movie_Show_-_Charade_1963.HD.mov",
|
||||
"metadata": {
|
||||
"format": {
|
||||
"duration": "6879.329524",
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
},
|
||||
"streams": [
|
||||
{
|
||||
"codec_name": "h264",
|
||||
"codec_type": "video",
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"r_frame_rate": "60000/1001"
|
||||
},
|
||||
{
|
||||
"codec_name": "aac",
|
||||
"codec_type": "audio",
|
||||
"sample_rate": "44100",
|
||||
"channels": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/files/scan` — 掃描檔案系統
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/files/scan" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**實際回應**:
|
||||
```json
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"file_name": "Old_Time_Movie_Show_-_Charade_1963.HD.mov",
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/Old_Time_Movie_Show_-_Charade_1963.HD.mov",
|
||||
"file_size": 2361629896,
|
||||
"file_uuid": "384b0ff44aaaa1f14cb2cd63b3fea966",
|
||||
"status": "pending",
|
||||
"is_registered": true,
|
||||
"registration_time": "2026-04-28 18:25:14.010351+00"
|
||||
}
|
||||
],
|
||||
"total": 20,
|
||||
"registered_count": 20,
|
||||
"unregistered_count": 0
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/files/register` — 註冊新檔案
|
||||
|
||||
> **⚠️ 已知問題**: 此 endpoint 在 dev 環境有 SQLx 型別綁定問題 (probe_json text vs jsonb)。不影響 marcom 團隊的 GUI 設計。
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"file_path": "/path/to/video.mp4"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 人物管理 (People)
|
||||
|
||||
### `GET /api/v1/people` — 列出所有人物
|
||||
|
||||
**參數**: `page`, `page_size`
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/people?page=1&page_size=3" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"total": 100,
|
||||
"page": 1,
|
||||
"page_size": 3,
|
||||
"data": [
|
||||
{
|
||||
"identity_id": "a9a90105-6d6b-46ff-92da-0c3c1a57dff4",
|
||||
"name": "Trace 2 Fixed Format",
|
||||
"metadata": {},
|
||||
"created_at": "2026-04-28T06:10:14.582062Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/people/search` — 搜尋人物
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"query": "Trace",
|
||||
"limit": 3
|
||||
}
|
||||
```
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3003/api/v1/people/search" \
|
||||
-H "X-API-Key: muser_test_001" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query":"Trace","limit":3}'
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"total": 3,
|
||||
"page": 1,
|
||||
"page_size": 20,
|
||||
"data": [
|
||||
{
|
||||
"identity_id": "a9a90105-6d6b-46ff-92da-0c3c1a57dff4",
|
||||
"name": "Trace 2 Fixed Format",
|
||||
"metadata": {},
|
||||
"created_at": "2026-04-28T06:10:14.582062Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/people/candidates` — 列出未命名人物候選
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/people/candidates" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Identity 詳細資訊
|
||||
|
||||
### `GET /api/v1/identities/:uuid` — 取得人物詳情
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/identities/a9a90105-6d6b-46ff-92da-0c3c1a57dff4" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"uuid": "a9a90105-6d6b-46ff-92da-0c3c1a57dff4",
|
||||
"name": "Trace 2 Fixed Format",
|
||||
"identity_type": "people",
|
||||
"source": "auto_trace",
|
||||
"status": "active",
|
||||
"metadata": {},
|
||||
"reference_data": {
|
||||
"trace_id": 2,
|
||||
"quality_avg": 0.8748,
|
||||
"trace_stats": {
|
||||
"start_frame": 155,
|
||||
"end_frame": 297,
|
||||
"avg_confidence": 0.8624,
|
||||
"duration_seconds": 6.5,
|
||||
"total_appearances": 143
|
||||
},
|
||||
"angles_covered": ["profile_right", "three_quarter"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/identities/:uuid/files` — 取得人物出現的檔案列表
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/identities/a9a90105-6d6b-46ff-92da-0c3c1a57dff4/files" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 搜尋 (Search)
|
||||
|
||||
### `POST /api/v1/search` — 向量搜尋
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"query": "person talking",
|
||||
"limit": 5
|
||||
}
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"results": [],
|
||||
"query": "person talking"
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/search/bm25` — BM25 全文搜尋
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"query": "test",
|
||||
"limit": 3
|
||||
}
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uuid": "a1b10138a6bbb0cd",
|
||||
"chunk_id": "sentence_1006",
|
||||
"text": "...",
|
||||
"bm25_score": 5.23
|
||||
}
|
||||
],
|
||||
"query": "test"
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/search/hybrid` — 混合搜尋 (向量 + BM25)
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"query": "test",
|
||||
"limit": 3
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 人臉 (Face)
|
||||
|
||||
### `GET /api/v1/face/list` — 列出人臉
|
||||
|
||||
**必填參數**: `file_uuid`
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/face/list?file_uuid=384b0ff44aaaa1f14cb2cd63b3fea966&page=1&page_size=3" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Found 2 faces",
|
||||
"faces": [
|
||||
{
|
||||
"face_id": "identity_8d7c22e3a6794a8c93b4a3655f106944",
|
||||
"name": "Cary Grant",
|
||||
"created_at": "2026-04-18T10:44:48.571407+00:00",
|
||||
"is_active": true
|
||||
},
|
||||
{
|
||||
"face_id": "identity_9c5d1e8965eb49ae83d9b88db12fbb18",
|
||||
"name": "Audrey Hepburn",
|
||||
"created_at": "2026-04-18T10:44:31.536039+00:00",
|
||||
"is_active": true
|
||||
}
|
||||
],
|
||||
"count": 2,
|
||||
"page": 1,
|
||||
"page_size": 3
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 任務 (Jobs)
|
||||
|
||||
### `GET /api/v1/jobs` — 列出處理任務
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/jobs?page=1&page_size=2" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"jobs": [
|
||||
{
|
||||
"id": 32,
|
||||
"uuid": "942d0bdf5d6fb6ac18b47deb031e60c3",
|
||||
"status": "running",
|
||||
"current_processor": null,
|
||||
"progress_current": 0,
|
||||
"progress_total": 0,
|
||||
"created_at": "2026-04-28 15:55:47.911654",
|
||||
"started_at": null
|
||||
}
|
||||
],
|
||||
"count": 19,
|
||||
"page": 1,
|
||||
"page_size": 2
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/rules/:rule/status` — 取得規則狀態
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/rules/default/status" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"rule": "default",
|
||||
"supported_processor_ids": [],
|
||||
"active_jobs": []
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 統計 (Stats)
|
||||
|
||||
### `GET /api/v1/stats/ingest` — 取得匯入統計
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/stats/ingest" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"total_videos": 21,
|
||||
"total_chunks": 0,
|
||||
"sentence_chunks": 0,
|
||||
"cut_chunks": 0,
|
||||
"time_chunks": 0,
|
||||
"searchable_chunks": 0,
|
||||
"chunks_with_visual": 0,
|
||||
"chunks_with_summary": 0,
|
||||
"pending_videos": 12
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 影片管理 (Videos)
|
||||
|
||||
> **注意**: `/api/v1/videos` 主要用於後端列表與詳細資料查詢。前端常用 `/api/v1/files` 作為主要資源入口。
|
||||
|
||||
### `GET /api/v1/videos` — 列出影片
|
||||
|
||||
**參數**: `page`, `page_size`
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/videos?page=1&page_size=1" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"file_uuid": "e79890f13e2e0bebf6c67b436f2c4279",
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/view7.mp4",
|
||||
"file_name": "view7.mp4",
|
||||
"file_type": null,
|
||||
"duration": 11.066667,
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"status": "pending",
|
||||
"processing_status": {},
|
||||
"birth_registration": {},
|
||||
"created_at": "2026-04-30 18:17:46.161312+00",
|
||||
"registration_time": "2026-04-30 18:17:46.161312+00",
|
||||
"file_size": null,
|
||||
"probe_json": "...",
|
||||
"total_frames": 332
|
||||
}
|
||||
],
|
||||
"count": 22,
|
||||
"page": 1,
|
||||
"page_size": 1
|
||||
}
|
||||
```
|
||||
|
||||
### `DELETE /api/v1/videos/:uuid` — 刪除影片
|
||||
|
||||
刪除影片及其所有關聯資料 (faces, chunks, etc.)。
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s -X DELETE "http://localhost:3003/api/v1/videos/<file_uuid>" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
### `GET /api/v1/progress/:uuid` — 取得處理進度
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/progress/e79890f13e2e0bebf6c67b436f2c4279" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"uuid": "e79890f13e2e0bebf6c67b436f2c4279",
|
||||
"file_name": "view7.mp4",
|
||||
"duration": 11.066667,
|
||||
"overall_progress": 0,
|
||||
"cpu_percent": 0.0,
|
||||
"gpu_percent": null,
|
||||
"memory_percent": 0.2,
|
||||
"memory_mb": 29504,
|
||||
"processors": [
|
||||
{
|
||||
"name": "asr",
|
||||
"status": "pending",
|
||||
"current": 0,
|
||||
"total": 0,
|
||||
"progress": 0,
|
||||
"message": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 其他工具 (Utilities)
|
||||
|
||||
### `GET /api/v1/lookup` — 查詢檔案資訊
|
||||
|
||||
可透過 `uuid` 或 `path` 查詢。
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/lookup?uuid=e79890f13e2e0bebf6c67b436f2c4279" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"file_uuid": "e79890f13e2e0bebf6c67b436f2c4279",
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/view7.mp4",
|
||||
"file_name": "view7.mp4",
|
||||
"duration": 11.066667
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. 人臉 (Face)
|
||||
|
||||
### `GET /api/v1/face/list` — 列出人臉
|
||||
|
||||
**必填參數**: `file_uuid`
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/face/list?file_uuid=e79890f13e2e0bebf6c67b436f2c4279&page=1&page_size=2" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"faces": [
|
||||
{
|
||||
"face_id": "identity_8d7c22e3a6794a8c93b4a3655f106944",
|
||||
"name": "Cary Grant",
|
||||
"is_active": true
|
||||
}
|
||||
],
|
||||
"count": 2
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/face/:face_id` — 取得人臉詳情
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/face/identity_8d7c22e3a6794a8c93b4a3655f106944" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"face_id": "identity_8d7c22e3a6794a8c93b4a3655f106944",
|
||||
"name": "Cary Grant",
|
||||
"has_embedding": false,
|
||||
"is_active": true
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/face/recognize` — 進行人臉辨識
|
||||
|
||||
> **注意**: 此端點會掃描影片並返回逐幀結果。若影片過大可能耗時較長。
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"file_uuid": "e79890f13e2e0bebf6c67b436f2c4279",
|
||||
"image_path": "/tmp/test.jpg"
|
||||
}
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"result": {
|
||||
"frame_count": 332,
|
||||
"fps": 30.0,
|
||||
"faces": [ ... ]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 快照與截圖管理 (Snapshots)
|
||||
|
||||
> **設計概念**: 快照分為「熱 (hot)」與「冷 (cold)」。冷快照需遷移至記憶體 (migrate) 後才能進行高效存取。
|
||||
|
||||
### `GET /api/v1/files/:uuid/snapshots` — 列出快照狀態
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/files/e79890f13e2e0bebf6c67b436f2c4279/snapshots" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"file_uuid": "e79890f13e2e0bebf6c67b436f2c4279",
|
||||
"tier": "cold",
|
||||
"hits": 0,
|
||||
"types": [
|
||||
"products", "ocr", "logos", "faces"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/files/:uuid/snapshots/status` — 檢查詳細狀態
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/files/e79890f13e2e0bebf6c67b436f2c4279/snapshots/status" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"status": "cold"
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/files/:uuid/snapshots/migrate` — 載入快照至記憶體
|
||||
|
||||
將冷快照載入記憶體以加速讀取。
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"parent_uuid": "e79890f13e2e0bebf6c67b436f2c4279"
|
||||
}
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Migrated 4 snapshot types",
|
||||
"migrated_types": ["products", "ocr", "logos", "faces"]
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/files/:uuid/snapshots/teardown` — 釋放記憶體
|
||||
|
||||
釋放已載入的快照資源。
|
||||
|
||||
---
|
||||
|
||||
## 13. 身份綁定與訊號 (Identity Binding)
|
||||
|
||||
### `POST /api/v1/identities/bind` — 綁定人臉/聲音至身份
|
||||
|
||||
將特定的人臉或聲音訊號綁定到全域身份。若身份不存在,需提供 `name` 自動建立。
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"name": "John Doe",
|
||||
"binding_type": "face",
|
||||
"binding_value": "identity_xxx"
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/identities/unbind` — 解綁身份
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"binding_type": "face",
|
||||
"binding_value": "identity_xxx"
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/signals/unbound` — 列出未綁定訊號
|
||||
|
||||
列出檔案中尚未被綁定的臉部或聲音訊號。
|
||||
|
||||
**參數**: `uuid`, `binding_type` (face/speaker)
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/signals/unbound?uuid=e79890f13e2e0bebf6c67b436f2c4279&binding_type=face" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Found 0 unbound face signals",
|
||||
"data": []
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14. 身份來源與詳細 (Identity Sources)
|
||||
|
||||
### `GET /api/v1/identities/:uuid` — 取得身份詳情
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/identities/a9a90105-6d6b-46ff-92da-0c3c1a57dff4" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"uuid": "a9a90105-6d6b-46ff-92da-0c3c1a57dff4",
|
||||
"name": "Trace 2 Fixed Format",
|
||||
"identity_type": "people",
|
||||
"source": "auto_trace",
|
||||
"status": "active",
|
||||
"reference_data": {
|
||||
"trace_id": 2,
|
||||
"quality_avg": 0.8748
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/identities/:identity_id/faces` — 取得身份下的所有臉部
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/identities/22/faces?page=1&page_size=5" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"identity_id": 22,
|
||||
"faces": [],
|
||||
"total": 0
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/files/:uuid/identities` — 取得檔案中出現的身份
|
||||
|
||||
**curl**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/files/e79890f13e2e0bebf6c67b436f2c4279/identities" \
|
||||
-H "X-API-Key: muser_test_001"
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"file_uuid": "e79890f13e2e0bebf6c67b436f2c4279",
|
||||
"total": 0,
|
||||
"data": []
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 15. 進階視覺搜尋 (Visual Search Variants)
|
||||
|
||||
### `POST /api/v1/search/visual/class` — 依物件類別搜尋
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"uuid": "e79890f13e2e0bebf6c67b436f2c4279",
|
||||
"object_class": "person"
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/search/visual/density` — 依物件密度搜尋
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"uuid": "e79890f13e2e0bebf6c67b436f2c4279",
|
||||
"min_density": 0.1
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/search/visual/combination` — 依物件組合搜尋
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"uuid": "e79890f13e2e0bebf6c67b436f2c4279",
|
||||
"combination": [["person", 1]]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 16. 代理人 (Agents)
|
||||
|
||||
### `POST /api/v1/agents/identity/analyze` — 分析身份重複
|
||||
|
||||
分析檔案中的身份是否重複,提供合併建議。
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"file_uuid": "e79890f13e2e0bebf6c67b436f2c4279"
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/agents/5w1h/analyze` — 分析 5W1H 摘要
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"file_uuid": "e79890f13e2e0bebf6c67b436f2c4279",
|
||||
"chunk_id": "chunk_1"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 17. 資產操作 (Assets)
|
||||
|
||||
### `POST /api/v1/assets/:uuid/process` — 觸發處理流程
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"processors": ["asr", "cut", "face"]
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/unregister` — 解除註冊檔案
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"uuid": "e79890f13e2e0bebf6c67b436f2c4279"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 錯誤回應
|
||||
|
||||
| HTTP Code | 說明 |
|
||||
|-----------|------|
|
||||
| `400` | 請求參數錯誤 (例如缺少必填欄位) |
|
||||
| `401` | API Key 無效或缺失 |
|
||||
| `404` | 資源不存在 |
|
||||
| `422` | 請求格式錯誤 (JSON 解析失敗) |
|
||||
| `500` | 伺服器內部錯誤 |
|
||||
@@ -1,391 +0,0 @@
|
||||
# Momentry Core API 教育訓練手冊
|
||||
|
||||
> **對象**: marcom 團隊
|
||||
> **版本**: V1.4 | **日期**: 2026-03-25
|
||||
|
||||
---
|
||||
|
||||
## 1. 快速開始
|
||||
|
||||
### 基本資訊
|
||||
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| API 網址 | `https://api.momentry.ddns.net` |
|
||||
| 認證方式 | Header `X-API-Key` |
|
||||
| 格式 | JSON |
|
||||
|
||||
### Demo 測試帳號
|
||||
|
||||
#### API Key(用於 API 認證)
|
||||
|
||||
```
|
||||
X-API-Key: muser_68600856036340bcafc01930eb4bd839
|
||||
```
|
||||
|
||||
#### SFTPGo(用於影片上傳)
|
||||
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| SFTP 主機 | `sftpgo.momentry.ddns.net` |
|
||||
| SFTP 連接埠 | `2022` |
|
||||
| 用戶名 | `demo` |
|
||||
| 密碼 | `demopassword123` |
|
||||
| Web 管理介面 | `https://sftpgo.momentry.ddns.net` |
|
||||
|
||||
**使用方式**:透過 SFTP 上傳影片,系統會自動註冊並處理。
|
||||
|
||||
---
|
||||
|
||||
## 2. 快速範例
|
||||
|
||||
### 查詢所有影片
|
||||
|
||||
```bash
|
||||
curl -s -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
|
||||
"https://api.momentry.ddns.net/api/v1/videos"
|
||||
```
|
||||
|
||||
### 查詢單一影片
|
||||
|
||||
```bash
|
||||
curl -s -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
|
||||
"https://api.momentry.ddns.net/api/v1/videos/{uuid}"
|
||||
```
|
||||
|
||||
### 查詢處理任務狀態
|
||||
|
||||
```bash
|
||||
curl -s -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
|
||||
"https://api.momentry.ddns.net/api/v1/jobs/{uuid}"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. API 端點說明
|
||||
|
||||
### 3.1 影片相關
|
||||
|
||||
#### GET /api/v1/videos
|
||||
取得所有影片列表
|
||||
|
||||
**回應範例**:
|
||||
```json
|
||||
{
|
||||
"videos": [
|
||||
{
|
||||
"uuid": "5dea6618a606e7c7",
|
||||
"filename": "demo_video.mp4",
|
||||
"duration": 123.45,
|
||||
"status": "ready",
|
||||
"created_at": "2026-03-25T10:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### GET /api/v1/videos/:uuid
|
||||
取得單一影片詳情
|
||||
|
||||
### 3.2 搜尋與分段查詢
|
||||
|
||||
#### POST /api/v1/search
|
||||
向量搜尋,查詢分段(Chunk)詳情
|
||||
|
||||
**請求參數**:
|
||||
| 參數 | 類型 | 必填 | 說明 |
|
||||
|------|------|------|------|
|
||||
| `query` | string | 是 | 搜尋關鍵字 |
|
||||
| `limit` | number | 否 | 回傳數量(預設 10) |
|
||||
| `uuid` | string | 否 | 只搜尋指定影片 |
|
||||
|
||||
**請求範例**:
|
||||
```json
|
||||
{
|
||||
"query": "天氣",
|
||||
"limit": 10,
|
||||
"uuid": "5dea6618a606e7c7"
|
||||
}
|
||||
```
|
||||
|
||||
**回應範例**:
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uuid": "39567a0eb16f39fd",
|
||||
"chunk_id": "sentence_1471",
|
||||
"chunk_type": "sentence",
|
||||
"start_time": 5309.08,
|
||||
"end_time": 5311.08,
|
||||
"text": "influenced by a vital way,",
|
||||
"score": 0.68
|
||||
}
|
||||
],
|
||||
"query": "天氣"
|
||||
}
|
||||
```
|
||||
|
||||
**Chunk 欄位說明**:
|
||||
| 欄位 | 說明 | 範例 |
|
||||
|------|------|------|
|
||||
| `uuid` | 影片唯一識別碼 | `39567a0eb16f39fd` |
|
||||
| `chunk_id` | 分段識別碼 | `sentence_1471` |
|
||||
| `chunk_type` | 分段類型 | `sentence` / `cut` / `time` / `trace` / `story` |
|
||||
| `start_time` | 開始時間(秒) | `5309.08` |
|
||||
| `end_time` | 結束時間(秒) | `5311.08` |
|
||||
| `text` | 內容文字 | `influenced by a vital way` |
|
||||
| `score` | 相似度分數(0-1) | `0.68` |
|
||||
|
||||
**Chunk 類型說明**:
|
||||
| 類型 | 說明 | 來源 |
|
||||
|------|------|------|
|
||||
| `sentence` | 語音轉文字片段 | ASR 處理 |
|
||||
| `cut` | 場景變化片段 | CUT 處理 |
|
||||
| `time` | 固定時間分段 | 系統自動切割 |
|
||||
| `trace` | 軌跡追蹤片段 | YOLO 追蹤 |
|
||||
| `story` | 故事線片段(父子關係) | Story 分析 |
|
||||
|
||||
#### POST /api/v1/n8n/search
|
||||
n8n 專用搜尋(包含完整影片檔案路徑 file_path)
|
||||
|
||||
**請求參數**: 與 `/search` 相同
|
||||
|
||||
**回應範例**:
|
||||
```json
|
||||
{
|
||||
"query": "天氣",
|
||||
"count": 2,
|
||||
"hits": [
|
||||
{
|
||||
"id": "sentence_1471",
|
||||
"vid": "39567a0eb16f39fd",
|
||||
"chunk_type": "sentence",
|
||||
"start_frame": 318545,
|
||||
"end_frame": 318665,
|
||||
"fps": 59.94,
|
||||
"start_time": 5314.31,
|
||||
"end_time": 5316.32,
|
||||
"text": "influenced by a vital way,",
|
||||
"score": 0.68
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**與 /search 的差異**:
|
||||
| 欄位 | `/search` | `/n8n/search` |
|
||||
|------|-----------|----------------|
|
||||
| 影片 UUID | `uuid` | `vid` |
|
||||
| Chunk ID | `chunk_id` | `id` |
|
||||
| 開始時間 | `start_time` | `start_time` |
|
||||
| 結束時間 | `end_time` | `end_time` |
|
||||
| 相似度分數 | `score` | `score` |
|
||||
| **檔案路徑** | ❌ | ✅ `file_path` |
|
||||
|
||||
> **注意**: `file_path` 是影片的實際路徑,可用於本地播放。
|
||||
|
||||
### 3.3 任務相關
|
||||
|
||||
### 3.4 任務相關
|
||||
|
||||
#### GET /api/v1/jobs/:uuid
|
||||
查詢處理任務狀態
|
||||
|
||||
**回應範例**:
|
||||
```json
|
||||
{
|
||||
"uuid": "9760d0820f0cf9a7",
|
||||
"file_uuid": "5dea6618a606e7c7",
|
||||
"status": "completed",
|
||||
"progress": 100,
|
||||
"created_at": "2026-03-25T10:00:00Z",
|
||||
"completed_at": "2026-03-25T10:05:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
#### GET /api/v1/jobs
|
||||
查詢所有任務
|
||||
|
||||
**查詢參數**:
|
||||
| 參數 | 說明 | 範例 |
|
||||
|------|------|------|
|
||||
| `status` | 篩選狀態 | `pending`, `processing`, `completed`, `failed` |
|
||||
| `limit` | 回傳數量 | `10` |
|
||||
|
||||
**範例**:
|
||||
```bash
|
||||
curl -s -H "X-API-Key: ..." \
|
||||
"https://api.momentry.ddns.net/api/v1/jobs?status=completed&limit=5"
|
||||
```
|
||||
|
||||
### 3.5 系統管理
|
||||
|
||||
#### POST /api/v1/config/cache
|
||||
切換快取功能(管理員專用)
|
||||
|
||||
**請求範例**:
|
||||
```json
|
||||
{
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
**回應範例**:
|
||||
```json
|
||||
{
|
||||
"cache_enabled": true,
|
||||
"message": "Cache toggled successfully"
|
||||
}
|
||||
```
|
||||
|
||||
#### POST /api/v1/unregister
|
||||
刪除影片及其所有關聯資料(管理員專用)
|
||||
|
||||
**請求範例**:
|
||||
```json
|
||||
{
|
||||
"uuid": "5dea6618a606e7c7"
|
||||
}
|
||||
```
|
||||
|
||||
**回應範例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Video unregistered successfully",
|
||||
"uuid": "5dea6618a606e7c7"
|
||||
}
|
||||
```
|
||||
|
||||
**注意**: 此操作會刪除影片及其所有分段、處理結果、縮圖等關聯資料,**無法復原**。
|
||||
|
||||
### 3.6 健康檢查
|
||||
|
||||
#### GET /health
|
||||
服務健康狀態(**無需認證**)
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"version": "0.9.20260325_144654"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. n8n Workflow 範例
|
||||
|
||||
### 4.1 基本設定
|
||||
|
||||
在 n8n workflow 中使用 HTTP Request 節點:
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ HTTP Request │
|
||||
├─────────────────┤
|
||||
│ Method: GET │
|
||||
│ URL: https://api.momentry.ddns.net/api/v1/videos
|
||||
│ Headers: │
|
||||
│ X-API-Key: │
|
||||
│ [YOUR_KEY] │
|
||||
└────────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ 處理回應資料 │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
### 4.2 範例:檢查任務狀態
|
||||
|
||||
```javascript
|
||||
// n8n Function Node 範例
|
||||
const jobUuid = $input.item.json.uuid;
|
||||
|
||||
return [{
|
||||
json: {
|
||||
method: "GET",
|
||||
url: `https://api.momentry.ddns.net/api/v1/jobs/${jobUuid}`,
|
||||
headers: {
|
||||
"X-API-Key": "YOUR_API_KEY"
|
||||
}
|
||||
}
|
||||
}];
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 常見問題
|
||||
|
||||
### Q: 返回 401 錯誤怎麼辦?
|
||||
確認 Header 中有正確的 `X-API-Key` 值
|
||||
|
||||
### Q: 如何確認影片處理完成?
|
||||
```
|
||||
GET /api/v1/jobs/{uuid}
|
||||
```
|
||||
檢查 `status` 是否為 `completed`
|
||||
|
||||
### Q: 查不到資料?
|
||||
確認 UUID 格式正確(16碼 hex 字串)
|
||||
|
||||
---
|
||||
|
||||
## 6. 快速參考卡
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Momentry API 速查 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ 查詢所有影片 GET /api/v1/videos │
|
||||
│ 查詢單一影片 GET /api/v1/videos/:uuid │
|
||||
│ 向量搜尋 POST /api/v1/search │
|
||||
│ n8n 搜尋 POST /api/v1/n8n/search │
|
||||
│ 查詢任務狀態 GET /api/v1/jobs/:uuid │
|
||||
│ 查詢所有任務 GET /api/v1/jobs │
|
||||
│ 快取設定 POST /api/v1/config/cache (管理員) │
|
||||
│ 刪除影片 POST /api/v1/unregister (管理員) │
|
||||
│ 健康檢查 GET /health (免認證) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Header: X-API-Key: [YOUR_KEY] │
|
||||
│ URL: https://api.momentry.ddns.net │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 附錄:回應狀態說明
|
||||
|
||||
### 任務狀態 (status)
|
||||
|
||||
| 狀態 | 說明 |
|
||||
|------|------|
|
||||
| `pending` | 等待處理 |
|
||||
| `processing` | 處理中 |
|
||||
| `completed` | 已完成 |
|
||||
| `failed` | 處理失敗 |
|
||||
|
||||
### 影片狀態 (status)
|
||||
|
||||
| 狀態 | 說明 |
|
||||
|------|------|
|
||||
| `uploading` | 上傳中 |
|
||||
| `pending` | 等待處理 |
|
||||
| `processing` | 處理中 |
|
||||
| `ready` | 已就緒 |
|
||||
| `error` | 錯誤 |
|
||||
|
||||
---
|
||||
|
||||
## 附錄:版本歷史
|
||||
|
||||
| 版本 | 日期 | 內容 | 操作人 |
|
||||
|------|------|------|--------|
|
||||
| V1.0 | 2026-03-25 | 初版建立 | OpenCode |
|
||||
| V1.1 | 2026-03-25 | 新增快取/刪除 API、搜尋端點文件 | OpenCode |
|
||||
| V1.2 | 2026-03-25 | 新增 Chunk 欄位說明、類型、播放方式 | OpenCode |
|
||||
| V1.3 | 2026-03-25 | 新增 Demo 測試帳號(SFTPGo)| OpenCode |
|
||||
| V1.4 | 2026-03-25 | 更新 n8n 搜尋回傳欄位說明 (media_url→file_path) | OpenCode |
|
||||
| V1.5 | 2026-04-17 | 修正 API Key 格式、統一 n8n/search 欄位名稱 (start/end → start_time/end_time) | OpenCode |
|
||||
@@ -1,371 +0,0 @@
|
||||
# 🎬 連續演示模式使用指南
|
||||
|
||||
## 功能特點
|
||||
|
||||
### 1️⃣ 從頭到尾連續播放
|
||||
- 按 ASR 片段順序播放
|
||||
- 不跳躍、不間斷
|
||||
- 完整呈現所有內容
|
||||
|
||||
### 2️⃣ 顯示所有識別信息
|
||||
每個片段顯示:
|
||||
- **ASR**: 文字內容
|
||||
- **Speaker**: 說話人 + 演員名 + 角色名
|
||||
- **Face**: 人臉位置和置信度
|
||||
- **Pose**: 嘴部關鍵點
|
||||
- **更多**: 未來可擴展 OCR、場景等
|
||||
|
||||
### 3️⃣ 鍵盤控制
|
||||
- **SPACE**: 暫停/恢復
|
||||
- **Q**: 停止並退出
|
||||
|
||||
---
|
||||
|
||||
## 快速開始
|
||||
|
||||
### 基本用法
|
||||
|
||||
```bash
|
||||
./run_demo.sh --continuous
|
||||
```
|
||||
|
||||
### 顯示視頻
|
||||
|
||||
```bash
|
||||
./run_demo.sh --continuous --video
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用範例
|
||||
|
||||
### 1. 音頻連續播放(默認)
|
||||
|
||||
```bash
|
||||
./run_demo.sh --continuous
|
||||
```
|
||||
|
||||
**輸出示例**:
|
||||
```
|
||||
🎬 Continuous Demo Mode
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📺 Playing from beginning to end
|
||||
⏸️ Press SPACE to pause/resume
|
||||
⏹️ Press Q to quit
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
[1/1118] Segment
|
||||
================================================================================
|
||||
⏱ Time: 14.20s - 21.50s
|
||||
🎤 Speaker: SPEAKER_4 → James Coburn (Tex Panthollow)
|
||||
================================================================================
|
||||
▶️ Playing: 14.20s - 21.50s (7.30s)
|
||||
|
||||
[2/1118] Segment
|
||||
================================================================================
|
||||
⏱ Time: 22.40s - 58.30s
|
||||
🎤 Speaker: SPEAKER_4 → James Coburn (Tex Panthollow)
|
||||
================================================================================
|
||||
▶️ Playing: 22.40s - 58.30s (35.90s)
|
||||
```
|
||||
|
||||
### 2. 視頻連續播放
|
||||
|
||||
```bash
|
||||
./run_demo.sh --continuous --video
|
||||
```
|
||||
|
||||
**特點**:
|
||||
- 📺 彈出視頻窗口
|
||||
- 🎬 視聽同步
|
||||
- 💯 完整體驗
|
||||
|
||||
### 3. 自定義窗口大小
|
||||
|
||||
```bash
|
||||
./target/debug/integrated_player \
|
||||
--video /tmp/charade_audio.wav \
|
||||
--asrx /tmp/asrx_charade_optimized.json \
|
||||
--continuous-demo \
|
||||
--show-video \
|
||||
--video-width 1600 \
|
||||
--video-height 900
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 鍵盤控制詳解
|
||||
|
||||
### ⏸️ 暫停(SPACE)
|
||||
|
||||
**何時使用**:
|
||||
- 想仔細看某個片段
|
||||
- 需要暫停分析
|
||||
- 想跳過某些內容
|
||||
|
||||
**操作**:
|
||||
```
|
||||
⏸️ Paused - Press SPACE to resume
|
||||
```
|
||||
|
||||
再次按 SPACE 恢復播放。
|
||||
|
||||
### ⏹️ 退出(Q)
|
||||
|
||||
**何時使用**:
|
||||
- 演示完成
|
||||
- 想提前結束
|
||||
- 切換到其他任務
|
||||
|
||||
**操作**:
|
||||
```
|
||||
⏹️ Stopped by user
|
||||
```
|
||||
|
||||
立即停止並退出。
|
||||
|
||||
---
|
||||
|
||||
## 演示流程
|
||||
|
||||
### 完整流程圖
|
||||
|
||||
```
|
||||
開始
|
||||
↓
|
||||
載入數據(ASR, ASRX, Face, Pose)
|
||||
↓
|
||||
逐句播放 ASR 片段
|
||||
├─ 顯示文字
|
||||
├─ 顯示說話人
|
||||
├─ 顯示人臉
|
||||
├─ 顯示嘴部動作
|
||||
└─ 播放音頻/視頻
|
||||
↓
|
||||
用戶可隨時:
|
||||
├─ SPACE 暫停/恢復
|
||||
└─ Q 退出
|
||||
↓
|
||||
播放完成
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 信息顯示格式
|
||||
|
||||
### 標準格式
|
||||
|
||||
```
|
||||
[片段序號/總數] Segment
|
||||
================================================================================
|
||||
⏱ Time: 起始時間 - 結束時間
|
||||
📝 Text: 文字內容(如果有)
|
||||
🎤 Speaker: 說話人ID → 演員名 (角色名)
|
||||
👤 Face: 位置、大小、置信度(如果有)
|
||||
👄 Mouth landmarks: 嘴部關鍵點(如果有)
|
||||
================================================================================
|
||||
▶️ Playing: 起始時間 - 結束時間 (時長)
|
||||
```
|
||||
|
||||
### 實際範例
|
||||
|
||||
```
|
||||
[234/1118] Segment
|
||||
================================================================================
|
||||
⏱ Time: 299.50s - 303.10s
|
||||
🎤 Speaker: SPEAKER_1 → Audrey Hepburn (Regina Lampert)
|
||||
👤 Face: bbox=(1250,178) 147x206, confidence=0.88
|
||||
================================================================================
|
||||
▶️ Playing: 299.50s - 303.10s (3.60s)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 與其他模式對比
|
||||
|
||||
| 特性 | 連續模式 | 說話人演示 | 交互模式 |
|
||||
|------|----------|-----------|----------|
|
||||
| **播放方式** | 從頭到尾 | 按說話人 | 手動控制 |
|
||||
| **自動化** | ✅ 全自動 | ✅ 半自動 | ❌ 手動 |
|
||||
| **信息展示** | ✅ 完整 | ✅ 完整 | ✅ 按需 |
|
||||
| **暫停控制** | ✅ 支持 | ❌ 不支持 | N/A |
|
||||
| **適用場景** | 完整演示 | 說話人分析 | 深度分析 |
|
||||
|
||||
---
|
||||
|
||||
## 使用場景
|
||||
|
||||
### 1. 完整功能演示
|
||||
|
||||
```bash
|
||||
# 展示所有識別功能
|
||||
./run_demo.sh --continuous --video
|
||||
```
|
||||
|
||||
適用於:
|
||||
- 向客戶演示
|
||||
- 團隊內部展示
|
||||
- 功能驗收
|
||||
|
||||
### 2. 內容審核
|
||||
|
||||
```bash
|
||||
# 快速瀏覽所有內容
|
||||
./run_demo.sh --continuous
|
||||
```
|
||||
|
||||
適用於:
|
||||
- 檢查 ASR 準確度
|
||||
- 驗證說話人識別
|
||||
- 確認人臉檢測
|
||||
|
||||
### 3. 數據驗證
|
||||
|
||||
```bash
|
||||
# 驗證處理結果
|
||||
./target/debug/integrated_player \
|
||||
--video video.mp4 \
|
||||
--asr asr.json \
|
||||
--asrx asrx.json \
|
||||
--face face.json \
|
||||
--continuous-demo
|
||||
```
|
||||
|
||||
適用於:
|
||||
- 檢查處理質量
|
||||
- 發現問題片段
|
||||
- 優化處理參數
|
||||
|
||||
---
|
||||
|
||||
## 高級用法
|
||||
|
||||
### 1. 慢速播放
|
||||
|
||||
```bash
|
||||
# 慢速觀看細節
|
||||
./target/debug/integrated_player \
|
||||
--video video.mp4 \
|
||||
--asrx asrx.json \
|
||||
--continuous-demo \
|
||||
--show-video
|
||||
```
|
||||
|
||||
按 SPACE 暫停查看細節。
|
||||
|
||||
### 2. 快速瀏覽
|
||||
|
||||
```bash
|
||||
# 音頻模式快速瀏覽
|
||||
./run_demo.sh --continuous
|
||||
```
|
||||
|
||||
適合快速了解內容。
|
||||
|
||||
### 3. 特定時間段
|
||||
|
||||
結合交互模式:
|
||||
|
||||
```bash
|
||||
# 1. 先用交互模式找到起始時間
|
||||
./target/debug/integrated_player --video video.mp4 --asrx asrx.json
|
||||
|
||||
# 2. 輸入時間跳轉
|
||||
> 300
|
||||
|
||||
# 3. 改用連續模式從該時間開始
|
||||
# (需要修改代碼支持 --start 參數)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 性能優化
|
||||
|
||||
### 1. 音頻模式
|
||||
|
||||
```bash
|
||||
# 最快、最輕量
|
||||
./run_demo.sh --continuous
|
||||
```
|
||||
|
||||
### 2. 視頻模式
|
||||
|
||||
```bash
|
||||
# 較慢、更完整
|
||||
./run_demo.sh --continuous --video
|
||||
```
|
||||
|
||||
### 3. 自定義窗口
|
||||
|
||||
```bash
|
||||
# 小窗口更快
|
||||
--video-width 640 --video-height 480
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常見問題
|
||||
|
||||
### Q1: 播放速度太快?
|
||||
|
||||
A: 按 SPACE 暫停,仔細查看信息。
|
||||
|
||||
### Q2: 想跳過某些片段?
|
||||
|
||||
A: 按 Q 退出,使用交互模式手動選擇。
|
||||
|
||||
### Q3: 沒有 ASR 數據?
|
||||
|
||||
A: 連續模式需要 ASR 數據。請確保提供 `--asr` 參數。
|
||||
|
||||
### Q4: 某些片段沒有信息?
|
||||
|
||||
A: 正常現象。不是每個片段都有所有信息(Face、Pose等)。
|
||||
|
||||
### Q5: 可以調整播放速度嗎?
|
||||
|
||||
A: 連續模式按實際時長播放。需要快進請使用 `--demo` 模式。
|
||||
|
||||
---
|
||||
|
||||
## 命令速查
|
||||
|
||||
```bash
|
||||
# 連續音頻演示
|
||||
./run_demo.sh --continuous
|
||||
|
||||
# 連續視頻演示
|
||||
./run_demo.sh --continuous --video
|
||||
|
||||
# 自定義窗口
|
||||
./target/debug/integrated_player \
|
||||
--video video.mp4 \
|
||||
--asrx asrx.json \
|
||||
--continuous-demo \
|
||||
--show-video \
|
||||
--video-width 1600 \
|
||||
--video-height 900
|
||||
|
||||
# 鍵盤控制
|
||||
SPACE - 暫停/恢復
|
||||
Q - 退出
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 未來擴展
|
||||
|
||||
### 計劃功能
|
||||
|
||||
1. **進度條**: 顯示當前播放進度
|
||||
2. **時間跳轉**: 支持從指定時間開始
|
||||
3. **字幕疊加**: 在視頻上顯示 ASR 文字
|
||||
4. **人臉標註**: 在視頻上畫出人臉框
|
||||
5. **OCR 整合**: 顯示文字識別結果
|
||||
6. **場景識別**: 顯示場景類型
|
||||
|
||||
---
|
||||
|
||||
**更新日期**: 2026-04-02
|
||||
**版本**: 2.0.0
|
||||
**作者**: Momentry Team
|
||||
@@ -1,397 +0,0 @@
|
||||
# 整合播放器演示功能指南
|
||||
|
||||
## 🎬 自動演示模式
|
||||
|
||||
整合播放器支持自動演示功能,可以自動播放所有說話人的片段,展示各項功能。
|
||||
|
||||
---
|
||||
|
||||
## 快速開始
|
||||
|
||||
### 1. 快速演示(推薦初次使用)
|
||||
|
||||
```bash
|
||||
./run_demo.sh --quick
|
||||
```
|
||||
|
||||
- 每個說話人演示 1 個片段
|
||||
- 3 倍速播放
|
||||
- 適合快速了解功能
|
||||
|
||||
### 2. 標準演示
|
||||
|
||||
```bash
|
||||
./run_demo.sh
|
||||
```
|
||||
|
||||
- 每個說話人演示 3 個片段
|
||||
- 2 倍速播放
|
||||
- 完整展示所有功能
|
||||
|
||||
### 3. 自定義演示
|
||||
|
||||
```bash
|
||||
./target/debug/integrated_player \
|
||||
--video /tmp/charade_audio.wav \
|
||||
--asrx /tmp/asrx_charade_optimized.json \
|
||||
--demo \
|
||||
--demo-segments-per-speaker 5 \
|
||||
--demo-speed 1.5
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 演示參數
|
||||
|
||||
| 參數 | 說明 | 默認值 |
|
||||
|------|------|--------|
|
||||
| `--demo` | 啟用自動演示模式 | false |
|
||||
| `--demo-segments-per-speaker` | 每個說話人演示的片段數 | 3 |
|
||||
| `--demo-speed` | 演示速度(倍速) | 2.0 |
|
||||
|
||||
---
|
||||
|
||||
## 演示流程
|
||||
|
||||
演示模式會自動執行以下步驟:
|
||||
|
||||
1. **載入數據**
|
||||
- ASRX 說話人分離結果
|
||||
- Face 人臉檢測結果(如果提供)
|
||||
- Pose 姿態估計結果(如果提供)
|
||||
|
||||
2. **列出說話人**
|
||||
```
|
||||
📊 Speaker Statistics:
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
Speaker ID Actor Character Segments Duration
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
SPEAKER_0 Cary Grant Peter Joshua 654 1764.4s
|
||||
SPEAKER_1 Audrey Hepburn Regina Lampert 403 1119.4s
|
||||
SPEAKER_2 Walter Matthau Hamilton Bartholomew 49 65.7s
|
||||
SPEAKER_4 James Coburn Tex Panthollow 3 44.1s
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
```
|
||||
|
||||
3. **逐一演示每個說話人**
|
||||
|
||||
對每個說話人:
|
||||
- 顯示說話人信息(ID → 演員 → 角色)
|
||||
- 播放指定數量的片段
|
||||
- 顯示整合信息(文字、人臉、嘴部動作)
|
||||
- 暫停 2 秒後切換到下一個說話人
|
||||
|
||||
4. **演示示例輸出**
|
||||
|
||||
```
|
||||
================================================================================
|
||||
🎭 Demo: SPEAKER_1 → Audrey Hepburn (Regina Lampert)
|
||||
================================================================================
|
||||
|
||||
[Segment 1/3]
|
||||
|
||||
================================================================================
|
||||
⏱ Time: 299.50s - 303.10s
|
||||
🎤 Speaker: SPEAKER_1 → Audrey Hepburn (Regina Lampert)
|
||||
👤 Face: bbox=(1250,178) 147x206, confidence=0.88
|
||||
================================================================================
|
||||
|
||||
⏳ Playing audio (1.8s)...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 演示模式特點
|
||||
|
||||
### 1. 自動化播放
|
||||
- 無需手動輸入命令
|
||||
- 自動播放所有說話人
|
||||
- 適合展示和測試
|
||||
|
||||
### 2. 速度控制
|
||||
- 可調整播放速度
|
||||
- 支持快速瀏覽(3x)
|
||||
- 支持正常速度(1x)
|
||||
|
||||
### 3. 完整展示
|
||||
- ASR 文字
|
||||
- 人臉檢測
|
||||
- 說話人識別
|
||||
- 演員/角色映射
|
||||
- 嘴部動作(如果有 Pose 數據)
|
||||
|
||||
---
|
||||
|
||||
## 使用場景
|
||||
|
||||
### 1. 功能演示
|
||||
```bash
|
||||
# 展示給團隊或客戶
|
||||
./run_demo.sh
|
||||
```
|
||||
|
||||
### 2. 快速測試
|
||||
```bash
|
||||
# 開發過程中的快速測試
|
||||
./run_demo.sh --quick
|
||||
```
|
||||
|
||||
### 3. 數據驗證
|
||||
```bash
|
||||
# 驗證 ASRX 結果的正確性
|
||||
./target/debug/integrated_player \
|
||||
--video video.mp4 \
|
||||
--asrx asrx.json \
|
||||
--demo \
|
||||
--demo-speed 1.0 # 原速播放
|
||||
```
|
||||
|
||||
### 4. 說話人分析
|
||||
```bash
|
||||
# 分析特定說話人的表現
|
||||
./target/debug/integrated_player \
|
||||
--video video.mp4 \
|
||||
--asrx asrx.json \
|
||||
--demo \
|
||||
--demo-segments-per-speaker 10 # 更多片段
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 演示時間估算
|
||||
|
||||
以 Charade (114.7 分鐘) 為例:
|
||||
|
||||
| 模式 | 片段數/說話人 | 速度 | 總時間 |
|
||||
|------|---------------|------|--------|
|
||||
| 快速 | 1 | 3x | ~2 分鐘 |
|
||||
| 標準 | 3 | 2x | ~5 分鐘 |
|
||||
| 完整 | 5 | 1x | ~15 分鐘 |
|
||||
|
||||
計算公式:
|
||||
```
|
||||
總時間 = 說話人數 × 片段數 × 平均片段時長 / 速度
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 演示腳本詳解
|
||||
|
||||
### run_demo.sh
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 主要邏輯:
|
||||
|
||||
# 1. 檢查編譯
|
||||
if [ ! -f ./target/debug/integrated_player ]; then
|
||||
cargo build --bin integrated_player
|
||||
fi
|
||||
|
||||
# 2. 檢查數據
|
||||
if [ ! -f "$VIDEO" ]; then
|
||||
python3 scripts/asrx_self/test_long_movie.py
|
||||
fi
|
||||
|
||||
# 3. 運行演示
|
||||
if [ "$1" = "--quick" ]; then
|
||||
# 快速模式
|
||||
./target/debug/integrated_player \
|
||||
--demo \
|
||||
--demo-segments-per-speaker 1 \
|
||||
--demo-speed 3.0
|
||||
else
|
||||
# 標準模式
|
||||
./target/debug/integrated_player \
|
||||
--demo \
|
||||
--demo-segments-per-speaker 3 \
|
||||
--demo-speed 2.0
|
||||
fi
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 高級用法
|
||||
|
||||
### 1. 演示特定說話人
|
||||
|
||||
```bash
|
||||
# 只演示主要說話人
|
||||
./target/debug/integrated_player \
|
||||
--video video.mp4 \
|
||||
--asrx asrx.json \
|
||||
--auto-play-speaker \
|
||||
--speaker-name SPEAKER_0
|
||||
```
|
||||
|
||||
### 2. 演示整合 Face 數據
|
||||
|
||||
```bash
|
||||
./target/debug/integrated_player \
|
||||
--video video.mp4 \
|
||||
--asrx asrx.json \
|
||||
--face face.json \
|
||||
--demo \
|
||||
--demo-segments-per-speaker 3
|
||||
```
|
||||
|
||||
### 3. 完整演示(所有數據)
|
||||
|
||||
```bash
|
||||
./target/debug/integrated_player \
|
||||
--video video.mp4 \
|
||||
--asr asr.json \
|
||||
--face face.json \
|
||||
--asrx asrx.json \
|
||||
--pose pose.json \
|
||||
--demo \
|
||||
--demo-segments-per-speaker 5 \
|
||||
--demo-speed 1.5
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 輸出說明
|
||||
|
||||
### 演示輸出結構
|
||||
|
||||
```
|
||||
🎬 Auto Demo Mode
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
Segments per speaker: 3
|
||||
Demo speed: 2.0x
|
||||
|
||||
================================================================================
|
||||
🎭 Demo: SPEAKER_0 → Cary Grant (Peter Joshua)
|
||||
================================================================================
|
||||
|
||||
[Segment 1/3]
|
||||
|
||||
================================================================================
|
||||
⏱ Time: 14.20s - 21.50s
|
||||
🎤 Speaker: SPEAKER_0 → Cary Grant (Peter Joshua)
|
||||
👤 Face: bbox=(1250,178) 147x206, confidence=0.88
|
||||
================================================================================
|
||||
|
||||
⏳ Playing audio (3.6s)...
|
||||
|
||||
[Segment 2/3]
|
||||
...
|
||||
|
||||
⏸️ Pausing 2 seconds before next speaker...
|
||||
|
||||
================================================================================
|
||||
🎭 Demo: SPEAKER_1 → Audrey Hepburn (Regina Lampert)
|
||||
================================================================================
|
||||
...
|
||||
|
||||
================================================================================
|
||||
✅ Demo completed!
|
||||
================================================================================
|
||||
```
|
||||
|
||||
### 信息解讀
|
||||
|
||||
- **🎭 Demo**: 說話人演示標題
|
||||
- **⏱ Time**: 片段時間範圍
|
||||
- **🎤 Speaker**: 說話人 → 演員 → 角色
|
||||
- **👤 Face**: 人臉位置和置信度
|
||||
- **👄 Mouth**: 嘴部關鍵點(如果有)
|
||||
- **⏳ Playing**: 正在播放音頻
|
||||
- **⏸️ Pausing**: 說話人切換暫停
|
||||
|
||||
---
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 問題 1: 找不到測試數據
|
||||
|
||||
```bash
|
||||
# 解決方案:生成測試數據
|
||||
cd scripts/asrx_self
|
||||
python3 test_long_movie.py
|
||||
cd ../..
|
||||
```
|
||||
|
||||
### 問題 2: 播放失敗
|
||||
|
||||
```bash
|
||||
# 檢查 ffplay
|
||||
which ffplay
|
||||
|
||||
# 安裝 ffmpeg
|
||||
brew install ffmpeg
|
||||
```
|
||||
|
||||
### 問題 3: 演示太快/太慢
|
||||
|
||||
```bash
|
||||
# 調整速度參數
|
||||
--demo-speed 1.0 # 原速
|
||||
--demo-speed 2.0 # 2 倍速(默認)
|
||||
--demo-speed 3.0 # 3 倍速
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 性能優化
|
||||
|
||||
### 減少片段數
|
||||
|
||||
```bash
|
||||
--demo-segments-per-speaker 1 # 每個說話人只演示 1 個片段
|
||||
```
|
||||
|
||||
### 提高速度
|
||||
|
||||
```bash
|
||||
--demo-speed 4.0 # 4 倍速快速瀏覽
|
||||
```
|
||||
|
||||
### 演示主要說話人
|
||||
|
||||
```bash
|
||||
# 只演示片段數最多的說話人
|
||||
--auto-play-speaker --speaker-name SPEAKER_0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 比較:演示模式 vs 交互模式
|
||||
|
||||
| 特性 | 演示模式 | 交互模式 |
|
||||
|------|----------|----------|
|
||||
| 自動化 | ✅ 全自動 | ❌ 需手動輸入 |
|
||||
| 速度控制 | ✅ 可調整 | ❌ 固定速度 |
|
||||
| 說話人選擇 | ❌ 按順序 | ✅ 自由選擇 |
|
||||
| 時間跳轉 | ❌ 不支持 | ✅ 支持 |
|
||||
| 適用場景 | 展示、測試 | 分析、開發 |
|
||||
|
||||
---
|
||||
|
||||
## 相關命令
|
||||
|
||||
```bash
|
||||
# 查看幫助
|
||||
./target/debug/integrated_player --help
|
||||
|
||||
# 快速測試
|
||||
./run_demo.sh --quick
|
||||
|
||||
# 標準演示
|
||||
./run_demo.sh
|
||||
|
||||
# 自定義演示
|
||||
./target/debug/integrated_player \
|
||||
--video video.mp4 \
|
||||
--asrx asrx.json \
|
||||
--demo \
|
||||
--demo-segments-per-speaker 5 \
|
||||
--demo-speed 1.5
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**創建日期**: 2026-04-02
|
||||
**版本**: 1.1.0
|
||||
**作者**: Momentry Team
|
||||
@@ -1,705 +0,0 @@
|
||||
---
|
||||
document_type: "implementation_guide"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Momentry Core API 示範手冊"
|
||||
date: "2026-03-25"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "示範手冊"
|
||||
ai_query_hints:
|
||||
- "查詢 Momentry Core API 示範手冊 的內容"
|
||||
- "Momentry Core API 示範手冊 的主要目的是什麼?"
|
||||
- "如何操作或實施 Momentry Core API 示範手冊?"
|
||||
---
|
||||
|
||||
# Momentry Core API 示範手冊
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-03-25 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-25 | 創建示範手冊,包含 Demo API Key 與完整範例 | OpenCode | deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
**狀態**: 完成
|
||||
|
||||
---
|
||||
|
||||
## 快速開始
|
||||
|
||||
### Demo API Key
|
||||
|
||||
```
|
||||
API Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69
|
||||
Key ID: muser_68600856036340bcafc01930eb4bd839
|
||||
過期日: 2027-03-25
|
||||
```
|
||||
|
||||
### 測試連線
|
||||
|
||||
```bash
|
||||
curl http://localhost:3002/health
|
||||
```
|
||||
|
||||
```json
|
||||
{"status":"ok","version":"0.1.0","uptime_ms":456464}
|
||||
```
|
||||
|
||||
### 測試認證
|
||||
|
||||
```bash
|
||||
curl -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
|
||||
http://localhost:3002/api/v1/videos | jq '.videos | length'
|
||||
```
|
||||
|
||||
```json
|
||||
13
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 環境 URL
|
||||
|
||||
| 環境 | URL | 用途 |
|
||||
|------|-----|------|
|
||||
| **本地開發** | `http://localhost:3002` | 本機開發測試 |
|
||||
| **外部訪問** | `https://api.momentry.ddns.net` | n8n/WordPress/curl 生產環境 |
|
||||
|
||||
---
|
||||
|
||||
## 端點總覽
|
||||
|
||||
| 方法 | 端點 | 說明 | 認證 |
|
||||
|------|------|------|------|
|
||||
| GET | `/health` | 健康檢查 | 公開 |
|
||||
| GET | `/health/detailed` | 詳細健康檢查 | 公開 |
|
||||
| POST | `/api/v1/register` | 註冊影片 | 需要 |
|
||||
| POST | `/api/v1/probe` | 探測影片資訊 | 需要 |
|
||||
| POST | `/api/v1/search` | 語意搜尋 | 需要 |
|
||||
| POST | `/api/v1/n8n/search` | n8n 格式搜尋 | 需要 |
|
||||
| POST | `/api/v1/search/hybrid` | 混合搜尋 | 需要 |
|
||||
| GET | `/api/v1/videos` | 列出所有影片 | 需要 |
|
||||
| GET | `/api/v1/lookup` | 查詢影片 UUID | 需要 |
|
||||
| GET | `/api/v1/progress/:uuid` | 處理進度 | 需要 |
|
||||
| GET | `/api/v1/jobs` | 任務列表 | 需要 |
|
||||
| GET | `/api/v1/jobs/:uuid` | 任務詳情 | 需要 |
|
||||
|
||||
---
|
||||
|
||||
## 1. curl 範例
|
||||
|
||||
### 基本格式
|
||||
|
||||
```bash
|
||||
curl -H "X-API-Key: YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
URL
|
||||
```
|
||||
|
||||
### 1.1 健康檢查(公開)
|
||||
|
||||
```bash
|
||||
# 基本健康檢查
|
||||
curl http://localhost:3002/health
|
||||
|
||||
# 詳細健康檢查(含服務狀態)
|
||||
curl http://localhost:3002/health/detailed
|
||||
```
|
||||
|
||||
### 1.2 列出影片
|
||||
|
||||
```bash
|
||||
curl -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
|
||||
http://localhost:3002/api/v1/videos | jq '.'
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"videos": [
|
||||
{
|
||||
"uuid": "952f5854b9febad1",
|
||||
"file_name": "ExaSAN PCIe series - Director Ou Yu-Zhi Shares His Experience.mp4",
|
||||
"duration": 159.637188,
|
||||
"width": 640,
|
||||
"height": 360
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 1.3 搜尋影片
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
-H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "ExaSAN", "limit": 5}' \
|
||||
http://localhost:3002/api/v1/search | jq '.'
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uuid": "952f5854b9febad1",
|
||||
"chunk_id": "...",
|
||||
"text": "...",
|
||||
"score": 0.85,
|
||||
"start_time": 0.0,
|
||||
"end_time": 5.0
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
"query": "ExaSAN",
|
||||
"took_ms": 123
|
||||
}
|
||||
```
|
||||
|
||||
### 1.4 查詢進度
|
||||
|
||||
```bash
|
||||
curl -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
|
||||
http://localhost:3002/api/v1/progress/952f5854b9febad1 | jq '.'
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"uuid": "952f5854b9febad1",
|
||||
"overall_progress": 67,
|
||||
"current_processor": "yolo",
|
||||
"processors": [
|
||||
{"name": "asr", "status": "completed"},
|
||||
{"name": "cut", "status": "completed"},
|
||||
{"name": "yolo", "status": "running"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. n8n 範例
|
||||
|
||||
### 2.1 HTTP Request 節點設定
|
||||
|
||||
```
|
||||
Method: POST
|
||||
URL: https://api.momentry.ddns.net/api/v1/search
|
||||
Authentication: None (使用 Header)
|
||||
|
||||
Headers:
|
||||
┌─────────────────────┬──────────────────────────────────────────────────┐
|
||||
│ Name │ Value │
|
||||
├─────────────────────┼──────────────────────────────────────────────────┤
|
||||
│ X-API-Key │ muser_68600856036340bcafc01930eb4bd839_... │
|
||||
│ Content-Type │ application/json │
|
||||
└─────────────────────┴──────────────────────────────────────────────────┘
|
||||
|
||||
Body Content (JSON):
|
||||
{
|
||||
"query": "{{ $json.search_term }}",
|
||||
"limit": 5
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 n8n 搜尋 Workflow
|
||||
|
||||
```json
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"name": "Manual Trigger",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"position": [250, 300]
|
||||
},
|
||||
{
|
||||
"name": "Set Search Term",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"parameters": {
|
||||
"values": {
|
||||
"json": {
|
||||
"search_term": "ExaSAN"
|
||||
}
|
||||
}
|
||||
},
|
||||
"position": [450, 300]
|
||||
},
|
||||
{
|
||||
"name": "Search Videos",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://api.momentry.ddns.net/api/v1/search",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "X-API-Key",
|
||||
"value": "muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyContentType": "json",
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ { \"query\": $json.search_term, \"limit\": 5 } }}"
|
||||
},
|
||||
"position": [650, 300]
|
||||
},
|
||||
{
|
||||
"name": "Process Results",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"parameters": {
|
||||
"jsCode": "// Extract video results\nconst results = $input.first().json.results;\nreturn results.map(r => ({\n uuid: r.uuid,\n text: r.text,\n score: r.score,\n time: `${r.start_time}s - ${r.end_time}s`\n}));"
|
||||
},
|
||||
"position": [850, 300]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Manual Trigger": {
|
||||
"main": [[{"node": "Set Search Term"}]]
|
||||
},
|
||||
"Set Search Term": {
|
||||
"main": [[{"node": "Search Videos"}]]
|
||||
},
|
||||
"Search Videos": {
|
||||
"main": [[{"node": "Process Results"}]]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 n8n 列出影片 Workflow
|
||||
|
||||
```json
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"name": "Get Videos",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://api.momentry.ddns.net/api/v1/videos",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "X-API-Key",
|
||||
"value": "muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"position": [450, 300]
|
||||
},
|
||||
{
|
||||
"name": "Extract Video List",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"parameters": {
|
||||
"jsCode": "const videos = $input.first().json.videos;\nreturn videos.map(v => ({\n json: {\n uuid: v.uuid,\n name: v.file_name,\n duration: Math.round(v.duration) + 's',\n resolution: `${v.width}x${v.height}`\n }\n}));"
|
||||
},
|
||||
"position": [650, 300]
|
||||
},
|
||||
{
|
||||
"name": "Slack Notification",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"parameters": {
|
||||
"channel": "#momentry",
|
||||
"text": "=Found {{ $json.length }} videos:\n{{ $json.map(v => `• ${v.name} (${v.duration})`).join(`\n`) }}"
|
||||
},
|
||||
"position": [850, 300]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 n8n 定時同步 Workflow
|
||||
|
||||
```json
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"name": "Schedule Trigger",
|
||||
"type": "n8n-nodes-base.scheduleTrigger",
|
||||
"parameters": {
|
||||
"rule": {
|
||||
"interval": [{"field": "hours", "hours": 1}]
|
||||
}
|
||||
},
|
||||
"position": [250, 300]
|
||||
},
|
||||
{
|
||||
"name": "Get Pending Videos",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://api.momentry.ddns.net/api/v1/videos"
|
||||
},
|
||||
"position": [450, 300]
|
||||
},
|
||||
{
|
||||
"name": "Filter Processing",
|
||||
"type": "n8n-nodes-base.filter",
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {"caseSensitive": true},
|
||||
"conditions": [
|
||||
{"id": "status", "leftValue": "{{ $json.status }}", "rightValue": "processing"}
|
||||
]
|
||||
}
|
||||
},
|
||||
"position": [650, 300]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. WordPress 範例
|
||||
|
||||
### 3.1 PHP 函數庫
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* Momentry API Client
|
||||
*/
|
||||
|
||||
class Momentry_API {
|
||||
private const API_URL = 'https://api.momentry.ddns.net';
|
||||
private const API_KEY = 'muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69';
|
||||
|
||||
/**
|
||||
* 發送 API 請求
|
||||
*/
|
||||
private function request(string $endpoint, array $data = [], string $method = 'GET'): array {
|
||||
$url = self::API_URL . $endpoint;
|
||||
|
||||
$args = [
|
||||
'headers' => [
|
||||
'X-API-Key' => self::API_KEY,
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
'timeout' => 30,
|
||||
];
|
||||
|
||||
if ($method === 'POST') {
|
||||
$args['method'] = 'POST';
|
||||
$args['body'] = json_encode($data);
|
||||
}
|
||||
|
||||
$response = wp_remote_request($url, $args);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
throw new Exception($response->get_error_message());
|
||||
}
|
||||
|
||||
return json_decode(wp_remote_retrieve_body($response), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出所有影片
|
||||
*/
|
||||
public function list_videos(): array {
|
||||
return $this->request('/api/v1/videos');
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜尋影片內容
|
||||
*/
|
||||
public function search(string $query, int $limit = 10): array {
|
||||
return $this->request('/api/v1/search', [
|
||||
'query' => $query,
|
||||
'limit' => $limit,
|
||||
], 'POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得影片進度
|
||||
*/
|
||||
public function get_progress(string $uuid): array {
|
||||
return $this->request("/api/v1/progress/{$uuid}");
|
||||
}
|
||||
|
||||
/**
|
||||
* 檢查健康狀態
|
||||
*/
|
||||
public function health_check(): array {
|
||||
return $this->request('/health');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 短代碼 (Shortcode)
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* WordPress 短代碼範例
|
||||
*/
|
||||
|
||||
// 註冊短代碼
|
||||
add_shortcode('momentry_videos', function($atts) {
|
||||
$atts = shortcode_atts([
|
||||
'limit' => 10,
|
||||
], $atts);
|
||||
|
||||
$api = new Momentry_API();
|
||||
|
||||
try {
|
||||
$result = $api->list_videos();
|
||||
$videos = array_slice($result['videos'], 0, $atts['limit']);
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div class="momentry-videos">
|
||||
<h3>影片列表</h3>
|
||||
<ul>
|
||||
<?php foreach ($videos as $video): ?>
|
||||
<li>
|
||||
<strong><?= esc_html($video['file_name']) ?></strong>
|
||||
<br>
|
||||
<small>
|
||||
UUID: <?= esc_html($video['uuid']) ?>
|
||||
| 時長: <?= gmdate("H:i:s", $video['duration']) ?>
|
||||
</small>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
|
||||
} catch (Exception $e) {
|
||||
return '<p class="error">載入失敗: ' . esc_html($e->getMessage()) . '</p>';
|
||||
}
|
||||
});
|
||||
|
||||
// 搜尋短代碼
|
||||
add_shortcode('momentry_search', function($atts, $content = '') {
|
||||
$query = sanitize_text_field($content);
|
||||
|
||||
if (empty($query)) {
|
||||
return '<p>請提供搜尋關鍵字</p>';
|
||||
}
|
||||
|
||||
$api = new Momentry_API();
|
||||
|
||||
try {
|
||||
$result = $api->search($query);
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div class="momentry-search-results">
|
||||
<h3>「<?= esc_html($query) ?>」搜尋結果</h3>
|
||||
<?php if (empty($result['results'])): ?>
|
||||
<p>沒有找到相關結果</p>
|
||||
<?php else: ?>
|
||||
<ul>
|
||||
<?php foreach ($result['results'] as $item): ?>
|
||||
<li>
|
||||
<a href="/video/<?= esc_attr($item['uuid']) ?>?t=<?= (int)$item['start_time'] ?>">
|
||||
<?= esc_html($item['text']) ?>
|
||||
</a>
|
||||
<br>
|
||||
<small>相似度: <?= round($item['score'] * 100) ?>%</small>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
|
||||
} catch (Exception $e) {
|
||||
return '<p class="error">搜尋失敗: ' . esc_html($e->getMessage()) . '</p>';
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 3.3 使用方式
|
||||
|
||||
在 WordPress 頁面或文章中:
|
||||
|
||||
```
|
||||
[momentry_videos limit="5"]
|
||||
|
||||
[momentry_search]ExaSAN[/momentry_search]
|
||||
```
|
||||
|
||||
### 3.4 REST API 整合
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* 註冊 WordPress REST API 端點
|
||||
*/
|
||||
|
||||
add_action('rest_api_init', function() {
|
||||
register_rest_route('momentry/v1', '/search', [
|
||||
'methods' => 'GET',
|
||||
'callback' => function(WP_REST_Request $request) {
|
||||
$query = sanitize_text_field($request->get_param('q'));
|
||||
|
||||
if (empty($query)) {
|
||||
return new WP_Error('missing_query', '需要搜尋關鍵字', ['status' => 400]);
|
||||
}
|
||||
|
||||
$api = new Momentry_API();
|
||||
$result = $api->search($query);
|
||||
|
||||
return new WP_REST_Response($result, 200);
|
||||
},
|
||||
'permission_callback' => '__return_true',
|
||||
]);
|
||||
});
|
||||
|
||||
// 使用方式: GET /wp-json/momentry/v1/search?q=ExaSAN
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 疑難排解
|
||||
|
||||
### 4.1 常見錯誤
|
||||
|
||||
| 錯誤 | 原因 | 解決方案 |
|
||||
|------|------|----------|
|
||||
| `401 Unauthorized` | API Key 無效或過期 | 檢查 API Key 是否正確 |
|
||||
| `500 Internal Server Error` | 伺服器錯誤 | 檢查 `/health/detailed` 服務狀態 |
|
||||
| `Connection Timeout` | 網路問題 | 確認 `api.momentry.ddns.net` 可達 |
|
||||
|
||||
### 4.2 測試腳本
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# test_api.sh - Momentry API 測試腳本
|
||||
|
||||
API_KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
|
||||
BASE_URL="http://localhost:3002"
|
||||
|
||||
echo "=== 1. 健康檢查 ==="
|
||||
curl -s "$BASE_URL/health" | jq .
|
||||
echo ""
|
||||
|
||||
echo "=== 2. 列出影片 ==="
|
||||
curl -s -H "X-API-Key: $API_KEY" "$BASE_URL/api/v1/videos" | jq '.videos | length'
|
||||
echo ""
|
||||
|
||||
echo "=== 3. 搜尋測試 ==="
|
||||
curl -s -X POST -H "X-API-Key: $API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "test", "limit": 3}' \
|
||||
"$BASE_URL/api/v1/search" | jq '.results | length'
|
||||
echo ""
|
||||
|
||||
echo "=== 完成 ==="
|
||||
```
|
||||
|
||||
### 4.3 驗證腳本
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# verify_auth.sh - 驗證 API Key
|
||||
|
||||
API_KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
|
||||
BASE_URL="http://localhost:3002"
|
||||
|
||||
# 測試 1: 無 API Key
|
||||
echo "測試 1: 無 API Key"
|
||||
RESULT=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/api/v1/videos")
|
||||
[ "$RESULT" = "401" ] && echo "✅ 正確拒絕 (401)" || echo "❌ 預期 401,實際 $RESULT"
|
||||
|
||||
# 測試 2: 有 API Key
|
||||
echo "測試 2: 有 API Key"
|
||||
RESULT=$(curl -s -H "X-API-Key: $API_KEY" "$BASE_URL/api/v1/videos")
|
||||
echo "$RESULT" | jq -e '.videos' > /dev/null && echo "✅ 成功取得資料" || echo "❌ 取得資料失敗"
|
||||
|
||||
# 測試 3: 無效 API Key
|
||||
echo "測試 3: 無效 API Key"
|
||||
RESULT=$(curl -s -o /dev/null -w "%{http_code}" -H "X-API-Key: invalid_key" "$BASE_URL/api/v1/videos")
|
||||
[ "$RESULT" = "401" ] && echo "✅ 正確拒絕 (401)" || echo "❌ 預期 401,實際 $RESULT"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. API Key 管理
|
||||
|
||||
### 5.1 建立新 API Key
|
||||
|
||||
```bash
|
||||
# 本地建立
|
||||
./target/release/momentry api-key create "My App" --key-type user --ttl 90
|
||||
```
|
||||
|
||||
### 5.2 列出 API Keys
|
||||
|
||||
```bash
|
||||
./target/release/momentry api-key list
|
||||
```
|
||||
|
||||
### 5.3 驗證 API Key
|
||||
|
||||
```bash
|
||||
./target/release/momentry api-key validate --key "YOUR_API_KEY"
|
||||
```
|
||||
|
||||
### 5.4 撤銷 API Key
|
||||
|
||||
```bash
|
||||
./target/release/momentry api-key revoke --key "YOUR_API_KEY"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 附錄
|
||||
|
||||
### A. 影片 UUID 說明
|
||||
|
||||
UUID 是基於檔案路徑的 SHA256 哈希前 16 位:
|
||||
|
||||
```
|
||||
/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4
|
||||
↓
|
||||
SHA256 Hash
|
||||
↓
|
||||
9760d0820f0cf9a7
|
||||
```
|
||||
|
||||
### B. 處理器狀態
|
||||
|
||||
| 狀態 | 說明 |
|
||||
|------|------|
|
||||
| `pending` | 等待處理 |
|
||||
| `running` | 處理中 |
|
||||
| `completed` | 已完成 |
|
||||
| `failed` | 失敗 |
|
||||
|
||||
### C. 支援的處理器
|
||||
|
||||
- **ASR**: 語音識別
|
||||
- **CUT**: 場景剪切
|
||||
- **YOLO**: 物件偵測
|
||||
|
||||
### D. 聯絡支援
|
||||
|
||||
- Email: support@momentry.ddns.net
|
||||
- 文件: https://docs.momentry.ddns.net
|
||||
- GitHub: https://github.com/anomalyco/momentry
|
||||
@@ -1,222 +0,0 @@
|
||||
# 🎬 演示功能總結
|
||||
|
||||
## 當前可用的演示模式
|
||||
|
||||
### 1️⃣ 說話人演示模式(已實現)
|
||||
|
||||
```bash
|
||||
# 快速演示(每個說話人 1 個片段)
|
||||
./run_demo.sh --quick
|
||||
|
||||
# 標準演示(每個說話人 3 個片段)
|
||||
./run_demo.sh
|
||||
|
||||
# 視頻演示
|
||||
./run_demo.sh --video
|
||||
```
|
||||
|
||||
**特點**:
|
||||
- ✅ 按 ASRX 數據播放
|
||||
- ✅ 顯示說話人統計
|
||||
- ✅ 顯示演員名和角色名
|
||||
- ✅ 支持音頻/視頻模式
|
||||
|
||||
---
|
||||
|
||||
### 2️⃣ 連續播放模式(簡化版)
|
||||
|
||||
```bash
|
||||
# 音頻模式
|
||||
./play_continuous.sh
|
||||
|
||||
# 視頻模式
|
||||
./play_continuous.sh --video
|
||||
```
|
||||
|
||||
**特點**:
|
||||
- ✅ 從頭到尾連續播放
|
||||
- ✅ 顯示說話人和時間信息
|
||||
- ✅ 支持視頻顯示
|
||||
- ⚠️ 不能暫停(按 Ctrl+C 停止)
|
||||
|
||||
**實現原理**:
|
||||
- 使用 `jq` 解析 ASRX JSON
|
||||
- 循環調用 `ffplay` 播放每個片段
|
||||
- 簡單高效
|
||||
|
||||
---
|
||||
|
||||
## 演示效果
|
||||
|
||||
### 說話人演示
|
||||
|
||||
```
|
||||
🎬 Integrated Player for ASR/Face/ASRX/Pose
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
Video: "/tmp/charade_audio.wav"
|
||||
✓ Loaded 1118 ASRX segments, 8 speakers
|
||||
|
||||
📊 Speaker Statistics:
|
||||
--------------------------------------------------------------------------------
|
||||
Speaker ID Actor Character Segments Duration
|
||||
--------------------------------------------------------------------------------
|
||||
SPEAKER_0 Cary Grant Peter Joshua 654 1764.4s
|
||||
SPEAKER_1 Audrey Hepburn Regina Lampert 403 1119.4s
|
||||
SPEAKER_2 Walter Matthau Hamilton Bartholomew 49 65.7s
|
||||
SPEAKER_4 James Coburn Tex Panthollow 3 44.1s
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
🎭 Demo: SPEAKER_0 → Cary Grant (Peter Joshua)
|
||||
================================================================================
|
||||
⏱ Time: 374.80s - 375.90s
|
||||
🎤 Speaker: SPEAKER_0 → Cary Grant (Peter Joshua)
|
||||
================================================================================
|
||||
⏳ Playing audio...
|
||||
```
|
||||
|
||||
### 連續播放
|
||||
|
||||
```
|
||||
🎬 连续演示模式
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📺 从头到尾播放所有 ASRX 片段
|
||||
⏸️ 按 Ctrl+C 停止
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📊 总片段数: 1118
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
[1/1118] 🎤 SPEAKER_5
|
||||
⏱ 1.8s - 2.6s (0.8s)
|
||||
🔊 播放中...
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
[2/1118] 🎤 SPEAKER_4
|
||||
⏱ 14.2s - 21.5s (7.3s)
|
||||
🔊 播放中...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用建議
|
||||
|
||||
### 適用場景
|
||||
|
||||
| 模式 | 適用場景 | 優點 | 缺點 |
|
||||
|------|----------|------|------|
|
||||
| **說話人演示** | 展示、分析 | 按說話人跳躍、完整信息 | 不連續 |
|
||||
| **連續播放** | 完整體驗 | 從頭到尾、不間斷 | 不能暫停 |
|
||||
|
||||
### 推薦用法
|
||||
|
||||
#### 1. 快速了解內容
|
||||
```bash
|
||||
./run_demo.sh --quick
|
||||
```
|
||||
|
||||
#### 2. 說話人分析
|
||||
```bash
|
||||
./run_demo.sh
|
||||
```
|
||||
|
||||
#### 3. 完整觀看
|
||||
```bash
|
||||
./play_continuous.sh --video
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 技術實現
|
||||
|
||||
### 說話人演示
|
||||
- **語言**: Rust
|
||||
- **實現**: `integrated_player` binary
|
||||
- **數據源**: ASRX JSON
|
||||
- **控制**: 預設片段數
|
||||
|
||||
### 連續播放
|
||||
- **語言**: Bash
|
||||
- **實現**: `play_continuous.sh` 腳本
|
||||
- **數據源**: ASRX JSON
|
||||
- **控制**: Ctrl+C 停止
|
||||
- **工具**: jq, ffplay
|
||||
|
||||
---
|
||||
|
||||
## 未來改進
|
||||
|
||||
### 計劃功能
|
||||
|
||||
1. **暫停控制**
|
||||
- 使用 Rust 實現
|
||||
- 支持鍵盤交互
|
||||
- 空格鍵暫停
|
||||
|
||||
2. **進度條**
|
||||
- 顯示當前進度
|
||||
- 剩餘時間估算
|
||||
|
||||
3. **字幕疊加**
|
||||
- 在視頻上顯示 ASR 文字
|
||||
- 支持多語言
|
||||
|
||||
4. **人臉標註**
|
||||
- 在視頻上畫出人臉框
|
||||
- 實時顯示檢測結果
|
||||
|
||||
5. **完整信息展示**
|
||||
- ASR 文字
|
||||
- 人臉檢測
|
||||
- 嘴部動作
|
||||
- OCR 文字
|
||||
- 場景識別
|
||||
|
||||
---
|
||||
|
||||
## 常見問題
|
||||
|
||||
### Q: 為什麼連續模式不能暫停?
|
||||
|
||||
A: 簡化版使用 Bash 腳本實現,沒有鍵盤監聽功能。完整版需要 Rust 實現,目前開發中。
|
||||
|
||||
### Q: 說話人演示為什麼會跳躍?
|
||||
|
||||
A: 說話人演示按說話人分組播放,會跳過其他說話人的片段。連續模式才會完整播放。
|
||||
|
||||
### Q: 如何查看所有片段?
|
||||
|
||||
A: 使用連續播放模式:
|
||||
```bash
|
||||
./play_continuous.sh
|
||||
```
|
||||
|
||||
### Q: 播放速度如何?
|
||||
|
||||
A: 兩種模式都按實際時長播放。說話人演示有 2 秒暫停。
|
||||
|
||||
---
|
||||
|
||||
## 快速命令參考
|
||||
|
||||
```bash
|
||||
# 說話人演示(音頻)
|
||||
./run_demo.sh
|
||||
|
||||
# 說話人演示(視頻)
|
||||
./run_demo.sh --video
|
||||
|
||||
# 快速演示
|
||||
./run_demo.sh --quick
|
||||
|
||||
# 連續播放(音頻)
|
||||
./play_continuous.sh
|
||||
|
||||
# 連續播放(視頻)
|
||||
./play_continuous.sh --video
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**更新日期**: 2026-04-02
|
||||
**版本**: 2.1.0
|
||||
**作者**: Momentry Team
|
||||
@@ -1,222 +0,0 @@
|
||||
# 🎬 演示功能使用說明
|
||||
|
||||
## 問題:只聽到聲音?
|
||||
|
||||
您遇到的情況是正常的!默認情況下,演示模式只播放音頻,不顯示視頻畫面。這樣可以:
|
||||
- 快速瀏覽所有說話人
|
||||
- 專注於語音內容
|
||||
- 不受視頻窗口干擾
|
||||
|
||||
---
|
||||
|
||||
## 解決方案:顯示視頻畫面
|
||||
|
||||
### 方法 1: 使用演示腳本(推薦)
|
||||
|
||||
```bash
|
||||
# 快速演示 + 顯示視頻
|
||||
./run_demo.sh --quick --video
|
||||
|
||||
# 標準演示 + 顯示視頻
|
||||
./run_demo.sh --video
|
||||
|
||||
# 完整演示 + 顯示視頻
|
||||
./run_demo.sh --video
|
||||
```
|
||||
|
||||
### 方法 2: 直接使用播放器
|
||||
|
||||
```bash
|
||||
# 啟用視頻顯示
|
||||
./target/debug/integrated_player \
|
||||
--video /tmp/charade_audio.wav \
|
||||
--asrx /tmp/asrx_charade_optimized.json \
|
||||
--demo \
|
||||
--show-video
|
||||
|
||||
# 自定義視頻窗口大小
|
||||
./target/debug/integrated_player \
|
||||
--video /tmp/charade_audio.wav \
|
||||
--asrx /tmp/asrx_charade_optimized.json \
|
||||
--demo \
|
||||
--show-video \
|
||||
--video-width 1200 \
|
||||
--video-height 800
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 完整參數說明
|
||||
|
||||
### 視頻相關參數
|
||||
|
||||
| 參數 | 說明 | 默認值 |
|
||||
|------|------|--------|
|
||||
| `--show-video` | 顯示視頻畫面 | false(僅音頻) |
|
||||
| `--video-width` | 視頻窗口寬度 | 800 |
|
||||
| `--video-height` | 視頻窗口高度 | 600 |
|
||||
|
||||
### 演示相關參數
|
||||
|
||||
| 參數 | 說明 | 默認值 |
|
||||
|------|------|--------|
|
||||
| `--demo` | 啟用自動演示模式 | false |
|
||||
| `--demo-segments-per-speaker` | 每個說話人的片段數 | 3 |
|
||||
| `--demo-speed` | 演示速度倍數 | 2.0 |
|
||||
|
||||
---
|
||||
|
||||
## 使用範例
|
||||
|
||||
### 1. 音頻模式(默認)
|
||||
|
||||
```bash
|
||||
# 僅播放音頻,不顯示視頻
|
||||
./run_demo.sh --quick
|
||||
```
|
||||
|
||||
**優點**:
|
||||
- ✅ 快速瀏覽
|
||||
- ✅ 專注語音
|
||||
- ✅ 不受干擾
|
||||
|
||||
**適用場景**:
|
||||
- 快速測試
|
||||
- 語音內容分析
|
||||
- 後台播放
|
||||
|
||||
### 2. 視頻模式
|
||||
|
||||
```bash
|
||||
# 顯示視頻畫面
|
||||
./run_demo.sh --video
|
||||
```
|
||||
|
||||
**優點**:
|
||||
- ✅ 視聽結合
|
||||
- ✅ 更直觀
|
||||
- ✅ 完整體驗
|
||||
|
||||
**適用場景**:
|
||||
- 功能演示
|
||||
- 內容展示
|
||||
- 完整測試
|
||||
|
||||
### 3. 自定義模式
|
||||
|
||||
```bash
|
||||
# 慢速演示 + 大窗口
|
||||
./target/debug/integrated_player \
|
||||
--video /tmp/charade_audio.wav \
|
||||
--asrx /tmp/asrx_charade_optimized.json \
|
||||
--demo \
|
||||
--show-video \
|
||||
--demo-speed 1.0 \
|
||||
--video-width 1600 \
|
||||
--video-height 900 \
|
||||
--demo-segments-per-speaker 5
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 對比:音頻模式 vs 視頻模式
|
||||
|
||||
| 特性 | 音頻模式 | 視頻模式 |
|
||||
|------|----------|----------|
|
||||
| **播放速度** | 快 | 慢 |
|
||||
| **資源占用** | 低 | 高 |
|
||||
| **窗口干擾** | 無 | 有 |
|
||||
| **體驗完整度** | 70% | 100% |
|
||||
| **適用場景** | 測試、分析 | 演示、展示 |
|
||||
|
||||
---
|
||||
|
||||
## 推薦使用方式
|
||||
|
||||
### 開發測試
|
||||
```bash
|
||||
# 快速音頻測試
|
||||
./run_demo.sh --quick
|
||||
```
|
||||
|
||||
### 功能演示
|
||||
```bash
|
||||
# 視頻演示
|
||||
./run_demo.sh --video
|
||||
```
|
||||
|
||||
### 完整體驗
|
||||
```bash
|
||||
# 慢速視頻演示
|
||||
./target/debug/integrated_player \
|
||||
--video /tmp/charade_audio.wav \
|
||||
--asrx /tmp/asrx_charade_optimized.json \
|
||||
--face /tmp/face_long.json \
|
||||
--demo \
|
||||
--show-video \
|
||||
--demo-speed 1.0 \
|
||||
--demo-segments-per-speaker 5
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常見問題
|
||||
|
||||
### Q: 為什麼默認不顯示視頻?
|
||||
|
||||
A:
|
||||
1. **性能考慮**:音頻模式更快、更輕量
|
||||
2. **專注內容**:不受視覺干擾
|
||||
3. **測試效率**:快速瀏覽所有說話人
|
||||
|
||||
### Q: 如何調整視頻窗口大小?
|
||||
|
||||
A:
|
||||
```bash
|
||||
--video-width 1200 --video-height 800
|
||||
```
|
||||
|
||||
### Q: 可以同時顯示字幕嗎?
|
||||
|
||||
A: 目前終端會顯示 ASR 文字,未來可以考慮在視頻上疊加字幕。
|
||||
|
||||
### Q: 視頻播放卡頓?
|
||||
|
||||
A:
|
||||
1. 使用較低的 `--demo-speed`(如 1.5)
|
||||
2. 減少 `--demo-segments-per-speaker`
|
||||
3. 確保系統性能足夠
|
||||
|
||||
---
|
||||
|
||||
## 完整演示命令集合
|
||||
|
||||
```bash
|
||||
# 1. 快速音頻演示
|
||||
./run_demo.sh --quick
|
||||
|
||||
# 2. 快速視頻演示
|
||||
./run_demo.sh --quick --video
|
||||
|
||||
# 3. 標準音頻演示
|
||||
./run_demo.sh
|
||||
|
||||
# 4. 標準視頻演示
|
||||
./run_demo.sh --video
|
||||
|
||||
# 5. 完整視頻演示(慢速 + 大窗口)
|
||||
./target/debug/integrated_player \
|
||||
--video /tmp/charade_audio.wav \
|
||||
--asrx /tmp/asrx_charade_optimized.json \
|
||||
--demo \
|
||||
--show-video \
|
||||
--demo-speed 1.0 \
|
||||
--video-width 1600 \
|
||||
--video-height 900
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**更新日期**: 2026-04-02
|
||||
**版本**: 1.2.0
|
||||
**作者**: Momentry Team
|
||||
@@ -1,586 +0,0 @@
|
||||
# Momentry API 使用說明 (curl 範例)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 版本 | V1.4 |
|
||||
| 日期 | 2026-03-26 |
|
||||
| Base URL | `http://localhost:3002` |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.4 | 2026-03-26 | 新增: 任務管理端點 (`/api/v1/jobs`, `/api/v1/jobs/:uuid`),更新註冊端點回應格式 | OpenCode | deepseek-reasoner |
|
||||
| V1.3 | 2026-03-25 | 更新: n8n 搜尋回傳 `file_path` 取代 `media_url`,新增 API Key 驗證說明 | OpenCode | deepseek-reasoner |
|
||||
| V1.2 | 2026-03-23 | 建立 curl 範例文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
| V1.1 | 2026-03-18 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
|
||||
---
|
||||
|
||||
> **狀態說明**:
|
||||
> - ✅ **已實作**: 健康檢查、搜尋、影片管理端點
|
||||
> - ⚠️ **規劃中**: API Key 管理功能
|
||||
> - 🔧 **CLI**: 部分功能需使用命令列工具
|
||||
|
||||
---
|
||||
|
||||
## 目錄
|
||||
|
||||
1. [已實作端點](#1-已實作端點)
|
||||
2. [API Key 管理](#2-api-key-管理-規劃中)
|
||||
3. [影片管理](#3-影片管理)
|
||||
4. [查詢與搜索](#4-查詢與搜索)
|
||||
5. [系統狀態](#5-系統狀態)
|
||||
|
||||
---
|
||||
|
||||
## URL 選擇指南
|
||||
|
||||
### 兩種 URL 的使用情境
|
||||
|
||||
| 環境 | URL | 說明 |
|
||||
|------|-----|------|
|
||||
| **本地開發** | `http://localhost:3002` | 直接訪問 API,繞過反向代理 |
|
||||
| **外部訪問** | `https://api.momentry.ddns.net` | 通過 Caddy 反向代理訪問,需網路可達 |
|
||||
|
||||
### 何時使用 localhost:3002
|
||||
|
||||
- ✅ 開發/測試環境
|
||||
- ✅ 直接在伺服器上操作
|
||||
- ✅ 當 Caddy/反向代理有問題時
|
||||
- ✅ 需要快速除錯時
|
||||
|
||||
### 何時使用 api.momentry.ddns.net
|
||||
|
||||
- ✅ n8n workflow 中呼叫 API
|
||||
- ✅ 外部系統整合
|
||||
- ✅ 生產環境
|
||||
- ✅ 從其他機器訪問
|
||||
|
||||
### 快速切換範例
|
||||
|
||||
```bash
|
||||
# 本地測試
|
||||
curl http://localhost:3002/health
|
||||
|
||||
# 外部測試(功能相同)
|
||||
curl https://api.momentry.ddns.net/health
|
||||
```
|
||||
|
||||
### 常見問題
|
||||
|
||||
**Q: 為什麼有兩個 URL?**
|
||||
A: `localhost:3002` 是直接訪問,`api.momentry.ddns.net` 通過 Caddy 反向代理。
|
||||
|
||||
**Q: 兩者功能相同嗎?**
|
||||
A: 是的,所有端點和功能完全相同。
|
||||
|
||||
**Q: 502 錯誤時怎麼辦?**
|
||||
A: 如果 `api.momentry.ddns.net` 返回 502,檢查 Momentry API 服務是否運行:
|
||||
```bash
|
||||
launchctl list | grep momentry.api
|
||||
# 如果未運行
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API 認證
|
||||
|
||||
所有 `/api/v1/*` 端點(除了健康檢查)都需要 API Key 認證。請在請求標頭中加入:
|
||||
|
||||
```
|
||||
-H "X-API-Key: YOUR_API_KEY"
|
||||
```
|
||||
|
||||
**目前示範使用的 API Key**: `demo_api_key_12345`
|
||||
|
||||
> **注意**: 正式環境請使用安全的 API Key 管理機制。
|
||||
|
||||
---
|
||||
|
||||
## 1. 已實作端點
|
||||
|
||||
### 健康檢查
|
||||
|
||||
```bash
|
||||
curl http://localhost:3002/health
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{"status":"ok","version":"0.1.0","uptime_ms":123456}
|
||||
```
|
||||
|
||||
### 詳細健康檢查
|
||||
|
||||
```bash
|
||||
curl http://localhost:3002/health/detailed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. API Key 管理 *(規劃中)*
|
||||
|
||||
> ⚠️ **此功能尚未實作**。以下為規劃中的 API 說明,僅供參考。
|
||||
|
||||
### 2.1 建立 API Key
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/api-keys \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: your-admin-key" \
|
||||
-d '{
|
||||
"name": "my-service-key",
|
||||
"key_type": "service",
|
||||
"permissions": ["read", "write"],
|
||||
"ttl_days": 90
|
||||
}'
|
||||
```
|
||||
|
||||
### 2.2 列出所有 API Keys
|
||||
|
||||
```bash
|
||||
curl -X GET http://localhost:3002/api/v1/api-keys \
|
||||
-H "X-API-Key: your-admin-key"
|
||||
```
|
||||
|
||||
### 2.3 驗證 API Key
|
||||
|
||||
```bash
|
||||
curl -X GET http://localhost:3002/api/v1/api-keys/validate \
|
||||
-H "X-API-Key: key-to-validate"
|
||||
```
|
||||
|
||||
### 2.4 撤銷 API Key
|
||||
|
||||
```bash
|
||||
curl -X DELETE http://localhost:3002/api/v1/api-keys/msvc_a1b2c3d4_... \
|
||||
-H "X-API-Key: your-admin-key"
|
||||
```
|
||||
|
||||
### 2.5 請求 Key 輪換
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/api-keys/msvc_a1b2c3d4_.../rotate \
|
||||
-H "X-API-Key: your-admin-key" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"reason": "scheduled_rotation"}'
|
||||
```
|
||||
|
||||
### 2.6 取得統計資訊
|
||||
|
||||
```bash
|
||||
curl -X GET http://localhost:3002/api/v1/api-keys/stats \
|
||||
-H "X-API-Key: your-admin-key"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 影片管理
|
||||
|
||||
### 3.1 註冊影片 ✅
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"path": "/path/to/video.mp4"}'
|
||||
```
|
||||
|
||||
**回應範例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"uuid": "a1b2c3d4e5f6g7h8",
|
||||
"video_id": 1,
|
||||
"job_id": 123,
|
||||
"file_name": "video.mp4",
|
||||
"duration": 120.5,
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"already_exists": false
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 列出所有影片 ✅
|
||||
|
||||
```bash
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos
|
||||
```
|
||||
|
||||
### 3.3 查詢影片 ✅
|
||||
|
||||
```bash
|
||||
# 依 UUID 查詢
|
||||
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3002/api/v1/lookup?uuid=a1b2c3d4e5f6g7h8"
|
||||
|
||||
# 依路徑查詢
|
||||
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3002/api/v1/lookup?path=/path/to/video.mp4"
|
||||
```
|
||||
|
||||
### 3.4 處理影片 🔧 *(CLI - 非 API)*
|
||||
|
||||
影片處理需要使用 CLI 命令:
|
||||
|
||||
```bash
|
||||
# 處理影片(生成 ASR, CUT, YOLO, OCR, Face, Pose 資料)
|
||||
cargo run --bin momentry -- process <uuid>
|
||||
|
||||
# 或處理多個影片
|
||||
cargo run --bin momentry -- process <uuid1> <uuid2> <uuid3>
|
||||
```
|
||||
|
||||
### 3.5 取得處理進度 ✅
|
||||
|
||||
```bash
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/progress/<uuid>
|
||||
```
|
||||
|
||||
**回應範例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"uuid": "a1b2c3d4e5f6g7h8",
|
||||
"overall_progress": 75,
|
||||
"processors": [
|
||||
{
|
||||
"name": "asr",
|
||||
"status": "complete",
|
||||
"current": 100,
|
||||
"total": 100,
|
||||
"progress": 100,
|
||||
"message": "7 segments"
|
||||
},
|
||||
{
|
||||
"name": "cut",
|
||||
"status": "complete",
|
||||
"current": 134,
|
||||
"total": 134,
|
||||
"progress": 100,
|
||||
"message": "134 scenes"
|
||||
},
|
||||
{
|
||||
"name": "yolo",
|
||||
"status": "progress",
|
||||
"current": 5000,
|
||||
"total": 14315,
|
||||
"progress": 35,
|
||||
"message": "frame 5000"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 3.6 任務管理 ✅
|
||||
|
||||
```bash
|
||||
# 列出所有任務
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/jobs
|
||||
|
||||
# 取得特定任務詳情
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/jobs/<uuid>
|
||||
```
|
||||
|
||||
**任務列表回應範例**:
|
||||
```json
|
||||
{
|
||||
"jobs": [
|
||||
{
|
||||
"id": 123,
|
||||
"uuid": "a1b2c3d4e5f6g7h8",
|
||||
"status": "pending",
|
||||
"current_processor": null,
|
||||
"progress_current": 0,
|
||||
"progress_total": 100,
|
||||
"created_at": "2026-03-26 10:30:00",
|
||||
"started_at": null
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**任務詳情回應範例**:
|
||||
```json
|
||||
{
|
||||
"id": 123,
|
||||
"uuid": "a1b2c3d4e5f6g7h8",
|
||||
"status": "processing",
|
||||
"current_processor": "asr",
|
||||
"progress_current": 50,
|
||||
"progress_total": 100,
|
||||
"processors": [
|
||||
{
|
||||
"processor_type": "asr",
|
||||
"status": "complete",
|
||||
"started_at": "2026-03-26 10:30:00",
|
||||
"completed_at": "2026-03-26 10:35:00",
|
||||
"duration_secs": 300.5,
|
||||
"error_message": null
|
||||
},
|
||||
{
|
||||
"processor_type": "cut",
|
||||
"status": "pending",
|
||||
"started_at": null,
|
||||
"completed_at": null,
|
||||
"duration_secs": null,
|
||||
"error_message": null
|
||||
}
|
||||
],
|
||||
"created_at": "2026-03-26 10:30:00",
|
||||
"started_at": "2026-03-26 10:30:00",
|
||||
"updated_at": "2026-03-26 10:35:00"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 查詢與搜索
|
||||
|
||||
### 4.1 語意搜尋 ✅
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{
|
||||
"query": "測試關鍵字",
|
||||
"limit": 5
|
||||
}'
|
||||
```
|
||||
|
||||
**回應範例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uuid": "a1b2c3d4e5f6g7h8",
|
||||
"chunk_id": "sentence_0006",
|
||||
"chunk_type": "sentence",
|
||||
"start_time": 48.8,
|
||||
"end_time": 55.44,
|
||||
"text": "fun plot twists...",
|
||||
"score": 0.526
|
||||
}
|
||||
],
|
||||
"query": "測試關鍵字"
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 n8n 格式搜尋 ✅
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/n8n/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{
|
||||
"query": "測試關鍵字",
|
||||
"limit": 5
|
||||
}'
|
||||
```
|
||||
|
||||
**回應範例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"query": "測試關鍵字",
|
||||
"count": 2,
|
||||
"hits": [
|
||||
{
|
||||
"id": "c_001",
|
||||
"vid": "a1b2c3d4e5f6g7h8",
|
||||
"start": 48.8,
|
||||
"end": 55.44,
|
||||
"title": "Chunk sentence_0006",
|
||||
"text": "fun plot twists...",
|
||||
"score": 0.92,
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 混合搜尋 ✅
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/search/hybrid \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{
|
||||
"query": "測試關鍵字",
|
||||
"limit": 5
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 系統狀態
|
||||
|
||||
### 5.1 健康檢查 ✅
|
||||
|
||||
```bash
|
||||
curl http://localhost:3002/health
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{"status":"ok","version":"0.1.0","uptime_ms":123456}
|
||||
```
|
||||
|
||||
### 5.2 詳細健康檢查 ✅
|
||||
|
||||
```bash
|
||||
curl http://localhost:3002/health/detailed
|
||||
```
|
||||
|
||||
**回應範例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status":"ok",
|
||||
"version":"0.1.0",
|
||||
"uptime_ms":123456,
|
||||
"services":{
|
||||
"postgres":{"status":"ok","latency_ms":42,"error":null},
|
||||
"redis":{"status":"ok","latency_ms":0,"error":null},
|
||||
"qdrant":{"status":"ok","latency_ms":15,"error":null}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. n8n Webhook 測試
|
||||
|
||||
### 測試 n8n Workflow
|
||||
|
||||
**重要**: 測試前請先在 n8n UI 中點擊 "Execute workflow" 按鈕
|
||||
|
||||
```bash
|
||||
# 測試 Video RAG Workflow (Test Mode)
|
||||
curl -X POST http://localhost:5678/webhook-test/video-rag-mcp \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query":"charade","limit":3}'
|
||||
|
||||
# 帶有 UUID 過濾的搜尋
|
||||
curl -X POST http://localhost:5678/webhook-test/video-rag-mcp \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query":"woody","limit":5,"uuid":"a1b10138a6bbb0cd"}'
|
||||
```
|
||||
|
||||
### 生產環境 Webhook
|
||||
|
||||
**注意**: 工作流程必須處於 Active 狀態
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:5678/webhook/video-rag-mcp \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query":"charade","limit":3}'
|
||||
```
|
||||
|
||||
### n8n Webhook 常見問題
|
||||
|
||||
**Q: webhook-test 返回 404**
|
||||
A: 需要在 n8n UI 中點擊 "Execute workflow" 按鈕後才能使用 test webhook
|
||||
|
||||
**Q: webhook (生產環境) 返回 404**
|
||||
A: 需要將工作流程切換為 Active 狀態 (右上角開關)
|
||||
|
||||
---
|
||||
|
||||
## 附錄
|
||||
|
||||
### A. 服務 URL 列表
|
||||
|
||||
| 服務 | URL |
|
||||
|------|-----|
|
||||
| Momentry API (本地) | `http://localhost:3002` |
|
||||
| Momentry API (外部) | `https://api.momentry.ddns.net` |
|
||||
| n8n Web UI | `https://n8n.momentry.ddns.net` |
|
||||
| n8n Webhook Test | `http://localhost:5678/webhook-test/{workflow-name}` |
|
||||
| n8n Webhook Prod | `http://localhost:5678/webhook/{workflow-name}` |
|
||||
|
||||
### B. 所有可用端點
|
||||
|
||||
| 端點 | 方法 | 狀態 | 說明 |
|
||||
|------|------|------|------|
|
||||
| `/health` | GET | ✅ | 健康檢查 |
|
||||
| `/health/detailed` | GET | ✅ | 詳細健康檢查 |
|
||||
| `/api/v1/register` | POST | ✅ | 註冊影片 |
|
||||
| `/api/v1/search` | POST | ✅ | 語意搜尋 |
|
||||
| `/api/v1/n8n/search` | POST | ✅ | n8n 格式搜尋 |
|
||||
| `/api/v1/search/hybrid` | POST | ✅ | 混合搜尋 |
|
||||
| `/api/v1/lookup` | GET | ✅ | 查詢影片 |
|
||||
| `/api/v1/videos` | GET | ✅ | 列出所有影片 |
|
||||
| `/api/v1/progress/:uuid` | GET | ✅ | 處理進度 |
|
||||
| `/api/v1/jobs` | GET | ✅ | 任務列表 |
|
||||
| `/api/v1/jobs/:uuid` | GET | ✅ | 任務詳情 |
|
||||
| `/api/v1/api-keys` | * | ⚠️ | API Key 管理 (規劃中) |
|
||||
|
||||
### C. 常見錯誤
|
||||
|
||||
| HTTP 狀態 | 說明 | 解決方式 |
|
||||
|-----------|------|----------|
|
||||
| 200 | 成功 | - |
|
||||
| 400 | 請求格式錯誤 | 檢查 JSON 格式 |
|
||||
| 404 | 端點不存在或資源未找到 | 確認端點 URL 正確 |
|
||||
| 500 | 伺服器內部錯誤 | 檢查 API 服務日誌 |
|
||||
| **502** | **Bad Gateway** | **API 服務未啟動,見下方說明** |
|
||||
|
||||
#### 502 Bad Gateway 錯誤
|
||||
|
||||
**問題**: 外部 URL `https://api.momentry.ddns.net` 返回 502
|
||||
|
||||
**原因**: Momentry Core API 服務未啟動
|
||||
|
||||
**解決方式**:
|
||||
|
||||
```bash
|
||||
# 1. 檢查服務狀態
|
||||
launchctl list | grep momentry.api
|
||||
|
||||
# 2. 如果未啟動,手動啟動
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
|
||||
|
||||
# 3. 或使用本地測試(繞過反向代理)
|
||||
curl http://localhost:3002/health
|
||||
|
||||
# 4. 檢查日誌
|
||||
tail -50 /Users/accusys/momentry/log/momentry_api.error.log
|
||||
```
|
||||
|
||||
### D. 範例腳本
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# api_test.sh - API 測試腳本
|
||||
|
||||
API_URL="http://localhost:3002"
|
||||
|
||||
# 健康檢查
|
||||
echo "=== Health Check ==="
|
||||
curl -s "$API_URL/health" | jq .
|
||||
|
||||
# 搜尋
|
||||
echo -e "\n=== Search ==="
|
||||
curl -s -X POST "$API_URL/api/v1/search" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "test", "limit": 3}' | jq .
|
||||
|
||||
# 列出影片
|
||||
echo -e "\n=== Videos ==="
|
||||
curl -s -H "X-API-Key: YOUR_API_KEY" "$API_URL/api/v1/videos" | jq '.videos | length'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 相關文件
|
||||
|
||||
- [API_INDEX.md](./API_INDEX.md) - 文件總覽(起點)
|
||||
- [API_ENDPOINTS.md](./API_ENDPOINTS.md) - 端點完整說明
|
||||
- [API_N8N_GUIDE.md](./API_N8N_GUIDE.md) - n8n 使用範例
|
||||
- [API_WORDPRESS_GUIDE.md](./API_WORDPRESS_GUIDE.md) - WordPress 使用範例
|
||||
@@ -1,771 +0,0 @@
|
||||
# Momentry Core API 使用範例總覽
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 版本 | V2.1 |
|
||||
| 日期 | 2026-03-26 |
|
||||
| Base URL (本地) | `http://localhost:3002` |
|
||||
| Base URL (外部) | `https://api.momentry.ddns.net` |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 |
|
||||
|------|------|------|--------|
|
||||
| V2.0 | 2026-03-25 | 創建完整範例總覽 | OpenCode |
|
||||
| V2.1 | 2026-03-26 | 更新API回應格式 (media_url→file_path) 與認證標頭 | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## 快速參考
|
||||
|
||||
### 環境 URL 選擇
|
||||
|
||||
| 環境 | URL | 用途 |
|
||||
|------|-----|------|
|
||||
| **本地開發** | `http://localhost:3002` | 開發/測試,直接訪問 API |
|
||||
| **外部訪問** | `https://api.momentry.ddns.net` | n8n、WordPress、curl 生產環境 |
|
||||
|
||||
### 所有可用端點
|
||||
|
||||
| 方法 | 端點 | 說明 |
|
||||
|------|------|------|
|
||||
| GET | `/health` | 健康檢查 |
|
||||
| GET | `/health/detailed` | 詳細健康檢查 |
|
||||
| POST | `/api/v1/search` | 語意搜尋(標準格式) |
|
||||
| POST | `/api/v1/n8n/search` | 語意搜尋(n8n 格式) |
|
||||
| POST | `/api/v1/search/hybrid` | 混合搜尋 |
|
||||
| POST | `/api/v1/register` | 註冊影片 |
|
||||
| POST | `/api/v1/probe` | 探測影片資訊 |
|
||||
| GET | `/api/v1/videos` | 列出所有影片 |
|
||||
| GET | `/api/v1/lookup` | 查詢影片 |
|
||||
| GET | `/api/v1/progress/:uuid` | 處理進度 |
|
||||
| GET | `/api/v1/jobs` | 任務列表 |
|
||||
| GET | `/api/v1/jobs/:uuid` | 任務詳情 |
|
||||
|
||||
---
|
||||
|
||||
## 認證
|
||||
|
||||
### API Key
|
||||
|
||||
所有 `/api/v1/*` 端點需要 API Key 認證。
|
||||
|
||||
```bash
|
||||
# 添加 API Key Header
|
||||
curl -H "X-API-Key: your-api-key" http://localhost:3002/api/v1/videos
|
||||
|
||||
# 範例
|
||||
curl -H "X-API-Key: muser_f08e13ba967e4d8ea8fc542ad9f99ac8_1774416728_90472a35" \
|
||||
http://localhost:3002/api/v1/videos
|
||||
```
|
||||
|
||||
### 響應狀態
|
||||
|
||||
| 狀態碼 | 說明 |
|
||||
|--------|------|
|
||||
| 200 | 成功 |
|
||||
| 401 | 未授權(缺少或無效 API Key) |
|
||||
| 500 | 伺服器錯誤 |
|
||||
|
||||
### 建立 API Key
|
||||
|
||||
```bash
|
||||
./target/release/momentry api-key create "My Key" --key-type user
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. curl 範例
|
||||
|
||||
### 基本語法
|
||||
|
||||
```bash
|
||||
# 格式
|
||||
curl [OPTIONS] URL
|
||||
|
||||
# 常用選項
|
||||
-X METHOD # HTTP 方法 (GET, POST, etc.)
|
||||
-H HEADER # 添加 HTTP 標頭
|
||||
-d DATA # POST 請求體
|
||||
-s # 靜默模式
|
||||
-w FORMAT # 輸出額外信息
|
||||
```
|
||||
|
||||
### 1.1 健康檢查
|
||||
|
||||
```bash
|
||||
# 基本健康檢查
|
||||
curl http://localhost:3002/health
|
||||
|
||||
# 詳細健康檢查
|
||||
curl http://localhost:3002/health/detailed
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{"status":"ok","version":"0.1.0","uptime_ms":123456}
|
||||
```
|
||||
|
||||
### 1.2 語意搜尋
|
||||
|
||||
```bash
|
||||
# 標準格式搜尋
|
||||
curl -X POST http://localhost:3002/api/v1/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "charade", "limit": 5}'
|
||||
|
||||
# n8n 格式搜尋(推薦)
|
||||
curl -X POST http://localhost:3002/api/v1/n8n/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "charade", "limit": 5}'
|
||||
|
||||
# 混合搜尋
|
||||
curl -X POST http://localhost:3002/api/v1/search/hybrid \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "charade", "limit": 5}'
|
||||
```
|
||||
|
||||
**標準格式回應**:
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uuid": "a1b10138a6bbb0cd",
|
||||
"chunk_id": "sentence_0001",
|
||||
"chunk_type": "sentence",
|
||||
"start_time": 48.8,
|
||||
"end_time": 55.44,
|
||||
"text": "fun plot twists...",
|
||||
"score": 0.92
|
||||
}
|
||||
],
|
||||
"query": "charade"
|
||||
}
|
||||
```
|
||||
|
||||
**n8n 格式回應**:
|
||||
```json
|
||||
{
|
||||
"query": "charade",
|
||||
"count": 1,
|
||||
"hits": [
|
||||
{
|
||||
"id": "sentence_0001",
|
||||
"vid": "a1b10138a6bbb0cd",
|
||||
"start": 48.8,
|
||||
"end": 55.44,
|
||||
"title": "Chunk sentence_0001",
|
||||
"text": "fun plot twists...",
|
||||
"score": 0.92,
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 1.3 影片管理
|
||||
|
||||
```bash
|
||||
# 列出所有影片
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos
|
||||
|
||||
# 查詢特定影片(依 UUID)
|
||||
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3002/api/v1/lookup?uuid=a1b10138a6bbb0cd"
|
||||
|
||||
# 查詢特定影片(依路徑)
|
||||
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3002/api/v1/lookup?path=/path/to/video.mp4"
|
||||
|
||||
# 取得處理進度
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/progress/a1b10138a6bbb0cd
|
||||
|
||||
# 探測影片(不註冊)
|
||||
curl -X POST http://localhost:3002/api/v1/probe \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"path": "/path/to/video.mp4"}'
|
||||
|
||||
# 註冊影片
|
||||
curl -X POST http://localhost:3002/api/v1/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"path": "/path/to/video.mp4"}'
|
||||
```
|
||||
|
||||
### 1.4 批次測試腳本
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# api_test.sh - API 測試腳本
|
||||
|
||||
API_URL="http://localhost:3002"
|
||||
|
||||
echo "=== 健康檢查 ==="
|
||||
curl -s "$API_URL/health" | jq .
|
||||
|
||||
echo -e "\n=== 語意搜尋 ==="
|
||||
curl -s -X POST "$API_URL/api/v1/search" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "charade", "limit": 3}' | jq .
|
||||
|
||||
echo -e "\n=== 影片列表 ==="
|
||||
curl -s -H "X-API-Key: YOUR_API_KEY" "$API_URL/api/v1/videos" | jq '.videos | length'
|
||||
```
|
||||
|
||||
### 1.5 外部 URL 範例
|
||||
|
||||
```bash
|
||||
# 使用外部 URL(需網路可達)
|
||||
curl https://api.momentry.ddns.net/health
|
||||
|
||||
# 外部搜尋
|
||||
curl -X POST https://api.momentry.ddns.net/api/v1/n8n/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "charade", "limit": 5}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. n8n 範例
|
||||
|
||||
### 2.1 HTTP Request Node 設定
|
||||
|
||||
```
|
||||
Node: HTTP Request
|
||||
├── URL: https://api.momentry.ddns.net/api/v1/n8n/search
|
||||
├── Method: POST
|
||||
├── Authentication: None
|
||||
├── Send Body: ✓ (checked)
|
||||
├── Content Type: JSON
|
||||
├── Body:
|
||||
│ {
|
||||
│ "query": "={{ $json.query }}",
|
||||
│ "limit": "={{ $json.limit || 10 }}"
|
||||
│ }
|
||||
├── Send Headers: ✓ (checked)
|
||||
└── Header Parameters:
|
||||
└── X-API-Key: {{ $env.MOMENTRY_API_KEY }}
|
||||
```
|
||||
|
||||
### 2.2 基本搜尋 Workflow
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Momentry Video Search",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"name": "Manual Trigger",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"position": [250, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "https://api.momentry.ddns.net/api/v1/n8n/search",
|
||||
"method": "POST",
|
||||
"sendBody": true,
|
||||
"contentType": "json",
|
||||
"body": {
|
||||
"query": "charade",
|
||||
"limit": 3
|
||||
}
|
||||
},
|
||||
"name": "Search Video API",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"position": [450, 300]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Manual Trigger": {
|
||||
"main": [[{"node": "Search Video API"}]]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 Webhook 動態搜尋
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Momentry Dynamic Search",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"httpMethod": "POST",
|
||||
"path": "search",
|
||||
"responseMode": "lastNode"
|
||||
},
|
||||
"name": "Webhook",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"position": [250, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "https://api.momentry.ddns.net/api/v1/n8n/search",
|
||||
"method": "POST",
|
||||
"sendBody": true,
|
||||
"contentType": "json",
|
||||
"body": {
|
||||
"query": "={{ JSON.stringify($json.body.query) }}",
|
||||
"limit": "={{ $json.body.limit || 5 }}"
|
||||
}
|
||||
},
|
||||
"name": "Search API",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"position": [450, 300]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Webhook": {
|
||||
"main": [[{"node": "Search API"}]]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 測試 Webhook
|
||||
|
||||
```bash
|
||||
# 測試模式(需先在 n8n UI 點擊 Execute)
|
||||
curl -X POST http://localhost:5678/webhook-test/video-rag-mcp \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query":"charade","limit":3}'
|
||||
|
||||
# 生產環境(需 workflow 為 Active 狀態)
|
||||
curl -X POST http://localhost:5678/webhook/video-rag-mcp \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query":"charade","limit":3}'
|
||||
```
|
||||
|
||||
### 2.5 健康檢查 Workflow
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Momentry Health Check",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"name": "Manual Trigger",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"position": [250, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "https://api.momentry.ddns.net/health",
|
||||
"method": "GET"
|
||||
},
|
||||
"name": "Health Check",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"position": [450, 300]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Manual Trigger": {
|
||||
"main": [[{"node": "Health Check"}]]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.6 錯誤處理
|
||||
|
||||
| 錯誤 | 原因 | 解決 |
|
||||
|------|------|------|
|
||||
| 502 Bad Gateway | API 服務未啟動 | `sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist` |
|
||||
| "Your request is invalid" | Body 格式設定錯誤 | 確認 Content Type: 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
|
||||
// 將文件路徑轉換為可訪問的 URL
|
||||
function convert_file_path_to_url($file_path) {
|
||||
// 範例: 將 SFTPGo 文件路徑轉換為 web URL
|
||||
// /Users/accusys/momentry/var/sftpgo/data/demo/video.mp4
|
||||
// → https://sftpgo.example.com/demo/video.mp4
|
||||
|
||||
// 移除基本路徑
|
||||
$base_path = '/Users/accusys/momentry/var/sftpgo/data/';
|
||||
if (strpos($file_path, $base_path) === 0) {
|
||||
$relative_path = substr($file_path, strlen($base_path));
|
||||
// 替換為實際的 SFTPGo web URL
|
||||
return 'https://sftpgo.example.com/' . $relative_path;
|
||||
}
|
||||
|
||||
// 如果無法轉換,返回原始路徑
|
||||
return $file_path;
|
||||
}
|
||||
|
||||
// 註冊短碼
|
||||
add_shortcode('momentry_search', function($atts) {
|
||||
$atts = shortcode_atts([
|
||||
'query' => '',
|
||||
'limit' => '10'
|
||||
], $atts);
|
||||
|
||||
if (empty($atts['query'])) {
|
||||
return '<p>請提供搜尋關鍵字</p>';
|
||||
}
|
||||
|
||||
$response = wp_remote_post('https://api.momentry.ddns.net/api/v1/n8n/search', [
|
||||
'headers' => [
|
||||
'Content-Type' => 'application/json',
|
||||
'X-API-Key' => 'YOUR_API_KEY' // 替換為實際的 API Key
|
||||
],
|
||||
'body' => json_encode([
|
||||
'query' => $atts['query'],
|
||||
'limit' => (int)$atts['limit']
|
||||
]),
|
||||
'timeout' => 30
|
||||
]);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
return '<p>搜尋服務暫時無法使用</p>';
|
||||
}
|
||||
|
||||
$data = json_decode(wp_remote_retrieve_body($response), true);
|
||||
|
||||
if (empty($data['hits'])) {
|
||||
return '<p>找不到相關結果</p>';
|
||||
}
|
||||
|
||||
$output = '<ul class="momentry-results">';
|
||||
foreach ($data['hits'] as $hit) {
|
||||
// 注意: API 現在返回 file_path 而非 media_url
|
||||
// 需要將文件路徑轉換為可訪問的 URL
|
||||
$file_path = $hit['file_path'];
|
||||
$video_url = convert_file_path_to_url($file_path); // 需要實作此函數
|
||||
|
||||
$output .= sprintf(
|
||||
'<li>%s <a href="%s?start=%s">播放</a></li>',
|
||||
esc_html($hit['text']),
|
||||
$video_url,
|
||||
$hit['start']
|
||||
);
|
||||
}
|
||||
$output .= '</ul>';
|
||||
|
||||
return $output;
|
||||
});
|
||||
?>
|
||||
```
|
||||
|
||||
**使用方式**:
|
||||
```
|
||||
[momentry_search query="charade" limit="5"]
|
||||
```
|
||||
|
||||
### 3.6 WordPress REST API Endpoint
|
||||
|
||||
在 WordPress REST API 中註冊自定義端點:
|
||||
|
||||
```php
|
||||
<?php
|
||||
// 註冊 REST API 端點
|
||||
add_action('rest_api_init', function() {
|
||||
register_rest_route('momentry/v1', '/search', [
|
||||
'methods' => 'POST',
|
||||
'callback' => function($request) {
|
||||
$response = wp_remote_post(
|
||||
'https://api.momentry.ddns.net/api/v1/n8n/search',
|
||||
[
|
||||
'headers' => ['Content-Type' => 'application/json'],
|
||||
'body' => json_encode([
|
||||
'query' => $request->get_param('query'),
|
||||
'limit' => $request->get_param('limit', 10)
|
||||
])
|
||||
]
|
||||
);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
return new WP_Error('api_error', 'API 請求失敗');
|
||||
}
|
||||
|
||||
return json_decode(wp_remote_retrieve_body($response));
|
||||
}
|
||||
]);
|
||||
});
|
||||
?>
|
||||
```
|
||||
|
||||
**呼叫方式**:
|
||||
```
|
||||
POST /wp-json/momentry/v1/search
|
||||
Body: {"query": "charade", "limit": 5}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 回應格式說明
|
||||
|
||||
### 4.1 n8n 格式 (`/api/v1/n8n/search`)
|
||||
|
||||
```json
|
||||
{
|
||||
"query": "charade",
|
||||
"count": 10,
|
||||
"hits": [
|
||||
{
|
||||
"id": "sentence_0001",
|
||||
"vid": "a1b10138a6bbb0cd",
|
||||
"start": 48.8,
|
||||
"end": 55.44,
|
||||
"title": "Chunk sentence_0001",
|
||||
"text": "fun plot twists...",
|
||||
"score": 0.92,
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 標準格式 (`/api/v1/search`)
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uuid": "a1b10138a6bbb0cd",
|
||||
"chunk_id": "sentence_0001",
|
||||
"chunk_type": "sentence",
|
||||
"start_time": 48.8,
|
||||
"end_time": 55.44,
|
||||
"text": "fun plot twists...",
|
||||
"score": 0.92
|
||||
}
|
||||
],
|
||||
"query": "charade"
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 健康檢查
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"version": "0.1.0",
|
||||
"uptime_ms": 123456
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 詳細健康檢查
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"version": "0.1.0",
|
||||
"uptime_ms": 123456,
|
||||
"services": {
|
||||
"postgres": {"status": "ok", "latency_ms": 42, "error": null},
|
||||
"redis": {"status": "ok", "latency_ms": 0, "error": null},
|
||||
"qdrant": {"status": "ok", "latency_ms": 15, "error": null},
|
||||
"mongodb": {"status": "ok", "latency_ms": 0, "error": null}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5 處理進度
|
||||
|
||||
```json
|
||||
{
|
||||
"uuid": "a1b10138a6bbb0cd",
|
||||
"file_name": "video.mp4",
|
||||
"duration": 120.5,
|
||||
"overall_progress": 75,
|
||||
"processors": [
|
||||
{"name": "asr", "status": "complete", "progress": 100},
|
||||
{"name": "cut", "status": "complete", "progress": 100},
|
||||
{"name": "yolo", "status": "progress", "progress": 35}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 4.6 Probe 回應
|
||||
|
||||
```json
|
||||
{
|
||||
"uuid": "a1b10138a6bbb0cd",
|
||||
"file_name": "video.mp4",
|
||||
"duration": 120.5,
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"fps": 30.0,
|
||||
"cached": false,
|
||||
"format": {
|
||||
"filename": "/path/to/video.mp4",
|
||||
"format_name": "mov,mp4,m4a,3gp,3g2,mj2",
|
||||
"duration": "120.5",
|
||||
"size": "12345678",
|
||||
"bit_rate": "819200"
|
||||
},
|
||||
"streams": [
|
||||
{
|
||||
"index": 0,
|
||||
"codec_name": "h264",
|
||||
"codec_type": "video",
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"r_frame_rate": "30/1",
|
||||
"duration": "120.5"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. HTTP 狀態碼
|
||||
|
||||
| 狀態 | 說明 | 解決 |
|
||||
|------|------|------|
|
||||
| 200 | 成功 | - |
|
||||
| 400 | 請求格式錯誤 | 檢查 JSON 格式 |
|
||||
| 404 | 端點或資源不存在 | 確認 URL 正確 |
|
||||
| 500 | 伺服器內部錯誤 | 檢查 API 服務日誌 |
|
||||
| 502 | API 服務未啟動 | 見下方說明 |
|
||||
|
||||
### 502 Bad Gateway 解決
|
||||
|
||||
```bash
|
||||
# 檢查服務狀態
|
||||
launchctl list | grep momentry.api
|
||||
|
||||
# 啟動服務
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
|
||||
|
||||
# 或使用本地測試
|
||||
curl http://localhost:3002/health
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 常見問題
|
||||
|
||||
### Q: 為什麼有兩個 URL?
|
||||
|
||||
| URL | 用途 |
|
||||
|-----|------|
|
||||
| `localhost:3002` | 直接訪問,繞過反向代理 |
|
||||
| `api.momentry.ddns.net` | 通過 Caddy 反向代理 |
|
||||
|
||||
### Q: 兩者功能相同嗎?
|
||||
|
||||
是的,所有端點和功能完全相同。
|
||||
|
||||
### Q: n8n webhook-test 返回 404?
|
||||
|
||||
需在 n8n UI 中點擊 "Execute workflow" 按鈕後才能使用測試 Webhook。
|
||||
|
||||
### Q: 生產環境 webhook 返回 404?
|
||||
|
||||
需將 workflow 切換為 Active 狀態(右上角開關)。
|
||||
|
||||
---
|
||||
|
||||
## 相關文件
|
||||
|
||||
- [API_INDEX.md](./API_INDEX.md) - 文件總覽
|
||||
- [API_ENDPOINTS.md](./API_ENDPOINTS.md) - 端點完整說明
|
||||
- [API_N8N_GUIDE.md](./API_N8N_GUIDE.md) - n8n 詳細指南
|
||||
- [API_WORDPRESS_GUIDE.md](./API_WORDPRESS_GUIDE.md) - WordPress 詳細指南
|
||||
@@ -1,236 +0,0 @@
|
||||
# API Key Management Integration Tests
|
||||
|
||||
## Test Environment Setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
```bash
|
||||
# Start services
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.postgresql.plist
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.redis.plist
|
||||
|
||||
# Set environment variables
|
||||
export DATABASE_URL="postgres://accusys@localhost:5432/momentry"
|
||||
export REDIS_URL="redis://:accusys@localhost:6379"
|
||||
export GITEA_URL="http://localhost:3000"
|
||||
export N8N_URL="https://n8n.momentry.ddns.net"
|
||||
```
|
||||
|
||||
### Run Tests
|
||||
|
||||
```bash
|
||||
# Run all unit tests
|
||||
cargo test --lib
|
||||
|
||||
# Run API key specific tests
|
||||
cargo test --lib api_key
|
||||
|
||||
# Run with output
|
||||
cargo test --lib -- --nocapture
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Test Cases
|
||||
|
||||
### 1. API Key Creation
|
||||
|
||||
```bash
|
||||
# Test: Create a service key
|
||||
momentry api-key create test-key --key-type service --ttl 90
|
||||
|
||||
# Expected Output:
|
||||
# ✅ API Key created successfully!
|
||||
# Key ID: msvc_...
|
||||
# API Key: msvc_...
|
||||
# Expires: 2026-06-19
|
||||
```
|
||||
|
||||
### 2. API Key Validation
|
||||
|
||||
```bash
|
||||
# Test: Validate the created key
|
||||
momentry api-key validate --key "msvc_..."
|
||||
|
||||
# Expected Output:
|
||||
# ✅ API Key is valid
|
||||
# Key ID: msvc_...
|
||||
# Name: test-key
|
||||
# Type: service
|
||||
```
|
||||
|
||||
### 3. API Key Listing
|
||||
|
||||
```bash
|
||||
# Test: List all keys
|
||||
momentry api-key list
|
||||
|
||||
# Expected Output:
|
||||
# 📋 API Key List
|
||||
# ┌────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ Status │ Name │ Type │ Usage │ Last Used │
|
||||
# ├────────────────────────────────────────────────────────────────────────────┤
|
||||
# │ ✓ active │ test-key │ "service" │ 0 │ never │
|
||||
# └────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 4. API Key Statistics
|
||||
|
||||
```bash
|
||||
# Test: Show statistics
|
||||
momentry api-key stats
|
||||
|
||||
# Expected Output:
|
||||
# 📊 API Key Statistics
|
||||
# ┌─────────────────────────────────────────┐
|
||||
# │ Total Keys: 1 │
|
||||
# │ Active Keys: 1 │
|
||||
# │ Expired Keys: 0 │
|
||||
# └─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 5. Gitea Token Creation
|
||||
|
||||
```bash
|
||||
# Test: Create Gitea token
|
||||
momentry gitea create \
|
||||
--username admin \
|
||||
--password "Test3200Test3200Test3200" \
|
||||
--token-name "test-token" \
|
||||
--scopes "read:repository,write:repository"
|
||||
|
||||
# Expected Output:
|
||||
# ✅ Gitea Token created successfully!
|
||||
# Token ID: ...
|
||||
# SHA1: ...
|
||||
```
|
||||
|
||||
### 6. n8n API Key Creation
|
||||
|
||||
```bash
|
||||
# Test: Create n8n API key
|
||||
momentry n8n create \
|
||||
--api-key "existing-n8n-key" \
|
||||
--label "test-key" \
|
||||
--expires-in-days 90
|
||||
|
||||
# Expected Output:
|
||||
# ✅ n8n API Key created successfully!
|
||||
# Key ID: ...
|
||||
# API Key: ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Automated Test Script
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# integration_test.sh
|
||||
|
||||
set -e
|
||||
|
||||
echo "=== API Key Integration Tests ==="
|
||||
|
||||
# 1. Create API key
|
||||
echo "1. Testing API key creation..."
|
||||
momentry api-key create integration-test --key-type service --ttl 30
|
||||
echo "✅ API key created"
|
||||
|
||||
# 2. List keys
|
||||
echo "2. Testing API key listing..."
|
||||
momentry api-key list
|
||||
echo "✅ API key list OK"
|
||||
|
||||
# 3. Show stats
|
||||
echo "3. Testing statistics..."
|
||||
momentry api-key stats
|
||||
echo "✅ Statistics OK"
|
||||
|
||||
# 4. Test Gitea integration
|
||||
echo "4. Testing Gitea integration..."
|
||||
GITEA_URL="http://localhost:3000" \
|
||||
momentry gitea list --username admin --password "Test3200Test3200Test3200"
|
||||
echo "✅ Gitea integration OK"
|
||||
|
||||
echo ""
|
||||
echo "=== All Tests Passed ==="
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Unit Test Coverage
|
||||
|
||||
| Module | Tests | Status |
|
||||
|--------|-------|--------|
|
||||
| `models.rs` | 0 | ✅ |
|
||||
| `service.rs` | 5 | ✅ |
|
||||
| `validator.rs` | 2 | ✅ |
|
||||
| `gitea.rs` | 3 | ✅ |
|
||||
| `n8n.rs` | 2 | ✅ |
|
||||
| `rotation.rs` | 4 | ✅ |
|
||||
| `anomaly.rs` | 0 | ✅ |
|
||||
| `blacklist.rs` | 5 | ✅ |
|
||||
| `encryption.rs` | 2 | ✅ |
|
||||
| `webhook.rs` | 2 | ✅ |
|
||||
| `error.rs` | 3 | ✅ |
|
||||
| `report.rs` | 1 | ✅ |
|
||||
| `cleanup.rs` | 1 | ✅ |
|
||||
| **Total** | **30** | **✅** |
|
||||
|
||||
---
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
### GitHub Actions / Gitea Actions
|
||||
|
||||
```yaml
|
||||
name: API Key Tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15
|
||||
env:
|
||||
POSTGRES_USER: accusys
|
||||
POSTGRES_DB: momentry_test
|
||||
ports:
|
||||
- 5432:5432
|
||||
redis:
|
||||
image: redis:7
|
||||
ports:
|
||||
- 6379:6379
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- run: cargo test --lib api_key
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Database connection failed**
|
||||
```bash
|
||||
# Check PostgreSQL status
|
||||
pg_isready -h localhost -p 5432
|
||||
```
|
||||
|
||||
2. **Redis connection failed**
|
||||
```bash
|
||||
# Check Redis status
|
||||
redis-cli -a accusys ping
|
||||
```
|
||||
|
||||
3. **Gitea authentication failed**
|
||||
```bash
|
||||
# Verify credentials
|
||||
curl -u admin:password http://localhost:3000/api/v1/user
|
||||
```
|
||||
@@ -1,222 +0,0 @@
|
||||
# n8n 呼叫 Momentry API 指南
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-23 |
|
||||
| 文件版本 | V1.1 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-23 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
| V1.1 | 2026-03-26 | 新增 API Key 驗證說明,更新 HTTP Request Node 設定 | OpenCode | deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
**用途**: 在 n8n workflow 中呼叫 Momentry API
|
||||
|
||||
---
|
||||
|
||||
## API URL
|
||||
|
||||
在 n8n HTTP Request Node 中,**請使用外部 URL**:
|
||||
|
||||
```
|
||||
https://api.momentry.ddns.net
|
||||
```
|
||||
|
||||
> ⚠️ **不要使用** `localhost:3002`,因為 n8n 需要從外部訪問 API。
|
||||
|
||||
---
|
||||
|
||||
## 常用端點
|
||||
|
||||
| 方法 | 端點 | 說明 |
|
||||
|------|------|------|
|
||||
| GET | `/health` | 健康檢查 |
|
||||
| POST | `/api/v1/n8n/search` | 語意搜尋(推薦) |
|
||||
| GET | `/api/v1/videos` | 列出所有影片 |
|
||||
| GET | `/api/v1/lookup` | 查詢影片 |
|
||||
| GET | `/api/v1/progress/:uuid` | 處理進度 |
|
||||
| GET | `/api/v1/jobs` | 任務列表 |
|
||||
| GET | `/api/v1/jobs/:uuid` | 任務詳情 |
|
||||
|
||||
---
|
||||
|
||||
## HTTP Request Node 設定
|
||||
|
||||
### 語意搜尋(推薦)
|
||||
|
||||
```
|
||||
Node: HTTP Request
|
||||
├── URL: https://api.momentry.ddns.net/api/v1/n8n/search
|
||||
├── Method: POST
|
||||
├── Authentication: None
|
||||
├── Send Body: ✓ (checked)
|
||||
├── Content Type: JSON
|
||||
├── Body:
|
||||
│ {
|
||||
│ "query": "={{ $json.query }}",
|
||||
│ "limit": "={{ $json.limit || 10 }}"
|
||||
│ }
|
||||
├── Send Headers: ✓ (checked)
|
||||
└── Header Parameters:
|
||||
└── X-API-Key: {{ $env.MOMENTRY_API_KEY }}
|
||||
```
|
||||
|
||||
### 測試用(固定關鍵字)
|
||||
|
||||
```
|
||||
Node: HTTP Request
|
||||
├── URL: https://api.momentry.ddns.net/api/v1/n8n/search
|
||||
├── Method: POST
|
||||
├── Send Body: ✓
|
||||
├── Content Type: JSON
|
||||
├── Body:
|
||||
│ {
|
||||
│ "query": "charade",
|
||||
│ "limit": 3
|
||||
│ }
|
||||
├── Send Headers: ✓ (checked)
|
||||
└── Header Parameters:
|
||||
└── X-API-Key: {{ $env.MOMENTRY_API_KEY }}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 完整 Workflow 範例
|
||||
|
||||
### 基本搜尋 Workflow
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Momentry Video Search",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"name": "Manual Trigger",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"position": [250, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "https://api.momentry.ddns.net/api/v1/n8n/search",
|
||||
"method": "POST",
|
||||
"sendBody": true,
|
||||
"contentType": "json",
|
||||
"body": {
|
||||
"query": "charade",
|
||||
"limit": 3
|
||||
}
|
||||
},
|
||||
"name": "Search Video API",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"position": [450, 300]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Manual Trigger": {
|
||||
"main": [[{"node": "Search Video API"}]]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 動態查詢 Workflow
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Momentry Dynamic Search",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"httpMethod": "POST",
|
||||
"path": "search",
|
||||
"responseMode": "lastNode"
|
||||
},
|
||||
"name": "Webhook",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"position": [250, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "https://api.momentry.ddns.net/api/v1/n8n/search",
|
||||
"method": "POST",
|
||||
"sendBody": true,
|
||||
"contentType": "json",
|
||||
"body": {
|
||||
"query": "={{ JSON.stringify($json.body.query) }}",
|
||||
"limit": "={{ $json.body.limit || 5 }}"
|
||||
}
|
||||
},
|
||||
"name": "Search API",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"position": [450, 300]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Webhook": {
|
||||
"main": [[{"node": "Search API"}]]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常見錯誤
|
||||
|
||||
### 錯誤: 502 Bad Gateway
|
||||
|
||||
**原因**: API 服務未啟動
|
||||
|
||||
**解決**:
|
||||
```bash
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
|
||||
```
|
||||
|
||||
### 錯誤: "Your request is invalid"
|
||||
|
||||
**原因**: Body 格式設定錯誤
|
||||
|
||||
**正確設定**:
|
||||
- `Content Type`: JSON
|
||||
- `Body`: 必須是有效的 JSON 物件
|
||||
|
||||
---
|
||||
|
||||
## curl 測試
|
||||
|
||||
在終端機中測試 API:
|
||||
|
||||
> **注意**: 所有 `/api/v1/*` 端點都需要 API Key 驗證。請設定環境變數或直接替換 API Key。
|
||||
|
||||
```bash
|
||||
# 設定環境變數(使用您的 API Key)
|
||||
export MOMENTRY_API_KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
|
||||
```
|
||||
|
||||
```bash
|
||||
# 健康檢查
|
||||
curl https://api.momentry.ddns.net/health
|
||||
|
||||
# 搜尋測試 (需要 API Key)
|
||||
curl -X POST https://api.momentry.ddns.net/api/v1/n8n/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: $MOMENTRY_API_KEY" \
|
||||
-d '{"query":"charade","limit":3}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 相關文件
|
||||
|
||||
- [API_INDEX.md](./API_INDEX.md) - 文件總覽
|
||||
- [API_ENDPOINTS.md](./API_ENDPOINTS.md) - 端點完整說明
|
||||
- [N8N_HTTP_REQUEST_GUIDE.md](./N8N_HTTP_REQUEST_GUIDE.md) - HTTP Request 詳細設定
|
||||
@@ -1,325 +0,0 @@
|
||||
# WordPress 呼叫 Momentry API 指南
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 版本 | V1.1 |
|
||||
| 日期 | 2026-03-25 |
|
||||
| 用途 | 在 WordPress 中呼叫 Momentry API |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.1 | 2026-03-25 | 更新: n8n 搜尋回傳 `file_path` 取代 `media_url`,新增 API Key 驗證說明 | OpenCode | deepseek-reasoner |
|
||||
| V1.0 | 2026-03-23 | 創建 WordPress API 指南 | Warren | OpenCode / MiniMax M2.5 |
|
||||
|
||||
---
|
||||
|
||||
## API URL
|
||||
|
||||
在 WordPress 中呼叫 API,**請使用外部 URL**:
|
||||
|
||||
```
|
||||
https://api.momentry.ddns.net
|
||||
```
|
||||
|
||||
> ⚠️ WordPress 運行於瀏覽器端,無法直接訪問 `localhost`。
|
||||
|
||||
---
|
||||
|
||||
## API 認證
|
||||
|
||||
所有 `/api/v1/*` 端點(除了健康檢查)都需要 API Key 認證。請在請求標頭中加入:
|
||||
|
||||
```
|
||||
'headers' => ['Content-Type' => 'application/json', 'X-API-Key' => 'YOUR_API_KEY']
|
||||
```
|
||||
|
||||
**目前示範使用的 API Key**: `demo_api_key_12345`
|
||||
|
||||
> **注意**: 正式環境請使用安全的 API Key 管理機制,避免在客戶端 JavaScript 中暴露 API Key。
|
||||
|
||||
---
|
||||
|
||||
## 常用端點
|
||||
|
||||
| 方法 | 端點 | 說明 |
|
||||
|------|------|------|
|
||||
| GET | `/health` | 健康檢查 |
|
||||
| POST | `/api/v1/search` | 語意搜尋(標準格式) |
|
||||
| GET | `/api/v1/videos` | 列出所有影片 |
|
||||
| GET | `/api/v1/lookup` | 查詢影片 |
|
||||
|
||||
---
|
||||
|
||||
## PHP 範例
|
||||
|
||||
### 基本搜尋
|
||||
|
||||
```php
|
||||
<?php
|
||||
$api_url = 'https://api.momentry.ddns.net/api/v1/n8n/search';
|
||||
|
||||
$data = [
|
||||
'query' => 'charade',
|
||||
'limit' => 10
|
||||
];
|
||||
|
||||
$response = wp_remote_post($api_url, [
|
||||
'headers' => ['Content-Type' => 'application/json', 'X-API-Key' => 'YOUR_API_KEY'],
|
||||
'body' => json_encode($data),
|
||||
'timeout' => 30
|
||||
]);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
echo '錯誤: ' . $response->get_error_message();
|
||||
} else {
|
||||
$body = json_decode(wp_remote_retrieve_body($response), true);
|
||||
print_r($body['hits']);
|
||||
}
|
||||
?>
|
||||
```
|
||||
|
||||
### 列出所有影片
|
||||
|
||||
```php
|
||||
<?php
|
||||
$api_url = 'https://api.momentry.ddns.net/api/v1/videos';
|
||||
|
||||
$response = wp_remote_get($api_url, [
|
||||
'headers' => ['X-API-Key' => 'YOUR_API_KEY'],
|
||||
'timeout' => 30
|
||||
]);
|
||||
|
||||
if (!is_wp_error($response)) {
|
||||
$body = json_decode(wp_remote_retrieve_body($response), true);
|
||||
foreach ($body['videos'] as $video) {
|
||||
echo $video['file_name'] . "\n";
|
||||
}
|
||||
}
|
||||
?>
|
||||
```
|
||||
|
||||
### 查詢特定影片
|
||||
|
||||
```php
|
||||
<?php
|
||||
$uuid = '5dea6618a606e7c7';
|
||||
$api_url = 'https://api.momentry.ddns.net/api/v1/lookup?uuid=' . $uuid;
|
||||
|
||||
$response = wp_remote_get($api_url, [
|
||||
'headers' => ['X-API-Key' => 'YOUR_API_KEY'],
|
||||
'timeout' => 30
|
||||
]);
|
||||
|
||||
if (!is_wp_error($response)) {
|
||||
$video = json_decode(wp_remote_retrieve_body($response), true);
|
||||
echo '檔案: ' . $video['file_name'] . "\n";
|
||||
echo '時長: ' . $video['duration'] . ' 秒';
|
||||
}
|
||||
?>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## JavaScript 範例
|
||||
|
||||
### 使用 fetch
|
||||
|
||||
```javascript
|
||||
// 搜尋影片
|
||||
async function searchVideos(query, limit = 10) {
|
||||
const response = await fetch('https://api.momentry.ddns.net/api/v1/n8n/search', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'X-API-Key': 'YOUR_API_KEY' },
|
||||
body: JSON.stringify({ query, limit })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('API 請求失敗');
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
// 使用範例
|
||||
searchVideos('charade', 5)
|
||||
.then(data => {
|
||||
data.hits.forEach(hit => {
|
||||
console.log(`${hit.text} (score: ${hit.score})`);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## WordPress Shortcode 範例
|
||||
|
||||
在 `functions.php` 中註冊短碼:
|
||||
|
||||
```php
|
||||
<?php
|
||||
// 將文件路徑轉換為可訪問的 URL
|
||||
function convert_file_path_to_url($file_path) {
|
||||
// 範例: 將 SFTPGo 文件路徑轉換為 web URL
|
||||
// /Users/accusys/momentry/var/sftpgo/data/demo/video.mp4
|
||||
// → https://sftpgo.example.com/demo/video.mp4
|
||||
|
||||
// 移除基本路徑
|
||||
$base_path = '/Users/accusys/momentry/var/sftpgo/data/';
|
||||
if (strpos($file_path, $base_path) === 0) {
|
||||
$relative_path = substr($file_path, strlen($base_path));
|
||||
// 替換為實際的 SFTPGo web URL
|
||||
return 'https://sftpgo.example.com/' . $relative_path;
|
||||
}
|
||||
|
||||
// 如果無法轉換,返回原始路徑
|
||||
return $file_path;
|
||||
}
|
||||
|
||||
// 註冊短碼
|
||||
add_shortcode('momentry_search', function($atts) {
|
||||
$atts = shortcode_atts([
|
||||
'query' => '',
|
||||
'limit' => '10'
|
||||
], $atts);
|
||||
|
||||
if (empty($atts['query'])) {
|
||||
return '<p>請提供搜尋關鍵字</p>';
|
||||
}
|
||||
|
||||
$response = wp_remote_post('https://api.momentry.ddns.net/api/v1/n8n/search', [
|
||||
'headers' => [
|
||||
'Content-Type' => 'application/json',
|
||||
'X-API-Key' => 'YOUR_API_KEY' // 替換為實際的 API Key
|
||||
],
|
||||
'body' => json_encode([
|
||||
'query' => $atts['query'],
|
||||
'limit' => (int)$atts['limit']
|
||||
]),
|
||||
'timeout' => 30
|
||||
]);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
return '<p>搜尋服務暫時無法使用</p>';
|
||||
}
|
||||
|
||||
$data = json_decode(wp_remote_retrieve_body($response), true);
|
||||
|
||||
if (empty($data['hits'])) {
|
||||
return '<p>找不到相關結果</p>';
|
||||
}
|
||||
|
||||
$output = '<ul class="momentry-results">';
|
||||
foreach ($data['hits'] as $hit) {
|
||||
// 注意: API 現在返回 file_path 而非 media_url
|
||||
// 需要將文件路徑轉換為可訪問的 URL
|
||||
$file_path = $hit['file_path'];
|
||||
$video_url = convert_file_path_to_url($file_path); // 需要實作此函數
|
||||
|
||||
$output .= sprintf(
|
||||
'<li>%s <a href="%s?start=%s">播放</a></li>',
|
||||
esc_html($hit['text']),
|
||||
$video_url,
|
||||
$hit['start']
|
||||
);
|
||||
}
|
||||
$output .= '</ul>';
|
||||
|
||||
return $output;
|
||||
});
|
||||
?>
|
||||
```
|
||||
|
||||
**使用方式**:
|
||||
```
|
||||
[momentry_search query="charade" limit="5"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## REST API Endpoint (WP >= 5.0)
|
||||
|
||||
在 WordPress REST API 中註冊自定義端點:
|
||||
|
||||
```php
|
||||
<?php
|
||||
// 註冊 REST API 端點
|
||||
add_action('rest_api_init', function() {
|
||||
register_rest_route('momentry/v1', '/search', [
|
||||
'methods' => 'POST',
|
||||
'callback' => function($request) {
|
||||
$response = wp_remote_post(
|
||||
'https://api.momentry.ddns.net/api/v1/n8n/search',
|
||||
[
|
||||
'headers' => ['Content-Type' => 'application/json', 'X-API-Key' => 'YOUR_API_KEY'],
|
||||
'body' => json_encode([
|
||||
'query' => $request->get_param('query'),
|
||||
'limit' => $request->get_param('limit', 10)
|
||||
])
|
||||
]
|
||||
);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
return new WP_Error('api_error', 'API 請求失敗');
|
||||
}
|
||||
|
||||
return json_decode(wp_remote_retrieve_body($response));
|
||||
}
|
||||
]);
|
||||
});
|
||||
?>
|
||||
```
|
||||
|
||||
**呼叫方式**:
|
||||
```
|
||||
POST /wp-json/momentry/v1/search
|
||||
Body: {"query": "charade", "limit": 5}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常見錯誤
|
||||
|
||||
### 錯誤: cURL error 7
|
||||
|
||||
**原因**: 無法連接到 API
|
||||
|
||||
**檢查**:
|
||||
1. API 服務是否啟動
|
||||
2. 網路是否可達
|
||||
|
||||
### 錯誤: 502 Bad Gateway
|
||||
|
||||
**原因**: API 服務未啟動
|
||||
|
||||
**解決**:
|
||||
```bash
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## curl 測試
|
||||
|
||||
在終端機中測試:
|
||||
|
||||
```bash
|
||||
# 健康檢查
|
||||
curl https://api.momentry.ddns.net/health
|
||||
|
||||
# 搜尋測試
|
||||
curl -X POST https://api.momentry.ddns.net/api/v1/n8n/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query":"charade","limit":5}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 相關文件
|
||||
|
||||
- [API_INDEX.md](./API_INDEX.md) - 文件總覽
|
||||
- [API_ENDPOINTS.md](./API_ENDPOINTS.md) - 端點完整說明
|
||||
- [API_N8N_GUIDE.md](./API_N8N_GUIDE.md) - n8n 使用範例
|
||||
@@ -1,667 +0,0 @@
|
||||
# Momentry Core 版本紀錄
|
||||
|
||||
## 版本命名規則
|
||||
|
||||
### Main Version (主版本)
|
||||
```
|
||||
v{major}.{minor}
|
||||
例: v0.1, v0.2, v1.0
|
||||
```
|
||||
|
||||
### Build Version (建置版本)
|
||||
```
|
||||
v{major}.{minor}.{YYYYMMDD_HHMMSS}
|
||||
例: v0.1.20260325_143000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 版本紀錄存放位置
|
||||
|
||||
```
|
||||
/Users/accusys/momentry/versions/
|
||||
├── current/ # 目前使用版本
|
||||
│ ├── binary # 當前 binary
|
||||
│ └── version.json # 版本資訊
|
||||
│
|
||||
├── releases/ # Release 版本存放
|
||||
│ ├── v0.1/
|
||||
│ │ ├── v0.1.20260325_143000/
|
||||
│ │ │ ├── binary
|
||||
│ │ │ └── version.json
|
||||
│ │ ├── v0.1.20260324_100000/
|
||||
│ │ │ └── ...
|
||||
│ │ └── release.json # v0.1 版本總覽
|
||||
│ │
|
||||
│ └── v0.2/
|
||||
│ └── ...
|
||||
│
|
||||
└── changelog.json # 全域版本變更記錄
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## version.json 格式
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "v0.1.20260325_143000",
|
||||
"main_version": "v0.1",
|
||||
"build_timestamp": "2026-03-25T14:30:00+08:00",
|
||||
"git_commit": "83ae050",
|
||||
"git_branch": "main",
|
||||
"git_message": "fix: save probe.json to OUTPUT_DIR instead of current directory",
|
||||
"features": [
|
||||
"API Key Authentication",
|
||||
"Job Worker System"
|
||||
],
|
||||
"fixes": [
|
||||
"get_processor_results_by_job column mapping"
|
||||
],
|
||||
"deployed_at": "2026-03-25T15:00:00+08:00",
|
||||
"deployed_by": "opencode",
|
||||
"status": "production"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## release.json 格式 (主版本總覽)
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "v0.1",
|
||||
"status": "production",
|
||||
"created_at": "2026-03-14T10:00:00+08:00",
|
||||
"current_build": "v0.1.20260325_143000",
|
||||
"builds": [
|
||||
{
|
||||
"build": "v0.1.20260325_143000",
|
||||
"date": "2026-03-25",
|
||||
"commits": ["83ae050", "171c36a"],
|
||||
"summary": "fix: save probe.json, add v2 backup versioning"
|
||||
},
|
||||
{
|
||||
"build": "v0.1.20260324_100000",
|
||||
"date": "2026-03-24",
|
||||
"commits": ["89fbfd6", "3edaf01"],
|
||||
"summary": "feat: add POST /api/v1/probe endpoint"
|
||||
}
|
||||
],
|
||||
"changelog": [
|
||||
"## v0.1.20260325_143000",
|
||||
"- 修復 processor_results 欄位映射錯誤",
|
||||
"- 添加 API Key 認證",
|
||||
"",
|
||||
"## v0.1.20260324_100000",
|
||||
"- 新增 Probe API"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## changelog.json 格式 (全域變更記錄)
|
||||
|
||||
```json
|
||||
{
|
||||
"updated_at": "2026-03-25T14:30:00+08:00",
|
||||
"versions": {
|
||||
"v0.1": {
|
||||
"status": "production",
|
||||
"current_build": "v0.1.20260325_143000",
|
||||
"build_count": 12
|
||||
},
|
||||
"v0.0": {
|
||||
"status": "deprecated",
|
||||
"final_build": "v0.0.20260310_090000"
|
||||
}
|
||||
},
|
||||
"recent_changes": [
|
||||
{
|
||||
"version": "v0.1.20260325_143000",
|
||||
"date": "2026-03-25",
|
||||
"changes": [
|
||||
{"type": "fix", "description": "get_processor_results_by_job column mapping"},
|
||||
{"type": "feat", "description": "API Key Authentication"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Release Script
|
||||
|
||||
### /Users/accusys/momentry/scripts/release.sh
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_DIR="/Users/accusys/momentry_core_0.1"
|
||||
VERSIONS_DIR="/Users/accusys/momentry/versions"
|
||||
BACKUP_DIR="/Users/accusys/momentry/backup/bin"
|
||||
CURRENT_BIN="/Users/accusys/momentry/bin/momentry"
|
||||
|
||||
# 顏色輸出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
# 解析命令列參數
|
||||
MAIN_VERSION=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-v|--version)
|
||||
MAIN_VERSION="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown option: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$MAIN_VERSION" ]; then
|
||||
log_error "請指定主版本: ./release.sh -v v0.1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "開始 Release ${MAIN_VERSION}..."
|
||||
|
||||
# 1. 取得 Git 資訊
|
||||
GIT_COMMIT=$(git -C "$PROJECT_DIR" rev-parse --short HEAD)
|
||||
GIT_BRANCH=$(git -C "$PROJECT_DIR" rev-parse --abbrev-ref HEAD)
|
||||
GIT_MESSAGE=$(git -C "$PROJECT_DIR" log -1 --pretty=%s)
|
||||
BUILD_TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
BUILD_VERSION="${MAIN_VERSION}.${BUILD_TIMESTAMP}"
|
||||
|
||||
log_info "Build Version: ${BUILD_VERSION}"
|
||||
log_info "Git Commit: ${GIT_COMMIT}"
|
||||
|
||||
# 2. 創建版本目錄
|
||||
BUILD_DIR="${VERSIONS_DIR}/releases/${MAIN_VERSION}/${BUILD_VERSION}"
|
||||
mkdir -p "$BUILD_DIR"
|
||||
mkdir -p "${VERSIONS_DIR}/current"
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
# 3. 停止 Production Service
|
||||
log_info "停止 Production Service..."
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.api.plist 2>/dev/null || true
|
||||
|
||||
# 4. 備份當前 Binary
|
||||
if [ -f "$CURRENT_BIN" ]; then
|
||||
OLD_VERSION=$(cat "${VERSIONS_DIR}/current/version.json" 2>/dev/null | jq -r '.version // "unknown"')
|
||||
log_info "備份當前版本: $OLD_VERSION"
|
||||
cp "$CURRENT_BIN" "${BACKUP_DIR}/momentry_${OLD_VERSION}_$(date +%Y%m%d_%H%M%S)"
|
||||
fi
|
||||
|
||||
# 5. 編譯 Release 版本
|
||||
log_info "編譯 Release 版本..."
|
||||
cd "$PROJECT_DIR"
|
||||
cargo build --release --bin momentry
|
||||
|
||||
# 6. 複製到版本目錄
|
||||
log_info "複製到版本目錄..."
|
||||
cp target/release/momentry "${BUILD_DIR}/binary"
|
||||
cp target/release/momentry "$CURRENT_BIN"
|
||||
|
||||
# 7. 生成 version.json
|
||||
cat > "${BUILD_DIR}/version.json" << EOF
|
||||
{
|
||||
"version": "${BUILD_VERSION}",
|
||||
"main_version": "${MAIN_VERSION}",
|
||||
"build_timestamp": "$(date -Iseconds)",
|
||||
"git_commit": "${GIT_COMMIT}",
|
||||
"git_branch": "${GIT_BRANCH}",
|
||||
"git_message": "${GIT_MESSAGE}",
|
||||
"features": [],
|
||||
"fixes": [],
|
||||
"deployed_at": null,
|
||||
"deployed_by": null,
|
||||
"status": "built"
|
||||
}
|
||||
EOF
|
||||
|
||||
# 8. 更新 current
|
||||
cp "${BUILD_DIR}/version.json" "${VERSIONS_DIR}/current/version.json"
|
||||
|
||||
# 9. 更新 changelog.json
|
||||
UPDATE_CHANGELOG="
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
changelog_path = '${VERSIONS_DIR}/changelog.json'
|
||||
build_info = {
|
||||
'version': '${BUILD_VERSION}',
|
||||
'date': datetime.now().strftime('%Y-%m-%d'),
|
||||
'commit': '${GIT_COMMIT}',
|
||||
'message': '${GIT_MESSAGE}'
|
||||
}
|
||||
|
||||
try:
|
||||
with open(changelog_path, 'r') as f:
|
||||
changelog = json.load(f)
|
||||
except FileNotFoundError:
|
||||
changelog = {'updated_at': '', 'versions': {}, 'recent_changes': []}
|
||||
|
||||
changelog['updated_at'] = datetime.now().isoformat()
|
||||
if '${MAIN_VERSION}' not in changelog['versions']:
|
||||
changelog['versions']['${MAIN_VERSION}'] = {'status': 'building', 'build_count': 0}
|
||||
|
||||
changelog['versions']['${MAIN_VERSION}']['build_count'] += 1
|
||||
changelog['versions']['${MAIN_VERSION}']['current_build'] = '${BUILD_VERSION}'
|
||||
changelog['recent_changes'].insert(0, build_info)
|
||||
|
||||
with open(changelog_path, 'w') as f:
|
||||
json.dump(changelog, f, indent=2, ensure_ascii=False)
|
||||
"
|
||||
python3 -c "$UPDATE_CHANGELOG"
|
||||
|
||||
# 10. 啟動 Production Service
|
||||
log_info "啟動 Production Service..."
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
|
||||
|
||||
# 11. 驗證
|
||||
sleep 3
|
||||
if curl -s http://localhost:3002/health > /dev/null; then
|
||||
log_info "✓ Release 成功!"
|
||||
log_info "版本: ${BUILD_VERSION}"
|
||||
log_info "目錄: ${BUILD_DIR}"
|
||||
else
|
||||
log_error "✗ Release 失敗!請檢查服務狀態。"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 查詢版本指令
|
||||
|
||||
### 查詢目前版本
|
||||
```bash
|
||||
cat /Users/accusys/momentry/versions/current/version.json
|
||||
```
|
||||
|
||||
### 查詢所有 Release
|
||||
```bash
|
||||
ls -la /Users/accusys/momentry/versions/releases/
|
||||
```
|
||||
|
||||
### 查詢版本歷史
|
||||
```bash
|
||||
cat /Users/accusys/momentry/versions/changelog.json | python3 -m json.tool
|
||||
```
|
||||
|
||||
### 查詢特定主版本
|
||||
```bash
|
||||
ls /Users/accusys/momentry/versions/releases/v0.1/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 版本狀態
|
||||
|
||||
| 狀態 | 說明 |
|
||||
|------|------|
|
||||
| `building` | 建置中 |
|
||||
| `built` | 已建置,未部署 |
|
||||
| `testing` | 測試中 |
|
||||
| `production` | 正式環境使用中 |
|
||||
| `deprecated` | 已棄用 |
|
||||
| `archived` | 已封存 |
|
||||
|
||||
---
|
||||
|
||||
## 版本流程圖
|
||||
|
||||
```
|
||||
develop (git branch)
|
||||
│
|
||||
▼
|
||||
feature/bugfix commit
|
||||
│
|
||||
▼
|
||||
develop ──────────────────┐
|
||||
│ │
|
||||
│ (merge to main) │
|
||||
▼ │
|
||||
main (git) │
|
||||
│ │
|
||||
▼ │
|
||||
Build v0.1.20260325_143000
|
||||
│ │
|
||||
├──► testing (3003) │
|
||||
│ │
|
||||
│ (approve) │
|
||||
▼ ▼
|
||||
v0.1 ───────────────────┘
|
||||
│
|
||||
├──► releases/v0.1/v0.1.20260325_143000/
|
||||
│
|
||||
├──► current/ (production)
|
||||
│
|
||||
▼
|
||||
changelog.json (update)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Release Note (版本發布說明)
|
||||
|
||||
### Release Note 存放位置
|
||||
|
||||
```
|
||||
/Users/accusys/momentry/versions/releases/{主版本}/{建置版本}/
|
||||
├── binary
|
||||
├── version.json
|
||||
└── RELEASE_NOTE.md # 發布說明 (Markdown)
|
||||
```
|
||||
|
||||
### Release Note 範本
|
||||
|
||||
```markdown
|
||||
# Momentry Core v0.1.20260325_143000 Release Note
|
||||
|
||||
## 版本資訊
|
||||
- **Build Version**: v0.1.20260325_143000
|
||||
- **Main Version**: v0.1
|
||||
- **Build Date**: 2026-03-25 14:30:00
|
||||
- **Git Commit**: 83ae050
|
||||
|
||||
## 新功能 (Features)
|
||||
|
||||
### API Key 認證系統
|
||||
- 添加 API Key 認證中介層
|
||||
- 所有 `/api/v1/*` 端點需要 `X-API-Key` header
|
||||
- 支援 API Key 使用追蹤和審計日誌
|
||||
|
||||
### Job Worker 系統
|
||||
- 新增 Job Worker 二進位檔
|
||||
- 支援最多 2 個並發處理器
|
||||
- 新增 `/api/v1/jobs/:uuid` 端點查詢任務詳情
|
||||
|
||||
## 錯誤修復 (Bug Fixes)
|
||||
|
||||
| Issue | 描述 |
|
||||
|-------|------|
|
||||
| #001 | 修復 `get_processor_results_by_job` 欄位映射錯誤 |
|
||||
| #002 | 修復 API Key 驗證時區處理問題 |
|
||||
|
||||
## API 變更 (API Changes)
|
||||
|
||||
### 新增端點
|
||||
| Method | Endpoint | 說明 |
|
||||
|--------|----------|------|
|
||||
| GET | `/api/v1/jobs` | 取得所有任務列表 |
|
||||
| GET | `/api/v1/jobs/:uuid` | 取得特定任務詳情 |
|
||||
|
||||
### 認證變更
|
||||
| 端點 | 舊版 | 新版 |
|
||||
|------|------|------|
|
||||
| `/api/v1/*` | 公開 | 需要 API Key |
|
||||
|
||||
## 升級指南
|
||||
|
||||
### 從舊版升級
|
||||
1. 備份當前版本
|
||||
2. 停止服務
|
||||
3. 替換 binary
|
||||
4. 重啟服務
|
||||
5. 更新 API Key 配置
|
||||
|
||||
### API Key 配置
|
||||
```bash
|
||||
# 請求範例
|
||||
curl -H "X-API-Key: your_api_key" \
|
||||
"http://localhost:3002/api/v1/videos"
|
||||
```
|
||||
|
||||
## 已知問題 (Known Issues)
|
||||
|
||||
- 暫無
|
||||
|
||||
## 相關文檔
|
||||
|
||||
- [API 文檔](../docs/API_INDEX.md)
|
||||
- [版本管理規範](../docs/VERSION_MANAGEMENT.md)
|
||||
|
||||
---
|
||||
|
||||
## Release Note 自動生成 Script
|
||||
|
||||
### /Users/accusys/momentry/scripts/generate_release_note.sh
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
BUILD_VERSION=$1
|
||||
MAIN_VERSION=$2
|
||||
BUILD_DIR="/Users/accusys/momentry/versions/releases/${MAIN_VERSION}/${BUILD_VERSION}"
|
||||
|
||||
# 取得 Git 資訊
|
||||
GIT_COMMITS=$(git log --oneline -10)
|
||||
GIT_CHANGES=$(git diff --stat HEAD~5..HEAD)
|
||||
|
||||
cat > "${BUILD_DIR}/RELEASE_NOTE.md" << EOF
|
||||
# Momentry Core ${BUILD_VERSION} Release Note
|
||||
|
||||
## 版本資訊
|
||||
- **Build Version**: ${BUILD_VERSION}
|
||||
- **Main Version**: ${MAIN_VERSION}
|
||||
- **Build Date**: $(date '+%Y-%m-%d %H:%M:%S')
|
||||
- **Git Commit**: $(git rev-parse --short HEAD)
|
||||
|
||||
## 變更內容
|
||||
|
||||
### Commit 記錄
|
||||
\`\`\`
|
||||
${GIT_COMMITS}
|
||||
\`\`\`
|
||||
|
||||
### 變更統計
|
||||
\`\`\`
|
||||
${GIT_CHANGES}
|
||||
\`\`\`
|
||||
|
||||
## 新功能
|
||||
|
||||
## 錯誤修復
|
||||
|
||||
## API 變更
|
||||
|
||||
## 升級指南
|
||||
|
||||
## 已知問題
|
||||
|
||||
EOF
|
||||
|
||||
echo "Release Note 生成完成: ${BUILD_DIR}/RELEASE_NOTE.md"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Release Note 查詢
|
||||
|
||||
### 查詢所有 Release Note
|
||||
```bash
|
||||
find /Users/accusys/momentry/versions/releases -name "RELEASE_NOTE.md"
|
||||
```
|
||||
|
||||
### 查看特定版本 Release Note
|
||||
```bash
|
||||
cat /Users/accusys/momentry/versions/releases/v0.1/v0.1.20260325_143000/RELEASE_NOTE.md
|
||||
```
|
||||
|
||||
### 查詢最新版本 Release Note
|
||||
```bash
|
||||
cat /Users/accusys/momentry/versions/current/RELEASE_NOTE.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Release Note 範例
|
||||
|
||||
### 完整 Release Note 範例
|
||||
|
||||
\`\`\`markdown
|
||||
# Momentry Core v0.1.20260325_143000 Release Note
|
||||
|
||||
## 版本資訊
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| Build Version | v0.1.20260325_143000 |
|
||||
| Main Version | v0.1 |
|
||||
| Build Date | 2026-03-25 14:30:00 |
|
||||
| Git Commit | 83ae050 |
|
||||
| Status | ✅ Production |
|
||||
|
||||
## 新功能 (Features)
|
||||
|
||||
### 1. API Key 認證系統
|
||||
添加完整的 API Key 認證系統,保護所有 API 端點。
|
||||
|
||||
**功能:**
|
||||
- SHA256 key hash 驗證
|
||||
- 使用統計追蹤
|
||||
- 審計日誌記錄
|
||||
- 異常檢測
|
||||
|
||||
**API 使用方式:**
|
||||
\`\`\`bash
|
||||
curl -H "X-API-Key: your_key" \\
|
||||
"http://localhost:3002/api/v1/videos"
|
||||
\`\`\`
|
||||
|
||||
### 2. Job Worker 系統
|
||||
新增獨立的 Job Worker 處理影片處理任務。
|
||||
|
||||
**特性:**
|
||||
- 最多 2 個並發處理器
|
||||
- Polling-based 任務獲取
|
||||
- 自動進度追蹤
|
||||
|
||||
## 錯誤修復 (Bug Fixes)
|
||||
|
||||
| Issue | 描述 | 嚴重性 |
|
||||
|-------|------|--------|
|
||||
| #001 | 修復 `get_processor_results_by_job` TIMESTAMP 欄位映射 | 🔴 高 |
|
||||
| #002 | 修復 3002 port 衝突問題 | 🟡 中 |
|
||||
|
||||
## API 變更
|
||||
|
||||
### 新增端點
|
||||
| Method | Endpoint | 說明 |
|
||||
|--------|----------|------|
|
||||
| GET | `/api/v1/jobs` | 取得任務列表 |
|
||||
| GET | `/api/v1/jobs/:uuid` | 取得任務詳情 |
|
||||
|
||||
### 端點認證狀態
|
||||
| 端點 | 認證需求 |
|
||||
|------|----------|
|
||||
| `/health` | ❌ 不需要 |
|
||||
| `/api/v1/*` | ✅ 需要 `X-API-Key` |
|
||||
|
||||
## 升級指南
|
||||
|
||||
### 前置需求
|
||||
- PostgreSQL 資料庫
|
||||
- Redis 伺服器
|
||||
- MongoDB 快取
|
||||
|
||||
### 升級步驟
|
||||
1. **備份當前版本**
|
||||
\`\`\`bash
|
||||
cp /Users/accusys/momentry/bin/momentry \\
|
||||
/Users/accusys/momentry/backup/bin/momentry_$(date +%Y%m%d)
|
||||
\`\`\`
|
||||
|
||||
2. **停止服務**
|
||||
\`\`\`bash
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.api.plist
|
||||
\`\`\`
|
||||
|
||||
3. **替換 Binary**
|
||||
\`\`\`bash
|
||||
cp v0.1.20260325_143000/binary /Users/accusys/momentry/bin/momentry
|
||||
\`\`\`
|
||||
|
||||
4. **重啟服務**
|
||||
\`\`\`bash
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
|
||||
\`\`\`
|
||||
|
||||
5. **驗證**
|
||||
\`\`\`bash
|
||||
curl http://localhost:3002/health
|
||||
\`\`\`
|
||||
|
||||
## 已知問題 (Known Issues)
|
||||
|
||||
- 暫無
|
||||
|
||||
## 技術細節
|
||||
|
||||
### 認證流程
|
||||
\`\`\`
|
||||
Client Request
|
||||
│
|
||||
▼
|
||||
[X-API-Key Header] ──► Middleware
|
||||
│ │
|
||||
│ ▼
|
||||
│ Hash Key (SHA256)
|
||||
│ │
|
||||
│ ▼
|
||||
│ DB Lookup
|
||||
│ │
|
||||
│ ▼
|
||||
│ Validate Status
|
||||
│ │
|
||||
▼ ▼
|
||||
Handler ◄────────────────────┘
|
||||
\`\`\`
|
||||
|
||||
### 資料庫變更
|
||||
\`\`\`sql
|
||||
-- 新增 duration_secs 欄位
|
||||
ALTER TABLE processor_results
|
||||
ADD COLUMN IF NOT EXISTS duration_secs DOUBLE PRECISION;
|
||||
\`\`\`
|
||||
|
||||
## 回滾指南
|
||||
|
||||
如需回滾到上一版本:
|
||||
\`\`\`bash
|
||||
# 1. 停止服務
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.api.plist
|
||||
|
||||
# 2. 恢復舊版
|
||||
cp /Users/accusys/momentry/backup/bin/momentry_v0.1.20260324_100000 \\
|
||||
/Users/accusys/momentry/bin/momentry
|
||||
|
||||
# 3. 重啟服務
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
|
||||
\`\`\`
|
||||
|
||||
## 聯繫與支援
|
||||
|
||||
- **Issue Tracker**: https://gitea.momentry.ddns.net/momentry/momentry_core/issues
|
||||
- **文檔**: https://docs.momentry.ddns.net
|
||||
|
||||
---
|
||||
|
||||
*Generated: $(date '+%Y-%m-%d %H:%M:%S')*
|
||||
\`\`\`
|
||||
|
||||
```
|
||||
@@ -1,686 +0,0 @@
|
||||
# Momentry Core API 示範手冊
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-03-25 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-25 | 創建示範手冊,包含 Demo API Key 與完整範例 | OpenCode | deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
**狀態**: 完成
|
||||
|
||||
---
|
||||
|
||||
## 快速開始
|
||||
|
||||
### Demo API Key
|
||||
|
||||
```
|
||||
API Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69
|
||||
Key ID: muser_68600856036340bcafc01930eb4bd839
|
||||
過期日: 2027-03-25
|
||||
```
|
||||
|
||||
### 測試連線
|
||||
|
||||
```bash
|
||||
curl http://localhost:3002/health
|
||||
```
|
||||
|
||||
```json
|
||||
{"status":"ok","version":"0.1.0","uptime_ms":456464}
|
||||
```
|
||||
|
||||
### 測試認證
|
||||
|
||||
```bash
|
||||
curl -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
|
||||
http://localhost:3002/api/v1/videos | jq '.videos | length'
|
||||
```
|
||||
|
||||
```json
|
||||
13
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 環境 URL
|
||||
|
||||
| 環境 | URL | 用途 |
|
||||
|------|-----|------|
|
||||
| **本地開發** | `http://localhost:3002` | 本機開發測試 |
|
||||
| **外部訪問** | `https://api.momentry.ddns.net` | n8n/WordPress/curl 生產環境 |
|
||||
|
||||
---
|
||||
|
||||
## 端點總覽
|
||||
|
||||
| 方法 | 端點 | 說明 | 認證 |
|
||||
|------|------|------|------|
|
||||
| GET | `/health` | 健康檢查 | 公開 |
|
||||
| GET | `/health/detailed` | 詳細健康檢查 | 公開 |
|
||||
| POST | `/api/v1/register` | 註冊影片 | 需要 |
|
||||
| POST | `/api/v1/probe` | 探測影片資訊 | 需要 |
|
||||
| POST | `/api/v1/search` | 語意搜尋 | 需要 |
|
||||
| POST | `/api/v1/n8n/search` | n8n 格式搜尋 | 需要 |
|
||||
| POST | `/api/v1/search/hybrid` | 混合搜尋 | 需要 |
|
||||
| GET | `/api/v1/videos` | 列出所有影片 | 需要 |
|
||||
| GET | `/api/v1/lookup` | 查詢影片 UUID | 需要 |
|
||||
| GET | `/api/v1/progress/:uuid` | 處理進度 | 需要 |
|
||||
| GET | `/api/v1/jobs` | 任務列表 | 需要 |
|
||||
| GET | `/api/v1/jobs/:uuid` | 任務詳情 | 需要 |
|
||||
|
||||
---
|
||||
|
||||
## 1. curl 範例
|
||||
|
||||
### 基本格式
|
||||
|
||||
```bash
|
||||
curl -H "X-API-Key: YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
URL
|
||||
```
|
||||
|
||||
### 1.1 健康檢查(公開)
|
||||
|
||||
```bash
|
||||
# 基本健康檢查
|
||||
curl http://localhost:3002/health
|
||||
|
||||
# 詳細健康檢查(含服務狀態)
|
||||
curl http://localhost:3002/health/detailed
|
||||
```
|
||||
|
||||
### 1.2 列出影片
|
||||
|
||||
```bash
|
||||
curl -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
|
||||
http://localhost:3002/api/v1/videos | jq '.'
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"videos": [
|
||||
{
|
||||
"uuid": "952f5854b9febad1",
|
||||
"file_name": "ExaSAN PCIe series - Director Ou Yu-Zhi Shares His Experience.mp4",
|
||||
"duration": 159.637188,
|
||||
"width": 640,
|
||||
"height": 360
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 1.3 搜尋影片
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
-H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "ExaSAN", "limit": 5}' \
|
||||
http://localhost:3002/api/v1/search | jq '.'
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uuid": "952f5854b9febad1",
|
||||
"chunk_id": "...",
|
||||
"text": "...",
|
||||
"score": 0.85,
|
||||
"start_time": 0.0,
|
||||
"end_time": 5.0
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
"query": "ExaSAN",
|
||||
"took_ms": 123
|
||||
}
|
||||
```
|
||||
|
||||
### 1.4 查詢進度
|
||||
|
||||
```bash
|
||||
curl -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
|
||||
http://localhost:3002/api/v1/progress/952f5854b9febad1 | jq '.'
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"uuid": "952f5854b9febad1",
|
||||
"overall_progress": 67,
|
||||
"current_processor": "yolo",
|
||||
"processors": [
|
||||
{"name": "asr", "status": "completed"},
|
||||
{"name": "cut", "status": "completed"},
|
||||
{"name": "yolo", "status": "running"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. n8n 範例
|
||||
|
||||
### 2.1 HTTP Request 節點設定
|
||||
|
||||
```
|
||||
Method: POST
|
||||
URL: https://api.momentry.ddns.net/api/v1/search
|
||||
Authentication: None (使用 Header)
|
||||
|
||||
Headers:
|
||||
┌─────────────────────┬──────────────────────────────────────────────────┐
|
||||
│ Name │ Value │
|
||||
├─────────────────────┼──────────────────────────────────────────────────┤
|
||||
│ X-API-Key │ muser_68600856036340bcafc01930eb4bd839_... │
|
||||
│ Content-Type │ application/json │
|
||||
└─────────────────────┴──────────────────────────────────────────────────┘
|
||||
|
||||
Body Content (JSON):
|
||||
{
|
||||
"query": "{{ $json.search_term }}",
|
||||
"limit": 5
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 n8n 搜尋 Workflow
|
||||
|
||||
```json
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"name": "Manual Trigger",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"position": [250, 300]
|
||||
},
|
||||
{
|
||||
"name": "Set Search Term",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"parameters": {
|
||||
"values": {
|
||||
"json": {
|
||||
"search_term": "ExaSAN"
|
||||
}
|
||||
}
|
||||
},
|
||||
"position": [450, 300]
|
||||
},
|
||||
{
|
||||
"name": "Search Videos",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://api.momentry.ddns.net/api/v1/search",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "X-API-Key",
|
||||
"value": "muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyContentType": "json",
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ { \"query\": $json.search_term, \"limit\": 5 } }}"
|
||||
},
|
||||
"position": [650, 300]
|
||||
},
|
||||
{
|
||||
"name": "Process Results",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"parameters": {
|
||||
"jsCode": "// Extract video results\nconst results = $input.first().json.results;\nreturn results.map(r => ({\n uuid: r.uuid,\n text: r.text,\n score: r.score,\n time: `${r.start_time}s - ${r.end_time}s`\n}));"
|
||||
},
|
||||
"position": [850, 300]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Manual Trigger": {
|
||||
"main": [[{"node": "Set Search Term"}]]
|
||||
},
|
||||
"Set Search Term": {
|
||||
"main": [[{"node": "Search Videos"}]]
|
||||
},
|
||||
"Search Videos": {
|
||||
"main": [[{"node": "Process Results"}]]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 n8n 列出影片 Workflow
|
||||
|
||||
```json
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"name": "Get Videos",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://api.momentry.ddns.net/api/v1/videos",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "X-API-Key",
|
||||
"value": "muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"position": [450, 300]
|
||||
},
|
||||
{
|
||||
"name": "Extract Video List",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"parameters": {
|
||||
"jsCode": "const videos = $input.first().json.videos;\nreturn videos.map(v => ({\n json: {\n uuid: v.uuid,\n name: v.file_name,\n duration: Math.round(v.duration) + 's',\n resolution: `${v.width}x${v.height}`\n }\n}));"
|
||||
},
|
||||
"position": [650, 300]
|
||||
},
|
||||
{
|
||||
"name": "Slack Notification",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"parameters": {
|
||||
"channel": "#momentry",
|
||||
"text": "=Found {{ $json.length }} videos:\n{{ $json.map(v => `• ${v.name} (${v.duration})`).join(`\n`) }}"
|
||||
},
|
||||
"position": [850, 300]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 n8n 定時同步 Workflow
|
||||
|
||||
```json
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"name": "Schedule Trigger",
|
||||
"type": "n8n-nodes-base.scheduleTrigger",
|
||||
"parameters": {
|
||||
"rule": {
|
||||
"interval": [{"field": "hours", "hours": 1}]
|
||||
}
|
||||
},
|
||||
"position": [250, 300]
|
||||
},
|
||||
{
|
||||
"name": "Get Pending Videos",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://api.momentry.ddns.net/api/v1/videos"
|
||||
},
|
||||
"position": [450, 300]
|
||||
},
|
||||
{
|
||||
"name": "Filter Processing",
|
||||
"type": "n8n-nodes-base.filter",
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {"caseSensitive": true},
|
||||
"conditions": [
|
||||
{"id": "status", "leftValue": "{{ $json.status }}", "rightValue": "processing"}
|
||||
]
|
||||
}
|
||||
},
|
||||
"position": [650, 300]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. WordPress 範例
|
||||
|
||||
### 3.1 PHP 函數庫
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* Momentry API Client
|
||||
*/
|
||||
|
||||
class Momentry_API {
|
||||
private const API_URL = 'https://api.momentry.ddns.net';
|
||||
private const API_KEY = 'muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69';
|
||||
|
||||
/**
|
||||
* 發送 API 請求
|
||||
*/
|
||||
private function request(string $endpoint, array $data = [], string $method = 'GET'): array {
|
||||
$url = self::API_URL . $endpoint;
|
||||
|
||||
$args = [
|
||||
'headers' => [
|
||||
'X-API-Key' => self::API_KEY,
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
'timeout' => 30,
|
||||
];
|
||||
|
||||
if ($method === 'POST') {
|
||||
$args['method'] = 'POST';
|
||||
$args['body'] = json_encode($data);
|
||||
}
|
||||
|
||||
$response = wp_remote_request($url, $args);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
throw new Exception($response->get_error_message());
|
||||
}
|
||||
|
||||
return json_decode(wp_remote_retrieve_body($response), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出所有影片
|
||||
*/
|
||||
public function list_videos(): array {
|
||||
return $this->request('/api/v1/videos');
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜尋影片內容
|
||||
*/
|
||||
public function search(string $query, int $limit = 10): array {
|
||||
return $this->request('/api/v1/search', [
|
||||
'query' => $query,
|
||||
'limit' => $limit,
|
||||
], 'POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得影片進度
|
||||
*/
|
||||
public function get_progress(string $uuid): array {
|
||||
return $this->request("/api/v1/progress/{$uuid}");
|
||||
}
|
||||
|
||||
/**
|
||||
* 檢查健康狀態
|
||||
*/
|
||||
public function health_check(): array {
|
||||
return $this->request('/health');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 短代碼 (Shortcode)
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* WordPress 短代碼範例
|
||||
*/
|
||||
|
||||
// 註冊短代碼
|
||||
add_shortcode('momentry_videos', function($atts) {
|
||||
$atts = shortcode_atts([
|
||||
'limit' => 10,
|
||||
], $atts);
|
||||
|
||||
$api = new Momentry_API();
|
||||
|
||||
try {
|
||||
$result = $api->list_videos();
|
||||
$videos = array_slice($result['videos'], 0, $atts['limit']);
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div class="momentry-videos">
|
||||
<h3>影片列表</h3>
|
||||
<ul>
|
||||
<?php foreach ($videos as $video): ?>
|
||||
<li>
|
||||
<strong><?= esc_html($video['file_name']) ?></strong>
|
||||
<br>
|
||||
<small>
|
||||
UUID: <?= esc_html($video['uuid']) ?>
|
||||
| 時長: <?= gmdate("H:i:s", $video['duration']) ?>
|
||||
</small>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
|
||||
} catch (Exception $e) {
|
||||
return '<p class="error">載入失敗: ' . esc_html($e->getMessage()) . '</p>';
|
||||
}
|
||||
});
|
||||
|
||||
// 搜尋短代碼
|
||||
add_shortcode('momentry_search', function($atts, $content = '') {
|
||||
$query = sanitize_text_field($content);
|
||||
|
||||
if (empty($query)) {
|
||||
return '<p>請提供搜尋關鍵字</p>';
|
||||
}
|
||||
|
||||
$api = new Momentry_API();
|
||||
|
||||
try {
|
||||
$result = $api->search($query);
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div class="momentry-search-results">
|
||||
<h3>「<?= esc_html($query) ?>」搜尋結果</h3>
|
||||
<?php if (empty($result['results'])): ?>
|
||||
<p>沒有找到相關結果</p>
|
||||
<?php else: ?>
|
||||
<ul>
|
||||
<?php foreach ($result['results'] as $item): ?>
|
||||
<li>
|
||||
<a href="/video/<?= esc_attr($item['uuid']) ?>?t=<?= (int)$item['start_time'] ?>">
|
||||
<?= esc_html($item['text']) ?>
|
||||
</a>
|
||||
<br>
|
||||
<small>相似度: <?= round($item['score'] * 100) ?>%</small>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
|
||||
} catch (Exception $e) {
|
||||
return '<p class="error">搜尋失敗: ' . esc_html($e->getMessage()) . '</p>';
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 3.3 使用方式
|
||||
|
||||
在 WordPress 頁面或文章中:
|
||||
|
||||
```
|
||||
[momentry_videos limit="5"]
|
||||
|
||||
[momentry_search]ExaSAN[/momentry_search]
|
||||
```
|
||||
|
||||
### 3.4 REST API 整合
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* 註冊 WordPress REST API 端點
|
||||
*/
|
||||
|
||||
add_action('rest_api_init', function() {
|
||||
register_rest_route('momentry/v1', '/search', [
|
||||
'methods' => 'GET',
|
||||
'callback' => function(WP_REST_Request $request) {
|
||||
$query = sanitize_text_field($request->get_param('q'));
|
||||
|
||||
if (empty($query)) {
|
||||
return new WP_Error('missing_query', '需要搜尋關鍵字', ['status' => 400]);
|
||||
}
|
||||
|
||||
$api = new Momentry_API();
|
||||
$result = $api->search($query);
|
||||
|
||||
return new WP_REST_Response($result, 200);
|
||||
},
|
||||
'permission_callback' => '__return_true',
|
||||
]);
|
||||
});
|
||||
|
||||
// 使用方式: GET /wp-json/momentry/v1/search?q=ExaSAN
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 疑難排解
|
||||
|
||||
### 4.1 常見錯誤
|
||||
|
||||
| 錯誤 | 原因 | 解決方案 |
|
||||
|------|------|----------|
|
||||
| `401 Unauthorized` | API Key 無效或過期 | 檢查 API Key 是否正確 |
|
||||
| `500 Internal Server Error` | 伺服器錯誤 | 檢查 `/health/detailed` 服務狀態 |
|
||||
| `Connection Timeout` | 網路問題 | 確認 `api.momentry.ddns.net` 可達 |
|
||||
|
||||
### 4.2 測試腳本
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# test_api.sh - Momentry API 測試腳本
|
||||
|
||||
API_KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
|
||||
BASE_URL="http://localhost:3002"
|
||||
|
||||
echo "=== 1. 健康檢查 ==="
|
||||
curl -s "$BASE_URL/health" | jq .
|
||||
echo ""
|
||||
|
||||
echo "=== 2. 列出影片 ==="
|
||||
curl -s -H "X-API-Key: $API_KEY" "$BASE_URL/api/v1/videos" | jq '.videos | length'
|
||||
echo ""
|
||||
|
||||
echo "=== 3. 搜尋測試 ==="
|
||||
curl -s -X POST -H "X-API-Key: $API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "test", "limit": 3}' \
|
||||
"$BASE_URL/api/v1/search" | jq '.results | length'
|
||||
echo ""
|
||||
|
||||
echo "=== 完成 ==="
|
||||
```
|
||||
|
||||
### 4.3 驗證腳本
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# verify_auth.sh - 驗證 API Key
|
||||
|
||||
API_KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
|
||||
BASE_URL="http://localhost:3002"
|
||||
|
||||
# 測試 1: 無 API Key
|
||||
echo "測試 1: 無 API Key"
|
||||
RESULT=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/api/v1/videos")
|
||||
[ "$RESULT" = "401" ] && echo "✅ 正確拒絕 (401)" || echo "❌ 預期 401,實際 $RESULT"
|
||||
|
||||
# 測試 2: 有 API Key
|
||||
echo "測試 2: 有 API Key"
|
||||
RESULT=$(curl -s -H "X-API-Key: $API_KEY" "$BASE_URL/api/v1/videos")
|
||||
echo "$RESULT" | jq -e '.videos' > /dev/null && echo "✅ 成功取得資料" || echo "❌ 取得資料失敗"
|
||||
|
||||
# 測試 3: 無效 API Key
|
||||
echo "測試 3: 無效 API Key"
|
||||
RESULT=$(curl -s -o /dev/null -w "%{http_code}" -H "X-API-Key: invalid_key" "$BASE_URL/api/v1/videos")
|
||||
[ "$RESULT" = "401" ] && echo "✅ 正確拒絕 (401)" || echo "❌ 預期 401,實際 $RESULT"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. API Key 管理
|
||||
|
||||
### 5.1 建立新 API Key
|
||||
|
||||
```bash
|
||||
# 本地建立
|
||||
./target/release/momentry api-key create "My App" --key-type user --ttl 90
|
||||
```
|
||||
|
||||
### 5.2 列出 API Keys
|
||||
|
||||
```bash
|
||||
./target/release/momentry api-key list
|
||||
```
|
||||
|
||||
### 5.3 驗證 API Key
|
||||
|
||||
```bash
|
||||
./target/release/momentry api-key validate --key "YOUR_API_KEY"
|
||||
```
|
||||
|
||||
### 5.4 撤銷 API Key
|
||||
|
||||
```bash
|
||||
./target/release/momentry api-key revoke --key "YOUR_API_KEY"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 附錄
|
||||
|
||||
### A. 影片 UUID 說明
|
||||
|
||||
UUID 是基於檔案路徑的 SHA256 哈希前 16 位:
|
||||
|
||||
```
|
||||
/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4
|
||||
↓
|
||||
SHA256 Hash
|
||||
↓
|
||||
9760d0820f0cf9a7
|
||||
```
|
||||
|
||||
### B. 處理器狀態
|
||||
|
||||
| 狀態 | 說明 |
|
||||
|------|------|
|
||||
| `pending` | 等待處理 |
|
||||
| `running` | 處理中 |
|
||||
| `completed` | 已完成 |
|
||||
| `failed` | 失敗 |
|
||||
|
||||
### C. 支援的處理器
|
||||
|
||||
- **ASR**: 語音識別
|
||||
- **CUT**: 場景剪切
|
||||
- **YOLO**: 物件偵測
|
||||
|
||||
### D. 聯絡支援
|
||||
|
||||
- Email: support@momentry.ddns.net
|
||||
- 文件: https://docs.momentry.ddns.net
|
||||
- GitHub: https://github.com/anomalyco/momentry
|
||||
@@ -1,707 +0,0 @@
|
||||
# Momentry 系統全新 Mac 安裝指南
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立時間 | 2026-03-23 |
|
||||
| 文件版本 | V1.0 |
|
||||
| 適用對象 | 全新 Mac (Intel 或 Apple Silicon) |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 |
|
||||
|------|------|------|--------|
|
||||
| V1.0 | 2026-03-23 | 創建文件 | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## 快速概覽
|
||||
|
||||
### 安裝時間估算
|
||||
|
||||
| 項目 | 時間 |
|
||||
|------|------|
|
||||
| 系統準備 | 30 分鐘 |
|
||||
| Homebrew 安裝 | 15 分鐘 |
|
||||
| 資料庫服務 | 30 分鐘 |
|
||||
| 應用服務 | 60 分鐘 |
|
||||
| Momentry Core | 20 分鐘 |
|
||||
| **總計** | **~2.5 小時** |
|
||||
|
||||
### 系統需求
|
||||
|
||||
| 項目 | 最低需求 | 推薦需求 |
|
||||
|------|----------|----------|
|
||||
| macOS | 13.0 Ventura | 14.0+ Sonoma |
|
||||
| 記憶體 | 8GB | 16GB+ |
|
||||
| 儲存空間 | 100GB | 500GB+ |
|
||||
| CPU | Apple Silicon M1 或 Intel i5 | M2/M3 或 Intel i7+ |
|
||||
|
||||
---
|
||||
|
||||
## 第一部分:系統準備
|
||||
|
||||
### 1.1 macOS 初始設定
|
||||
|
||||
首次開機後,執行以下設定:
|
||||
|
||||
```bash
|
||||
# 1. 設定電腦名稱
|
||||
sudo scutil --set ComputerName "Momentry"
|
||||
sudo scutil --set LocalHostName "momentry"
|
||||
|
||||
# 2. 啟用 SSH 遠端登入
|
||||
sudo systemsetup -setremotelogin on
|
||||
|
||||
# 3. 關閉休眠 (防止遠端斷線)
|
||||
sudo pmset -a sleep 0
|
||||
sudo pmset -a hibernatemode 0
|
||||
|
||||
# 4. 允許任何來源 App (安裝開發工具用)
|
||||
sudo spctl --master-disable
|
||||
|
||||
# 5. 設定時區
|
||||
sudo systemsetup -settimezone "Asia/Taipei"
|
||||
```
|
||||
|
||||
### 1.2 建立管理員帳戶
|
||||
|
||||
```bash
|
||||
# 建立 Momentry 管理員帳戶 (可選)
|
||||
sudo dscl . -create /Users/momentry
|
||||
sudo dscl . -create /Users/momentry UserShell /bin/zsh
|
||||
sudo dscl . -create /Users/momentry RealName "Momentry Admin"
|
||||
sudo dscl . -create /Users/momentry PrimaryGroupID 80
|
||||
sudo dscl . -create /Users/momentry UniqueID 1001
|
||||
sudo dscl . -passwd /Users/momentry "momentry_password"
|
||||
sudo dscl . -append /Groups/admin GroupMembership momentry
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 第二部分:Homebrew 安裝
|
||||
|
||||
### 2.1 安裝 Homebrew
|
||||
|
||||
```bash
|
||||
# 安裝 Homebrew
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
|
||||
# Apple Silicon 設定
|
||||
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
|
||||
eval "$(/opt/homebrew/bin/brew shellenv)"
|
||||
|
||||
# Intel Mac 設定
|
||||
# echo 'eval "$(/usr/local/bin/brew shellenv)"' >> ~/.zprofile
|
||||
# eval "$(/usr/local/bin/brew shellenv)"
|
||||
|
||||
# 驗證
|
||||
brew --version
|
||||
```
|
||||
|
||||
### 2.2 安裝基礎工具
|
||||
|
||||
```bash
|
||||
# 開發工具
|
||||
brew install \
|
||||
git \
|
||||
curl \
|
||||
wget \
|
||||
jq \
|
||||
yq \
|
||||
tree \
|
||||
htop \
|
||||
tmux \
|
||||
zsh \
|
||||
zsh-completions \
|
||||
ncdu \
|
||||
httpie \
|
||||
openssl \
|
||||
readline \
|
||||
sqlite \
|
||||
python@3.11 \
|
||||
rustup-init
|
||||
|
||||
# Rust 初始化
|
||||
rustup-init -y
|
||||
source "$HOME/.cargo/env"
|
||||
|
||||
# Python 虛擬環境
|
||||
python3.11 -m venv ~/.venv
|
||||
source ~/.venv/bin/activate
|
||||
pip install --upgrade pip
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 第三部分:建立目錄結構
|
||||
|
||||
```bash
|
||||
# 建立目錄結構
|
||||
mkdir -p /Users/accusys/momentry/{var,etc,log,scripts,backup}
|
||||
mkdir -p /Users/accusys/momentry/var/{postgresql,mongodb,mariadb,redis,qdrant,n8n,ollama,sftpgo}
|
||||
mkdir -p /Users/accusys/momentry/etc/{sftpgo,caddy,gitea,php}
|
||||
mkdir -p /Users/accusys/momentry/scripts
|
||||
mkdir -p /Users/accusys/momentry/backup/{daily,weekly,monthly}
|
||||
mkdir -p /Users/accusys/workspace/sftpgo
|
||||
mkdir -p /Users/accusys/sftpgo_test/{demo,uploads}
|
||||
|
||||
# 設定權限
|
||||
chown -R accusys:staff /Users/accusys/momentry
|
||||
chown -R accusys:staff /Users/accusys/workspace
|
||||
chown -R accusys:staff /Users/accusys/sftpgo_test
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 第四部分:資料庫服務安裝
|
||||
|
||||
### 4.1 PostgreSQL
|
||||
|
||||
```bash
|
||||
# 安裝
|
||||
brew install postgresql@18
|
||||
|
||||
# 啟動服務
|
||||
brew services start postgresql@18
|
||||
|
||||
# 建立資料庫
|
||||
createdb momentry
|
||||
createdb video_register
|
||||
createdb n8n
|
||||
createdb sftpgo
|
||||
|
||||
# 建立用戶
|
||||
psql -U accusys -d postgres << 'EOF'
|
||||
CREATE USER sftpgo WITH PASSWORD 'sftpgo_pass_2026';
|
||||
GRANT ALL PRIVILEGES ON DATABASE sftpgo TO sftpgo;
|
||||
GRANT ALL PRIVILEGES ON DATABASE momentry TO accusys;
|
||||
GRANT ALL PRIVILEGES ON DATABASE video_register TO accusys;
|
||||
GRANT ALL PRIVILEGES ON DATABASE n8n TO accusys;
|
||||
EOF
|
||||
|
||||
# 驗證
|
||||
pg_isready -h 127.0.0.1 -p 5432
|
||||
```
|
||||
|
||||
### 4.2 MongoDB
|
||||
|
||||
```bash
|
||||
# 安裝
|
||||
brew tap mongodb/brew
|
||||
brew install mongodb-community
|
||||
|
||||
# 建立資料目錄
|
||||
mkdir -p /Users/accusys/momentry/var/mongodb
|
||||
chown -R accusys:staff /Users/accusys/momentry/var/mongodb
|
||||
|
||||
# 啟動服務
|
||||
brew services start mongodb-community
|
||||
|
||||
# 建立用戶
|
||||
mongosh admin --eval "
|
||||
db.createUser({
|
||||
user: 'accusys',
|
||||
pwd: 'Test3200Test3200',
|
||||
roles: [
|
||||
{ role: 'readWriteAnyDatabase', db: 'admin' },
|
||||
{ role: 'dbAdminAnyDatabase', db: 'admin' }
|
||||
]
|
||||
});
|
||||
"
|
||||
|
||||
# 驗證
|
||||
mongosh --quiet --eval "db.adminCommand('ping')"
|
||||
```
|
||||
|
||||
### 4.3 Redis
|
||||
|
||||
```bash
|
||||
# 安裝
|
||||
brew install redis
|
||||
|
||||
# 建立配置目錄
|
||||
mkdir -p /Users/accusys/momentry/etc/redis
|
||||
|
||||
# 建立配置文件
|
||||
cat > /Users/accusys/momentry/etc/redis/redis.conf << 'EOF'
|
||||
requirepass accusys
|
||||
dir /Users/accusys/momentry/var/redis
|
||||
logfile /Users/accusys/momentry/log/redis.log
|
||||
daemonize no
|
||||
port 6379
|
||||
bind 127.0.0.1
|
||||
EOF
|
||||
|
||||
# 啟動服務
|
||||
redis-server /Users/accusys/momentry/etc/redis/redis.conf &
|
||||
sleep 2
|
||||
|
||||
# 驗證
|
||||
redis-cli -a accusys ping
|
||||
```
|
||||
|
||||
### 4.4 MariaDB (WordPress)
|
||||
|
||||
```bash
|
||||
# 安裝
|
||||
brew install mariadb
|
||||
|
||||
# 啟動服務
|
||||
brew services start mariadb
|
||||
|
||||
# 安全設定
|
||||
mysql_secure_installation
|
||||
|
||||
# 建立資料庫
|
||||
mysql -u root << 'EOF'
|
||||
CREATE DATABASE wordpress CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE USER 'wordpress'@'localhost' IDENTIFIED BY 'wordpress_password';
|
||||
GRANT ALL PRIVILEGES ON wordpress.* TO 'wordpress'@'localhost';
|
||||
FLUSH PRIVILEGES;
|
||||
EOF
|
||||
|
||||
# 驗證
|
||||
mysql -u root -e "SHOW DATABASES;"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 第五部分:應用服務安裝
|
||||
|
||||
### 5.1 Ollama (LLM)
|
||||
|
||||
```bash
|
||||
# 安裝
|
||||
brew install ollama
|
||||
|
||||
# 建立資料目錄
|
||||
mkdir -p /Users/accusys/momentry/var/ollama
|
||||
mkdir -p ~/.ollama/models
|
||||
|
||||
# 啟動服務
|
||||
brew services start ollama
|
||||
|
||||
# 下載模型
|
||||
ollama pull llama3.2
|
||||
ollama pull nomic-embed-text
|
||||
|
||||
# 驗證
|
||||
curl -s http://localhost:11434/api/tags | jq '.models[].name'
|
||||
```
|
||||
|
||||
### 5.2 Caddy (反向代理)
|
||||
|
||||
```bash
|
||||
# 安裝
|
||||
brew install caddy
|
||||
|
||||
# 建立配置目錄
|
||||
mkdir -p /Users/accusys/momentry/etc/caddy
|
||||
|
||||
# 建立 Caddyfile
|
||||
cat > /Users/accusys/momentry/etc/caddy/Caddyfile << 'EOF'
|
||||
{
|
||||
admin off
|
||||
auto_https off
|
||||
}
|
||||
|
||||
# 範例:本地開發
|
||||
:8080 {
|
||||
respond "Momentry Server" 200
|
||||
}
|
||||
EOF
|
||||
|
||||
# 啟動服務
|
||||
brew services start caddy
|
||||
|
||||
# 驗證
|
||||
curl -s http://localhost:8080
|
||||
```
|
||||
|
||||
### 5.3 Gitea (Git 服務)
|
||||
|
||||
```bash
|
||||
# 安裝
|
||||
brew install gitea
|
||||
|
||||
# 建立資料目錄
|
||||
mkdir -p /Users/accusys/momentry/var/gitea
|
||||
mkdir -p /Users/accusys/momentry/etc/gitea
|
||||
|
||||
# 複製設定檔
|
||||
cp /opt/homebrew/etc/gitea/conf/app.ini /Users/accusys/momentry/etc/gitea/
|
||||
|
||||
# 啟動服務
|
||||
brew services start gitea
|
||||
|
||||
# 驗證
|
||||
curl -s http://localhost:3000
|
||||
```
|
||||
|
||||
### 5.4 n8n (工作流程自動化)
|
||||
|
||||
```bash
|
||||
# 安裝 Node.js (如果尚未安裝)
|
||||
brew install node@22
|
||||
|
||||
# 全域安裝 n8n
|
||||
npm install -g n8n
|
||||
|
||||
# 建立資料目錄
|
||||
mkdir -p /Users/accusys/momentry/var/n8n
|
||||
|
||||
# 設定環境變數
|
||||
export N8N_DATA_DIR=/Users/accusys/momentry/var/n8n
|
||||
export DB_TYPE=postgresdb
|
||||
export DB_POSTGRES_HOST=127.0.0.1
|
||||
export DB_POSTGRES_PORT=5432
|
||||
export DB_POSTGRES_DATABASE=n8n
|
||||
export DB_POSTGRES_USER=accusys
|
||||
export DB_POSTGRES_PASSWORD=accusys
|
||||
export N8N_ENCRYPTION_KEY=Test3200Test3200Test3200
|
||||
|
||||
# 啟動服務
|
||||
n8n start &
|
||||
|
||||
# 驗證
|
||||
curl -s http://localhost:5678
|
||||
```
|
||||
|
||||
### 5.5 SFTPGo (SFTP 服務)
|
||||
|
||||
```bash
|
||||
# 安裝
|
||||
brew install sftpgo
|
||||
|
||||
# 建立配置目錄
|
||||
mkdir -p /Users/accusys/momentry/etc/sftpgo
|
||||
|
||||
# 建立配置文件
|
||||
cat > /Users/accusys/momentry/etc/sftpgo/sftpgo.json << 'EOF'
|
||||
{
|
||||
"data_provider": {
|
||||
"driver": "postgresql",
|
||||
"host": "127.0.0.1",
|
||||
"port": 5432,
|
||||
"username": "sftpgo",
|
||||
"password": "sftpgo_pass_2026",
|
||||
"name": "sftpgo",
|
||||
"create_default_admin": true
|
||||
},
|
||||
"httpd": {
|
||||
"bind_port": 8080,
|
||||
"bind_address": "127.0.0.1"
|
||||
},
|
||||
"sftpd": {
|
||||
"bindings": [
|
||||
{"port": 2022}
|
||||
]
|
||||
},
|
||||
"ftpd": {
|
||||
"bindings": [
|
||||
{"port": 0}
|
||||
]
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# 建立 launchd plist
|
||||
cat > /Library/LaunchDaemons/com.momentry.sftpgo.plist << 'EOF'
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.momentry.sftpgo</string>
|
||||
<key>UserName</key>
|
||||
<string>accusys</string>
|
||||
<key>WorkingDirectory</key>
|
||||
<string>/Users/accusys/workspace/sftpgo</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/opt/homebrew/bin/sftpgo</string>
|
||||
<string>serve</string>
|
||||
<string>--config-file</string>
|
||||
<string>/Users/accusys/momentry/etc/sftpgo/sftpgo.json</string>
|
||||
</array>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>PATH</key>
|
||||
<string>/opt/homebrew/bin:/opt/homebrew/sbin:/usr/bin:/bin</string>
|
||||
<key>HOME</key>
|
||||
<string>/Users/accusys</string>
|
||||
<key>SFTPGO_DEFAULT_ADMIN_USERNAME</key>
|
||||
<string>admin</string>
|
||||
<key>SFTPGO_DEFAULT_ADMIN_PASSWORD</key>
|
||||
<string>Test3200Test3200</string>
|
||||
</dict>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/Users/accusys/momentry/log/sftpgo.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/Users/accusys/momentry/log/sftpgo.error.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
|
||||
# 載入服務
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.sftpgo.plist
|
||||
|
||||
# 驗證
|
||||
sleep 3
|
||||
curl -s -X POST http://localhost:8080/api/v2/token -u "admin:Test3200Test3200"
|
||||
```
|
||||
|
||||
### 5.6 Qdrant (向量資料庫)
|
||||
|
||||
```bash
|
||||
# 安裝 Rust (如果尚未安裝)
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
source "$HOME/.cargo/env"
|
||||
|
||||
# 安裝 Qdrant
|
||||
cargo install qdrant
|
||||
|
||||
# 建立資料目錄
|
||||
mkdir -p /Users/accusys/momentry/var/qdrant/storage
|
||||
|
||||
# 啟動服務
|
||||
qdrant --data-path /Users/accusys/momentry/var/qdrant/storage --api-key Test3200Test3200Test3200 &
|
||||
|
||||
# 驗證
|
||||
sleep 3
|
||||
curl -s http://localhost:6333/collections -H "api-key: Test3200Test3200Test3200"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 第六部分:Momentry Core 安裝
|
||||
|
||||
### 6.1 安裝依賴
|
||||
|
||||
```bash
|
||||
# 安裝 Python 依賴
|
||||
pip install \
|
||||
openai-whisper \
|
||||
ultralytics \
|
||||
opencv-python \
|
||||
pillow \
|
||||
python-dotenv \
|
||||
requests \
|
||||
httpx
|
||||
|
||||
# 安裝 FFmpeg
|
||||
brew install ffmpeg
|
||||
```
|
||||
|
||||
### 6.2 安裝 Momentry Core
|
||||
|
||||
```bash
|
||||
# 克隆或進入專案目錄
|
||||
cd /Users/accusys/momentry_core_0.1
|
||||
|
||||
# 編譯專案
|
||||
cargo build --release
|
||||
|
||||
# 複製執行檔
|
||||
cp target/release/momentry /usr/local/bin/
|
||||
|
||||
# 建立 launchd plist
|
||||
cat > /Library/LaunchDaemons/com.momentry.api.plist << 'EOF'
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.momentry.api</string>
|
||||
<key>UserName</key>
|
||||
<string>accusys</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/usr/local/bin/momentry</string>
|
||||
<string>server</string>
|
||||
<string>--host</string>
|
||||
<string>0.0.0.0</string>
|
||||
<string>--port</string>
|
||||
<string>3002</string>
|
||||
</array>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>DATABASE_URL</key>
|
||||
<string>postgres://accusys:accusys@127.0.0.1:5432/momentry</string>
|
||||
<key>RUST_LOG</key>
|
||||
<string>info</string>
|
||||
</dict>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/Users/accusys/momentry/log/momentry.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/Users/accusys/momentry/log/momentry.error.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
|
||||
# 載入服務
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
|
||||
|
||||
# 驗證
|
||||
sleep 3
|
||||
curl -s http://localhost:3002/health
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 第七部分:服務驗證
|
||||
|
||||
### 7.1 健康檢查腳本
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# health_check.sh
|
||||
|
||||
echo "=========================================="
|
||||
echo "Momentry System Health Check"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# PostgreSQL
|
||||
pg_isready -h 127.0.0.1 -p 5432 > /dev/null 2>&1
|
||||
[ $? -eq 0 ] && echo "✅ PostgreSQL" || echo "❌ PostgreSQL"
|
||||
|
||||
# MongoDB
|
||||
mongosh --quiet --eval "db.adminCommand('ping')" > /dev/null 2>&1
|
||||
[ $? -eq 0 ] && echo "✅ MongoDB" || echo "❌ MongoDB"
|
||||
|
||||
# Redis
|
||||
redis-cli -a accusys ping > /dev/null 2>&1
|
||||
[ $? -eq 0 ] && echo "✅ Redis" || echo "❌ Redis"
|
||||
|
||||
# Ollama
|
||||
curl -s http://localhost:11434/api/tags > /dev/null 2>&1
|
||||
[ $? -eq 0 ] && echo "✅ Ollama" || echo "❌ Ollama"
|
||||
|
||||
# n8n
|
||||
curl -s http://localhost:5678 > /dev/null 2>&1
|
||||
[ $? -eq 0 ] && echo "✅ n8n" || echo "❌ n8n"
|
||||
|
||||
# SFTPGo
|
||||
curl -s http://localhost:8080 > /dev/null 2>&1
|
||||
[ $? -eq 0 ] && echo "✅ SFTPGo" || echo "❌ SFTPGo"
|
||||
|
||||
# Qdrant
|
||||
curl -s http://localhost:6333/ > /dev/null 2>&1
|
||||
[ $? -eq 0 ] && echo "✅ Qdrant" || echo "❌ Qdrant"
|
||||
|
||||
# Momentry API
|
||||
curl -s http://localhost:3002/health > /dev/null 2>&1
|
||||
[ $? -eq 0 ] && echo "✅ Momentry API" || echo "❌ Momentry API"
|
||||
|
||||
# Caddy
|
||||
curl -sI http://localhost:8080 > /dev/null 2>&1
|
||||
[ $? -eq 0 ] && echo "✅ Caddy" || echo "❌ Caddy"
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
```
|
||||
|
||||
### 7.2 執行驗證
|
||||
|
||||
```bash
|
||||
# 儲存並執行
|
||||
chmod +x health_check.sh
|
||||
./health_check.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 第八部分:重要憑證總覽
|
||||
|
||||
### 8.1 服務密碼
|
||||
|
||||
| 服務 | 用戶名 | 密碼 | 用途 |
|
||||
|------|--------|------|------|
|
||||
| **PostgreSQL** | accusys | `accusys` | 主要資料庫 |
|
||||
| **PostgreSQL** | sftpgo | `sftpgo_pass_2026` | SFTPGo 資料庫 |
|
||||
| **MongoDB** | accusys | `Test3200Test3200` | 文件資料庫 |
|
||||
| **Redis** | - | `accusys` | 快取服務 |
|
||||
| **n8n** | - | `Test3200Test3200Test3200` | n8n 加密金鑰 |
|
||||
| **SFTPGo** | admin | `Test3200Test3200` | SFTPGo 管理員 |
|
||||
| **Qdrant** | - | `Test3200Test3200Test3200` | Qdrant API Key |
|
||||
| **MariaDB** | root | (設定時指定) | WordPress 資料庫 |
|
||||
| **MariaDB** | wordpress | `wordpress_password` | WordPress 資料庫用戶 |
|
||||
|
||||
### 8.2 服務連接埠
|
||||
|
||||
| 服務 | 連接埠 | 協定 |
|
||||
|------|--------|------|
|
||||
| PostgreSQL | 5432 | TCP |
|
||||
| MongoDB | 27017 | TCP |
|
||||
| Redis | 6379 | TCP |
|
||||
| Ollama | 11434 | HTTP |
|
||||
| n8n | 5678 | HTTP |
|
||||
| SFTPGo | 8080 | HTTP |
|
||||
| SFTPGo | 2022 | SFTP |
|
||||
| Qdrant | 6333 | HTTP |
|
||||
| Momentry API | 3002 | HTTP |
|
||||
| Caddy | 80/443 | HTTP/HTTPS |
|
||||
| Gitea | 3000 | HTTP |
|
||||
| PHP-FPM | 9000 | TCP |
|
||||
|
||||
---
|
||||
|
||||
## 第九部分:相關文檔
|
||||
|
||||
| 文檔 | 說明 |
|
||||
|------|------|
|
||||
| `SERVICES.md` | 服務詳細說明 |
|
||||
| `INSTALL_POSTGRESQL.md` | PostgreSQL 安裝指南 |
|
||||
| `INSTALL_MONGODB.md` | MongoDB 安裝指南 |
|
||||
| `INSTALL_REDIS.md` | Redis 安裝指南 |
|
||||
| `INSTALL_QDRANT.md` | Qdrant 安裝指南 |
|
||||
| `INSTALL_N8N.md` | n8n 安裝指南 |
|
||||
| `INSTALL_SFTPGO.md` | SFTPGo 安裝指南 |
|
||||
| `SFTPGO_DEMO_USER.md` | SFTPGo Demo 用戶指南 |
|
||||
| `MOMENTRY_CORE_MONITORING.md` | 監控規範 |
|
||||
| `API_INDEX.md` | API 文件索引 |
|
||||
|
||||
---
|
||||
|
||||
## 附錄:常見問題
|
||||
|
||||
### Q1: n8n 無法連接 PostgreSQL
|
||||
|
||||
確保資料庫用戶有登入權限:
|
||||
```bash
|
||||
psql -U accusys -d postgres -c "ALTER USER accusys WITH LOGIN;"
|
||||
```
|
||||
|
||||
### Q2: SFTPGo 無法啟動
|
||||
|
||||
檢查 PostgreSQL 是否運行:
|
||||
```bash
|
||||
pg_isready -h 127.0.0.1 -p 5432
|
||||
```
|
||||
|
||||
### Q3: Qdrant API Key 無效
|
||||
|
||||
使用正確的 API Key:
|
||||
```bash
|
||||
curl -H "api-key: Test3200Test3200Test3200" http://localhost:6333/collections
|
||||
```
|
||||
|
||||
### Q4: Momentry API 無法啟動
|
||||
|
||||
檢查環境變數:
|
||||
```bash
|
||||
export DATABASE_URL="postgres://accusys:accusys@127.0.0.1:5432/momentry"
|
||||
export RUST_LOG=debug
|
||||
momentry server --host 0.0.0.0 --port 3002
|
||||
```
|
||||
@@ -1,467 +0,0 @@
|
||||
# Caddy 安裝指南 (本地部署)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-16 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
本文檔說明如何在 macOS 上安裝 Caddy Web Server,配置為本地部署,作為反向代理伺服器。
|
||||
|
||||
---
|
||||
|
||||
## 當前狀態
|
||||
|
||||
| 項目 | 狀態 |
|
||||
|------|------|
|
||||
| Caddy | ✅ 已安裝 v2.10.2 |
|
||||
| 設定檔 | /Users/accusys/momentry/etc/Caddyfile |
|
||||
| 日誌目錄 | /Users/accusys/momentry/log/ |
|
||||
| Plist | /Library/LaunchDaemons/com.momentry.caddy.plist |
|
||||
|
||||
---
|
||||
|
||||
## 安裝步驟
|
||||
|
||||
### Step 1: 安裝 Caddy (使用 brew)
|
||||
|
||||
```bash
|
||||
# 安裝 Caddy
|
||||
brew install caddy
|
||||
```
|
||||
|
||||
**驗證**:
|
||||
```bash
|
||||
caddy --version
|
||||
# v2.10.2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 建立目錄
|
||||
|
||||
```bash
|
||||
# 建立配置目錄
|
||||
mkdir -p /Users/accusys/momentry/etc/caddy
|
||||
|
||||
# 建立日誌目錄
|
||||
mkdir -p /Users/accusys/momentry/log
|
||||
|
||||
# 建立數據目錄
|
||||
mkdir -p /Users/accusys/momentry/var/caddy
|
||||
|
||||
# 建立日誌文件
|
||||
touch /Users/accusys/momentry/log/caddy.log
|
||||
touch /Users/accusys/momentry/log/caddy.error.log
|
||||
|
||||
# 設定權限
|
||||
# 注意: Caddy 使用 ports 80/443,必須以 root 身份運行
|
||||
# 因此 var/caddy 目錄需要 root:admin 權限
|
||||
chown -R accusys:staff /Users/accusys/momentry/etc/caddy
|
||||
chown -R accusys:staff /Users/accusys/momentry/log
|
||||
sudo chown -R root:admin /Users/accusys/momentry/var/caddy
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 建立設定檔
|
||||
|
||||
建立 `/Users/accusys/momentry/etc/Caddyfile`:
|
||||
|
||||
```Caddyfile
|
||||
{
|
||||
email admin@accusys.com.tw
|
||||
metrics
|
||||
}
|
||||
|
||||
# 定義日誌 Snippet
|
||||
(common_log) {
|
||||
log {
|
||||
output file /Users/accusys/momentry/log/{args[0]}.log {
|
||||
roll_size 100mb
|
||||
roll_keep 5
|
||||
roll_keep_for 720h
|
||||
}
|
||||
format json
|
||||
}
|
||||
}
|
||||
|
||||
# Example: 反向代理到本地服務
|
||||
example.momentry.ddns.net {
|
||||
reverse_proxy localhost:8080 {
|
||||
header_up Host {upstream_hostport}
|
||||
}
|
||||
import common_log example_access
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 使用 plist 開機自動啟動
|
||||
|
||||
**注意**: Caddy 需要使用 ports 80 和 443,必須以 root 身份運行。
|
||||
|
||||
```bash
|
||||
# 複製 plist 到 LaunchDaemons 目錄
|
||||
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.caddy.plist /Library/LaunchDaemons/
|
||||
|
||||
# 載入並啟動
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.caddy.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 監控配置
|
||||
|
||||
### 添加到監控配置
|
||||
|
||||
在 `monitor/config/monitor_config.yaml` 中添加:
|
||||
|
||||
```yaml
|
||||
service:
|
||||
services:
|
||||
- name: "caddy"
|
||||
type: "http"
|
||||
port: 80
|
||||
host: "localhost"
|
||||
check_url: "http://localhost:2019/config/"
|
||||
timeout: 5
|
||||
enabled: true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 卸載步驟
|
||||
|
||||
### 重要: 路徑說明
|
||||
|
||||
| 路徑 | 類型 | 說明 |
|
||||
|------|------|------|
|
||||
| `/Users/accusys/momentry/etc/caddy/` | 配置 | **不要刪除** - Caddy 配置 |
|
||||
| `/Users/accusys/momentry/log/` | 日誌 | **不要刪除** - 日誌目錄 |
|
||||
| `/Users/accusys/momentry/var/caddy/` | 數據 | **不要刪除** - Caddy 數據 |
|
||||
| `/opt/homebrew/opt/caddy/` | 安裝 | **刪除** - Caddy 安裝目錄 |
|
||||
| `/opt/homebrew/opt/caddy/` | 安裝 | **刪除** - Caddy 安裝目錄 |
|
||||
|
||||
### Step 1: 停止 Caddy
|
||||
|
||||
```bash
|
||||
# 找到 Caddy 進程
|
||||
ps aux | grep caddy | grep -v grep
|
||||
|
||||
# 停止 Caddy
|
||||
pkill caddy
|
||||
|
||||
# 確認停止
|
||||
ps aux | grep caddy | grep -v grep || echo "Caddy 已停止"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 卸載 Caddy
|
||||
|
||||
```bash
|
||||
# 卸載 Caddy
|
||||
brew uninstall caddy
|
||||
|
||||
# 移除 plist
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.caddy.plist
|
||||
sudo rm /Library/LaunchDaemons/com.momentry.caddy.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 刪除專屬檔案
|
||||
|
||||
```bash
|
||||
# 刪除配置目錄 (可選)
|
||||
rm -rf /Users/accusys/momentry/etc/Caddyfile
|
||||
|
||||
# 刪除日誌 (可選)
|
||||
rm -f /Users/accusys/momentry/log/caddy.log
|
||||
rm -f /Users/accusys/momentry/log/caddy.error.log
|
||||
|
||||
# 刪除數據目錄 (可選)
|
||||
rm -rf /Users/accusys/momentry/var/caddy
|
||||
```
|
||||
|
||||
**注意: 不要刪除以下共用目錄**:
|
||||
```bash
|
||||
# 這些是共用的,不要刪除!
|
||||
# /Users/accusys/momentry/etc
|
||||
# /Users/accusys/momentry/log
|
||||
# /Users/accusys/momentry/var
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 卸載後檢查清單
|
||||
|
||||
```bash
|
||||
echo "=== Caddy 卸載後檢查 ==="
|
||||
|
||||
# 1. 檢查 Caddy 進程
|
||||
echo "1. Caddy 進程:"
|
||||
ps aux | grep caddy | grep -v grep && echo " ✗ 仍在運行" || echo " ✓ 已停止"
|
||||
|
||||
# 2. Port 80/443
|
||||
echo "2. Port 80/443:"
|
||||
(lsof -i :80 > /dev/null 2>&1 || lsof -i :443 > /dev/null 2>&1) && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
|
||||
|
||||
# 3. caddy 命令
|
||||
echo "3. caddy 命令:"
|
||||
which caddy > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 4. brew 安裝
|
||||
echo "4. brew 安裝:"
|
||||
brew list caddy > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 5. launchctl 服務
|
||||
echo "5. launchctl 服務:"
|
||||
sudo launchctl list | grep caddy > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 6. 配置目錄 (可選刪除)
|
||||
echo "6. 配置目錄:"
|
||||
[ -d "/Users/accusys/momentry/etc" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
|
||||
|
||||
# 7. 日誌目錄 (可選刪除)
|
||||
echo "7. 日誌目錄:"
|
||||
[ -d "/Users/accusys/momentry/log" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
|
||||
```
|
||||
|
||||
**預期結果**:
|
||||
```
|
||||
=== Caddy 卸載後檢查 ===
|
||||
1. Caddy 進程:
|
||||
✓ 已停止
|
||||
2. Port 80/443:
|
||||
✓ 已釋放
|
||||
3. caddy 命令:
|
||||
✓ 已移除
|
||||
4. brew 安裝:
|
||||
✓ 已移除
|
||||
5. launchctl 服務:
|
||||
✓ 已移除
|
||||
6. 配置目錄:
|
||||
✓ 保留 (或 ✗ 已刪除)
|
||||
7. 日誌目錄:
|
||||
✓ 保留 (或 ✗ 已刪除)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 手動檢查命令
|
||||
|
||||
```bash
|
||||
# 1. 檢查進程
|
||||
ps aux | grep caddy | grep -v grep
|
||||
|
||||
# 2. 檢查 Port
|
||||
lsof -i :80
|
||||
lsof -i :443
|
||||
lsof -i :2019
|
||||
|
||||
# 3. 測試配置語法
|
||||
caddy validate --config /Users/accusys/momentry/etc/Caddyfile
|
||||
|
||||
# 4. 查看 Caddy 版本
|
||||
caddy version
|
||||
|
||||
# 5. 重新載入配置
|
||||
caddy reload --config /Users/accusys/momentry/etc/Caddyfile
|
||||
|
||||
# 6. 查看日誌
|
||||
tail -20 /Users/accusys/momentry/log/caddy.log
|
||||
|
||||
# 7. 查看 Caddy 適配的網站
|
||||
curl -I http://localhost:2019/config/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Caddyfile 範例
|
||||
|
||||
### 基本反向代理
|
||||
|
||||
```Caddyfile
|
||||
{
|
||||
email admin@accusys.com.tw
|
||||
}
|
||||
|
||||
# 反向代理到本地服務
|
||||
example.local {
|
||||
reverse_proxy localhost:8080
|
||||
}
|
||||
```
|
||||
|
||||
### 帶 SSL 的反向代理
|
||||
|
||||
```Caddyfile
|
||||
{
|
||||
email admin@accusys.com.tw
|
||||
}
|
||||
|
||||
# 使用 Let's Encrypt 自動 SSL
|
||||
example.momentry.ddns.net {
|
||||
reverse_proxy localhost:8080 {
|
||||
header_up Host {upstream_hostport}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 多站點配置
|
||||
|
||||
```Caddyfile
|
||||
{
|
||||
email admin@accusys.com.tw
|
||||
}
|
||||
|
||||
# 站點 1
|
||||
site1.example.com {
|
||||
reverse_proxy localhost:8080
|
||||
}
|
||||
|
||||
# 站點 2
|
||||
site2.example.com {
|
||||
reverse_proxy localhost:8081
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 環境變數
|
||||
|
||||
在 `.env` 中:
|
||||
|
||||
```env
|
||||
CADDY_CONFIG=/Users/accusys/momentry/etc/Caddyfile
|
||||
CADDY_HOME=/Users/accusys/.local/share/caddy
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 故障排除
|
||||
|
||||
### Caddy 無法啟動
|
||||
|
||||
```bash
|
||||
# 檢查日誌
|
||||
tail -f /Users/accusys/momentry/log/caddy.log
|
||||
|
||||
# 檢查配置語法
|
||||
caddy validate --config /Users/accusys/momentry/etc/Caddyfile
|
||||
|
||||
# 檢查目錄權限
|
||||
ls -la /Users/accusys/momentry/etc/
|
||||
|
||||
# 重新設定權限
|
||||
chown -R $(whoami):staff /Users/accusys/momentry/etc
|
||||
```
|
||||
|
||||
### Port 被佔用
|
||||
|
||||
```bash
|
||||
# 檢查哪個程序佔用 port 80
|
||||
lsof -i :80
|
||||
|
||||
# 終止佔用程序
|
||||
kill <PID>
|
||||
```
|
||||
|
||||
### 需要重新載入配置
|
||||
|
||||
```bash
|
||||
# 重新載入配置 (熱重載)
|
||||
caddy reload --config /Users/accusys/momentry/etc/Caddyfile
|
||||
|
||||
# 或者停止後重新啟動
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.caddy.plist
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.caddy.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 檔案位置
|
||||
|
||||
| 類型 | 路徑 | 說明 |
|
||||
|------|------|------|
|
||||
| 安裝 | `/opt/homebrew/opt/caddy/` | Caddy 安裝目錄 |
|
||||
| 執行檔 | `/opt/homebrew/opt/caddy/bin/caddy` | Caddy 執行檔 |
|
||||
| 配置 | `/Users/accusys/momentry/etc/Caddyfile` | 設定檔 |
|
||||
| 日誌 | `/Users/accusys/momentry/log/caddy.log` | 執行日誌 |
|
||||
| 錯誤日誌 | `/Users/accusys/momentry/log/caddy.error.log` | 錯誤日誌 |
|
||||
| 數據 | `/Users/accusys/momentry/var/caddy/` | Caddy 數據目錄 |
|
||||
| plist | `/Library/LaunchDaemons/com.momentry.caddy.plist` | 開機啟動 |
|
||||
| 備份 | `/Users/accusys/momentry/var/caddy_backup/Caddyfile` | 配置備份 |
|
||||
|
||||
---
|
||||
|
||||
## 常用指令
|
||||
|
||||
```bash
|
||||
# 驗證配置
|
||||
caddy validate --config /Users/accusys/momentry/etc/Caddyfile
|
||||
|
||||
# 熱重載配置
|
||||
caddy reload --config /Users/accusys/momentry/etc/Caddyfile
|
||||
|
||||
# 停止 Caddy
|
||||
caddy stop
|
||||
|
||||
# 啟動 Caddy
|
||||
caddy start --config /Users/accusys/momentry/etc/Caddyfile
|
||||
|
||||
# 適配所有網站
|
||||
caddy adapt --config /Users/accusys/momentry/etc/Caddyfile --adapter caddyfile
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 備份與恢復
|
||||
|
||||
### 備份
|
||||
|
||||
```bash
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
BACKUP_DIR="/Users/accusys/momentry/backup/daily/caddy"
|
||||
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
# 備份配置
|
||||
tar -czf "$BACKUP_DIR/caddy_cfg_${TIMESTAMP}.tar.gz" \
|
||||
/Users/accusys/momentry/etc/Caddyfile
|
||||
|
||||
# 驗證
|
||||
sha256sum "$BACKUP_DIR/caddy_cfg_${TIMESTAMP}.tar.gz" > "$BACKUP_DIR/caddy_${TIMESTAMP}.sha256"
|
||||
```
|
||||
|
||||
### 恢復
|
||||
|
||||
```bash
|
||||
# 解壓配置
|
||||
tar -xzf /Users/accusys/momentry/backup/daily/caddy/caddy_cfg_20260316_102416.tar.gz -C /
|
||||
|
||||
# 驗證並重載
|
||||
caddy validate --config /Users/accusys/momentry/etc/Caddyfile
|
||||
caddy reload --config /Users/accusys/momentry/etc/Caddyfile
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 版本資訊
|
||||
|
||||
- 版本: 2.10.2
|
||||
- 配置: /Users/accusys/momentry/etc/Caddyfile
|
||||
- 日誌目錄: /Users/accusys/momentry/log/
|
||||
@@ -1,410 +0,0 @@
|
||||
# Gitea 安裝指南 (本地部署)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-15 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-15 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
本文檔說明如何在 macOS 上安裝 Gitea Git 服務,配置為本地部署。
|
||||
|
||||
---
|
||||
|
||||
## 當前狀態
|
||||
|
||||
| 項目 | 狀態 |
|
||||
|------|------|
|
||||
| Gitea | ✅ 已安裝 v1.25.3 |
|
||||
| 數據目錄 | /Users/accusys/momentry/var/gitea/ |
|
||||
| 配置目錄 | /Users/accusys/momentry/etc/gitea/ |
|
||||
| 日誌目錄 | /Users/accusys/momentry/log/ |
|
||||
| Plist | /Library/LaunchDaemons/com.momentry.gitea.plist |
|
||||
|
||||
---
|
||||
|
||||
## 安裝步驟
|
||||
|
||||
### Step 1: 安裝 Gitea (使用 brew)
|
||||
|
||||
```bash
|
||||
# 安裝 Gitea
|
||||
brew install gitea
|
||||
```
|
||||
|
||||
**驗證**:
|
||||
```bash
|
||||
gitea --version
|
||||
# gitea version 1.25.3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 建立目錄
|
||||
|
||||
```bash
|
||||
# 建立數據目錄
|
||||
mkdir -p /Users/accusys/momentry/var/gitea/data
|
||||
mkdir -p /Users/accusys/momentry/var/gitea/log
|
||||
|
||||
# 建立配置目錄
|
||||
mkdir -p /Users/accusys/momentry/etc/gitea
|
||||
|
||||
# 建立日誌目錄
|
||||
mkdir -p /Users/accusys/momentry/log
|
||||
|
||||
# 建立日誌文件
|
||||
touch /Users/accusys/momentry/log/gitea.log
|
||||
touch /Users/accusys/momentry/log/gitea.error.log
|
||||
|
||||
# 設定權限
|
||||
chown -R accusys:staff /Users/accusys/momentry/var/gitea
|
||||
chown -R accusys:staff /Users/accusys/momentry/etc/gitea
|
||||
chown -R accusys:staff /Users/accusys/momentry/log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 建立設定檔
|
||||
|
||||
建立 `/Users/accusys/momentry/etc/gitea/app.ini`:
|
||||
|
||||
```ini
|
||||
APP_NAME = Gitea: Git with a cup of tea
|
||||
RUN_USER = accusys
|
||||
WORK_PATH = /Users/accusys/momentry/var/gitea
|
||||
RUN_MODE = prod
|
||||
|
||||
[database]
|
||||
DB_TYPE = postgres
|
||||
HOST = 127.0.0.1:5432
|
||||
NAME = gitea
|
||||
USER = gitea
|
||||
PASSWD = gitea_pass
|
||||
SSL_MODE = disable
|
||||
|
||||
[repository]
|
||||
ROOT = /Users/accusys/momentry/var/gitea/data/gitea-repositories
|
||||
|
||||
[server]
|
||||
SSH_DOMAIN = gitea.momentry.ddns.net
|
||||
DOMAIN = gitea.momentry.ddns.net
|
||||
HTTP_PORT = 3000
|
||||
ROOT_URL = http://gitea.momentry.ddns.net:3000/
|
||||
APP_DATA_PATH = /Users/accusys/momentry/var/gitea/data
|
||||
DISABLE_SSH = false
|
||||
SSH_PORT = 2222
|
||||
LFS_START_SERVER = true
|
||||
OFFLINE_MODE = true
|
||||
|
||||
[lfs]
|
||||
PATH = /Users/accusys/momentry/var/gitea/data/lfs
|
||||
|
||||
[log]
|
||||
MODE = console, file
|
||||
ROOT_PATH = /Users/accusys/momentry/log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 使用 plist 開機自動啟動
|
||||
|
||||
```bash
|
||||
# 複製 plist 到 LaunchDaemons 目錄
|
||||
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.gitea.plist /Library/LaunchDaemons/
|
||||
|
||||
# 載入並啟動
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.gitea.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 監控配置
|
||||
|
||||
### 添加到監控配置
|
||||
|
||||
在 `monitor/config/monitor_config.yaml` 中添加:
|
||||
|
||||
```yaml
|
||||
service:
|
||||
services:
|
||||
- name: "gitea"
|
||||
type: "http"
|
||||
port: 3000
|
||||
host: "localhost"
|
||||
check_url: "http://localhost:3000/"
|
||||
timeout: 5
|
||||
enabled: true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 卸載步驟
|
||||
|
||||
### 重要: 路徑說明
|
||||
|
||||
| 路徑 | 類型 | 說明 |
|
||||
|------|------|------|
|
||||
| `/Users/accusys/momentry/var/gitea/` | 數據 | **不要刪除** - Gitea 數據 |
|
||||
| `/Users/accusys/momentry/etc/gitea/` | 配置 | **不要刪除** - Gitea 配置 |
|
||||
| `/Users/accusys/momentry/log/` | 日誌 | **不要刪除** - 日誌目錄 |
|
||||
| `/opt/homebrew/opt/gitea/` | 安裝 | **刪除** - Gitea 安裝目錄 |
|
||||
|
||||
### Step 1: 停止 Gitea
|
||||
|
||||
```bash
|
||||
# 找到 Gitea 進程
|
||||
ps aux | grep gitea | grep -v grep
|
||||
|
||||
# 停止 Gitea
|
||||
pkill gitea
|
||||
|
||||
# 確認停止
|
||||
ps aux | grep gitea | grep -v grep || echo "Gitea 已停止"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 卸載 Gitea
|
||||
|
||||
```bash
|
||||
# 卸載 Gitea
|
||||
brew uninstall gitea
|
||||
|
||||
# 移除 plist
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.gitea.plist
|
||||
sudo rm /Library/LaunchDaemons/com.momentry.gitea.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 刪除專屬檔案
|
||||
|
||||
```bash
|
||||
# 刪除數據目錄 (可選)
|
||||
rm -rf /Users/accusys/momentry/var/gitea
|
||||
|
||||
# 刪除配置目錄 (可選)
|
||||
rm -rf /Users/accusys/momentry/etc/gitea
|
||||
|
||||
# 刪除日誌 (可選)
|
||||
rm -f /Users/accusys/momentry/log/gitea.log
|
||||
rm -f /Users/accusys/momentry/log/gitea.error.log
|
||||
```
|
||||
|
||||
**注意: 不要刪除以下共用目錄**:
|
||||
```bash
|
||||
# 這些是共用的,不要刪除!
|
||||
# /Users/accusys/momentry/var
|
||||
# /Users/accusys/momentry/etc
|
||||
# /Users/accusys/momentry/log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 卸載後檢查清單
|
||||
|
||||
```bash
|
||||
echo "=== Gitea 卸載後檢查 ==="
|
||||
|
||||
# 1. 檢查 Gitea 進程
|
||||
echo "1. Gitea 進程:"
|
||||
ps aux | grep gitea | grep -v grep && echo " ✗ 仍在運行" || echo " ✓ 已停止"
|
||||
|
||||
# 2. Port 3000
|
||||
echo "2. Port 3000:"
|
||||
lsof -i :3000 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
|
||||
|
||||
# 3. gitea 命令
|
||||
echo "3. gitea 命令:"
|
||||
which gitea > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 4. brew 安裝
|
||||
echo "4. brew 安裝:"
|
||||
brew list gitea > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 5. launchctl 服務
|
||||
echo "5. launchctl 服務:"
|
||||
sudo launchctl list | grep gitea > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 6. 數據目錄 (可選刪除)
|
||||
echo "6. 數據目錄:"
|
||||
[ -d "/Users/accusys/momentry/var/gitea" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
|
||||
|
||||
# 7. 配置目錄 (可選刪除)
|
||||
echo "7. 配置目錄:"
|
||||
[ -d "/Users/accusys/momentry/etc/gitea" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
|
||||
```
|
||||
|
||||
**預期結果**:
|
||||
```
|
||||
=== Gitea 卸載後檢查 ===
|
||||
1. Gitea 進程:
|
||||
✓ 已停止
|
||||
2. Port 3000:
|
||||
✓ 已釋放
|
||||
3. gitea 命令:
|
||||
✓ 已移除
|
||||
4. brew 安裝:
|
||||
✓ 已移除
|
||||
5. launchctl 服務:
|
||||
✓ 已移除
|
||||
6. 數據目錄:
|
||||
✓ 保留 (或 ✗ 已刪除)
|
||||
7. 配置目錄:
|
||||
✓ 保留 (或 ✗ 已刪除)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 手動檢查命令
|
||||
|
||||
```bash
|
||||
# 1. 檢查進程
|
||||
ps aux | grep gitea | grep -v grep
|
||||
|
||||
# 2. 檢查 Port
|
||||
lsof -i :3000
|
||||
|
||||
# 3. 測試連線
|
||||
curl http://localhost:3000/
|
||||
|
||||
# 4. 查看版本
|
||||
gitea --version
|
||||
|
||||
# 5. 驗證配置
|
||||
gitea doctor --config /Users/accusys/momentry/etc/gitea/app.ini
|
||||
|
||||
# 6. 查看日誌
|
||||
tail -20 /Users/accusys/momentry/log/gitea.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 連線資訊
|
||||
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| URL | http://localhost:3000 |
|
||||
| Domain | gitea.momentry.ddns.net |
|
||||
| SSH Port | 2222 |
|
||||
| Database | PostgreSQL (gitea) |
|
||||
|
||||
---
|
||||
|
||||
## 環境變數
|
||||
|
||||
在 `.env` 中:
|
||||
|
||||
```env
|
||||
GITEA_URL=http://localhost:3000
|
||||
GITEA_ROOT=/Users/accusys/momentry/var/gitea/data/gitea-repositories
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 故障排除
|
||||
|
||||
### Gitea 無法啟動
|
||||
|
||||
```bash
|
||||
# 檢查日誌
|
||||
tail -f /Users/accusys/momentry/log/gitea.log
|
||||
|
||||
# 檢查配置語法
|
||||
gitea doctor --config /Users/accusys/momentry/etc/gitea/app.ini
|
||||
|
||||
# 檢查目錄權限
|
||||
ls -la /Users/accusys/momentry/var/gitea/
|
||||
|
||||
# 重新設定權限
|
||||
chown -R $(whoami):staff /Users/accusys/momentry/var/gitea
|
||||
```
|
||||
|
||||
### Port 被佔用
|
||||
|
||||
```bash
|
||||
# 檢查哪個程序佔用 port 3000
|
||||
lsof -i :3000
|
||||
|
||||
# 終止佔用程序
|
||||
kill <PID>
|
||||
```
|
||||
|
||||
### 需要重新載入 plist
|
||||
|
||||
```bash
|
||||
# 卸載舊服務 (如果存在)
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.gitea.plist 2>/dev/null
|
||||
|
||||
# 載入新服務
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.gitea.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 檔案位置
|
||||
|
||||
| 類型 | 路徑 | 說明 |
|
||||
|------|------|------|
|
||||
| 安裝 | `/opt/homebrew/opt/gitea/` | Gitea 安裝目錄 |
|
||||
| 執行檔 | `/opt/homebrew/opt/gitea/bin/gitea` | Gitea 執行檔 |
|
||||
| 數據目錄 | `/Users/accusys/momentry/var/gitea/data/` | 數據儲存 |
|
||||
| 配置 | `/Users/accusys/momentry/etc/gitea/app.ini` | 設定檔 |
|
||||
| 日誌 | `/Users/accusys/momentry/log/gitea.log` | 執行日誌 |
|
||||
| 錯誤日誌 | `/Users/accusys/momentry/log/gitea.error.log` | 錯誤日誌 |
|
||||
| plist | `/Library/LaunchDaemons/com.momentry.gitea.plist` | 開機啟動 |
|
||||
| 備份 | `/Users/accusys/momentry/var/gitea_backup/app.ini` | 配置備份 |
|
||||
|
||||
---
|
||||
|
||||
## 資料庫資訊
|
||||
|
||||
Gitea 使用 PostgreSQL 作為資料庫:
|
||||
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| Database | gitea |
|
||||
| User | gitea |
|
||||
| Host | 127.0.0.1:5432 |
|
||||
| Password | gitea_pass |
|
||||
|
||||
---
|
||||
|
||||
## 常用指令
|
||||
|
||||
```bash
|
||||
# 啟動 Gitea
|
||||
gitea web --config /Users/accusys/momentry/etc/gitea/app.ini
|
||||
|
||||
# 驗證配置
|
||||
gitea doctor --config /Users/accusys/momentry/etc/gitea/app.ini
|
||||
|
||||
# 查看版本
|
||||
gitea --version
|
||||
|
||||
# 備份數據
|
||||
gitea dump --config /Users/accusys/momentry/etc/gitea/app.ini --zipfile /Users/accusys/momentry/var/gitea_backup.zip
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 版本資訊
|
||||
|
||||
- 版本: 1.25.3
|
||||
- HTTP Port: 3000
|
||||
- SSH Port: 2222
|
||||
- 數據目錄: /Users/accusys/momentry/var/gitea/
|
||||
- 配置: /Users/accusys/momentry/etc/gitea/app.ini
|
||||
- 日誌目錄: /Users/accusys/momentry/log/
|
||||
@@ -1,393 +0,0 @@
|
||||
# Gitea MCP Server 安裝指南 (本地部署)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-03-24 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-24 | 創建文件 | OpenCode | OpenCode / big-pickle |
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
本文檔說明如何在 macOS 上安裝 Gitea MCP Server,配置為透過 OpenCode MCP 整合存取 Gitea API。
|
||||
|
||||
Gitea MCP Server 是一個 MCP (Model Context Protocol) 伺服器,提供對 Gitea API 的完整存取能力,包括 repos、issues、pull requests、workflows 等資源管理。
|
||||
|
||||
---
|
||||
|
||||
## 當前狀態
|
||||
|
||||
| 項目 | 狀態 |
|
||||
|------|------|
|
||||
| Gitea MCP Server | ✅ 已安裝 |
|
||||
| 安裝方式 | Homebrew (`gitea-mcp-server`) |
|
||||
| Plist | /Library/LaunchDaemons/com.momentry.gitea-mcp-server.plist |
|
||||
| 執行身份 | accusys |
|
||||
| 監聽端口 | 8787 |
|
||||
| Gitea 主機 | http://localhost:3000 |
|
||||
| Launchd 狀態 | ✅ 已註冊 |
|
||||
| RunAtLoad | ✅ 已設定 |
|
||||
| KeepAlive | ✅ 已設定 |
|
||||
|
||||
---
|
||||
|
||||
## 前置條件
|
||||
|
||||
- Gitea 服務已運行(端口 3000)
|
||||
- Homebrew 已安裝
|
||||
- 管理員權限
|
||||
|
||||
---
|
||||
|
||||
## 安裝步驟
|
||||
|
||||
### Step 1: 安裝 Gitea MCP Server
|
||||
|
||||
```bash
|
||||
brew install gitea-mcp-server
|
||||
```
|
||||
|
||||
**驗證**:
|
||||
|
||||
```bash
|
||||
which gitea-mcp-server
|
||||
# /opt/homebrew/bin/gitea-mcp-server
|
||||
|
||||
/opt/homebrew/bin/gitea-mcp-server --help
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 創建日誌目錄
|
||||
|
||||
```bash
|
||||
mkdir -p /Users/accusys/momentry/log
|
||||
|
||||
touch /Users/accusys/momentry/log/gitea-mcp-server.log
|
||||
touch /Users/accusys/momentry/log/gitea-mcp-server.error.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 獲取 Gitea API Token
|
||||
|
||||
#### 步驟 3.1: 登入 Gitea
|
||||
|
||||
1. 開啟瀏覽器,訪問 http://localhost:3000
|
||||
2. 使用管理員帳號登入
|
||||
|
||||
#### 步驟 3.2: 生成 Personal Access Token
|
||||
|
||||
1. 點擊右上角頭像 → **Settings**
|
||||
2. 左側選單選擇 **Applications**
|
||||
3. 在 **Access Tokens** 區塊:
|
||||
- **Token Name**: `opencode-mcp`
|
||||
- **Expiration**: 選擇過期時間(如 365 days)
|
||||
- **Scopes**: 勾選需要的權限
|
||||
- `repo` - 倉庫操作
|
||||
- `read:user` - 讀取用戶資訊
|
||||
- `read:org` - 讀取組織資訊
|
||||
4. 點擊 **Generate Token**
|
||||
5. **立即複製** 生成的 Token
|
||||
|
||||
#### 步驟 3.3: 驗證 Token
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: token <YOUR_TOKEN>" http://localhost:3000/api/v1/user
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 創建 Plist 檔案
|
||||
|
||||
```bash
|
||||
sudo tee /Library/LaunchDaemons/com.momentry.gitea-mcp-server.plist << 'EOF'
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.momentry.gitea-mcp-server</string>
|
||||
<key>UserName</key>
|
||||
<string>accusys</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/opt/homebrew/bin/gitea-mcp-server</string>
|
||||
<string>-transport</string>
|
||||
<string>http</string>
|
||||
<string>-port</string>
|
||||
<string>8787</string>
|
||||
<string>-host</string>
|
||||
<string>http://localhost:3000</string>
|
||||
<string>-token</string>
|
||||
<string><GITEA_TOKEN></string>
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/Users/accusys/momentry/log/gitea-mcp-server.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/Users/accusys/momentry/log/gitea-mcp-server.error.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
```
|
||||
|
||||
**注意**: 將 `<GITEA_TOKEN>` 替換為實際的 Token 值。
|
||||
|
||||
---
|
||||
|
||||
### Step 5: 設定權限
|
||||
|
||||
```bash
|
||||
sudo chown root:wheel /Library/LaunchDaemons/com.momentry.gitea-mcp-server.plist
|
||||
sudo chmod 644 /Library/LaunchDaemons/com.momentry.gitea-mcp-server.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 6: 載入服務
|
||||
|
||||
```bash
|
||||
sudo launchctl bootstrap system /Library/LaunchDaemons/com.momentry.gitea-mcp-server.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 7: 驗證服務
|
||||
|
||||
```bash
|
||||
# 檢查服務狀態
|
||||
sudo launchctl list | grep gitea-mcp-server
|
||||
|
||||
# 檢查進程
|
||||
ps aux | grep gitea-mcp-server | grep -v grep
|
||||
|
||||
# 測試端點
|
||||
curl -s http://localhost:8787/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 8: 整合 OpenCode MCP
|
||||
|
||||
#### 步驟 8.1: 更新 OpenCode 配置
|
||||
|
||||
編輯 `~/.config/opencode/opencode.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"mcp": {
|
||||
"gitea": {
|
||||
"type": "local",
|
||||
"enabled": true,
|
||||
"command": [
|
||||
"/opt/homebrew/bin/gitea-mcp-server",
|
||||
"-token", "<GITEA_TOKEN>",
|
||||
"-host", "http://localhost:3000"
|
||||
]
|
||||
},
|
||||
"n8n": {
|
||||
"type": "local",
|
||||
"enabled": true,
|
||||
"command": ["/opt/homebrew/bin/mcp-n8n"],
|
||||
"environment": {
|
||||
"N8N_BASE_URL": "http://localhost:5678",
|
||||
"N8N_API_KEY": "<N8N_API_KEY>"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 步驟 8.2: 重啟 OpenCode
|
||||
|
||||
```bash
|
||||
# 停止現有 OpenCode
|
||||
pkill -f opencode
|
||||
|
||||
# 重新啟動
|
||||
opencode
|
||||
```
|
||||
|
||||
#### 步驟 8.3: 驗證 MCP 連接
|
||||
|
||||
在 OpenCode 中執行:
|
||||
|
||||
```
|
||||
/mcps
|
||||
```
|
||||
|
||||
確認 gitea 顯示為 `connected`。
|
||||
|
||||
---
|
||||
|
||||
## Plist 參數說明
|
||||
|
||||
| 參數 | 說明 | 值 |
|
||||
|------|------|-----|
|
||||
| `-transport` | 傳輸類型 | `http` |
|
||||
| `-port` | HTTP 監聽端口 | `8787` |
|
||||
| `-host` | Gitea 主機 URL | `http://localhost:3000` |
|
||||
| `-token` | Gitea API Token | 見 Step 3 |
|
||||
|
||||
---
|
||||
|
||||
## 管理指令
|
||||
|
||||
### 啟動服務
|
||||
|
||||
```bash
|
||||
sudo launchctl bootstrap system /Library/LaunchDaemons/com.momentry.gitea-mcp-server.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 停止服務
|
||||
|
||||
```bash
|
||||
sudo launchctl bootout system/com.momentry.gitea-mcp-server
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 重啟服務
|
||||
|
||||
```bash
|
||||
sudo launchctl bootout system/com.momentry.gitea-mcp-server
|
||||
sudo launchctl bootstrap system /Library/LaunchDaemons/com.momentry.gitea-mcp-server.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 查看日誌
|
||||
|
||||
```bash
|
||||
# 即時查看日誌
|
||||
tail -f /Users/accusys/momentry/log/gitea-mcp-server.log
|
||||
|
||||
# 錯誤日誌
|
||||
tail -f /Users/accusys/momentry/log/gitea-mcp-server.error.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 卸載步驟
|
||||
|
||||
### Step 1: 停止服務
|
||||
|
||||
```bash
|
||||
sudo launchctl bootout system/com.momentry.gitea-mcp-server
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 移除 Plist
|
||||
|
||||
```bash
|
||||
sudo rm /Library/LaunchDaemons/com.momentry.gitea-mcp-server.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 從 OpenCode 配置移除
|
||||
|
||||
編輯 `~/.config/opencode/opencode.json`,移除 `mcp.gitea` 區塊。
|
||||
|
||||
---
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 服務無法啟動
|
||||
|
||||
1. 檢查 Token 是否正確
|
||||
2. 檢查 Gitea 是否運行:`curl -s http://localhost:3000/`
|
||||
3. 檢查日誌:`/Users/accusys/momentry/log/gitea-mcp-server.error.log`
|
||||
|
||||
---
|
||||
|
||||
### Token 無效
|
||||
|
||||
1. 確認 Token 未過期
|
||||
2. 確認 Token 有足夠的權限
|
||||
3. 重新生成 Token
|
||||
|
||||
---
|
||||
|
||||
### 端口被佔用
|
||||
|
||||
```bash
|
||||
# 檢查端口占用
|
||||
lsof -i :8787
|
||||
|
||||
# 修改 plist 中的端口後重新載入
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### OpenCode MCP 未顯示
|
||||
|
||||
1. 確認 OpenCode 已重啟
|
||||
2. 檢查 `~/.config/opencode/opencode.json` 格式正確
|
||||
3. 確認 gitea-mcp-server 程序正在運行
|
||||
|
||||
---
|
||||
|
||||
## 檔案位置
|
||||
|
||||
| 類型 | 路徑 | 說明 |
|
||||
|------|------|------|
|
||||
| Plist | /Library/LaunchDaemons/com.momentry.gitea-mcp-server.plist | Launchd 服務配置 |
|
||||
| 日誌 | /Users/accusys/momentry/log/gitea-mcp-server.log | 標準輸出日誌 |
|
||||
| 錯誤日誌 | /Users/accusys/momentry/log/gitea-mcp-server.error.log | 錯誤日誌 |
|
||||
| OpenCode 配置 | ~/.config/opencode/opencode.json | MCP 設定檔 |
|
||||
| Gitea | http://localhost:3000 | Gitea Web UI |
|
||||
|
||||
---
|
||||
|
||||
## 常用指令
|
||||
|
||||
```bash
|
||||
# 驗證服務狀態
|
||||
sudo launchctl list | grep gitea-mcp-server
|
||||
|
||||
# 查看服務 PID
|
||||
ps aux | grep gitea-mcp-server | grep -v grep
|
||||
|
||||
# 測試端點
|
||||
curl -s http://localhost:8787/
|
||||
|
||||
# 驗證 Gitea 連接
|
||||
curl -H "Authorization: token <TOKEN>" http://localhost:3000/api/v1/user
|
||||
|
||||
# 查看即時日誌
|
||||
tail -f /Users/accusys/momentry/log/gitea-mcp-server.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 版本資訊
|
||||
|
||||
- 版本: 1.0
|
||||
- 安裝日期: 2026-03-24
|
||||
- 文件更新: 2026-03-24
|
||||
|
||||
---
|
||||
|
||||
## 相關文件
|
||||
|
||||
- [OpenCode MCP 整合](./N8N_MCP_SETUP.md) - n8n MCP 設定說明
|
||||
- [服務總覽](./SERVICES.md) - 所有服務狀態總覽
|
||||
- [待解決問題](./PENDING_ISSUES.md) - MCP 安裝狀態追蹤
|
||||
@@ -1,396 +0,0 @@
|
||||
# MariaDB 安裝指南 (本地部署)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-16 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
本文檔說明如何在 macOS 上安裝 MariaDB,配置為本地部署,支援遠端訪問。
|
||||
|
||||
---
|
||||
|
||||
## 當前狀態
|
||||
|
||||
| 項目 | 狀態 |
|
||||
|------|------|
|
||||
| MariaDB | ✅ 已安裝 v12.1.2 |
|
||||
| 數據目錄 | /Users/accusys/momentry/var/mariadb/ |
|
||||
| 日誌目錄 | /Users/accusys/momentry/log/ |
|
||||
| Plist | /Library/LaunchDaemons/com.momentry.mariadb.plist |
|
||||
|
||||
---
|
||||
|
||||
## 安裝步驟
|
||||
|
||||
### Step 1: 安裝 MariaDB (使用 brew)
|
||||
|
||||
```bash
|
||||
# 安裝 MariaDB
|
||||
brew install mariadb
|
||||
```
|
||||
|
||||
**驗證**:
|
||||
```bash
|
||||
mariadb --version
|
||||
# mariadb from 12.1.2-MariaDB
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 建立目錄結構
|
||||
|
||||
```bash
|
||||
# 建立數據目錄
|
||||
mkdir -p /Users/accusys/momentry/var/mariadb
|
||||
|
||||
# 建立配置目錄
|
||||
mkdir -p /Users/accusys/momentry/etc/mariadb
|
||||
|
||||
# 建立日誌目錄
|
||||
mkdir -p /Users/accusys/momentry/log
|
||||
|
||||
# 建立日誌文件
|
||||
touch /Users/accusys/momentry/log/mariadb.log
|
||||
touch /Users/accusys/momentry/log/mariadb.error.log
|
||||
|
||||
# 設定權限
|
||||
chown -R accusys:staff /Users/accusys/momentry/var/mariadb
|
||||
chown -R accusys:staff /Users/accusys/momentry/etc/mariadb
|
||||
chown -R accusys:staff /Users/accusys/momentry/log
|
||||
```
|
||||
|
||||
**注意**: 如果需要從舊數據遷移,需要先初始化新目錄:
|
||||
```bash
|
||||
# 初始化新數據目錄
|
||||
mysql_install_db --datadir=/Users/accusys/momentry/var/mariadb
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 使用 plist 開機自動啟動
|
||||
|
||||
```bash
|
||||
# 複製 plist 到 LaunchDaemons 目錄
|
||||
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.mariadb.plist /Library/LaunchDaemons/
|
||||
|
||||
# 載入並啟動
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.mariadb.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 監控配置
|
||||
|
||||
### 添加到監控配置
|
||||
|
||||
在 `monitor/config/monitor_config.yaml` 中添加:
|
||||
|
||||
```yaml
|
||||
database:
|
||||
mariadb:
|
||||
enabled: true
|
||||
host: "localhost"
|
||||
port: 3306
|
||||
user: "root"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 卸載步驟
|
||||
|
||||
### 重要: 路徑說明
|
||||
|
||||
| 路徑 | 類型 | 說明 |
|
||||
|------|------|------|
|
||||
| `/Users/accusys/momentry/var/mariadb/` | 數據 | **不要刪除** - 數據目錄 |
|
||||
| `/Users/accusys/momentry/etc/mariadb/` | 配置 | **不要刪除** - 配置目錄 |
|
||||
| `/Users/accusys/momentry/log/` | 日誌 | **不要刪除** - 日誌目錄 |
|
||||
| `/opt/homebrew/opt/mariadb/` | 安裝 | **刪除** - MariaDB 安裝目錄 |
|
||||
|
||||
### Step 1: 停止 MariaDB
|
||||
|
||||
```bash
|
||||
# 找到 MariaDB 進程
|
||||
ps aux | grep mariadb | grep -v grep
|
||||
|
||||
# 停止 MariaDB
|
||||
mysqladmin -u root -p shutdown
|
||||
# 或
|
||||
pkill mariadbd
|
||||
|
||||
# 確認停止
|
||||
ps aux | grep mariadb | grep -v grep || echo "MariaDB 已停止"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 卸載 MariaDB
|
||||
|
||||
```bash
|
||||
# 卸載 MariaDB
|
||||
brew uninstall mariadb
|
||||
|
||||
# 移除 plist
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.mariadb.plist
|
||||
sudo rm /Library/LaunchDaemons/com.momentry.mariadb.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 刪除專屬檔案
|
||||
|
||||
```bash
|
||||
# 刪除數據目錄 (可選)
|
||||
rm -rf /Users/accusys/momentry/var/mariadb
|
||||
|
||||
# 刪除日誌 (可選)
|
||||
rm -f /Users/accusys/momentry/log/mariadb.log
|
||||
rm -f /Users/accusys/momentry/log/mariadb.error.log
|
||||
```
|
||||
|
||||
**注意: 不要刪除以下共用目錄**:
|
||||
```bash
|
||||
# 這些是共用的,不要刪除!
|
||||
# /Users/accusys/momentry/var
|
||||
# /Users/accusys/momentry/log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 卸載後檢查清單
|
||||
|
||||
```bash
|
||||
echo "=== MariaDB 卸載後檢查 ==="
|
||||
|
||||
# 1. 檢查 MariaDB 進程
|
||||
echo "1. MariaDB 進程:"
|
||||
ps aux | grep mariadb | grep -v grep && echo " ✗ 仍在運行" || echo " ✓ 已停止"
|
||||
|
||||
# 2. Port 3306
|
||||
echo "2. Port 3306:"
|
||||
lsof -i :3306 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
|
||||
|
||||
# 3. mariadb 命令
|
||||
echo "3. mariadb 命令:"
|
||||
which mariadb > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 4. brew 安裝
|
||||
echo "4. brew 安裝:"
|
||||
brew list mariadb > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 5. launchctl 服務
|
||||
echo "5. launchctl 服務:"
|
||||
sudo launchctl list | grep mariadb > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 6. 數據目錄 (可選刪除)
|
||||
echo "6. 數據目錄:"
|
||||
[ -d "/Users/accusys/momentry/var/mariadb" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
|
||||
|
||||
# 7. 日誌目錄 (可選刪除)
|
||||
echo "7. 日誌目錄:"
|
||||
[ -d "/Users/accusys/momentry/log" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
|
||||
```
|
||||
|
||||
**預期結果**:
|
||||
```
|
||||
=== MariaDB 卸載後檢查 ===
|
||||
1. MariaDB 進程:
|
||||
✓ 已停止
|
||||
2. Port 3306:
|
||||
✓ 已釋放
|
||||
3. mariadb 命令:
|
||||
✓ 已移除
|
||||
4. brew 安裝:
|
||||
✓ 已移除
|
||||
5. launchctl 服務:
|
||||
✓ 已移除
|
||||
6. 數據目錄:
|
||||
✓ 保留 (或 ✗ 已刪除)
|
||||
7. 日誌目錄:
|
||||
✓ 保留 (或 ✗ 已刪除)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 手動檢查命令
|
||||
|
||||
```bash
|
||||
# 1. 檢查進程
|
||||
ps aux | grep mariadb | grep -v grep
|
||||
|
||||
# 2. 檢查 Port
|
||||
lsof -i :3306
|
||||
|
||||
# 3. 測試連線
|
||||
mariadb -u root -e "SELECT 1;"
|
||||
|
||||
# 4. 查看所有數據庫
|
||||
mariadb -u root -e "SHOW DATABASES;"
|
||||
|
||||
# 5. 查看用戶
|
||||
mariadb -u root -e "SELECT User, Host FROM mysql.user;"
|
||||
|
||||
# 6. 查看表
|
||||
mariadb -u root -e "USE mysql; SHOW TABLES;"
|
||||
|
||||
# 7. 查看日誌
|
||||
tail -20 /Users/accusys/momentry/log/mariadb.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 連線資訊
|
||||
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| Host | localhost |
|
||||
| Port | 3306 |
|
||||
| User | root |
|
||||
|
||||
---
|
||||
|
||||
## 環境變數
|
||||
|
||||
在 `.env` 中:
|
||||
|
||||
```env
|
||||
MARIADB_URL=mariadb://root@localhost:3306
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 遠端訪問
|
||||
|
||||
- MariaDB 綁定到所有網路介面 (0.0.0.0)
|
||||
- 本地網路其他機器可透過 IP 訪問
|
||||
- 請設定用戶權限限制訪問
|
||||
|
||||
---
|
||||
|
||||
## 故障排除
|
||||
|
||||
### MariaDB 無法啟動
|
||||
|
||||
```bash
|
||||
# 檢查日誌
|
||||
tail -f /Users/accusys/momentry/log/mariadb.log
|
||||
|
||||
# 檢查目錄權限
|
||||
ls -la /Users/accusys/momentry/var/mariadb/
|
||||
|
||||
# 重新設定權限
|
||||
chown -R $(whoami):staff /Users/accusys/momentry/var/mariadb
|
||||
```
|
||||
|
||||
### Port 被佔用
|
||||
|
||||
```bash
|
||||
# 檢查哪個程序佔用 port 3306
|
||||
lsof -i :3306
|
||||
|
||||
# 終止佔用程序
|
||||
kill <PID>
|
||||
```
|
||||
|
||||
### 需要重新載入 plist
|
||||
|
||||
```bash
|
||||
# 卸載舊服務 (如果存在)
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.mariadb.plist 2>/dev/null
|
||||
|
||||
# 載入新服務
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.mariadb.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 檔案位置
|
||||
|
||||
| 類型 | 路徑 | 說明 |
|
||||
|------|------|------|
|
||||
| 安裝 | `/opt/homebrew/opt/mariadb/` | MariaDB 安裝目錄 |
|
||||
| 執行檔 | `/opt/homebrew/opt/mariadb/bin/mariadbd` | MariaDB 執行檔 |
|
||||
| 數據目錄 | `/Users/accusys/momentry/var/mariadb/` | 數據儲存 |
|
||||
| 日誌 | `/Users/accusys/momentry/log/mariadb.log` | 執行日誌 |
|
||||
| 錯誤日誌 | `/Users/accusys/momentry/log/mariadb.error.log` | 錯誤日誌 |
|
||||
| plist | `/Library/LaunchDaemons/com.momentry.mariadb.plist` | 開機啟動 |
|
||||
| 備份 | `/Users/accusys/momentry/var/mariadb_backup/` | 數據備份 |
|
||||
|
||||
---
|
||||
|
||||
## 備份與恢復
|
||||
|
||||
### 備份用戶配置
|
||||
|
||||
已創建專用備份用戶:
|
||||
- 用戶名:`momentry_backup`
|
||||
- 密碼:`momentry_backup_pwd_2026`
|
||||
- 權限:SELECT, LOCK TABLES, SHOW VIEW, EVENT, TRIGGER
|
||||
|
||||
### 備份 (mysqldump)
|
||||
|
||||
```bash
|
||||
# 備份所有數據庫
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
mysqldump -u momentry_backup -pmomentry_backup_pwd_2026 --all-databases | gzip > \
|
||||
/Users/accusys/momentry/backup/daily/mariadb/mariadb_db_all_${TIMESTAMP}.sql.gz
|
||||
|
||||
# 備份指定數據庫 (WordPress)
|
||||
mysqldump -u momentry_backup -pmomentry_backup_pwd_2026 wordpress | gzip > \
|
||||
/Users/accusys/momentry/backup/daily/mariadb/mariadb_db_wordpress_${TIMESTAMP}.sql.gz
|
||||
|
||||
# 驗證
|
||||
sha256sum /Users/accusys/momentry/backup/daily/mariadb/mariadb_db_*.sql.gz > \
|
||||
/Users/accusys/momentry/backup/daily/mariadb/mariadb_db_${TIMESTAMP}.sha256
|
||||
```
|
||||
|
||||
### 恢復 (mysql)
|
||||
|
||||
```bash
|
||||
# 恢復所有數據庫
|
||||
gunzip < /Users/accusys/momentry/backup/daily/mariadb/mariadb_db_all_20260316_101802.sql.gz | \
|
||||
mysql -u momentry_backup -pmomentry_backup_pwd_2026
|
||||
|
||||
# 恢復指定數據庫
|
||||
gunzip < /Users/accusys/momentry/backup/daily/mariadb/mariadb_db_wordpress_20260316_101802.sql.gz | \
|
||||
mysql -u momentry_backup -pmomentry_backup_pwd_2026 wordpress
|
||||
```
|
||||
|
||||
### 數據目錄複製 (完整遷移) - 離線
|
||||
|
||||
```bash
|
||||
# 1. 停止 MariaDB
|
||||
mysqladmin -u momentry_backup -pmomentry_backup_pwd_2026 shutdown
|
||||
|
||||
# 2. 複製數據目錄
|
||||
cp -r /opt/homebrew/var/mysql/* /Users/accusys/momentry/var/mariadb/
|
||||
|
||||
# 3. 設定權限
|
||||
chown -R $(whoami):staff /Users/accusys/momentry/var/mariadb
|
||||
|
||||
# 4. 啟動 MariaDB
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.mariadb.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 版本資訊
|
||||
|
||||
- 版本: 12.1.2
|
||||
- Port: 3306
|
||||
- User: root
|
||||
- 數據目錄: /Users/accusys/momentry/var/mariadb/
|
||||
- 日誌目錄: /Users/accusys/momentry/log/
|
||||
@@ -1,464 +0,0 @@
|
||||
# Momentry Core API 安裝指南 (本地部署)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-03-23 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-23 | 創建文件 | OpenCode | - |
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
本文檔說明如何在 macOS 上安裝 Momentry Core API 服務,配置為本地部署,並設定開機自動啟動。
|
||||
|
||||
Momentry Core API 是一個 Rust 編寫的數位資產管理 API 服務,提供:
|
||||
- 影片搜尋 API (`/api/v1/search`)
|
||||
- n8n 整合 API (`/api/v1/n8n/search`)
|
||||
- 健康檢查端點 (`/health`)
|
||||
- 影片註冊與處理功能
|
||||
|
||||
---
|
||||
|
||||
## 當前狀態
|
||||
|
||||
| 項目 | 狀態 |
|
||||
|------|------|
|
||||
| Momentry Core API | ✅ 已安裝 v0.1.0 |
|
||||
| Binary | `/Users/accusys/momentry_core_0.1/target/release/momentry` |
|
||||
| Port | 3002 |
|
||||
| 反向代理 | Caddy (`api.momentry.ddns.net`) |
|
||||
| 數據庫 | PostgreSQL (momentry) |
|
||||
| 向量庫 | Qdrant |
|
||||
| Cache | Redis |
|
||||
| launchd plist | ✅ 已建立 (/Library/LaunchDaemons/com.momentry.api.plist) |
|
||||
|
||||
---
|
||||
|
||||
## 系統需求
|
||||
|
||||
### 必要服務
|
||||
|
||||
| 服務 | 版本 | 用途 |
|
||||
|------|------|------|
|
||||
| PostgreSQL | 16+ | 主數據庫 |
|
||||
| Redis | 1.0+ | 快取與佇列 |
|
||||
| Qdrant | 1.7+ | 向量搜尋 |
|
||||
| Ollama | 最新 | LLM 與 Embedding |
|
||||
|
||||
### Rust 環境
|
||||
|
||||
```bash
|
||||
# 檢查 Rust 版本
|
||||
rustc --version
|
||||
cargo --version
|
||||
|
||||
# 如需安裝 Rust
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 安裝步驟
|
||||
|
||||
### Step 1: 編譯 Momentry Core
|
||||
|
||||
```bash
|
||||
# 進入專案目錄
|
||||
cd /Users/accusys/momentry_core_0.1
|
||||
|
||||
# 編譯 release 版本
|
||||
cargo build --release
|
||||
|
||||
# 驗證編譯結果
|
||||
ls -la target/release/momentry
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 設定環境變數
|
||||
|
||||
建立環境變數檔案:
|
||||
|
||||
```bash
|
||||
# 建立執行目錄
|
||||
mkdir -p /Users/accusys/momentry_core_0.1/momentry_runtime/env
|
||||
|
||||
# 建立環境變數檔案
|
||||
cat > /Users/accusys/momentry_core_0.1/momentry_runtime/env/momentry.env << 'EOF'
|
||||
# Database Configuration
|
||||
DATABASE_URL=postgres://accusys@localhost:5432/momentry
|
||||
|
||||
# Redis Configuration
|
||||
REDIS_URL=redis://:accusys@localhost:6379
|
||||
REDIS_PASSWORD=accusys
|
||||
|
||||
# API Server
|
||||
API_HOST=127.0.0.1
|
||||
API_PORT=3002
|
||||
|
||||
# Ollama (LLM)
|
||||
OLLAMA_HOST=http://localhost:11434
|
||||
|
||||
# Qdrant (Vector Database)
|
||||
QDRANT_URL=http://localhost:6333
|
||||
QDRANT_COLLECTION=momentry_chunks
|
||||
EOF
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 手動啟動服務
|
||||
|
||||
```bash
|
||||
# 啟動 API 服務
|
||||
cd /Users/accusys/momentry_core_0.1
|
||||
./target/release/momentry server --port 3002
|
||||
|
||||
# 驗證服務
|
||||
curl http://localhost:3002/health
|
||||
# {"status":"ok","version":"0.1.0","uptime_ms":1234}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 設定 Caddy 反向代理
|
||||
|
||||
在 `/Users/accusys/momentry/etc/Caddyfile` 中新增:
|
||||
|
||||
```caddy
|
||||
# Momentry Core API
|
||||
api.momentry.ddns.net {
|
||||
reverse_proxy localhost:3002
|
||||
import common_log momentry_api_access
|
||||
}
|
||||
```
|
||||
|
||||
重新載入 Caddy:
|
||||
|
||||
```bash
|
||||
# 重新載入配置
|
||||
caddy reload --config /Users/accusys/momentry/etc/Caddyfile
|
||||
|
||||
# 驗證
|
||||
curl -sk https://api.momentry.ddns.net/health
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 5: 建立 launchd plist (開機自動啟動)
|
||||
|
||||
建立 plist 檔案:
|
||||
|
||||
```bash
|
||||
sudo tee /Library/LaunchDaemons/com.momentry.api.plist << 'EOF'
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.momentry.api</string>
|
||||
|
||||
<key>UserName</key>
|
||||
<string>accusys</string>
|
||||
|
||||
<key>GroupName</key>
|
||||
<string>staff</string>
|
||||
|
||||
<key>WorkingDirectory</key>
|
||||
<string>/Users/accusys/momentry_core_0.1</string>
|
||||
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/Users/accusys/momentry_core_0.1/target/release/momentry</string>
|
||||
<string>server</string>
|
||||
<string>--port</string>
|
||||
<string>3002</string>
|
||||
</array>
|
||||
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>PATH</key>
|
||||
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
||||
|
||||
<key>DATABASE_URL</key>
|
||||
<string>postgres://accusys@localhost:5432/momentry</string>
|
||||
|
||||
<key>REDIS_URL</key>
|
||||
<string>redis://:accusys@localhost:6379</string>
|
||||
|
||||
<key>REDIS_PASSWORD</key>
|
||||
<string>accusys</string>
|
||||
|
||||
<key>OLLAMA_HOST</key>
|
||||
<string>http://localhost:11434</string>
|
||||
|
||||
<key>QDRANT_URL</key>
|
||||
<string>http://localhost:6333</string>
|
||||
</dict>
|
||||
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
|
||||
<key>StandardOutPath</key>
|
||||
<string>/Users/accusys/momentry/log/momentry_api.log</string>
|
||||
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/Users/accusys/momentry/log/momentry_api.error.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
```
|
||||
|
||||
建立日誌檔案:
|
||||
|
||||
```bash
|
||||
# 建立日誌目錄(如不存在)
|
||||
mkdir -p /Users/accusys/momentry/log
|
||||
|
||||
# 建立日誌檔案
|
||||
touch /Users/accusys/momentry/log/momentry_api.log
|
||||
touch /Users/accusys/momentry/log/momentry_api.error.log
|
||||
|
||||
# 設定權限
|
||||
chown accusys:staff /Users/accusys/momentry/log/momentry_api.log
|
||||
chown accusys:staff /Users/accusys/momentry/log/momentry_api.error.log
|
||||
```
|
||||
|
||||
載入服務:
|
||||
|
||||
```bash
|
||||
# 載入服務
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
|
||||
|
||||
# 驗證服務
|
||||
launchctl list | grep momentry.api
|
||||
|
||||
# 檢查服務狀態
|
||||
curl http://localhost:3002/health
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 卸載步驟
|
||||
|
||||
### Step 1: 停止並移除服務
|
||||
|
||||
```bash
|
||||
# 停止服務
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.api.plist
|
||||
|
||||
# 移除 plist
|
||||
sudo rm /Library/LaunchDaemons/com.momentry.api.plist
|
||||
```
|
||||
|
||||
### Step 2: 移除 Caddy 配置
|
||||
|
||||
從 `/Users/accusys/momentry/etc/Caddyfile` 中移除 `api.momentry.ddns.net` 區塊。
|
||||
|
||||
```bash
|
||||
# 重新載入 Caddy
|
||||
caddy reload --config /Users/accusys/momentry/etc/Caddyfile
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 故障排除
|
||||
|
||||
### API 返回 502 Bad Gateway
|
||||
|
||||
**問題**: `api.momentry.ddns.net` 返回 502 錯誤
|
||||
|
||||
**原因**: Momentry Core API 服務未啟動
|
||||
|
||||
**解決方案**:
|
||||
|
||||
```bash
|
||||
# 檢查服務狀態
|
||||
launchctl list | grep momentry.api
|
||||
|
||||
# 如服務未啟動,手動啟動
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
|
||||
|
||||
# 或手動啟動測試
|
||||
cd /Users/accusys/momentry_core_0.1
|
||||
./target/release/momentry server --port 3002
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### API 返回 404 Not Found
|
||||
|
||||
**問題**: 端點返回 404
|
||||
|
||||
**原因**: Binary 過舊,缺少該端點
|
||||
|
||||
**解決方案**:
|
||||
|
||||
```bash
|
||||
# 重新編譯
|
||||
cd /Users/accusys/momentry_core_0.1
|
||||
cargo build --release
|
||||
|
||||
# 重啟服務
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.api.plist
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 服務無法啟動
|
||||
|
||||
**問題**: launchd 無法啟動服務
|
||||
|
||||
**檢查步驟**:
|
||||
|
||||
```bash
|
||||
# 檢查日誌
|
||||
tail -50 /Users/accusys/momentry/log/momentry_api.error.log
|
||||
|
||||
# 檢查 plist 語法
|
||||
plutil -lint /Library/LaunchDaemons/com.momentry.api.plist
|
||||
|
||||
# 檢查權限
|
||||
ls -la /Users/accusys/momentry_core_0.1/target/release/momentry
|
||||
|
||||
# 手動測試
|
||||
/Users/accusys/momentry_core_0.1/target/release/momentry server --port 3002
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 檔案位置
|
||||
|
||||
| 類型 | 路徑 | 說明 |
|
||||
|------|------|------|
|
||||
| Binary | `/Users/accusys/momentry_core_0.1/target/release/momentry` | 執行檔 |
|
||||
| 環境變數 | `/Users/accusys/momentry_core_0.1/momentry_runtime/env/momentry.env` | 環境設定 |
|
||||
| launchd plist | `/Library/LaunchDaemons/com.momentry.api.plist` | 開機啟動配置 |
|
||||
| 日誌 | `/Users/accusys/momentry/log/momentry_api.log` | 標準輸出 |
|
||||
| 錯誤日誌 | `/Users/accusys/momentry/log/momentry_api.error.log` | 錯誤輸出 |
|
||||
| Caddy 配置 | `/Users/accusys/momentry/etc/Caddyfile` | 反向代理配置 |
|
||||
|
||||
---
|
||||
|
||||
## 常用指令
|
||||
|
||||
### 服務管理
|
||||
|
||||
```bash
|
||||
# 啟動服務
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
|
||||
|
||||
# 停止服務
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.api.plist
|
||||
|
||||
# 重啟服務
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.api.plist
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
|
||||
|
||||
# 檢查服務狀態
|
||||
launchctl list | grep momentry.api
|
||||
```
|
||||
|
||||
### 健康檢查
|
||||
|
||||
```bash
|
||||
# 本地健康檢查
|
||||
curl http://localhost:3002/health
|
||||
|
||||
# 詳細健康檢查
|
||||
curl http://localhost:3002/health/detailed
|
||||
|
||||
# 外部健康檢查
|
||||
curl -sk https://api.momentry.ddns.net/health
|
||||
```
|
||||
|
||||
### API 測試
|
||||
|
||||
```bash
|
||||
# 搜尋 API
|
||||
curl -X POST http://localhost:3002/api/v1/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query":"test"}'
|
||||
|
||||
# n8n 搜尋 API
|
||||
curl -X POST http://localhost:3002/api/v1/n8n/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query":"test"}'
|
||||
|
||||
# 列出影片
|
||||
curl http://localhost:3002/api/v1/videos
|
||||
```
|
||||
|
||||
### 日誌查看
|
||||
|
||||
```bash
|
||||
# 查看最近的日誌
|
||||
tail -50 /Users/accusys/momentry/log/momentry_api.log
|
||||
|
||||
# 即時監控日誌
|
||||
tail -f /Users/accusys/momentry/log/momentry_api.log
|
||||
|
||||
# 查看錯誤日誌
|
||||
tail -50 /Users/accusys/momentry/log/momentry_api.error.log
|
||||
```
|
||||
|
||||
### 重新編譯
|
||||
|
||||
```bash
|
||||
# 編譯 release 版本
|
||||
cd /Users/accusys/momentry_core_0.1
|
||||
cargo build --release
|
||||
|
||||
# 編譯後重啟服務
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.api.plist
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API 端點
|
||||
|
||||
| 端點 | 方法 | 說明 |
|
||||
|------|------|------|
|
||||
| `/health` | GET | 健康檢查 |
|
||||
| `/health/detailed` | GET | 詳細健康檢查 |
|
||||
| `/api/v1/register` | POST | 註冊影片 |
|
||||
| `/api/v1/search` | POST | 搜尋影片 |
|
||||
| `/api/v1/n8n/search` | POST | n8n 格式搜尋 |
|
||||
| `/api/v1/search/hybrid` | POST | 混合搜尋 |
|
||||
| `/api/v1/lookup` | GET | 查詢 UUID |
|
||||
| `/api/v1/videos` | GET | 列出所有影片 |
|
||||
| `/api/v1/progress/:uuid` | GET | 查詢處理進度 |
|
||||
|
||||
---
|
||||
|
||||
## 版本資訊
|
||||
|
||||
- 版本: 0.1.0
|
||||
- 安裝日期: 2026-03-23
|
||||
- Rust 版本: 1.xx
|
||||
- 文件更新: 2026-03-23
|
||||
|
||||
---
|
||||
|
||||
## 相關文件
|
||||
|
||||
- `docs/SERVICES.md` - 服務總覽
|
||||
- `docs/API_REFERENCE.md` - API 參考
|
||||
- `docs/INSTALL_POSTGRESQL.md` - PostgreSQL 安裝
|
||||
- `docs/INSTALL_REDIS.md` - Redis 安裝
|
||||
- `docs/INSTALL_QDRANT.md` - Qdrant 安裝
|
||||
- `docs/PENDING_ISSUES.md` - 待解決問題
|
||||
@@ -1,392 +0,0 @@
|
||||
# MongoDB 安裝指南 (本地部署)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-15 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-15 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
本文檔說明如何在 macOS 上安裝 MongoDB Community Edition,配置為本地部署,支援遠端訪問。
|
||||
|
||||
---
|
||||
|
||||
## 當前狀態
|
||||
|
||||
| 項目 | 狀態 |
|
||||
|------|------|
|
||||
| MongoDB (mongodb-community) | ✅ 已安裝 v8.2.6 |
|
||||
| 數據目錄 | /opt/homebrew/var/mongodb |
|
||||
| 日誌目錄 | /Users/accusys/momentry/log |
|
||||
|
||||
---
|
||||
|
||||
## 安裝步驟
|
||||
|
||||
### Step 1: 安裝 MongoDB Community
|
||||
|
||||
```bash
|
||||
# 安裝 MongoDB Community
|
||||
brew tap mongodb/brew
|
||||
brew install mongodb-community
|
||||
```
|
||||
|
||||
**驗證**:
|
||||
```bash
|
||||
mongod --version
|
||||
# db version v8.x.x
|
||||
mongosh --version
|
||||
# 2.7.x
|
||||
sudo launchctl list | grep mongo
|
||||
# 確認 MongoDB 服務已載入
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 數據目錄 (已存在 - 共用)
|
||||
|
||||
數據目錄使用 homebrew 預設位置:
|
||||
- 數據目錄: `/opt/homebrew/var/mongodb`
|
||||
- 配置目錄: `/opt/homebrew/etc/mongod.conf`
|
||||
- 日誌目錄: `/Users/accusys/momentry/log`
|
||||
|
||||
**建立配置目錄和日誌文件**:
|
||||
```bash
|
||||
# 建立配置目錄
|
||||
mkdir -p /Users/accusys/momentry/etc/mongodb
|
||||
|
||||
# 建立日誌文件
|
||||
touch /Users/accusys/momentry/log/mongodb.log
|
||||
touch /Users/accusys/momentry/log/mongodb.error.log
|
||||
|
||||
# 確認權限:
|
||||
ls -la /Users/accusys/momentry/
|
||||
chown -R accusys:staff /Users/accusys/momentry
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 使用 LaunchAgent 啟動 (開機自動)
|
||||
|
||||
```bash
|
||||
# 複製 plist 到 LaunchDaemons 目錄 (開機自動需要 root 權限)
|
||||
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.mongodb.plist \
|
||||
/Library/LaunchDaemons/
|
||||
|
||||
# 載入並啟動
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.mongodb.plist
|
||||
|
||||
# 驗證
|
||||
launchctl list | grep mongodb
|
||||
pgrep -a mongod
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 建立資料庫用戶
|
||||
|
||||
```bash
|
||||
mongosh --eval '
|
||||
use admin
|
||||
db.createUser({
|
||||
user: "accusys",
|
||||
pwd: "Test3200Test3200",
|
||||
roles: [
|
||||
{ role: "readWrite", db: "momentry" },
|
||||
{ role: "dbAdmin", db: "momentry" }
|
||||
]
|
||||
})
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 驗證安裝
|
||||
|
||||
```bash
|
||||
# 檢查進程
|
||||
pgrep -a mongod
|
||||
|
||||
# 檢查端口
|
||||
lsof -i :27017
|
||||
|
||||
# 測試連線
|
||||
mongosh --eval "db.adminCommand('ping')"
|
||||
|
||||
# 檢查 LaunchAgent
|
||||
launchctl list | grep mongodb
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 監控配置
|
||||
|
||||
### 添加到監控配置
|
||||
|
||||
在 `monitor/config/monitor_config.yaml` 中添加:
|
||||
|
||||
```yaml
|
||||
database:
|
||||
mongodb:
|
||||
enabled: true
|
||||
host: "localhost"
|
||||
port: 27017
|
||||
user: "accusys"
|
||||
database: "momentry"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 卸載步驟
|
||||
|
||||
### 重要: 路徑說明
|
||||
|
||||
| 路徑 | 類型 | 說明 |
|
||||
|------|------|------|
|
||||
| `/Users/accusys/momentry/` | 共用 | **不要刪除** - 多個系統共用 |
|
||||
| `/Users/accusys/momentry/var` | 共用 | **不要刪除** - 數據目錄 |
|
||||
| `/Users/accusys/momentry/etc/mongodb/` | 配置 | **不要刪除** - MongoDB 配置 |
|
||||
| `/Users/accusys/momentry/log` | 共用 | **不要刪除** - 日誌目錄 |
|
||||
| `~/.mongosh_history` | 專屬 | 可選刪除 - Mongo Shell 歷史 |
|
||||
|
||||
### Step 1: 停止 MongoDB
|
||||
|
||||
```bash
|
||||
# 找到 MongoDB 進程
|
||||
ps aux | grep mongod | grep -v grep
|
||||
|
||||
# 停止 MongoDB
|
||||
pkill mongod
|
||||
# 或
|
||||
kill <PID>
|
||||
|
||||
# 確認停止
|
||||
ps aux | grep mongod | grep -v grep || echo "MongoDB 已停止"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 卸載 MongoDB
|
||||
|
||||
```bash
|
||||
# 完全卸載 (保留數據)
|
||||
brew uninstall mongodb-community
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 刪除專屬檔案
|
||||
|
||||
```bash
|
||||
# 刪除 MongoDB 專屬配置 (如果有)
|
||||
rm -f ~/.mongosh_history
|
||||
|
||||
# 刪除臨時文件 (可選)
|
||||
rm -rf /tmp/mongodb-*
|
||||
```
|
||||
|
||||
**注意: 不要刪除以下共用目錄**:
|
||||
```bash
|
||||
# 這些是共用的,不要刪除!
|
||||
# /Users/accusys/momentry/var
|
||||
# /Users/accusys/momentry/log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 卸載後檢查清單
|
||||
|
||||
```bash
|
||||
echo "=== MongoDB 卸載後檢查 ==="
|
||||
|
||||
# 1. 檢查 MongoDB 進程
|
||||
echo "1. MongoDB 進程:"
|
||||
ps aux | grep mongod | grep -v grep && echo " ✗ 仍在運行" || echo " ✓ 已停止"
|
||||
|
||||
# 2. 檢查 Port 27017
|
||||
echo "2. Port 27017:"
|
||||
lsof -i :27017 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
|
||||
|
||||
# 3. 檢查 mongod 命令
|
||||
echo "3. mongod 命令:"
|
||||
which mongod > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 4. 檢查 launchctl
|
||||
echo "4. launchctl 服務:"
|
||||
sudo launchctl list | grep mongo > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 5. 檢查 Homebrew
|
||||
echo "5. Homebrew 移除:"
|
||||
brew list mongo > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 6. 檢查數據目錄 (應該存在)
|
||||
echo "6. 數據目錄:"
|
||||
[ -d "/Users/accusys/momentry/var" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
|
||||
|
||||
# 7. 檢查日誌目錄 (應該存在)
|
||||
echo "7. 日誌目錄:"
|
||||
[ -d "/Users/accusys/momentry/log" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
|
||||
```
|
||||
|
||||
**預期結果**:
|
||||
```
|
||||
=== MongoDB 卸載後檢查 ===
|
||||
1. MongoDB 進程:
|
||||
✓ 已停止
|
||||
2. Port 27017:
|
||||
✓ 已釋放
|
||||
3. mongod 命令:
|
||||
✓ 已移除
|
||||
4. launchctl 服務:
|
||||
✓ 已移除
|
||||
5. Homebrew 移除:
|
||||
✓ 已移除
|
||||
6. 數據目錄:
|
||||
✓ 保留
|
||||
7. 日誌目錄:
|
||||
✓ 保留
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 手動檢查命令
|
||||
|
||||
```bash
|
||||
# 1. 檢查 Process 是否運行
|
||||
ps aux | grep mongo | grep -v grep
|
||||
|
||||
# 2. 檢查 Port 27017
|
||||
lsof -i :27017
|
||||
|
||||
# 3. 測試連線 (無認證)
|
||||
mongosh --eval "db.adminCommand('ping')"
|
||||
|
||||
# 4. 測試連線 (有認證)
|
||||
mongosh "mongodb://accusys:Test3200Test3200@localhost:27017/admin" --eval "db.adminCommand('ping')"
|
||||
|
||||
# 5. 查看所有資料庫
|
||||
mongosh "mongodb://accusys:Test3200Test3200@localhost:27017/admin" --quiet --eval "db.adminCommand({listDatabases:1}).databases"
|
||||
|
||||
# 6. 查看用戶
|
||||
mongosh "mongodb://accusys:Test3200Test3200@localhost:27017/admin" --quiet --eval "db.getUser('accusys')"
|
||||
|
||||
# 7. 查看日誌
|
||||
tail -20 /Users/accusys/momentry/log/mongodb.log
|
||||
tail -20 /Users/accusys/momentry/log/mongodb.error.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 管理命令
|
||||
|
||||
### 啟動/停止
|
||||
|
||||
```bash
|
||||
# 使用 LaunchAgent (開機自動 - LaunchDaemons 目錄)
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.mongodb.plist # 啟動
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.mongodb.plist # 停止
|
||||
|
||||
# 手動啟動 (僅除錯用)
|
||||
nohup /opt/homebrew/bin/mongod \
|
||||
--dbpath /Users/accusys/momentry/var \
|
||||
--logpath /Users/accusys/momentry/log/mongodb.log \
|
||||
--port 27017 \
|
||||
--bind_ip 0.0.0.0 \
|
||||
> /Users/accusys/momentry/log/mongodb.log 2>&1 &
|
||||
|
||||
# 強制停止
|
||||
pkill mongod
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 連線字串
|
||||
|
||||
```bash
|
||||
# 無認證 (本地)
|
||||
mongodb://localhost:27017
|
||||
|
||||
# 有認證 (admin 資料庫)
|
||||
mongodb://accusys:Test3200Test3200@localhost:27017/admin
|
||||
|
||||
# 有認證 (momentry 資料庫)
|
||||
mongodb://accusys:Test3200Test3200@localhost:27017/momentry?authSource=admin
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 環境變數
|
||||
|
||||
在 `.env` 中:
|
||||
|
||||
```env
|
||||
MONGODB_URL=mongodb://accusys:Test3200Test3200@localhost:27017/admin
|
||||
MONGODB_DATABASE=momentry
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 遠端訪問
|
||||
|
||||
- MongoDB 綁定到 `0.0.0.0` (所有網路介面)
|
||||
- 本地網路其他機器可透過 IP 訪問
|
||||
- 建議設定防火牆規則限制訪問 IP
|
||||
|
||||
---
|
||||
|
||||
## 故障排除
|
||||
|
||||
### MongoDB 無法啟動
|
||||
|
||||
```bash
|
||||
# 檢查日誌
|
||||
tail -f /Users/accusys/momentry/log/mongodb.log
|
||||
|
||||
# 檢查目錄權限
|
||||
ls -la /Users/accusys/momentry/
|
||||
|
||||
# 重新設定權限
|
||||
chown -R $(whoami):staff /Users/accusys/momentry
|
||||
```
|
||||
|
||||
### Port 被佔用
|
||||
|
||||
```bash
|
||||
# 檢查哪個程序佔用 port 27017
|
||||
lsof -i :27017
|
||||
|
||||
# 終止佔用程序
|
||||
kill <PID>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 檔案位置
|
||||
|
||||
| 類型 | 路徑 | 說明 |
|
||||
|------|------|------|
|
||||
| 數據目錄 | `/Users/accusys/momentry/var` | **共用 - 不要刪除** |
|
||||
| 日誌目錄 | `/Users/accusys/momentry/log` | **共用 - 不要刪除** |
|
||||
| mongod | `/opt/homebrew/bin/mongod` | 安裝後存在 |
|
||||
| Homebrew | `/opt/homebrew/Cellar/mongodb-community/` | 卸載時刪除 |
|
||||
| Homebrew | `/opt/homebrew/Cellar/mongodb-database-tools/` | 卸載時刪除 |
|
||||
| Homebrew | `/opt/homebrew/Cellar/mongosh/` | 卸載時刪除 |
|
||||
| 配置檔 | `/opt/homebrew/etc/mongod.conf` | 卸載時刪除 |
|
||||
|
||||
---
|
||||
|
||||
## 版本資訊
|
||||
|
||||
- 用戶: accusys
|
||||
- 密碼: Test3200Test3200
|
||||
- 數據目錄: /Users/accusys/momentry/var (共用 - 不要刪除!)
|
||||
- 日誌目錄: /Users/accusys/momentry/log (共用 - 不要刪除!)
|
||||
@@ -1,489 +0,0 @@
|
||||
# n8n 安裝指南 (本地部署)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-16 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
本文檔說明如何在 macOS 上安裝 n8n 工作流自動化平台,配置為本地部署,使用 Queue 模式。
|
||||
|
||||
---
|
||||
|
||||
## 當前狀態
|
||||
|
||||
| 項目 | 狀態 |
|
||||
|------|------|
|
||||
| n8n | ✅ 已安裝 v2.12.3 |
|
||||
| 數據目錄 | /Users/accusys/momentry/var/n8n/ |
|
||||
| 日誌目錄 | /Users/accusys/momentry/log/ |
|
||||
| Main Plist | /Library/LaunchDaemons/com.momentry.n8n.main.plist |
|
||||
| Worker Plist | /Library/LaunchDaemons/com.momentry.n8n.worker.plist |
|
||||
| 數據庫 | PostgreSQL (n8n) |
|
||||
| 隊列 | Redis |
|
||||
| Launchd 狀態 | ✅ Main + Worker 已註冊 |
|
||||
| RunAtLoad | ✅ 已設定 |
|
||||
| KeepAlive | ✅ 已設定 |
|
||||
|
||||
### 重要更新 (2026-03-24)
|
||||
|
||||
1. **n8n Main + Worker**: 兩個服務都使用自定義 plist
|
||||
2. **Runner 禁用**: 為避免端口衝突,Main 服務設定 `N8N_RUNNERS_ENABLED=false`
|
||||
3. **Worker 端口**: Worker 使用 5681, 5682, 5690, 5691 端口
|
||||
|
||||
---
|
||||
|
||||
## 安裝步驟
|
||||
|
||||
### Step 1: 安裝 n8n (使用 brew)
|
||||
|
||||
```bash
|
||||
# 安裝 n8n
|
||||
brew install n8n
|
||||
```
|
||||
|
||||
**驗證**:
|
||||
```bash
|
||||
n8n --version
|
||||
# 2.12.3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 建立目錄
|
||||
|
||||
```bash
|
||||
# 建立數據目錄
|
||||
mkdir -p /Users/accusys/momentry/var/n8n
|
||||
|
||||
# 建立配置目錄
|
||||
mkdir -p /Users/accusys/momentry/etc/n8n
|
||||
|
||||
# 建立日誌目錄
|
||||
mkdir -p /Users/accusys/momentry/log
|
||||
|
||||
# 建立日誌文件
|
||||
touch /Users/accusys/momentry/log/n8n.main.log
|
||||
touch /Users/accusys/momentry/log/n8n.main.error.log
|
||||
touch /Users/accusys/momentry/log/n8n.worker.log
|
||||
touch /Users/accusys/momentry/log/n8n.worker.error.log
|
||||
|
||||
# 設定權限
|
||||
chown -R accusys:staff /Users/accusys/momentry/var/n8n
|
||||
chown -R accusys:staff /Users/accusys/momentry/etc/n8n
|
||||
chown -R accusys:staff /Users/accusys/momentry/log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 數據遷移 (如果從舊位置遷移)
|
||||
|
||||
```bash
|
||||
# 停止舊服務
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.n8n.main.plist
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.n8n.worker.plist
|
||||
|
||||
# 複製數據
|
||||
cp -r /Users/accusys/.n8n/* /Users/accusys/momentry/var/n8n/
|
||||
|
||||
# 設定權限
|
||||
chown -R accusys:staff /Users/accusys/momentry/var/n8n
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 使用 plist 開機自動啟動
|
||||
|
||||
```bash
|
||||
# 複製 plist 到 LaunchDaemons 目錄
|
||||
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.n8n.main.plist /Library/LaunchDaemons/
|
||||
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.n8n.worker.plist /Library/LaunchDaemons/
|
||||
|
||||
# 移除舊 plist (如果存在)
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.n8n.main.plist 2>/dev/null
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.n8n.worker.plist 2>/dev/null
|
||||
sudo rm /Library/LaunchDaemons/com.n8n.main.plist 2>/dev/null
|
||||
sudo rm /Library/LaunchDaemons/com.n8n.worker.plist 2>/dev/null
|
||||
|
||||
# 載入並啟動
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.n8n.main.plist
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.n8n.worker.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 監控配置
|
||||
|
||||
### 添加到監控配置
|
||||
|
||||
在 `monitor/config/monitor_config.yaml` 中添加:
|
||||
|
||||
```yaml
|
||||
service:
|
||||
services:
|
||||
- name: "n8n"
|
||||
type: "http"
|
||||
port: 5678
|
||||
host: "localhost"
|
||||
check_url: "http://localhost:5678/"
|
||||
timeout: 5
|
||||
enabled: true
|
||||
```
|
||||
|
||||
### 添加健康檢查函數
|
||||
|
||||
在 `monitor/service/health_check.sh` 中添加:
|
||||
|
||||
```bash
|
||||
check_n8n() {
|
||||
local start=$(date +%s%N)
|
||||
if curl -s http://localhost:5678/ > /dev/null 2>&1; then
|
||||
local end=$(date +%s%N)
|
||||
local ms=$(( (end - start) / 1000000 ))
|
||||
echo -e "${GREEN}✓${NC} n8n (5678) - ${ms}ms"
|
||||
record_service "n8n" "up" "$ms" ""
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}✗${NC} n8n (5678) - Down"
|
||||
record_service "n8n" "down" "0" "Connection failed"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
```
|
||||
|
||||
### n8n Workflow 監控
|
||||
|
||||
n8n 有專門的工作流監控腳本,請參考 `monitor/workflow/n8n_workflow_monitor.sh`。
|
||||
|
||||
---
|
||||
|
||||
## 卸載步驟
|
||||
|
||||
### 重要: 路徑說明
|
||||
|
||||
| 路徑 | 類型 | 說明 |
|
||||
|------|------|------|
|
||||
| `/Users/accusys/momentry/var/n8n/` | 數據 | **不要刪除** - n8n 數據 |
|
||||
| `/Users/accusys/momentry/etc/n8n/` | 配置 | **不要刪除** - n8n 配置 |
|
||||
| `/Users/accusys/momentry/log/` | 日誌 | **不要刪除** - n8n 日誌 |
|
||||
| `/opt/homebrew/lib/node_modules/n8n/` | 安裝 | **刪除** - n8n 安裝目錄 |
|
||||
|
||||
### Step 1: 停止 n8n
|
||||
|
||||
```bash
|
||||
# 停止 n8n 服務
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.n8n.main.plist
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.n8n.worker.plist
|
||||
|
||||
# 確認停止
|
||||
ps aux | grep n8n | grep -v grep || echo "n8n 已停止"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 卸載 n8n
|
||||
|
||||
```bash
|
||||
# 卸載 n8n
|
||||
brew uninstall n8n
|
||||
|
||||
# 移除 plist
|
||||
sudo rm /Library/LaunchDaemons/com.momentry.n8n.main.plist
|
||||
sudo rm /Library/LaunchDaemons/com.momentry.n8n.worker.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 刪除專屬檔案
|
||||
|
||||
```bash
|
||||
# 刪除數據目錄 (可選)
|
||||
rm -rf /Users/accusys/momentry/var/n8n
|
||||
|
||||
# 刪除日誌 (可選)
|
||||
rm -f /Users/accusys/momentry/log/n8n-*.log
|
||||
```
|
||||
|
||||
**注意: 不要刪除以下共用目錄**:
|
||||
```bash
|
||||
# 這些是共用的,不要刪除!
|
||||
# /Users/accusys/momentry/var
|
||||
# /Users/accusys/momentry/log
|
||||
# PostgreSQL n8n 數據庫 (如需保留)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 卸載後檢查清單
|
||||
|
||||
```bash
|
||||
echo "=== n8n 卸載後檢查 ==="
|
||||
|
||||
# 1. 檢查 n8n 進程
|
||||
echo "1. n8n Main 進程:"
|
||||
ps aux | grep "n8n.*start" | grep -v grep && echo " ✗ 仍在運行" || echo " ✓ 已停止"
|
||||
|
||||
echo "2. n8n Worker 進程:"
|
||||
ps aux | grep "n8n.*worker" | grep -v grep && echo " ✗ 仍在運行" || echo " ✓ 已停止"
|
||||
|
||||
# 2. Port 8085
|
||||
echo "3. Port 8085:"
|
||||
lsof -i :8085 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
|
||||
|
||||
# 3. Port 5679 (Worker)
|
||||
echo "4. Port 5679 (Worker):"
|
||||
lsof -i :5679 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
|
||||
|
||||
# 4. n8n 命令
|
||||
echo "5. n8n 命令:"
|
||||
which n8n > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 5. brew 安裝
|
||||
echo "6. brew 安裝:"
|
||||
brew list n8n > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 6. launchctl 服務
|
||||
echo "7. launchctl 服務:"
|
||||
sudo launchctl list | grep n8n > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 備份步驟
|
||||
|
||||
### 備份 n8n 數據
|
||||
|
||||
```bash
|
||||
# 建立備份目錄
|
||||
mkdir -p /Users/accusys/momentry/var/n8n_backup
|
||||
|
||||
# 1. 備份 PostgreSQL 數據庫
|
||||
PGPASSWORD=accusys pg_dump -U accusys -d n8n > /Users/accusys/momentry/var/n8n_backup/n8n_database_$(date +%Y%m%d).sql
|
||||
|
||||
# 2. 備份用戶數據夾
|
||||
cp -r /Users/accusys/momentry/var/n8n /Users/accusys/momentry/var/n8n_backup/
|
||||
|
||||
# 3. 壓縮備份
|
||||
cd /Users/accusys/momentry/var && tar -czvf n8n_backup_$(date +%Y%m%d).tar.gz n8n_backup/
|
||||
|
||||
# 4. 清理臨時備份
|
||||
rm -rf /Users/accusys/momentry/var/n8n_backup
|
||||
```
|
||||
|
||||
### 還原數據
|
||||
|
||||
```bash
|
||||
# 1. 停止 n8n
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.n8n.main.plist
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.n8n.worker.plist
|
||||
|
||||
# 2. 還原 PostgreSQL 數據庫
|
||||
PGPASSWORD=accusys psql -U accusys -d n8n < /Users/accusys/momentry/var/n8n_backup/n8n_database_20260314.sql
|
||||
|
||||
# 3. 還原用戶數據夾
|
||||
rm -rf /Users/accusys/momentry/var/n8n
|
||||
cp -r /Users/accusys/momentry/var/n8n_backup/n8n /Users/accusys/momentry/var/n8n
|
||||
chown -R accusys:staff /Users/accusys/momentry/var/n8n
|
||||
|
||||
# 4. 啟動 n8n
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.n8n.main.plist
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.n8n.worker.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 手動檢查命令
|
||||
|
||||
```bash
|
||||
# 1. 檢查進程
|
||||
ps aux | grep n8n | grep -v grep
|
||||
|
||||
# 2. 檢查 Port
|
||||
lsof -i :5678
|
||||
lsof -i :5679
|
||||
|
||||
# 3. 測試連線
|
||||
curl http://localhost:5678/
|
||||
|
||||
# 4. 查看版本
|
||||
n8n --version
|
||||
|
||||
# 5. 查看日誌
|
||||
tail -20 /Users/accusys/momentry/log/n8n-main.log
|
||||
tail -20 /Users/accusys/momentry/log/n8n-worker.log
|
||||
|
||||
# 6. 查看錯誤日誌
|
||||
tail -20 /Users/accusys/momentry/log/n8n-main.error.log
|
||||
tail -20 /Users/accusys/momentry/log/n8n-worker.error.log
|
||||
|
||||
# 7. 檢查 launchctl 狀態
|
||||
sudo launchctl list | grep n8n
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 連線資訊
|
||||
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| URL | http://localhost:5678 |
|
||||
| Domain | n8n.momentry.ddns.net |
|
||||
| 數據庫 | PostgreSQL (n8n) |
|
||||
| 隊列 | Redis |
|
||||
| Encryption Key | Test3200Test3200Test3200 |
|
||||
|
||||
### Queue 模式端口
|
||||
|
||||
| 服務 | Port |
|
||||
|------|------|
|
||||
| Main | 5678 |
|
||||
| Task Broker (Worker 連接) | 5679 |
|
||||
|
||||
---
|
||||
|
||||
## 環境變數
|
||||
|
||||
在 `.env` 中:
|
||||
|
||||
```env
|
||||
N8N_URL=http://localhost:5678
|
||||
N8N_USER_FOLDER=/Users/accusys/momentry/var/n8n
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 故障排除
|
||||
|
||||
### n8n 無法啟動
|
||||
|
||||
```bash
|
||||
# 檢查日誌
|
||||
tail -f /Users/accusys/momentry/log/n8n-main.log
|
||||
tail -f /Users/accusys/momentry/log/n8n-worker.log
|
||||
|
||||
# 檢查環境變數
|
||||
launchctl list | grep n8n
|
||||
|
||||
# 檢查數據庫連線
|
||||
PGPASSWORD=accusys psql -U accusys -d n8n -c "SELECT 1"
|
||||
|
||||
# 檢查 Redis 連線
|
||||
redis-cli -a accusys ping
|
||||
```
|
||||
|
||||
### Port 被佔用
|
||||
|
||||
```bash
|
||||
# 檢查哪個程序佔用 port
|
||||
lsof -i :8085
|
||||
|
||||
# 終止佔用程序
|
||||
kill <PID>
|
||||
```
|
||||
|
||||
### 數據庫連線失敗
|
||||
|
||||
```bash
|
||||
# 檢查 PostgreSQL
|
||||
pg_isready -h 127.0.0.1 -p 5432 -U n8n
|
||||
|
||||
# 測試連線
|
||||
PGPASSWORD=accusys psql -h 127.0.0.1 -U n8n -d n8n -c "SELECT version();"
|
||||
```
|
||||
|
||||
### 需要重新載入 plist
|
||||
|
||||
```bash
|
||||
# 卸載舊服務
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.n8n.main.plist
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.n8n.worker.plist
|
||||
|
||||
# 載入新服務
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.n8n.main.plist
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.n8n.worker.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 檔案位置
|
||||
|
||||
| 類型 | 路徑 | 說明 |
|
||||
|------|------|------|
|
||||
| 安裝 | `/opt/homebrew/lib/node_modules/n8n/` | n8n 安裝目錄 |
|
||||
| 執行檔 | `/opt/homebrew/bin/n8n` | n8n 執行檔 |
|
||||
| 數據目錄 | `/Users/accusys/momentry/var/n8n/` | 數據儲存 |
|
||||
| 配置目錄 | `/Users/accusys/momentry/etc/n8n/` | 配置儲存 |
|
||||
| Main 日誌 | `/Users/accusys/momentry/log/n8n.main.log` | 主服務日誌 |
|
||||
| Main 錯誤日誌 | `/Users/accusys/momentry/log/n8n.main.error.log` | 主服務錯誤日誌 |
|
||||
| Worker 日誌 | `/Users/accusys/momentry/log/n8n.worker.log` | Worker 日誌 |
|
||||
| Worker 錯誤日誌 | `/Users/accusys/momentry/log/n8n.worker.error.log` | Worker 錯誤日誌 |
|
||||
| Main Plist | `/Library/LaunchDaemons/com.momentry.n8n.main.plist` | 主服務開機啟動 |
|
||||
| Worker Plist | `/Library/LaunchDaemons/com.momentry.n8n.worker.plist` | Worker 開機啟動 |
|
||||
| 備份 | `/Users/accusys/momentry/var/n8n_backup/` | 數據備份 |
|
||||
|
||||
---
|
||||
|
||||
## 數據庫資訊
|
||||
|
||||
n8n 使用 PostgreSQL 作為數據庫:
|
||||
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| Database | n8n |
|
||||
| User | n8n |
|
||||
| Host | 127.0.0.1:5432 |
|
||||
| Password | accusys |
|
||||
|
||||
### Redis 隊列資訊
|
||||
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| Host | 127.0.0.1:6379 |
|
||||
| Password | accusys |
|
||||
| Mode | Queue (Bull) |
|
||||
|
||||
---
|
||||
|
||||
## 常用指令
|
||||
|
||||
```bash
|
||||
# 啟動 n8n Main
|
||||
n8n start
|
||||
|
||||
# 啟動 n8n Worker
|
||||
n8n worker
|
||||
|
||||
# 查看版本
|
||||
n8n --version
|
||||
|
||||
# 備份數據
|
||||
PGPASSWORD=accusys pg_dump -U accusys -d n8n > n8n_backup.sql
|
||||
|
||||
# 重新載入服務
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.n8n.main.plist
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.n8n.main.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 版本資訊
|
||||
|
||||
- 版本: 2.3.5
|
||||
- Main Port: 5678
|
||||
- Task Broker (Worker): 5679
|
||||
- 數據目錄: /Users/accusys/momentry/var/n8n/
|
||||
- 日誌目錄: /Users/accusys/momentry/log/
|
||||
- 數據庫: PostgreSQL n8n
|
||||
- 隊列: Redis
|
||||
@@ -1,375 +0,0 @@
|
||||
# Ollama 安裝指南 (本地部署)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-15 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-15 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
本文檔說明如何在 macOS 上安裝 Ollama,配置為本地部署,用於運行大型語言模型 (LLM)。
|
||||
|
||||
---
|
||||
|
||||
## 當前狀態
|
||||
|
||||
| 項目 | 狀態 |
|
||||
|------|------|
|
||||
| Ollama | ✅ 已安裝 v0.13.5 |
|
||||
| Port | 11434 |
|
||||
| Models 目錄 | /Users/accusys/momentry/var/ollama/models |
|
||||
| 日誌目錄 | /Users/accusys/momentry/log/ |
|
||||
| Plist | /Library/LaunchDaemons/com.momentry.ollama.plist |
|
||||
|
||||
---
|
||||
|
||||
## 安裝步驟
|
||||
|
||||
### Step 1: 安裝 Ollama (使用 brew)
|
||||
|
||||
```bash
|
||||
# 安裝 Ollama
|
||||
brew install ollama
|
||||
```
|
||||
|
||||
**驗證**:
|
||||
```bash
|
||||
ollama --version
|
||||
# ollama version is 0.13.5
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 建立目錄
|
||||
|
||||
```bash
|
||||
# 建立數據目錄
|
||||
mkdir -p /Users/accusys/momentry/var/ollama
|
||||
|
||||
# 建立配置目錄
|
||||
mkdir -p /Users/accusys/momentry/etc/ollama
|
||||
|
||||
# 建立日誌目錄
|
||||
mkdir -p /Users/accusys/momentry/log
|
||||
|
||||
# 建立日誌文件
|
||||
touch /Users/accusys/momentry/log/ollama.log
|
||||
touch /Users/accusys/momentry/log/ollama.error.log
|
||||
|
||||
# 設定權限
|
||||
chown -R accusys:staff /Users/accusys/momentry/var/ollama
|
||||
chown -R accusys:staff /Users/accusys/momentry/etc/ollama
|
||||
chown -R accusys:staff /Users/accusys/momentry/log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 使用 plist 開機自動啟動
|
||||
|
||||
```bash
|
||||
# 複製 plist 到 LaunchDaemons 目錄
|
||||
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.ollama.plist /Library/LaunchDaemons/
|
||||
|
||||
# 載入並啟動
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.ollama.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 監控配置
|
||||
|
||||
### 添加到監控配置
|
||||
|
||||
在 `monitor/config/monitor_config.yaml` 中添加:
|
||||
|
||||
```yaml
|
||||
service:
|
||||
services:
|
||||
- name: "ollama"
|
||||
type: "http"
|
||||
port: 11434
|
||||
host: "localhost"
|
||||
check_url: "http://localhost:11434/api/tags"
|
||||
timeout: 5
|
||||
enabled: true
|
||||
```
|
||||
|
||||
### 添加健康檢查函數
|
||||
|
||||
在 `monitor/service/health_check.sh` 中添加:
|
||||
|
||||
```bash
|
||||
check_ollama() {
|
||||
local start=$(date +%s%N)
|
||||
if curl -s http://localhost:11434/api/tags > /dev/null 2>&1; then
|
||||
local end=$(date +%s%N)
|
||||
local ms=$(( (end - start) / 1000000 ))
|
||||
echo -e "${GREEN}✓${NC} Ollama (11434) - ${ms}ms"
|
||||
record_service "ollama" "up" "$ms" ""
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}✗${NC} Ollama (11434) - Down"
|
||||
record_service "ollama" "down" "0" "Connection failed"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 卸載步驟
|
||||
|
||||
### 重要: 路徑說明
|
||||
|
||||
| 路徑 | 類型 | 說明 |
|
||||
|------|------|------|
|
||||
| `/Users/accusys/momentry/var/ollama/` | 數據 | **不要刪除** - Ollama 數據 |
|
||||
| `/Users/accusys/momentry/etc/ollama/` | 配置 | **不要刪除** - Ollama 配置 |
|
||||
| `/Users/accusys/momentry/log/` | 日誌 | **不要刪除** - 日誌目錄 |
|
||||
| `/opt/homebrew/opt/ollama/` | 安裝 | **刪除** - Ollama 安裝目錄 |
|
||||
|
||||
### Step 1: 停止 Ollama
|
||||
|
||||
```bash
|
||||
# 找到 Ollama 進程
|
||||
ps aux | grep ollama | grep -v grep
|
||||
|
||||
# 停止 Ollama
|
||||
pkill ollama
|
||||
|
||||
# 確認停止
|
||||
ps aux | grep ollama | grep -v grep || echo "Ollama 已停止"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 卸載 Ollama
|
||||
|
||||
```bash
|
||||
# 卸載 Ollama
|
||||
brew uninstall ollama
|
||||
|
||||
# 移除 plist
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.ollama.plist
|
||||
sudo rm /Library/LaunchDaemons/com.momentry.ollama.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 刪除專屬檔案
|
||||
|
||||
```bash
|
||||
# 刪除日誌 (可選)
|
||||
rm -f /Users/accusys/momentry/log/ollama.log
|
||||
rm -f /Users/accusys/momentry/log/ollama.error.log
|
||||
```
|
||||
|
||||
**注意: 不要刪除以下目錄**:
|
||||
```bash
|
||||
# 這些是重要的,不要刪除!
|
||||
# /Users/accusys/.ollama/models
|
||||
# /Users/accusys/momentry/log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 卸載後檢查清單
|
||||
|
||||
```bash
|
||||
echo "=== Ollama 卸載後檢查 ==="
|
||||
|
||||
# 1. 檢查 Ollama 進程
|
||||
echo "1. Ollama 進程:"
|
||||
ps aux | grep ollama | grep -v grep && echo " ✗ 仍在運行" || echo " ✓ 已停止"
|
||||
|
||||
# 2. Port 11434
|
||||
echo "2. Port 11434:"
|
||||
lsof -i :11434 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
|
||||
|
||||
# 3. ollama 命令
|
||||
echo "3. ollama 命令:"
|
||||
which ollama > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 4. brew 安裝
|
||||
echo "4. brew 安裝:"
|
||||
brew list ollama > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 5. launchctl 服務
|
||||
echo "5. launchctl 服務:"
|
||||
sudo launchctl list | grep ollama > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 6. 模型目錄 (應該保留)
|
||||
echo "6. 模型目錄:"
|
||||
[ -d "/Users/accusys/.ollama/models" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 手動檢查命令
|
||||
|
||||
```bash
|
||||
# 1. 檢查進程
|
||||
ps aux | grep ollama | grep -v grep
|
||||
|
||||
# 2. 檢查 Port
|
||||
lsof -i :11434
|
||||
|
||||
# 3. 測試連線
|
||||
curl http://localhost:11434/
|
||||
|
||||
# 4. 查看版本
|
||||
ollama --version
|
||||
|
||||
# 5. 查看已安裝的模型
|
||||
ollama list
|
||||
|
||||
# 6. 查看日誌
|
||||
tail -20 /Users/accusys/momentry/log/ollama.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 連線資訊
|
||||
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| Host | localhost |
|
||||
| Port | 11434 |
|
||||
| Models | /Users/accusys/.ollama/models |
|
||||
|
||||
---
|
||||
|
||||
## 環境變數
|
||||
|
||||
在 `.env` 中:
|
||||
|
||||
```env
|
||||
OLLAMA_HOST=0.0.0.0:11434
|
||||
OLLAMA_MODELS=/Users/accusys/.ollama/models
|
||||
OLLAMA_FLASH_ATTENTION=1
|
||||
OLLAMA_KV_CACHE_TYPE=q8_0
|
||||
```
|
||||
|
||||
### 環境變數說明
|
||||
|
||||
| 變數 | 說明 | 預設值 |
|
||||
|------|------|---------|
|
||||
| OLLAMA_HOST | 綁定主機和端口 | 127.0.0.1:11434 |
|
||||
| OLLAMA_MODELS | 模型儲存目錄 | ~/.ollama/models |
|
||||
| OLLAMA_FLASH_ATTENTION | 啟用 Flash Attention | 0 |
|
||||
| OLLAMA_KV_CACHE_TYPE | KV 緩存類型 | f16 |
|
||||
|
||||
---
|
||||
|
||||
## 遠端訪問
|
||||
|
||||
- Ollama 綁定到 `0.0.0.0:11434` (所有網路介面)
|
||||
- 本地網路其他機器可透過 IP 訪問
|
||||
- 請注意安全風險
|
||||
|
||||
---
|
||||
|
||||
## 故障排除
|
||||
|
||||
### Ollama 無法啟動
|
||||
|
||||
```bash
|
||||
# 檢查日誌
|
||||
tail -f /Users/accusys/momentry/log/ollama.log
|
||||
|
||||
# 檢查模型目錄權限
|
||||
ls -la /Users/accusys/.ollama/models/
|
||||
|
||||
# 重新設定權限
|
||||
chown -R $(whoami):staff /Users/accusys/.ollama
|
||||
```
|
||||
|
||||
### Port 被佔用
|
||||
|
||||
```bash
|
||||
# 檢查哪個程序佔用 port 11434
|
||||
lsof -i :11434
|
||||
|
||||
# 終止佔用程序
|
||||
kill <PID>
|
||||
```
|
||||
|
||||
### 需要重新載入 plist
|
||||
|
||||
```bash
|
||||
# 卸載舊服務 (如果存在)
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.ollama.plist 2>/dev/null
|
||||
|
||||
# 載入新服務
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.ollama.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 檔案位置
|
||||
|
||||
| 類型 | 路徑 | 說明 |
|
||||
|------|------|------|
|
||||
| 安裝 | `/opt/homebrew/opt/ollama/` | Ollama 安裝目錄 |
|
||||
| 執行檔 | `/opt/homebrew/opt/ollama/bin/ollama` | Ollama 執行檔 |
|
||||
| 數據目錄 | `/Users/accusys/momentry/var/ollama/` | 數據儲存 |
|
||||
| 配置目錄 | `/Users/accusys/momentry/etc/ollama/` | 配置儲存 |
|
||||
| 模型目錄 | `/Users/accusys/.ollama/models/` | AI 模型儲存 |
|
||||
| 日誌 | `/Users/accusys/momentry/log/ollama.log` | 執行日誌 |
|
||||
| 錯誤日誌 | `/Users/accusys/momentry/log/ollama.error.log` | 錯誤日誌 |
|
||||
| plist | `/Library/LaunchDaemons/com.momentry.ollama.plist` | 開機啟動 |
|
||||
| 備份 | `/Users/accusys/momentry/var/ollama_backup/environment.txt` | 環境變數備份 |
|
||||
|
||||
---
|
||||
|
||||
## 常用指令
|
||||
|
||||
```bash
|
||||
# 查看版本
|
||||
ollama --version
|
||||
|
||||
# 查看已安裝的模型
|
||||
ollama list
|
||||
|
||||
# 拉取模型
|
||||
ollama pull mistral
|
||||
ollama pull llama2
|
||||
|
||||
# 運行模型
|
||||
ollama run mistral
|
||||
|
||||
# 刪除模型
|
||||
ollama remove mistral
|
||||
|
||||
# 查看模型資訊
|
||||
ollama show mistral
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 已安裝的模型
|
||||
|
||||
查看已安裝的模型:
|
||||
```bash
|
||||
ollama list
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 版本資訊
|
||||
|
||||
- 版本: 0.13.5
|
||||
- Port: 11434
|
||||
- Models: /Users/accusys/.ollama/models/
|
||||
- 日誌目錄: /Users/accusys/momentry/log/
|
||||
@@ -1,395 +0,0 @@
|
||||
# PHP 安裝指南 (本地部署)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-16 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
本文檔說明如何在 macOS 上安裝 PHP 及 PHP-FPM,配置為本地部署。
|
||||
|
||||
---
|
||||
|
||||
## 當前狀態
|
||||
|
||||
| 項目 | 狀態 |
|
||||
|------|------|
|
||||
| PHP | ✅ 已安裝 v8.5.2 |
|
||||
| PHP-FPM | ✅ 執行中 |
|
||||
| 配置目錄 | /Users/accusys/momentry/etc/php/ |
|
||||
| 日誌目錄 | /Users/accusys/momentry/log/ |
|
||||
| Plist | /Library/LaunchDaemons/com.momentry.php.plist |
|
||||
|
||||
---
|
||||
|
||||
## 安裝步驟
|
||||
|
||||
### Step 1: 安裝 PHP (使用 brew)
|
||||
|
||||
```bash
|
||||
# 安裝 PHP
|
||||
brew install php
|
||||
|
||||
# 安裝 PHP-FPM (通常包含在 php 中)
|
||||
brew install php --fpm
|
||||
```
|
||||
|
||||
**驗證**:
|
||||
```bash
|
||||
php --version
|
||||
# PHP 8.5.2 (cli)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 建立目錄
|
||||
|
||||
```bash
|
||||
# 建立配置目錄
|
||||
mkdir -p /Users/accusys/momentry/etc/php
|
||||
|
||||
# 建立日誌目錄
|
||||
mkdir -p /Users/accusys/momentry/log
|
||||
|
||||
# 建立日誌文件
|
||||
touch /Users/accusys/momentry/log/php.log
|
||||
touch /Users/accusys/momentry/log/php.error.log
|
||||
|
||||
# 設定權限
|
||||
chown -R accusys:staff /Users/accusys/momentry/etc/php
|
||||
chown -R accusys:staff /Users/accusys/momentry/log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 建立設定檔
|
||||
|
||||
建立 `/Users/accusys/momentry/etc/php/php-fpm.conf`:
|
||||
|
||||
```ini
|
||||
[global]
|
||||
pid = /Users/accusys/momentry/var/php-fpm.pid
|
||||
error_log = /Users/accusys/momentry/log/php.error.log
|
||||
log_level = notice
|
||||
|
||||
[www]
|
||||
user = accusys
|
||||
group = staff
|
||||
listen = 127.0.0.1:9000
|
||||
listen.owner = accusys
|
||||
listen.group = staff
|
||||
pm = dynamic
|
||||
pm.max_children = 5
|
||||
pm.start_servers = 2
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
```
|
||||
|
||||
複製 php.ini:
|
||||
```bash
|
||||
cp /opt/homebrew/etc/php/8.5/php.ini /Users/accusys/momentry/etc/php/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 使用 plist 開機自動啟動
|
||||
|
||||
```bash
|
||||
# 複製 plist 到 LaunchDaemons 目錄
|
||||
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.php.plist /Library/LaunchDaemons/
|
||||
|
||||
# 載入並啟動
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.php.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 監控配置
|
||||
|
||||
### 添加到監控配置
|
||||
|
||||
在 `monitor/config/monitor_config.yaml` 中添加:
|
||||
|
||||
```yaml
|
||||
service:
|
||||
services:
|
||||
- name: "php-fpm"
|
||||
type: "tcp"
|
||||
port: 9000
|
||||
host: "localhost"
|
||||
timeout: 5
|
||||
enabled: true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 卸載步驟
|
||||
|
||||
### 重要: 路徑說明
|
||||
|
||||
| 路徑 | 類型 | 說明 |
|
||||
|------|------|------|
|
||||
| `/Users/accusys/momentry/etc/php/` | 配置 | **不要刪除** - PHP 配置 |
|
||||
| `/Users/accusys/momentry/log/` | 日誌 | **不要刪除** - 日誌目錄 |
|
||||
| `/opt/homebrew/opt/php/` | 安裝 | **刪除** - PHP 安裝目錄 |
|
||||
|
||||
### Step 1: 停止 PHP-FPM
|
||||
|
||||
```bash
|
||||
# 找到 PHP-FPM 進程
|
||||
ps aux | grep php-fpm | grep -v grep
|
||||
|
||||
# 停止 PHP-FPM
|
||||
pkill php-fpm
|
||||
|
||||
# 確認停止
|
||||
ps aux | grep php-fpm | grep -v grep || echo "PHP-FPM 已停止"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 卸載 PHP
|
||||
|
||||
```bash
|
||||
# 卸載 PHP
|
||||
brew uninstall php
|
||||
|
||||
# 移除 plist
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.php.plist
|
||||
sudo rm /Library/LaunchDaemons/com.momentry.php.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 刪除專屬檔案
|
||||
|
||||
```bash
|
||||
# 刪除配置目錄 (可選)
|
||||
rm -rf /Users/accusys/momentry/etc/php
|
||||
|
||||
# 刪除日誌 (可選)
|
||||
rm -f /Users/accusys/momentry/log/php.log
|
||||
rm -f /Users/accusys/momentry/log/php.error.log
|
||||
```
|
||||
|
||||
**注意: 不要刪除以下共用目錄**:
|
||||
```bash
|
||||
# 這些是共用的,不要刪除!
|
||||
# /Users/accusys/momentry/etc
|
||||
# /Users/accusys/momentry/log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 卸載後檢查清單
|
||||
|
||||
```bash
|
||||
echo "=== PHP 卸載後檢查 ==="
|
||||
|
||||
# 1. 檢查 PHP-FPM 進程
|
||||
echo "1. PHP-FPM 進程:"
|
||||
ps aux | grep php-fpm | grep -v grep && echo " ✗ 仍在運行" || echo " ✓ 已停止"
|
||||
|
||||
# 2. Port 9000
|
||||
echo "2. Port 9000:"
|
||||
lsof -i :9000 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
|
||||
|
||||
# 3. php 命令
|
||||
echo "3. php 命令:"
|
||||
which php > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 4. brew 安裝
|
||||
echo "4. brew 安裝:"
|
||||
brew list php > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 5. launchctl 服務
|
||||
echo "5. launchctl 服務:"
|
||||
sudo launchctl list | grep php > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 6. 配置目錄 (可選刪除)
|
||||
echo "6. 配置目錄:"
|
||||
[ -d "/Users/accusys/momentry/etc/php" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
|
||||
|
||||
# 7. 日誌目錄 (可選刪除)
|
||||
echo "7. 日誌目錄:"
|
||||
[ -d "/Users/accusys/momentry/log" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 手動檢查命令
|
||||
|
||||
```bash
|
||||
# 1. 檢查 PHP 版本
|
||||
php --version
|
||||
|
||||
# 2. 檢查 PHP-FPM 進程
|
||||
ps aux | grep php-fpm | grep -v grep
|
||||
|
||||
# 3. 檢查 Port
|
||||
lsof -i :9000
|
||||
|
||||
# 4. 測試 PHP
|
||||
php -r "echo 'PHP OK' . PHP_EOL;"
|
||||
|
||||
# 5. 查看 PHP 模組
|
||||
php -m
|
||||
|
||||
# 6. 查看 PHP 配置
|
||||
php -i | grep "Loaded Configuration File"
|
||||
|
||||
# 7. 查看日誌
|
||||
tail -20 /Users/accusys/momentry/log/php.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 連線資訊
|
||||
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| PHP-FPM Port | 9000 |
|
||||
| PHP Version | 8.5.2 |
|
||||
| Config | /Users/accusys/momentry/etc/php/php-fpm.conf |
|
||||
| php.ini | /Users/accusys/momentry/etc/php/php.ini |
|
||||
|
||||
---
|
||||
|
||||
## 環境變數
|
||||
|
||||
在 `.env` 中:
|
||||
|
||||
```env
|
||||
PHP_INI_SCAN_DIR=/Users/accusys/momentry/etc/php/conf.d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 故障排除
|
||||
|
||||
### PHP-FPM 無法啟動
|
||||
|
||||
```bash
|
||||
# 檢查日誌
|
||||
tail -f /Users/accusys/momentry/log/php.error.log
|
||||
|
||||
# 檢查配置語法
|
||||
/opt/homebrew/opt/php/sbin/php-fpm --test --fpm-config /Users/accusys/momentry/etc/php/php-fpm.conf
|
||||
|
||||
# 檢查目錄權限
|
||||
ls -la /Users/accusys/momentry/etc/php/
|
||||
|
||||
# 重新設定權限
|
||||
chown -R $(whoami):staff /Users/accusys/momentry/etc/php
|
||||
```
|
||||
|
||||
### Port 被佔用
|
||||
|
||||
```bash
|
||||
# 檢查哪個程序佔用 port 9000
|
||||
lsof -i :9000
|
||||
|
||||
# 終止佔用程序
|
||||
kill <PID>
|
||||
```
|
||||
|
||||
### 需要重新載入 plist
|
||||
|
||||
```bash
|
||||
# 卸載舊服務 (如果存在)
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.php.plist 2>/dev/null
|
||||
|
||||
# 載入新服務
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.php.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 檔案位置
|
||||
|
||||
| 類型 | 路徑 | 說明 |
|
||||
|------|------|------|
|
||||
| 安裝 | `/opt/homebrew/opt/php/` | PHP 安裝目錄 |
|
||||
| 執行檔 | `/opt/homebrew/bin/php` | PHP 執行檔 |
|
||||
| PHP-FPM | `/opt/homebrew/opt/php/sbin/php-fpm` | PHP-FPM 執行檔 |
|
||||
| php.ini | `/Users/accusys/momentry/etc/php/8.5/php.ini` | PHP 配置 |
|
||||
| PHP-FPM 配置 | `/Users/accusys/momentry/etc/php/8.5/php-fpm.conf` | PHP-FPM 主配置 |
|
||||
| PHP-FPM pool | `/Users/accusys/momentry/etc/php/8.5/php-fpm.d/` | Pool 配置 |
|
||||
| 日誌 | `/Users/accusys/momentry/log/php.log` | 執行日誌 |
|
||||
| 錯誤日誌 | `/opt/homebrew/var/log/php-fpm.log` | PHP-FPM 錯誤日誌 |
|
||||
| plist | `/Library/LaunchDaemons/com.momentry.php.plist` | 開機啟動 |
|
||||
| 備份 | `/Users/accusys/momentry/backup/daily/php/` | 配置備份 |
|
||||
|
||||
---
|
||||
|
||||
## 常用指令
|
||||
|
||||
```bash
|
||||
# 測試 PHP-FPM 配置
|
||||
/opt/homebrew/opt/php/sbin/php-fpm --test --fpm-config /Users/accusys/momentry/etc/php/php-fpm.conf
|
||||
|
||||
# 查看 PHP 模組
|
||||
php -m
|
||||
|
||||
# 查看已載入的配置
|
||||
php -i
|
||||
|
||||
# 測試 PHP 腳本
|
||||
php -r "echo 'Hello World' . PHP_EOL;"
|
||||
|
||||
# 查看 PHP-FPM 狀態
|
||||
curl http://127.0.0.1:9000/status
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 備份與恢復
|
||||
|
||||
### 備份
|
||||
|
||||
```bash
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
BACKUP_DIR="/Users/accusys/momentry/backup/daily/php"
|
||||
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
# 備份配置 (注意:PHP-FPM 實際使用 /Users/accusys/momentry/etc/php/8.5/ 配置)
|
||||
tar -czf "$BACKUP_DIR/php_cfg_${TIMESTAMP}.tar.gz" \
|
||||
/Users/accusys/momentry/etc/php/8.5/php.ini \
|
||||
/Users/accusys/momentry/etc/php/8.5/php-fpm.conf \
|
||||
/Users/accusys/momentry/etc/php/8.5/php-fpm.d/
|
||||
|
||||
# 驗證
|
||||
sha256sum "$BACKUP_DIR/php_cfg_${TIMESTAMP}.tar.gz" > "$BACKUP_DIR/php_${TIMESTAMP}.sha256"
|
||||
```
|
||||
|
||||
### 恢復
|
||||
|
||||
```bash
|
||||
# 解壓配置
|
||||
tar -xzf /Users/accusys/momentry/backup/daily/php/php_cfg_20260316_102727.tar.gz -C /
|
||||
|
||||
# 測試配置
|
||||
/opt/homebrew/opt/php/sbin/php-fpm --test --fpm-config /Users/accusys/momentry/etc/php/8.5/php-fpm.conf
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 版本資訊
|
||||
|
||||
- PHP Version: 8.5.2
|
||||
- PHP-FPM Port: 9000
|
||||
- 配置目錄: /Users/accusys/momentry/etc/php/
|
||||
- 日誌目錄: /Users/accusys/momentry/log/
|
||||
@@ -1,397 +0,0 @@
|
||||
# PostgreSQL 安裝指南 (本地部署)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-15 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-15 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
本文檔說明如何在 macOS 上安裝 PostgreSQL,配置為本地部署,支援遠端訪問。
|
||||
|
||||
---
|
||||
|
||||
## 當前狀態
|
||||
|
||||
| 項目 | 狀態 |
|
||||
|------|------|
|
||||
| PostgreSQL | ✅ 已安裝 v18.1 |
|
||||
| 數據目錄 | /Users/accusys/momentry/var/postgresql |
|
||||
| 日誌目錄 | /Users/accusys/momentry/log/ |
|
||||
| Plist | /Library/LaunchDaemons/com.momentry.postgresql.plist |
|
||||
| Launchd 狀態 | ✅ 已註冊 |
|
||||
| RunAtLoad | ✅ 已設定 |
|
||||
| KeepAlive | ✅ 已設定 |
|
||||
|
||||
### 重要更新 (2026-03-24)
|
||||
|
||||
1. **資料目錄已變更**: 從 `/opt/homebrew/var/postgresql@18` 遷移到 `/Users/accusys/momentry/var/postgresql`
|
||||
2. **統一管理**: 所有 Momentry 服務現在都使用 `/Library/LaunchDaemons/` 下的自定義 plist
|
||||
3. **避免衝突**: 刪除了 homebrew plist,避免 reboot 後使用舊資料目錄
|
||||
|
||||
---
|
||||
|
||||
## 安裝步驟
|
||||
|
||||
### Step 1: 安裝 PostgreSQL (使用 brew)
|
||||
|
||||
```bash
|
||||
# 安裝 PostgreSQL
|
||||
brew install postgresql@18
|
||||
```
|
||||
|
||||
**驗證**:
|
||||
```bash
|
||||
postgres --version
|
||||
# postgres (PostgreSQL) 18.1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 建立目錄結構
|
||||
|
||||
```bash
|
||||
# 建立數據目錄
|
||||
mkdir -p /Users/accusys/momentry/var/postgresql
|
||||
|
||||
# 建立配置目錄
|
||||
mkdir -p /Users/accusys/momentry/etc/postgresql
|
||||
|
||||
# 建立日誌目錄
|
||||
mkdir -p /Users/accusys/momentry/log
|
||||
|
||||
# 建立日誌文件
|
||||
touch /Users/accusys/momentry/log/postgresql.log
|
||||
touch /Users/accusys/momentry/log/postgresql.error.log
|
||||
|
||||
# 設定權限
|
||||
chown -R accusys:staff /Users/accusys/momentry/var/postgresql
|
||||
chown -R accusys:staff /Users/accusys/momentry/etc/postgresql
|
||||
chown -R accusys:staff /Users/accusys/momentry/log
|
||||
```
|
||||
|
||||
**注意**: 如果需要從舊數據遷移,需要先初始化新目錄:
|
||||
```bash
|
||||
# 初始化新數據目錄 (會創建默認數據庫)
|
||||
initdb -D /Users/accusys/momentry/var/postgresql -U accusys
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 使用 plist 開機自動啟動
|
||||
|
||||
```bash
|
||||
# 複製 plist 到 LaunchDaemons 目錄
|
||||
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.postgresql.plist /Library/LaunchDaemons/
|
||||
|
||||
# 載入並啟動
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.postgresql.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 監控配置
|
||||
|
||||
### 添加到監控配置
|
||||
|
||||
在 `monitor/config/monitor_config.yaml` 中添加:
|
||||
|
||||
```yaml
|
||||
database:
|
||||
postgresql:
|
||||
enabled: true
|
||||
host: "localhost"
|
||||
port: 5432
|
||||
user: "accusys"
|
||||
database: "momentry"
|
||||
```
|
||||
|
||||
### 添加健康檢查函數
|
||||
|
||||
在 `monitor/database/postgres_monitor.sh` 中已包含 PostgreSQL 監控。
|
||||
|
||||
---
|
||||
|
||||
## 卸載步驟
|
||||
|
||||
### 重要: 路徑說明
|
||||
|
||||
| 路徑 | 類型 | 說明 |
|
||||
|------|------|------|
|
||||
| `/Users/accusys/momentry/var/postgresql/` | 數據 | **不要刪除** - 數據目錄 |
|
||||
| `/Users/accusys/momentry/etc/postgresql/` | 配置 | **不要刪除** - 配置目錄 |
|
||||
| `/Users/accusys/momentry/log/` | 日誌 | **不要刪除** - 日誌目錄 |
|
||||
| `/opt/homebrew/opt/postgresql@18/` | 安裝 | **刪除** - PostgreSQL 安裝目錄 |
|
||||
|
||||
### Step 1: 停止 PostgreSQL
|
||||
|
||||
```bash
|
||||
# 找到 PostgreSQL 進程
|
||||
ps aux | grep postgres | grep -v grep
|
||||
|
||||
# 停止 PostgreSQL
|
||||
pg_ctl -D /opt/homebrew/var/postgresql@18 stop
|
||||
# 或
|
||||
pkill -f postgresql
|
||||
|
||||
# 確認停止
|
||||
ps aux | grep postgres | grep -v grep || echo "PostgreSQL 已停止"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 卸載 PostgreSQL
|
||||
|
||||
```bash
|
||||
# 卸載 PostgreSQL
|
||||
brew uninstall postgresql@18
|
||||
|
||||
# 移除 plist
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.postgresql.plist
|
||||
sudo rm /Library/LaunchDaemons/com.momentry.postgresql.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 刪除專屬檔案
|
||||
|
||||
```bash
|
||||
# 刪除數據目錄 (可選)
|
||||
rm -rf /Users/accusys/momentry/var/postgresql
|
||||
|
||||
# 刪除日誌 (可選)
|
||||
rm -f /Users/accusys/momentry/log/postgresql.log
|
||||
rm -f /Users/accusys/momentry/log/postgresql.error.log
|
||||
```
|
||||
|
||||
**注意: 不要刪除以下共用目錄**:
|
||||
```bash
|
||||
# 這些是共用的,不要刪除!
|
||||
# /Users/accusys/momentry/var
|
||||
# /Users/accusys/momentry/log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 卸載後檢查清單
|
||||
|
||||
```bash
|
||||
echo "=== PostgreSQL 卸載後檢查 ==="
|
||||
|
||||
# 1. 檢查 PostgreSQL 進程
|
||||
echo "1. PostgreSQL 進程:"
|
||||
ps aux | grep postgres | grep -v grep && echo " ✗ 仍在運行" || echo " ✓ 已停止"
|
||||
|
||||
# 2. Port 5432
|
||||
echo "2. Port 5432:"
|
||||
lsof -i :5432 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
|
||||
|
||||
# 3. postgres 命令
|
||||
echo "3. postgres 命令:"
|
||||
which postgres > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 4. brew 安裝
|
||||
echo "4. brew 安裝:"
|
||||
brew list postgresql@18 > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 5. launchctl 服務
|
||||
echo "5. launchctl 服務:"
|
||||
sudo launchctl list | grep postgresql > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 6. 數據目錄 (可選刪除)
|
||||
echo "6. 數據目錄:"
|
||||
[ -d "/Users/accusys/momentry/var/postgresql" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
|
||||
|
||||
# 7. 日誌目錄 (可選刪除)
|
||||
echo "7. 日誌目錄:"
|
||||
[ -d "/Users/accusys/momentry/log" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
|
||||
```
|
||||
|
||||
**預期結果**:
|
||||
```
|
||||
=== PostgreSQL 卸載後檢查 ===
|
||||
1. PostgreSQL 進程:
|
||||
✓ 已停止
|
||||
2. Port 5432:
|
||||
✓ 已釋放
|
||||
3. postgres 命令:
|
||||
✓ 已移除
|
||||
4. brew 安裝:
|
||||
✓ 已移除
|
||||
5. launchctl 服務:
|
||||
✓ 已移除
|
||||
6. 數據目錄:
|
||||
✓ 保留 (或 ✗ 已刪除)
|
||||
7. 日誌目錄:
|
||||
✓ 保留 (或 ✗ 已刪除)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 手動檢查命令
|
||||
|
||||
```bash
|
||||
# 1. 檢查進程
|
||||
ps aux | grep postgres | grep -v grep
|
||||
|
||||
# 2. 檢查 Port
|
||||
lsof -i :5432
|
||||
|
||||
# 3. 測試連線
|
||||
psql -U accusys -l
|
||||
|
||||
# 4. 查看所有數據庫
|
||||
psql -U accusys -c "\l"
|
||||
|
||||
# 5. 查看連接
|
||||
psql -U accusys -c "\conninfo"
|
||||
|
||||
# 6. 查看表
|
||||
psql -U accusys -d momentry -c "\dt"
|
||||
|
||||
# 7. 查看日誌
|
||||
tail -20 /Users/accusys/momentry/log/postgresql.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 連線資訊
|
||||
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| Host | localhost |
|
||||
| Port | 5432 |
|
||||
| User | accusys |
|
||||
| Database | momentry, video_register, gitea, n8n |
|
||||
|
||||
---
|
||||
|
||||
## 環境變數
|
||||
|
||||
在 `.env` 中:
|
||||
|
||||
```env
|
||||
POSTGRES_URL=postgresql://accusys@localhost:5432
|
||||
POSTGRES_DB=momentry
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 遠端訪問
|
||||
|
||||
- PostgreSQL 綁定到所有網路介面 (0.0.0.0)
|
||||
- 本地網路其他機器可透過 IP 訪問
|
||||
- 請設定 `pg_hba.conf` 限制訪問 IP
|
||||
|
||||
---
|
||||
|
||||
## 故障排除
|
||||
|
||||
### PostgreSQL 無法啟動
|
||||
|
||||
```bash
|
||||
# 檢查日誌
|
||||
tail -f /Users/accusys/momentry/log/postgresql.log
|
||||
|
||||
# 檢查目錄權限
|
||||
ls -la /Users/accusys/momentry/var/postgresql/
|
||||
|
||||
# 重新設定權限
|
||||
chown -R $(whoami):staff /Users/accusys/momentry/var/postgresql
|
||||
```
|
||||
|
||||
### Port 被佔用
|
||||
|
||||
```bash
|
||||
# 檢查哪個程序佔用 port 5432
|
||||
lsof -i :5432
|
||||
|
||||
# 終止佔用程序
|
||||
kill <PID>
|
||||
```
|
||||
|
||||
### 需要重新載入 plist
|
||||
|
||||
```bash
|
||||
# 卸載舊服務 (如果存在)
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.postgresql.plist 2>/dev/null
|
||||
|
||||
# 載入新服務
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.postgresql.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 檔案位置
|
||||
|
||||
| 類型 | 路徑 | 說明 |
|
||||
|------|------|------|
|
||||
| 安裝 | `/opt/homebrew/opt/postgresql@18/` | PostgreSQL 安裝目錄 |
|
||||
| 執行檔 | `/opt/homebrew/opt/postgresql@18/bin/postgres` | PostgreSQL 執行檔 |
|
||||
| 數據目錄 | `/Users/accusys/momentry/var/postgresql/` | 數據儲存 |
|
||||
| 日誌 | `/Users/accusys/momentry/log/postgresql.log` | 執行日誌 |
|
||||
| 錯誤日誌 | `/Users/accusys/momentry/log/postgresql.error.log` | 錯誤日誌 |
|
||||
| plist | `/Library/LaunchDaemons/com.momentry.postgresql.plist` | 開機啟動 |
|
||||
| 備份 | `/Users/accusys/momentry/var/momentry_db_backup_latest.sql` | momentry 數據庫備份 |
|
||||
| 備份 | `/Users/accusys/momentry/var/video_register_db_backup_latest.sql` | video_register 數據庫備份 |
|
||||
|
||||
---
|
||||
|
||||
## 備份與恢復
|
||||
|
||||
### 備份 (pg_dump)
|
||||
|
||||
```bash
|
||||
# 備份 momentry 數據庫
|
||||
pg_dump -U accusys momentry > /Users/accusys/momentry/var/momentry_db_backup_latest.sql
|
||||
|
||||
# 備份 video_register 數據庫
|
||||
pg_dump -U accusys video_register > /Users/accusys/momentry/var/video_register_db_backup_latest.sql
|
||||
```
|
||||
|
||||
### 恢復 (psql)
|
||||
|
||||
```bash
|
||||
# 恢復 momentry 數據庫
|
||||
psql -U accusys -d momentry < /Users/accusys/momentry/var/momentry_db_backup_latest.sql
|
||||
|
||||
# 恢復 video_register 數據庫
|
||||
psql -U accusys -d video_register < /Users/accusys/momentry/var/video_register_db_backup_latest.sql
|
||||
```
|
||||
|
||||
### 數據目錄複製 (完整遷移)
|
||||
|
||||
```bash
|
||||
# 1. 停止 PostgreSQL
|
||||
pg_ctl -D /Users/accusys/momentry/var/postgresql stop
|
||||
|
||||
# 2. 複製數據目錄
|
||||
cp -r /opt/homebrew/var/postgresql@18/* /Users/accusys/momentry/var/postgresql/
|
||||
|
||||
# 3. 設定權限
|
||||
chown -R $(whoami):staff /Users/accusys/momentry/var/postgresql
|
||||
|
||||
# 4. 啟動 PostgreSQL
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.postgresql.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 版本資訊
|
||||
|
||||
- 版本: 18.1
|
||||
- Port: 5432
|
||||
- User: accusys
|
||||
- 數據目錄: /Users/accusys/momentry/var/postgresql/
|
||||
- 日誌目錄: /Users/accusys/momentry/log/
|
||||
@@ -1,472 +0,0 @@
|
||||
# Qdrant 安裝指南 (本地部署)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-16 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
本文檔說明如何在 macOS 上安裝 Qdrant Vector Database,配置為本地部署,支援遠端訪問。
|
||||
|
||||
---
|
||||
|
||||
## 當前狀態
|
||||
|
||||
| 項目 | 狀態 |
|
||||
|------|------|
|
||||
| Qdrant | ✅ 已安裝 v1.17.0 |
|
||||
| 數據目錄 | /Users/accusys/momentry/var/qdrant/ |
|
||||
| 日誌目錄 | /Users/accusys/momentry/log/ |
|
||||
| Plist | /Library/LaunchDaemons/com.momentry.qdrant.plist |
|
||||
|
||||
---
|
||||
|
||||
## 安裝步驟
|
||||
|
||||
### Step 1: 安裝 Qdrant (使用 cargo)
|
||||
|
||||
```bash
|
||||
# 安裝 Qdrant 從 GitHub
|
||||
cargo install --git https://github.com/qdrant/qdrant.git --locked
|
||||
```
|
||||
|
||||
**驗證**:
|
||||
```bash
|
||||
qdrant --version
|
||||
# qdrant 1.17.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 驗證 Qdrant 安裝
|
||||
|
||||
```bash
|
||||
# 驗證 Qdrant 安裝
|
||||
~/.cargo/bin/qdrant --version
|
||||
# qdrant 1.17.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 建立目錄結構
|
||||
|
||||
```bash
|
||||
# 建立數據目錄
|
||||
mkdir -p /Users/accusys/momentry/var/qdrant
|
||||
|
||||
# 建立配置目錄
|
||||
mkdir -p /Users/accusys/momentry/etc/qdrant
|
||||
|
||||
# 建立日誌目錄
|
||||
mkdir -p /Users/accusys/momentry/log
|
||||
|
||||
# 建立日誌文件
|
||||
touch /Users/accusys/momentry/log/qdrant.log
|
||||
touch /Users/accusys/momentry/log/qdrant.error.log
|
||||
|
||||
# 設定權限
|
||||
chown -R accusys:staff /Users/accusys/momentry/var/qdrant
|
||||
chown -R accusys:staff /Users/accusys/momentry/etc/qdrant
|
||||
chown -R accusys:staff /Users/accusys/momentry/log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 使用 plist 開機自動啟動
|
||||
|
||||
```bash
|
||||
# 複製 plist 到 LaunchDaemons 目錄
|
||||
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.qdrant.plist /Library/LaunchDaemons/
|
||||
|
||||
# 載入並啟動
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.qdrant.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 監控配置
|
||||
|
||||
### 添加到監控配置
|
||||
|
||||
在 `monitor/config/monitor_config.yaml` 中添加:
|
||||
|
||||
```yaml
|
||||
database:
|
||||
qdrant:
|
||||
enabled: true
|
||||
host: "localhost"
|
||||
port: 6333
|
||||
```
|
||||
|
||||
### 添加健康檢查函數
|
||||
|
||||
在 `monitor/database/qdrant_monitor.sh` 中已包含 Qdrant 監控。
|
||||
|
||||
---
|
||||
|
||||
## 卸載步驟
|
||||
|
||||
### 重要: 路徑說明
|
||||
|
||||
| 路徑 | 類型 | 說明 |
|
||||
|------|------|------|
|
||||
| `/Users/accusys/momentry/var/qdrant/` | 數據 | **不要刪除** - Qdrant 數據 |
|
||||
| `/Users/accusys/momentry/etc/qdrant/` | 配置 | **不要刪除** - Qdrant 配置 |
|
||||
| `/Users/accusys/momentry/log/` | 日誌 | **不要刪除** - 日誌目錄 |
|
||||
| `~/.cargo/bin/qdrant` | 安裝 | **刪除** - Qdrant 執行檔 |
|
||||
|
||||
### Step 1: 停止 Qdrant
|
||||
|
||||
```bash
|
||||
# 找到 Qdrant 進程
|
||||
ps aux | grep qdrant | grep -v grep
|
||||
|
||||
# 停止 Qdrant
|
||||
pkill qdrant
|
||||
# 或
|
||||
kill <PID>
|
||||
|
||||
# 確認停止
|
||||
ps aux | grep qdrant | grep -v grep || echo "Qdrant 已停止"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 卸載 Qdrant (cargo)
|
||||
|
||||
```bash
|
||||
# 移除 cargo 安裝的 Qdrant
|
||||
cargo uninstall qdrant
|
||||
|
||||
# 移除 plist
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.qdrant.plist
|
||||
sudo rm /Library/LaunchDaemons/com.momentry.qdrant.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 刪除專屬檔案
|
||||
|
||||
```bash
|
||||
# 刪除數據目錄 (可選)
|
||||
rm -rf /Users/accusys/momentry/var/qdrant
|
||||
|
||||
# 刪除日誌 (可選)
|
||||
rm -f /Users/accusys/momentry/log/qdrant.log
|
||||
rm -f /opt/homebrew/var/log/qdrant.error.log
|
||||
```
|
||||
|
||||
**注意: 不要刪除以下共用目錄**:
|
||||
```bash
|
||||
# 這些是共用的,不要刪除!
|
||||
# /Users/accusys/momentry/var
|
||||
# /Users/accusys/momentry/log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 卸載後檢查清單
|
||||
|
||||
```bash
|
||||
echo "=== Qdrant 卸載後檢查 ==="
|
||||
|
||||
# 1. 檢查 Qdrant 進程
|
||||
echo "1. Qdrant 進程:"
|
||||
ps aux | grep qdrant | grep -v grep && echo " ✗ 仍在運行" || echo " ✓ 已停止"
|
||||
|
||||
# 2. Port 6333
|
||||
echo "2. Port 6333:"
|
||||
lsof -i :6333 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
|
||||
|
||||
# 3. Port 6334
|
||||
echo "3. Port 6334:"
|
||||
lsof -i :6334 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
|
||||
|
||||
# 4. qdrant 命令
|
||||
echo "4. qdrant 命令:"
|
||||
which qdrant > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 5. cargo 安裝
|
||||
echo "5. cargo 安裝:"
|
||||
cargo install --list | grep qdrant > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 6. launchctl 服務
|
||||
echo "6. launchctl 服務:"
|
||||
sudo launchctl list | grep qdrant > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 7. 數據目錄 (可選刪除)
|
||||
echo "7. 數據目錄:"
|
||||
[ -d "/Users/accusys/momentry/var/qdrant" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
|
||||
|
||||
# 8. 日誌目錄 (可選刪除)
|
||||
echo "8. 日誌目錄:"
|
||||
[ -d "/Users/accusys/momentry/log" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
|
||||
```
|
||||
|
||||
**預期結果**:
|
||||
```
|
||||
=== Qdrant 卸載後檢查 ===
|
||||
1. Qdrant 進程:
|
||||
✓ 已停止
|
||||
2. Port 6333:
|
||||
✓ 已釋放
|
||||
3. Port 6334:
|
||||
✓ 已釋放
|
||||
4. qdrant 命令:
|
||||
✓ 已移除
|
||||
5. cargo 安裝:
|
||||
✓ 已移除
|
||||
6. launchctl 服務:
|
||||
✓ 已移除
|
||||
7. 數據目錄:
|
||||
✓ 保留 (或 ✗ 已刪除)
|
||||
8. 日誌目錄:
|
||||
✓ 保留 (或 ✗ 已刪除)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 手動檢查命令
|
||||
|
||||
```bash
|
||||
# 1. 檢查進程
|
||||
ps aux | grep qdrant | grep -v grep
|
||||
|
||||
# 2. 檢查 Port
|
||||
lsof -i :6333
|
||||
lsof -i :6334
|
||||
|
||||
# 3. 測試連線 (無認證)
|
||||
curl http://localhost:6333/collections
|
||||
|
||||
# 4. 測試連線 (有認證)
|
||||
curl -H "api-key: Test3200Test3200Test3200" http://localhost:6333/collections
|
||||
|
||||
# 5. 查看所有 collections
|
||||
curl -s -H "api-key: Test3200Test3200Test3200" http://localhost:6333/collections
|
||||
|
||||
# 6. 查看日誌
|
||||
tail -20 /Users/accusys/momentry/log/qdrant.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 連線資訊
|
||||
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| REST API | http://localhost:6333 |
|
||||
| gRPC | localhost:6334 |
|
||||
| API Key | Test3200Test3200Test3200 |
|
||||
| Qdrant UI | http://localhost:6333/dashboard |
|
||||
|
||||
---
|
||||
|
||||
## 環境變數
|
||||
|
||||
在 `.env` 中:
|
||||
|
||||
```env
|
||||
QDRANT_URL=http://localhost:6333
|
||||
QDRANT_API_KEY=Test3200Test3200Test3200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 遠端訪問
|
||||
|
||||
- Qdrant 綁定到 `0.0.0.0` (所有網路介面)
|
||||
- 本地網路其他機器可透過 IP 訪問
|
||||
- 建議設定防火牆規則限制訪問 IP
|
||||
|
||||
---
|
||||
|
||||
## 故障排除
|
||||
|
||||
### Qdrant 無法啟動
|
||||
|
||||
```bash
|
||||
# 檢查日誌
|
||||
tail -f /Users/accusys/momentry/log/qdrant.log
|
||||
|
||||
# 檢查目錄權限
|
||||
ls -la /Users/accusys/momentry/var/qdrant/
|
||||
|
||||
# 重新設定權限
|
||||
chown -R $(whoami):staff /Users/accusys/momentry/var/qdrant
|
||||
```
|
||||
|
||||
### Port 被佔用
|
||||
|
||||
```bash
|
||||
# 檢查哪個程序佔用 port 6333
|
||||
lsof -i :6333
|
||||
|
||||
# 終止佔用程序
|
||||
kill <PID>
|
||||
```
|
||||
|
||||
### 需要重新載入 plist
|
||||
|
||||
```bash
|
||||
# 卸載舊服務 (如果存在)
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.qdrant.plist 2>/dev/null
|
||||
|
||||
# 載入新服務
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.qdrant.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 檔案位置
|
||||
|
||||
| 類型 | 路徑 | 說明 |
|
||||
|------|------|------|
|
||||
| `/Users/accusys/.cargo/bin/qdrant` | 二進制 | cargo 安裝位置 (直接使用) |
|
||||
| `/opt/homebrew/bin/qdrant` | 符號連結 | ~~已棄用~~ - 不再需要 |
|
||||
| 數據目錄 | `/Users/accusys/momentry/var/qdrant/` | 數據儲存 |
|
||||
| 日誌 | `/Users/accusys/momentry/log/qdrant.log` | 執行日誌 |
|
||||
| 錯誤日誌 | `/opt/homebrew/var/log/qdrant.error.log` | 錯誤日誌 |
|
||||
| plist | `/Library/LaunchDaemons/com.momentry.qdrant.plist` | 開機啟動 |
|
||||
| 備份 | `/Users/accusys/momentry/var/qdrant_backup/` | 數據備份 |
|
||||
|
||||
---
|
||||
|
||||
## 備份與恢復
|
||||
|
||||
### 備份
|
||||
|
||||
Qdrant 提供兩種備份方式:Snapshots (推薦) 和手動複製。
|
||||
|
||||
#### 方式一:使用 Snapshots API (推薦)
|
||||
|
||||
```bash
|
||||
# 創建備份目錄
|
||||
mkdir -p /Users/accusys/momentry/var/qdrant_backup
|
||||
|
||||
# 獲取所有 collections
|
||||
curl -s -H "api-key: Test3200Test3200Test3200" \
|
||||
http://localhost:6333/collections | jq -r '.result[].name'
|
||||
|
||||
# 為每個 collection 創建 snapshot
|
||||
# 假設 collection 名稱為 "chunks"
|
||||
curl -X POST -H "api-key: Test3200Test3200Test3200" \
|
||||
http://localhost:6333/collections/chunks/snapshots \
|
||||
-o /Users/accusys/momentry/var/qdrant_backup/chunks_snapshot_$(date +%Y%m%d).tar.gz
|
||||
```
|
||||
|
||||
#### 方式二:手動複製數據目錄 (停機備份)
|
||||
|
||||
```bash
|
||||
# 停止 Qdrant
|
||||
pkill qdrant
|
||||
|
||||
# 等待停止
|
||||
sleep 2
|
||||
|
||||
# 複製數據目錄
|
||||
TIMESTAMP=$(date +%Y%m%d)
|
||||
mkdir -p /Users/accusys/momentry/var/qdrant_backup
|
||||
tar -czf /Users/accusys/momentry/var/qdrant_backup/qdrant_data_${TIMESTAMP}.tar.gz \
|
||||
-C /Users/accusys/momentry/var qdrant/
|
||||
|
||||
# 啟動 Qdrant
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.qdrant.plist
|
||||
```
|
||||
|
||||
#### 自動備份腳本
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# backup_qdrant.sh
|
||||
set -e
|
||||
|
||||
QDRANT_HOST="localhost"
|
||||
QDRANT_PORT="6333"
|
||||
QDRANT_API_KEY="Test3200Test3200Test3200"
|
||||
BACKUP_DIR="/Users/accusys/momentry/var/qdrant_backup"
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
echo "開始 Qdrant 備份..."
|
||||
|
||||
# 獲取所有 collections
|
||||
COLLECTIONS=$(curl -s -H "api-key: $QDRANT_API_KEY" \
|
||||
http://${QDRANT_HOST}:${QDRANT_PORT}/collections | \
|
||||
jq -r '.result[].name')
|
||||
|
||||
if [ -z "$COLLECTIONS" ]; then
|
||||
echo "警告: 沒有找到任何 collections"
|
||||
else
|
||||
for COLLECTION in $COLLECTIONS; do
|
||||
echo "備份 collection: $COLLECTION"
|
||||
curl -X POST -H "api-key: $QDRANT_API_KEY" \
|
||||
"http://${QDRANT_HOST}:${QDRANT_PORT}/collections/${COLLECTION}/snapshots" \
|
||||
-o "${BACKUP_DIR}/${COLLECTION}_${TIMESTAMP}.tar.gz" 2>/dev/null || \
|
||||
echo "警告: $COLLECTION 備份失敗"
|
||||
done
|
||||
fi
|
||||
|
||||
# 壓縮所有 snapshot
|
||||
cd "$BACKUP_DIR"
|
||||
tar -czf qdrant_snapshots_${TIMESTAMP}.tar.gz *.tar.gz 2>/dev/null || true
|
||||
rm -f *.tar.gz
|
||||
|
||||
# 清理 30 天前的備份
|
||||
find "$BACKUP_DIR" -name "qdrant_snapshots_*.tar.gz" -mtime +30 -delete
|
||||
|
||||
echo "Qdrant 備份完成: ${BACKUP_DIR}/qdrant_snapshots_${TIMESTAMP}.tar.gz"
|
||||
```
|
||||
|
||||
### 恢復
|
||||
|
||||
```bash
|
||||
# 停止 Qdrant
|
||||
pkill qdrant
|
||||
sleep 2
|
||||
|
||||
# 解壓縮備份
|
||||
TIMESTAMP="20260315"
|
||||
tar -xzf /Users/accusys/momentry/var/qdrant_backup/qdrant_snapshots_${TIMESTAMP}.tar.gz \
|
||||
-C /Users/accusys/momentry/var/qdrant_backup/
|
||||
|
||||
# 恢復數據目錄 (方式二備份)
|
||||
# rm -rf /Users/accusys/momentry/var/qdrant/*
|
||||
# tar -xzf /Users/accusys/momentry/var/qdrant_backup/qdrant_data_${TIMESTAMP}.tar.gz \
|
||||
# -C /Users/accusys/momentry/var/
|
||||
|
||||
# 啟動 Qdrant
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.qdrant.plist
|
||||
```
|
||||
|
||||
### 排程備份
|
||||
|
||||
```bash
|
||||
# 編輯 crontab
|
||||
crontab -e
|
||||
|
||||
# 添加每天凌晨 3 點執行備份
|
||||
0 3 * * * /Users/accusys/momentry/scripts/backup_qdrant.sh >> /Users/accusys/momentry/log/backup.log 2>&1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 版本資訊
|
||||
|
||||
- 版本: 1.17.0
|
||||
- API Key: Test3200Test3200Test3200
|
||||
- 數據目錄: /Users/accusys/momentry/var/qdrant/
|
||||
- 日誌目錄: /Users/accusys/momentry/log/
|
||||
@@ -1,481 +0,0 @@
|
||||
# Redis 安裝指南 (本地部署)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-15 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-15 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
| V1.1 | 2026-03-21 | 更新 rust redis crate 版本至 0.32.7 | OpenCode | - |
|
||||
| V1.2 | 2026-03-21 | 添加 Redis 用戶配置說明 | OpenCode | - |
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
本文檔說明如何在 macOS 上安裝 Redis,配置為本地部署,支援遠端訪問。
|
||||
|
||||
---
|
||||
|
||||
## 當前狀態
|
||||
|
||||
| 項目 | 狀態 |
|
||||
|------|------|
|
||||
| Redis | ✅ 已安裝 v8.4.0 |
|
||||
| 數據目錄 | /opt/homebrew/var/db/redis/ |
|
||||
| 日誌目錄 | /Users/accusys/momentry/log/ |
|
||||
| Plist | /Library/LaunchDaemons/com.momentry.redis.plist |
|
||||
|
||||
---
|
||||
|
||||
## 安裝步驟
|
||||
|
||||
### Step 1: 安裝 Redis (使用 brew)
|
||||
|
||||
```bash
|
||||
# 安裝 Redis
|
||||
brew install redis
|
||||
```
|
||||
|
||||
**驗證**:
|
||||
```bash
|
||||
redis-server --version
|
||||
# Redis server v8.4.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 建立目錄結構
|
||||
|
||||
```bash
|
||||
# 建立數據目錄
|
||||
mkdir -p /Users/accusys/momentry/var/redis
|
||||
|
||||
# 建立配置目錄
|
||||
mkdir -p /Users/accusys/momentry/etc/redis
|
||||
|
||||
# 建立日誌目錄
|
||||
mkdir -p /Users/accusys/momentry/log
|
||||
|
||||
# 建立日誌文件
|
||||
touch /Users/accusys/momentry/log/redis.log
|
||||
touch /Users/accusys/momentry/log/redis.error.log
|
||||
|
||||
# 設定權限
|
||||
chown -R accusys:staff /Users/accusys/momentry/var/redis
|
||||
chown -R accusys:staff /Users/accusys/momentry/etc/redis
|
||||
chown -R accusys:staff /Users/accusys/momentry/log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 使用 plist 開機自動啟動
|
||||
|
||||
```bash
|
||||
# 複製 plist 到 LaunchDaemons 目錄
|
||||
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.redis.plist /Library/LaunchDaemons/
|
||||
|
||||
# 載入並啟動
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.redis.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 卸載步驟
|
||||
|
||||
### 重要: 路徑說明
|
||||
|
||||
| 路徑 | 類型 | 說明 |
|
||||
|------|------|------|
|
||||
| `/Users/accusys/momentry/var/redis/` | 數據 | **不要刪除** - 數據目錄 |
|
||||
| `/Users/accusys/momentry/etc/redis/` | 配置 | **不要刪除** - 配置目錄 |
|
||||
| `/Users/accusys/momentry/log/` | 日誌 | **不要刪除** - 日誌目錄 |
|
||||
| `/opt/homebrew/opt/redis/` | 安裝 | **刪除** - Redis 安裝目錄 |
|
||||
|
||||
### Step 1: 停止 Redis
|
||||
|
||||
```bash
|
||||
# 找到 Redis 進程
|
||||
ps aux | grep redis | grep -v grep
|
||||
|
||||
# 停止 Redis
|
||||
redis-cli -a accusys SHUTDOWN
|
||||
# 或
|
||||
pkill redis-server
|
||||
|
||||
# 確認停止
|
||||
ps aux | grep redis | grep -v grep || echo "Redis 已停止"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 卸載 Redis
|
||||
|
||||
```bash
|
||||
# 卸載 Redis
|
||||
brew uninstall redis
|
||||
|
||||
# 移除 plist
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.redis.plist
|
||||
sudo rm /Library/LaunchDaemons/com.momentry.redis.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 刪除專屬檔案
|
||||
|
||||
```bash
|
||||
# 刪除數據目錄 (可選)
|
||||
rm -rf /Users/accusys/momentry/var/redis
|
||||
|
||||
# 刪除配置目錄 (可選)
|
||||
rm -rf /Users/accusys/momentry/etc/redis
|
||||
|
||||
# 刪除日誌 (可選)
|
||||
rm -f /Users/accusys/momentry/log/redis.log
|
||||
rm -f /Users/accusys/momentry/log/redis.error.log
|
||||
```
|
||||
|
||||
**注意: 不要刪除以下共用目錄**:
|
||||
```bash
|
||||
# 這些是共用的,不要刪除!
|
||||
# /Users/accusys/momentry/var
|
||||
# /Users/accusys/momentry/etc
|
||||
# /Users/accusys/momentry/log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 卸載後檢查清單
|
||||
|
||||
```bash
|
||||
echo "=== Redis 卸載後檢查 ==="
|
||||
|
||||
# 1. 檢查 Redis 進程
|
||||
echo "1. Redis 進程:"
|
||||
ps aux | grep redis | grep -v grep && echo " ✗ 仍在運行" || echo " ✓ 已停止"
|
||||
|
||||
# 2. Port 6379
|
||||
echo "2. Port 6379:"
|
||||
lsof -i :6379 > /dev/null 2>&1 && echo " ✗ 仍被佔用" || echo " ✓ 已釋放"
|
||||
|
||||
# 3. redis-server 命令
|
||||
echo "3. redis-server 命令:"
|
||||
which redis-server > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 4. brew 安裝
|
||||
echo "4. brew 安裝:"
|
||||
brew list redis > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 5. launchctl 服務
|
||||
echo "5. launchctl 服務:"
|
||||
sudo launchctl list | grep redis > /dev/null 2>&1 && echo " ✗ 仍存在" || echo " ✓ 已移除"
|
||||
|
||||
# 6. 數據目錄 (可選刪除)
|
||||
echo "6. 數據目錄:"
|
||||
[ -d "/Users/accusys/momentry/var/redis" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
|
||||
|
||||
# 7. 日誌目錄 (可選刪除)
|
||||
echo "7. 日誌目錄:"
|
||||
[ -d "/Users/accusys/momentry/log" ] && echo " ✓ 保留" || echo " ✗ 已刪除"
|
||||
```
|
||||
|
||||
**預期結果**:
|
||||
```
|
||||
=== Redis 卸載後檢查 ===
|
||||
1. Redis 進程:
|
||||
✓ 已停止
|
||||
2. Port 6379:
|
||||
✓ 已釋放
|
||||
3. redis-server 命令:
|
||||
✓ 已移除
|
||||
4. brew 安裝:
|
||||
✓ 已移除
|
||||
5. launchctl 服務:
|
||||
✓ 已移除
|
||||
6. 數據目錄:
|
||||
✓ 保留 (或 ✗ 已刪除)
|
||||
7. 日誌目錄:
|
||||
✓ 保留 (或 ✗ 已刪除)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 監控配置
|
||||
|
||||
### 添加到監控配置
|
||||
|
||||
在 `monitor/config/monitor_config.yaml` 中添加:
|
||||
|
||||
```yaml
|
||||
service:
|
||||
services:
|
||||
- name: "redis"
|
||||
type: "tcp"
|
||||
port: 6379
|
||||
host: "localhost"
|
||||
timeout: 5
|
||||
enabled: true
|
||||
```
|
||||
|
||||
### 添加健康檢查函數
|
||||
|
||||
在 `monitor/service/health_check.sh` 中添加:
|
||||
|
||||
```bash
|
||||
check_redis() {
|
||||
local start=$(date +%s%N)
|
||||
if redis-cli -a accusys ping > /dev/null 2>&1; then
|
||||
local end=$(date +%s%N)
|
||||
local ms=$(( (end - start) / 1000000 ))
|
||||
echo -e "${GREEN}✓${NC} Redis (6379) - ${ms}ms"
|
||||
record_service "redis" "up" "$ms" ""
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}✗${NC} Redis (6379) - Down"
|
||||
record_service "redis" "down" "0" "Connection failed"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
# 1. 檢查進程
|
||||
ps aux | grep redis | grep -v grep
|
||||
|
||||
# 2. 檢查 Port
|
||||
lsof -i :6379
|
||||
|
||||
# 3. 測試連線 (無認證)
|
||||
redis-cli -a accusys PING
|
||||
|
||||
# 4. 測試連線 (有認證)
|
||||
redis-cli -a accusys -e "PING"
|
||||
|
||||
# 5. 查看所有 keys
|
||||
redis-cli -a accusys KEYS '*'
|
||||
|
||||
# 6. 查看 info
|
||||
redis-cli -a accusys INFO
|
||||
|
||||
# 7. 查看日誌
|
||||
tail -20 /Users/accusys/momentry/log/redis.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 連線資訊
|
||||
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| Host | localhost |
|
||||
| Port | 6379 |
|
||||
| Password | accusys |
|
||||
|
||||
---
|
||||
|
||||
## 環境變數
|
||||
|
||||
在 `.env` 中:
|
||||
|
||||
```env
|
||||
REDIS_URL=redis://:accusys@localhost:6379
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 遠端訪問
|
||||
|
||||
- Redis 綁定到 `0.0.0.0` (所有網路介面)
|
||||
- 本地網路其他機器可透過 IP 訪問
|
||||
- 密碼認證: `accusys`
|
||||
|
||||
---
|
||||
|
||||
## 故障排除
|
||||
|
||||
### Redis 無法啟動
|
||||
|
||||
```bash
|
||||
# 檢查日誌
|
||||
tail -f /Users/accusys/momentry/log/redis.log
|
||||
|
||||
# 檢查目錄權限
|
||||
ls -la /Users/accusys/momentry/var/redis/
|
||||
|
||||
# 重新設定權限
|
||||
chown -R $(whoami):staff /Users/accusys/momentry/var/redis
|
||||
```
|
||||
|
||||
### Port 被佔用
|
||||
|
||||
```bash
|
||||
# 檢查哪個程序佔用 port 6379
|
||||
lsof -i :6379
|
||||
|
||||
# 終止佔用程序
|
||||
kill <PID>
|
||||
```
|
||||
|
||||
### 需要重新載入 plist
|
||||
|
||||
```bash
|
||||
# 卸載舊服務 (如果存在)
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.redis.plist 2>/dev/null
|
||||
|
||||
# 載入新服務
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.redis.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 檔案位置
|
||||
|
||||
| 類型 | 路徑 | 說明 |
|
||||
|------|------|------|
|
||||
| 安裝 | `/opt/homebrew/opt/redis/` | Redis 安裝目錄 |
|
||||
| 執行檔 | `/opt/homebrew/opt/redis/bin/redis-server` | Redis 執行檔 |
|
||||
| 數據目錄 | `/Users/accusys/momentry/var/redis/` | 數據儲存 |
|
||||
| 配置目錄 | `/Users/accusys/momentry/etc/redis/` | 配置儲存 |
|
||||
| 日誌 | `/Users/accusys/momentry/log/redis.log` | 執行日誌 |
|
||||
| 錯誤日誌 | `/Users/accusys/momentry/log/redis.error.log` | 錯誤日誌 |
|
||||
| plist | `/Library/LaunchDaemons/com.momentry.redis.plist` | 開機啟動 |
|
||||
| 備份 | `/Users/accusys/momentry/var/redis_backup_latest.rdb` | 數據備份 |
|
||||
|
||||
---
|
||||
|
||||
## 備份與恢復
|
||||
|
||||
### 備份
|
||||
|
||||
```bash
|
||||
# 觸發保存並備份
|
||||
redis-cli -a accusys SAVE
|
||||
cp /opt/homebrew/var/db/redis/dump.rdb /Users/accusys/momentry/var/redis_backup_latest.rdb
|
||||
```
|
||||
|
||||
### 恢復
|
||||
|
||||
```bash
|
||||
# 停止 Redis
|
||||
redis-cli -a accusys SHUTDOWN
|
||||
|
||||
# 複製備份文件覆蓋
|
||||
cp /Users/accusys/momentry/var/redis_backup_latest.rdb /Users/accusys/momentry/var/redis/dump.rdb
|
||||
|
||||
# 啟動 Redis
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.redis.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 版本資訊
|
||||
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| Redis Server | 8.4.0 |
|
||||
| Rust redis crate | 0.32.7 |
|
||||
| Port | 6379 |
|
||||
| Password | accusys |
|
||||
| 數據目錄 | /Users/accusys/momentry/var/redis/ |
|
||||
| 日誌目錄 | /Users/accusys/momentry/log/ |
|
||||
|
||||
---
|
||||
|
||||
## Rust redis crate 版本
|
||||
|
||||
Cargo.toml 中的 redis 依賴:
|
||||
|
||||
```toml
|
||||
redis = { version = "0.32", features = ["tokio-comp"] }
|
||||
```
|
||||
|
||||
### 版本歷史
|
||||
|
||||
| 版本 | 日期 | 變更 |
|
||||
|------|------|-------|
|
||||
| 0.25.4 | - | 原始版本(有未來相容性警告) |
|
||||
| 0.32.7 | 2026-03-21 | **升級** - 修復 Rust 2024 never type 回退問題 |
|
||||
|
||||
### 升級說明
|
||||
|
||||
升級到 0.32.x 的優點:
|
||||
- 修復 Rust 2024 edition 未來相容性問題
|
||||
- API 完全向後相容
|
||||
- 無需修改現有程式碼
|
||||
|
||||
---
|
||||
|
||||
## Redis 用戶配置說明
|
||||
|
||||
### 當前狀態
|
||||
|
||||
| 項目 | 狀態 |
|
||||
|------|------|
|
||||
| 用戶類型 | 僅有 `default` 用戶 |
|
||||
| 自訂用戶 | ❌ 未配置 |
|
||||
| ACL 持久化 | ❌ 未配置 |
|
||||
|
||||
### Redis ACL 狀態
|
||||
|
||||
```bash
|
||||
# 查看 ACL
|
||||
redis-cli -a accusys ACL LIST
|
||||
|
||||
# 輸出:
|
||||
# user default on sanitize-payload #hash ~* &* +@all
|
||||
```
|
||||
|
||||
### 連線格式說明
|
||||
|
||||
| 格式 | 狀態 | 說明 |
|
||||
|------|------|------|
|
||||
| `redis://:accusys@localhost:6379` | ✅ 正確 | 使用默認用戶 + 密碼 |
|
||||
| `redis://accusys:accusys@localhost:6379` | ❌ 失敗 | 用戶 `accusys` 不存在 |
|
||||
|
||||
### 為何用戶名不可用
|
||||
|
||||
1. **Redis 啟動方式**:使用 `--requirepass` 參數,僅設定默認用戶密碼
|
||||
2. **無 ACL 配置文件**:未指定 `--aclfile` 參數
|
||||
3. **動態建立用戶**:手動建立的用戶不會持久化(重啟後消失)
|
||||
|
||||
### 解決方案
|
||||
|
||||
#### 方案 A:使用默認用戶(現行)
|
||||
|
||||
```env
|
||||
REDIS_URL=redis://:accusys@localhost:6379
|
||||
```
|
||||
|
||||
**適用於**:單一應用、簡單部署
|
||||
|
||||
#### 方案 B:建立 ACL 配置文件
|
||||
|
||||
```bash
|
||||
# 1. 建立 ACL 文件
|
||||
cat > /Users/accusys/momentry/etc/redis/users.acl << 'EOF'
|
||||
user default on sanitize-payload ~* &* +@all >accusys
|
||||
user accusys on sanitize-payload ~* &* +@all >accusys
|
||||
EOF
|
||||
|
||||
# 2. 修改 plist (添加 --aclfile 參數)
|
||||
# --aclfile /Users/accusys/momentry/etc/redis/users.acl
|
||||
|
||||
# 3. 重啟 Redis
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.redis.plist
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.redis.plist
|
||||
```
|
||||
|
||||
**適用於**:多應用、需要用戶隔離
|
||||
|
||||
### 參考
|
||||
|
||||
- 問題追蹤:`docs/PENDING_ISSUES.md` 問題 #5
|
||||
- 測試結果:2026-03-21 Redis 認證測試
|
||||
@@ -1,300 +0,0 @@
|
||||
# RustDesk 安裝指南 (本地部署)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-15 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-15 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
本文檔說明如何在 macOS 上安裝 RustDesk 遠端桌面服務,配置為本地部署。
|
||||
|
||||
---
|
||||
|
||||
## 當前狀態
|
||||
|
||||
| 項目 | 狀態 |
|
||||
|------|------|
|
||||
| RustDesk | ✅ 已安裝 |
|
||||
| 數據目錄 | /Users/accusys/momentry/var/rustdesk/ |
|
||||
| 日誌目錄 | /Users/accusys/momentry/log/ |
|
||||
| HBBS Plist | /Library/LaunchDaemons/com.momentry.rustdesk.hbbs.plist |
|
||||
| HBBR Plist | /Library/LaunchDaemons/com.momentry.rustdesk.hbbr.plist |
|
||||
|
||||
---
|
||||
|
||||
## 服務端口
|
||||
|
||||
| 服務 | Port | 協議 |
|
||||
|------|------|------|
|
||||
| hbbs (TCP) | 21115 | 主端口 |
|
||||
| hbbs (TCP/UDP) | 21116 | NAT 測試 |
|
||||
| hbbs (WebSocket) | 21118 | WebSocket |
|
||||
| hbbr (TCP) | 21117 | 中繼端口 |
|
||||
| hbbr (TCP) | 21119 | 中繼 extra |
|
||||
|
||||
---
|
||||
|
||||
## 安裝步驟
|
||||
|
||||
### Step 1: 安裝 RustDesk (使用 brew)
|
||||
|
||||
```bash
|
||||
# 安裝 RustDesk
|
||||
brew install rustdesk
|
||||
```
|
||||
|
||||
**驗證**:
|
||||
```bash
|
||||
hbbs --version
|
||||
hbbr --version
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 建立目錄
|
||||
|
||||
```bash
|
||||
# 建立數據目錄
|
||||
mkdir -p /Users/accusys/momentry/var/rustdesk
|
||||
|
||||
# 建立配置目錄
|
||||
mkdir -p /Users/accusys/momentry/etc/rustdesk
|
||||
|
||||
# 建立日誌目錄
|
||||
mkdir -p /Users/accusys/momentry/log
|
||||
|
||||
# 建立日誌文件
|
||||
touch /Users/accusys/momentry/log/rustdesk.hbbs.log
|
||||
touch /Users/accusys/momentry/log/rustdesk.hbbs.error.log
|
||||
touch /Users/accusys/momentry/log/rustdesk.hbbr.log
|
||||
touch /Users/accusys/momentry/log/rustdesk.hbbr.error.log
|
||||
|
||||
# 設定權限
|
||||
chown -R accusys:staff /Users/accusys/momentry/var/rustdesk
|
||||
chown -R accusys:staff /Users/accusys/momentry/etc/rustdesk
|
||||
chown -R accusys:staff /Users/accusys/momentry/log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 使用 plist 開機自動啟動
|
||||
|
||||
```bash
|
||||
# 複製 plist 到 LaunchDaemons 目錄
|
||||
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.rustdesk.hbbs.plist /Library/LaunchDaemons/
|
||||
sudo cp /Users/accusys/momentry_core_0.1/momentry_runtime/plist/com.momentry.rustdesk.hbbr.plist /Library/LaunchDaemons/
|
||||
|
||||
# 移除舊 plist (如果存在)
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.rustdesk.hbbs.plist 2>/dev/null
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.rustdesk.hbbr.plist 2>/dev/null
|
||||
sudo rm /Library/LaunchDaemons/com.rustdesk.hbbs.plist 2>/dev/null
|
||||
sudo rm /Library/LaunchDaemons/com.rustdesk.hbbr.plist 2>/dev/null
|
||||
|
||||
# 載入並啟動
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.rustdesk.hbbs.plist
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.rustdesk.hbbr.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 監控配置
|
||||
|
||||
### 添加到監控配置
|
||||
|
||||
在 `monitor/config/monitor_config.yaml` 中添加:
|
||||
|
||||
```yaml
|
||||
service:
|
||||
services:
|
||||
- name: "rustdesk-hbbs"
|
||||
type: "tcp"
|
||||
port: 21115
|
||||
host: "localhost"
|
||||
timeout: 5
|
||||
enabled: true
|
||||
- name: "rustdesk-hbbr"
|
||||
type: "tcp"
|
||||
port: 21117
|
||||
host: "localhost"
|
||||
timeout: 5
|
||||
enabled: true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 卸載步驟
|
||||
|
||||
### 重要: 路徑說明
|
||||
|
||||
| 路徑 | 類型 | 說明 |
|
||||
|------|------|------|
|
||||
| `/Users/accusys/momentry/var/rustdesk/` | 數據 | **不要刪除** - RustDesk 數據 |
|
||||
| `/Users/accusys/momentry/log/` | 日誌 | **不要刪除** - 日誌目錄 |
|
||||
| `/opt/homebrew/bin/hbbr` | 安裝 | **刪除** - RustDesk 安裝 |
|
||||
| `/opt/homebrew/bin/hbbs` | 安裝 | **刪除** - RustDesk 安裝 |
|
||||
|
||||
### Step 1: 停止 RustDesk
|
||||
|
||||
```bash
|
||||
# 停止 RustDesk 服務
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.rustdesk.hbbs.plist
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.rustdesk.hbbr.plist
|
||||
|
||||
# 確認停止
|
||||
ps aux | grep rustdesk | grep -v grep || echo "RustDesk 已停止"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 卸載 RustDesk
|
||||
|
||||
```bash
|
||||
# 卸載 RustDesk
|
||||
brew uninstall rustdesk
|
||||
|
||||
# 移除 plist
|
||||
sudo rm /Library/LaunchDaemons/com.momentry.rustdesk.hbbs.plist
|
||||
sudo rm /Library/LaunchDaemons/com.momentry.rustdesk.hbbr.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 刪除專屬檔案
|
||||
|
||||
```bash
|
||||
# 刪除數據目錄 (可選)
|
||||
rm -rf /Users/accusys/momentry/var/rustdesk
|
||||
|
||||
# 刪除日誌 (可選)
|
||||
rm -f /Users/accusys/momentry/log/rustdesk-*.log
|
||||
```
|
||||
|
||||
**注意: 不要刪除以下共用目錄**:
|
||||
```bash
|
||||
# 這些是共用的,不要刪除!
|
||||
# /Users/accusys/momentry/var
|
||||
# /Users/accusys/momentry/log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 手動檢查命令
|
||||
|
||||
```bash
|
||||
# 1. 檢查進程
|
||||
ps aux | grep rustdesk | grep -v grep
|
||||
|
||||
# 2. 檢查 Port
|
||||
lsof -i :21115
|
||||
lsof -i :21116
|
||||
lsof -i :21117
|
||||
lsof -i :21118
|
||||
lsof -i :21119
|
||||
|
||||
# 3. 測試連線
|
||||
nc -zv localhost 21115
|
||||
nc -zv localhost 21116
|
||||
|
||||
# 4. 查看日誌
|
||||
tail -20 /Users/accusys/momentry/log/rustdesk-hbbs.log
|
||||
tail -20 /Users/accusys/momentry/log/rustdesk-hbbr.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 連線資訊
|
||||
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| Server ID | 59.124.167.225 |
|
||||
| NAT Test Port | 21116 |
|
||||
| Relay Port | 21117, 21119 |
|
||||
|
||||
---
|
||||
|
||||
## 故障排除
|
||||
|
||||
### RustDesk 無法啟動
|
||||
|
||||
```bash
|
||||
# 檢查日誌
|
||||
tail -f /Users/accusys/momentry/log/rustdesk-hbbs.log
|
||||
tail -f /Users/accusys/momentry/log/rustdesk-hbbr.log
|
||||
|
||||
# 檢查數據目錄權限
|
||||
ls -la /Users/accusys/momentry/var/rustdesk/
|
||||
|
||||
# 重新設定權限
|
||||
chown -R $(whoami):staff /Users/accusys/momentry/var/rustdesk
|
||||
```
|
||||
|
||||
### Port 被佔用
|
||||
|
||||
```bash
|
||||
# 檢查哪個程序佔用 port
|
||||
lsof -i :21116
|
||||
|
||||
# 終止佔用程序
|
||||
kill <PID>
|
||||
```
|
||||
|
||||
### 需要重新載入 plist
|
||||
|
||||
```bash
|
||||
# 卸載舊服務
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.rustdesk.hbbs.plist
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.rustdesk.hbbr.plist
|
||||
|
||||
# 載入新服務
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.rustdesk.hbbs.plist
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.rustdesk.hbbr.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 檔案位置
|
||||
|
||||
| 類型 | 路徑 | 說明 |
|
||||
|------|------|------|
|
||||
| 安裝 | `/opt/homebrew/bin/hbbs` | RustDesk Server 執行檔 |
|
||||
| 安裝 | `/opt/homebrew/bin/hbbr` | RustDesk Relay 執行檔 |
|
||||
| 數據目錄 | `/Users/accusys/momentry/var/rustdesk/` | 數據儲存 |
|
||||
| HBBS 日誌 | `/Users/accusys/momentry/log/rustdesk-hbbs.log` | 服務日誌 |
|
||||
| HBBR 日誌 | `/Users/accusys/momentry/log/rustdesk-hbbr.log` | 中繼日誌 |
|
||||
| HBBS Plist | `/Library/LaunchDaemons/com.momentry.rustdesk.hbbs.plist` | 開機啟動 |
|
||||
| HBBR Plist | `/Library/LaunchDaemons/com.momentry.rustdesk.hbbr.plist` | 開機啟動 |
|
||||
|
||||
---
|
||||
|
||||
## 版本資訊
|
||||
|
||||
- 安裝方式: Homebrew (Cask)
|
||||
- Client 版本: 1.4.6
|
||||
- Server 版本: 1.1.15 (hbbs/hbbr binaries from homebrew)
|
||||
- 數據目錄: /Users/accusys/momentry/var/rustdesk/
|
||||
- 日誌目錄: /Users/accusys/momentry/log/
|
||||
|
||||
---
|
||||
|
||||
## 注意事項
|
||||
|
||||
### Server 版本
|
||||
|
||||
Homebrew 的 RustDesk Cask 只提供客戶端應用程序。服務器二進制文件 (hbbs, hbbr) 需要從其他來源安裝或自行編譯。當前使用的版本較舊 (1.1.15)。
|
||||
|
||||
如需更新服務器版本,可以考慮:
|
||||
1. 從源代碼編譯最新版本
|
||||
2. 使用 RustDesk 官方提供的 Docker 鏡像
|
||||
3. 等待 Homebrew 添加服務器公式
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,332 +0,0 @@
|
||||
# WordPress 安裝指南 (Portal)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-22 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-22 | 創建文件 | Warren | OpenCode / big-pickle |
|
||||
|
||||
---
|
||||
|
||||
## 1. 概述
|
||||
|
||||
本文檔說明 Momentry Portal 的 WordPress 安裝配置,作為系統入口整合 n8n 自動化與 sftpgo 檔案服務。
|
||||
|
||||
---
|
||||
|
||||
## 2. 當前狀態
|
||||
|
||||
| 項目 | 狀態 |
|
||||
|------|------|
|
||||
| WordPress 版本 | 6.x |
|
||||
| URL | https://wp.momentry.ddns.net |
|
||||
| 安裝路徑 | `/Users/accusys/wordpress/web` |
|
||||
| 資料庫 | wordpress (MariaDB) |
|
||||
| 資料庫用戶 | wp_user |
|
||||
|
||||
---
|
||||
|
||||
## 3. 目錄結構
|
||||
|
||||
```
|
||||
/Users/accusys/wordpress/
|
||||
├── web/ # WordPress 主目錄
|
||||
│ ├── wp-admin/ # WordPress 管理面板
|
||||
│ ├── wp-content/ # 內容目錄
|
||||
│ │ ├── ai1wm-backups/ # 備份檔案 (*.wpress)
|
||||
│ │ ├── languages/ # 語言檔案
|
||||
│ │ ├── plugins/ # 插件目錄
|
||||
│ │ ├── themes/ # 主題目錄
|
||||
│ │ ├── uploads/ # 上傳檔案
|
||||
│ │ └── cache/ # 快取目錄
|
||||
│ ├── wp-includes/ # WordPress 核心
|
||||
│ └── wp-config.php # 配置文件
|
||||
├── docker-compose.yml # Docker 配置
|
||||
└── wordpress_backup.sql # 資料庫備份
|
||||
```
|
||||
|
||||
### 空間使用
|
||||
|
||||
| 目錄 | 大小 | 說明 |
|
||||
|------|------|------|
|
||||
| `ai1wm-backups/` | ~250MB | 完整備份 (保留 2 個) |
|
||||
| `plugins/` | 80MB | 插件 |
|
||||
| `themes/` | 14MB | 主題 |
|
||||
| `uploads/` | 12MB | 上傳檔案 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 程式碼位置
|
||||
|
||||
自訂程式碼存放位置:
|
||||
|
||||
| 類型 | 路徑 |
|
||||
|------|------|
|
||||
| 主題 | `/Users/accusys/wordpress/web/wp-content/themes/` |
|
||||
| 插件 | `/Users/accusys/wordpress/web/wp-content/plugins/` |
|
||||
| 必須插件 | `/Users/accusys/wordpress/web/wp-content/mu-plugins/` |
|
||||
|
||||
---
|
||||
|
||||
## 5. 插件清單
|
||||
|
||||
| 插件 | 用途 | 說明 |
|
||||
|------|------|------|
|
||||
| elementor | 頁面建構 | 視覺化頁面編輯器 |
|
||||
| all-in-one-wp-migration | 網站遷移/備份 | 完整網站備份工具 |
|
||||
| akismet | 垃圾留言過濾 | 保護留言區域 |
|
||||
| code-snippets | 自訂程式碼 | 無需修改主題即可添加 PHP |
|
||||
|
||||
### Elementor 版本
|
||||
- 版本: 最新穩定版
|
||||
- 用途: 頁面建構(開發階段)
|
||||
|
||||
### 未來計畫
|
||||
- Phase 2: OpenCode 重構
|
||||
- 目標: 交付無 Elementor 依賴版本
|
||||
|
||||
---
|
||||
|
||||
## 6. 主題清單
|
||||
|
||||
| 主題 | 說明 |
|
||||
|------|------|
|
||||
| twentytwentyfive | 目前使用主題 |
|
||||
| twentytwentyfour | 備用 |
|
||||
| twentytwentythree | 備用 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 整合計畫
|
||||
|
||||
### 7.1 n8n 整合
|
||||
|
||||
n8n 作為自動化引擎,WordPress 頁面透過 REST API 或 Webhook 與 n8n 通訊。
|
||||
|
||||
| 整合方式 | 說明 |
|
||||
|----------|------|
|
||||
| REST API | WordPress 呼叫 n8n API |
|
||||
| Webhook | n8n 觸發 WordPress 動作 |
|
||||
|
||||
### 7.2 sftpgo 整合
|
||||
|
||||
sftpgo 作為檔案服務,WordPress 頁面提供檔案上傳/下載功能。
|
||||
|
||||
| 整合方式 | 說明 |
|
||||
|----------|------|
|
||||
| WebDAV | 透過 WebDAV API 操作檔案 |
|
||||
| REST API | 透過 sftpgo API 操作檔案 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 管理命令
|
||||
|
||||
### 8.1 清理 ai1wm 備份
|
||||
|
||||
```bash
|
||||
# 查看現有備份
|
||||
ls -lt /Users/accusys/wordpress/web/wp-content/ai1wm-backups/*.wpress
|
||||
|
||||
# 保留最近 2 個,刪除舊的
|
||||
ls -t /Users/accusys/wordpress/web/wp-content/ai1wm-backups/*.wpress | tail -n +3 | xargs rm
|
||||
|
||||
# 驗證結果
|
||||
du -sh /Users/accusys/wordpress/web/wp-content/ai1wm-backups/
|
||||
```
|
||||
|
||||
### 8.2 清理 WordPress 快取
|
||||
|
||||
```bash
|
||||
# 刪除 Object Cache
|
||||
wp cache flush
|
||||
|
||||
# 刪除 Elementor 快取
|
||||
wp elementor flush_css
|
||||
|
||||
# 刪除全部快取
|
||||
wp cache flush && wp elementor flush_css
|
||||
```
|
||||
|
||||
### 8.3 資料庫操作
|
||||
|
||||
```bash
|
||||
# 匯出資料庫
|
||||
mysqldump -u wp_user -p wordpress > wordpress_backup.sql
|
||||
|
||||
# 匯入資料庫
|
||||
mysql -u wp_user -p wordpress < wordpress_backup.sql
|
||||
```
|
||||
|
||||
### 8.4 權限檢查
|
||||
|
||||
```bash
|
||||
# 檢查目錄權限
|
||||
ls -la /Users/accusys/wordpress/web/wp-content/
|
||||
|
||||
# 確認 wp-content 可寫入
|
||||
chown -R _www:_www /Users/accusys/wordpress/web/wp-content/
|
||||
chmod -R 755 /Users/accusys/wordpress/web/wp-content/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 故障排除
|
||||
|
||||
### 9.1 常見問題
|
||||
|
||||
| 問題 | 解決方案 |
|
||||
|------|----------|
|
||||
| 頁面載入緩慢 | 清理 Elementor/Object Cache |
|
||||
| 上傳檔案失敗 | 檢查 wp-content/uploads 權限 |
|
||||
| 502/504 錯誤 | 重啟 PHP-FPM |
|
||||
| 資料庫連線失敗 | 檢查 wp-config.php 設定 |
|
||||
|
||||
### 9.2 診斷命令
|
||||
|
||||
```bash
|
||||
# 檢查 PHP-FPM 狀態
|
||||
lsof -i :9000
|
||||
|
||||
# 檢查 MySQL/MariaDB 狀態
|
||||
lsof -i :3306
|
||||
|
||||
# 檢查 Apache/Nginx 狀態
|
||||
lsof -i :80
|
||||
|
||||
# 查看 WordPress 錯誤日誌
|
||||
tail -100 /Users/accusys/momentry/log/php-fpm.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 開發協作
|
||||
|
||||
### 10.1 與 marcom 團隊協作
|
||||
|
||||
| 角色 | 負責 |
|
||||
|------|------|
|
||||
| marcom 團隊 | Figma 設計 / Elementor 建構 |
|
||||
| OpenCode | 程式碼實作 / 重構 |
|
||||
|
||||
### 10.2 開發流程
|
||||
|
||||
```
|
||||
Phase 1: marcom 建構 (現在)
|
||||
└── Elementor 頁面建構
|
||||
|
||||
Phase 2: 交付審視 (TBD)
|
||||
└── 功能確認 / 重構評估
|
||||
|
||||
Phase 3: OpenCode 重構 (討論後)
|
||||
└── 純程式碼實作
|
||||
└── 交付客戶 (無 Elementor 依賴)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. PHP LSP 開發環境
|
||||
|
||||
### 11.1 軟體需求
|
||||
|
||||
| 軟體 | 版本 | 安裝方式 |
|
||||
|------|------|----------|
|
||||
| PHP | 8.0+ | 已安裝 (8.5.2) |
|
||||
| Composer | 2.0+ | `brew install composer` |
|
||||
| phpactor | Latest | PHAR 安裝 |
|
||||
|
||||
### 11.2 安裝步驟
|
||||
|
||||
#### 1. 安裝 Composer
|
||||
|
||||
```bash
|
||||
brew install composer
|
||||
```
|
||||
|
||||
#### 2. 安裝 phpactor
|
||||
|
||||
```bash
|
||||
curl -sSL https://github.com/phpactor/phpactor/releases/latest/download/phpactor.phar -o ~/bin/phpactor
|
||||
chmod +x ~/bin/phpactor
|
||||
export PATH="$HOME/bin:$PATH"
|
||||
```
|
||||
|
||||
#### 3. 安裝 WordPress Stubs
|
||||
|
||||
```bash
|
||||
cd /Users/accusys/wordpress/web
|
||||
composer require --dev php-stubs/wordpress-stubs
|
||||
```
|
||||
|
||||
#### 4. 設定 phpactor
|
||||
|
||||
```bash
|
||||
# 設定檔位置
|
||||
mkdir -p ~/.config/phpactor
|
||||
|
||||
# 設定內容
|
||||
cat > ~/.config/phpactor/phpactor.json << 'EOF'
|
||||
{
|
||||
"core.min_memory_limit": 1610612736,
|
||||
"worse_reflection.additive_stubs": [
|
||||
"/Users/accusys/wordpress/web/vendor/php-stubs/wordpress-stubs/wordpress-stubs.php"
|
||||
]
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
#### 5. 建立索引
|
||||
|
||||
```bash
|
||||
cd /Users/accusys/wordpress/web
|
||||
~/bin/phpactor index:build --reset
|
||||
```
|
||||
|
||||
### 11.3 OpenCode 使用方式
|
||||
|
||||
```bash
|
||||
# 確認安裝
|
||||
~/bin/phpactor --version
|
||||
|
||||
# 查詢類別
|
||||
~/bin/phpactor class:search "WP_User"
|
||||
|
||||
# 查看類別資訊
|
||||
~/bin/phpactor index:query WP_User
|
||||
|
||||
# 導航到定義
|
||||
~/bin/phpactor navigate /path/to/file.php
|
||||
|
||||
# 查詢參照
|
||||
~/bin/phpactor references /path/to/file.php
|
||||
```
|
||||
|
||||
### 11.4 常用指令
|
||||
|
||||
| 指令 | 用途 |
|
||||
|------|------|
|
||||
| `phpactor class:search` | 搜尋類別 |
|
||||
| `phpactor index:query` | 查詢索引 |
|
||||
| `phpactor index:build` | 建立索引 |
|
||||
| `phpactor index:clean` | 清除索引 |
|
||||
| `phpactor config:dump` | 顯示設定 |
|
||||
|
||||
---
|
||||
|
||||
## 12. 檔案位置
|
||||
|
||||
| 類型 | 路徑 | 說明 |
|
||||
|------|------|------|
|
||||
| WordPress 主目錄 | `/Users/accusys/wordpress/web` | 網站根目錄 |
|
||||
| 備份目錄 | `/Users/accusys/momentry/backup/wordpress/` | 每日備份 |
|
||||
| 日誌目錄 | `/Users/accusys/momentry/log/` | PHP/Apache 日誌 |
|
||||
| phpactor | `~/bin/phpactor` | PHP LSP 主程式 |
|
||||
| phpactor 設定 | `~/.config/phpactor/phpactor.json` | LSP 設定檔 |
|
||||
| WordPress Stubs | `/Users/accusys/wordpress/web/vendor/php-stubs/` | WordPress 函數定義 |
|
||||
@@ -1,249 +0,0 @@
|
||||
# n8n 整合範例
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-18 |
|
||||
| 文件版本 | V1.1 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-18 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
| V1.1 | 2026-03-25 | 更新API回應格式 (media_url→file_path) 與認證標頭 | OpenCode | deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
## 基本設定
|
||||
|
||||
### API 端點
|
||||
- **Base URL:** `http://localhost:3002/api/v1`
|
||||
- **Method:** `POST`
|
||||
- **Content-Type:** `application/json`
|
||||
- **Authentication:** `X-API-Key: YOUR_API_KEY` (所有 `/api/v1/*` 端點皆需要)
|
||||
|
||||
---
|
||||
|
||||
## Workflow 1: 基礎搜尋
|
||||
|
||||
### Trigger: Manual / Webhook
|
||||
|
||||
```
|
||||
[Manual Trigger]
|
||||
↓
|
||||
[HTTP Request] → POST http://localhost:3002/api/v1/search
|
||||
↓
|
||||
[Set] → 設定搜尋詞 "charade"
|
||||
↓
|
||||
[Code] → 處理回傳結果
|
||||
↓
|
||||
[Respond]
|
||||
```
|
||||
|
||||
### HTTP Request 設定
|
||||
```json
|
||||
{
|
||||
"url": "http://localhost:3002/api/v1/search",
|
||||
"method": "POST",
|
||||
"body": {
|
||||
"query": "={{ $json.searchTerm }}",
|
||||
"limit": 5
|
||||
},
|
||||
"options": {
|
||||
"headers": {
|
||||
"Content-Type": "application/json",
|
||||
"X-API-Key": "YOUR_API_KEY"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Code (處理結果)
|
||||
```javascript
|
||||
const results = $input.first().json.results;
|
||||
|
||||
const videoUrl = "https://wp.momentry.ddns.net/Old_Time_Movie_Show_-_Charade_1963.HD.mov";
|
||||
|
||||
return results.map(r => ({
|
||||
chunk_id: r.chunk_id,
|
||||
text: r.text,
|
||||
start: r.start_time,
|
||||
end: r.end_time,
|
||||
score: r.score,
|
||||
video_url: `${videoUrl}#t=${r.start_time},${r.end_time}`
|
||||
}));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Workflow 2: n8n 專用格式
|
||||
|
||||
使用 `/n8n/search` 端點(已包含 file_path)
|
||||
|
||||
### HTTP Request
|
||||
```json
|
||||
{
|
||||
"url": "http://localhost:3002/api/v1/n8n/search",
|
||||
"method": "POST",
|
||||
"body": {
|
||||
"query": "={{ $json.searchTerm }}",
|
||||
"limit": 5
|
||||
},
|
||||
"options": {
|
||||
"headers": {
|
||||
"Content-Type": "application/json",
|
||||
"X-API-Key": "YOUR_API_KEY"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 回傳格式
|
||||
```json
|
||||
{
|
||||
"query": "charade",
|
||||
"count": 5,
|
||||
"hits": [
|
||||
{
|
||||
"id": "sentence_0006",
|
||||
"vid": "a1b10138a6bbb0cd",
|
||||
"start": 48.8,
|
||||
"end": 55.44,
|
||||
"title": "Chunk sentence_0006",
|
||||
"text": "fun plot twists...",
|
||||
"score": 0.526,
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
> **注意**: API 現在返回 `file_path`(檔案系統路徑)而非 `media_url`(網頁 URL)。如需在網頁中播放影片,請將檔案路徑轉換為可訪問的 URL(例如透過 SFTPGo 分享連結)。
|
||||
|
||||
---
|
||||
|
||||
## Workflow 3: 訊息機器人整合
|
||||
|
||||
### Telegram Bot 範例
|
||||
|
||||
```
|
||||
[Webhook: Telegram]
|
||||
↓
|
||||
[Extract: /search charade]
|
||||
↓
|
||||
[HTTP Request] → POST /api/v1/search
|
||||
↓
|
||||
[Format Response]
|
||||
↓
|
||||
[Telegram: Send Message]
|
||||
```
|
||||
|
||||
### 回傳格式
|
||||
```
|
||||
🎬 搜尋結果: "charade"
|
||||
|
||||
1. "fun plot twists, Woody Dialog and charming performances..."
|
||||
⏱ 48.8s - 55.4s
|
||||
📊 分數: 0.526
|
||||
|
||||
2. "Don't you like me to say that a pretty girl..."
|
||||
⏱ 4745.6s - 4748.6s
|
||||
📊 分數: 0.525
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Workflow 4: 多影片搜尋
|
||||
|
||||
### 取得所有影片
|
||||
```
|
||||
[HTTP Request]
|
||||
GET http://localhost:3002/api/v1/videos
|
||||
```
|
||||
|
||||
### 依 UUID 篩選
|
||||
```json
|
||||
{
|
||||
"query": "charade",
|
||||
"limit": 5,
|
||||
"uuid": "a1b10138a6bbb0cd"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Workflow 5: 定時更新
|
||||
|
||||
```
|
||||
[Cron: 每小時]
|
||||
↓
|
||||
[HTTP Request] → GET /api/v1/videos
|
||||
↓
|
||||
[Loop Over Items]
|
||||
↓
|
||||
[Check: 新影片?]
|
||||
↓
|
||||
[Process: 執行 vectorize]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 實用場景
|
||||
|
||||
### 1. 客服機器人
|
||||
用戶問「這部片在哪一段有談到 charade?」
|
||||
→ 搜尋 API → 回傳時戳 → 直接播放該片段
|
||||
|
||||
### 2. 內容推薦
|
||||
根據用戶輸入的關鍵字,找到相關影片片段
|
||||
|
||||
### 3. 自動化剪輯
|
||||
搜尋多個片段 → 組合成精華影片
|
||||
|
||||
---
|
||||
|
||||
## 錯誤處理
|
||||
|
||||
```javascript
|
||||
const response = $input.first();
|
||||
|
||||
if (!response.json.results || response.json.results.length === 0) {
|
||||
return {
|
||||
success: false,
|
||||
message: "找不到相關結果"
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
count: response.json.results.length,
|
||||
data: response.json.results
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 測試用 cURL
|
||||
|
||||
```bash
|
||||
# 基本搜尋
|
||||
curl -X POST http://localhost:3002/api/v1/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "charade", "limit": 3}'
|
||||
|
||||
# n8n 格式
|
||||
curl -X POST http://localhost:3002/api/v1/n8n/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "charade", "limit": 3}'
|
||||
|
||||
# 取得影片列表
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos
|
||||
|
||||
# 取得特定影片的區塊
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos/a1b10138a6bbb0cd/chunks
|
||||
```
|
||||
@@ -1,355 +0,0 @@
|
||||
# n8n Video RAG Demo - API 執行記錄
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-22 |
|
||||
| 文件版本 | V1.1 |
|
||||
| 目標 | 完整執行 n8n Video RAG Workflow 並記錄所有 API 呼叫 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-22 | 創建文件 | Warren | OpenCode |
|
||||
| V1.1 | 2026-03-26 | 更新 API 範例,新增 X-API-Key 驗證標頭 | OpenCode | deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: SFTPGo 準備
|
||||
|
||||
### Step 1.1: 取得 Demo User Token
|
||||
|
||||
**API 呼叫:**
|
||||
```bash
|
||||
curl -X GET "http://localhost:8080/api/v2/user/token" \
|
||||
-u "demo:demopassword123"
|
||||
```
|
||||
|
||||
**Request:**
|
||||
```
|
||||
GET /api/v2/user/token
|
||||
Authorization: Basic ZG9tbzpkZW1vcGFzc3dvcmQxMjM=
|
||||
```
|
||||
|
||||
**Response (200 OK):**
|
||||
```json
|
||||
{
|
||||
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"expires_at": "2026-03-22T07:05:57Z"
|
||||
}
|
||||
```
|
||||
|
||||
**Token 有效期限:** 20 分鐘
|
||||
|
||||
**Session Token (Demo User):**
|
||||
```
|
||||
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiQVBJVXNlciIsIjo6MSJdLCJleHAiOjE3NzQxNjMxNTcsImlhdCI6MTc3NDE2MTk1NywianRpIjoiZDZ2cDA5YWcyZnIwMnY3aTlybDAiLCJuYmYiOjE3NzQxNjE5NDcsInN1YiI6IjE3NzQxNjE5NTM0OTMiLCJ1c2VybmFtZSI6ImRlbW8ifQ.yw0UCv8sQXXCkOr7qmK2ejLzuoA8IDrmC9bpgFE4R_Q
|
||||
```
|
||||
|
||||
**結果:** ✅ 成功
|
||||
|
||||
---
|
||||
|
||||
### Step 1.2: 上傳測試影片到 SFTPGo
|
||||
|
||||
**影片選擇:** `Old_Time_Movie_Show_-_Charade_1963.HD.mov` (2.3 GB)
|
||||
- 路徑: `/Users/accusys/test_video/Old_Time_Movie_Show_-_Charade_1963.HD.mov`
|
||||
- ASR Segments: 1,917 (已預處理)
|
||||
|
||||
**API 呼叫:**
|
||||
```bash
|
||||
TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
curl -X POST "http://localhost:8080/api/v2/user/files" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-F "path=/demo" \
|
||||
-F "mkdir_parents=true" \
|
||||
-F "filenames=@/Users/accusys/test_video/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
|
||||
```
|
||||
|
||||
**Request:**
|
||||
```
|
||||
POST /api/v2/user/files
|
||||
Authorization: Bearer <token>
|
||||
Content-Type: multipart/form-data
|
||||
|
||||
path: /demo
|
||||
mkdir_parents: true
|
||||
filenames: @/path/to/Old_Time_Movie_Show_-_Charade_1963.HD.mov
|
||||
```
|
||||
|
||||
**Response (201 Created):**
|
||||
```json
|
||||
{"message":"Upload completed"}
|
||||
```
|
||||
|
||||
**上傳統計:**
|
||||
- 檔案大小: 2,361,629,896 bytes (2.3 GB)
|
||||
- 上傳時間: 7 秒
|
||||
- 平均速度: ~337 MB/s
|
||||
|
||||
**結果:** ✅ 成功
|
||||
|
||||
---
|
||||
|
||||
### Step 1.3: 建立分享連結
|
||||
|
||||
**API 呼叫:**
|
||||
```bash
|
||||
curl -X POST "http://localhost:8080/api/v2/user/shares" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "Charade_1963_Demo",
|
||||
"paths": ["/Old_Time_Movie_Show_-_Charade_1963.HD.mov"],
|
||||
"scope": 1,
|
||||
"expires_at": 0
|
||||
}'
|
||||
```
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
POST /api/v2/user/shares
|
||||
Authorization: Bearer <token>
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "Charade_1963_Demo",
|
||||
"paths": ["/Old_Time_Movie_Show_-_Charade_1963.HD.mov"],
|
||||
"scope": 1,
|
||||
"expires_at": 0
|
||||
}
|
||||
```
|
||||
|
||||
**Response (200 OK):**
|
||||
```json
|
||||
{"message":"Share created"}
|
||||
```
|
||||
|
||||
**結果:** ✅ 成功
|
||||
|
||||
---
|
||||
|
||||
### Step 1.4: 驗證上傳結果
|
||||
|
||||
**API 呼叫 - 列出分享:**
|
||||
```bash
|
||||
curl -X GET "http://localhost:8080/api/v2/user/shares" \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "CjmQfrkXY5qDtC46WVZY2S",
|
||||
"name": "Charade_1963_Demo",
|
||||
"scope": 1,
|
||||
"paths": [
|
||||
"/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
|
||||
],
|
||||
"username": "demo",
|
||||
"created_at": 1774162072853,
|
||||
"updated_at": 1774162072853,
|
||||
"password": ""
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**分享連結:**
|
||||
- Share ID: `CjmQfrkXY5qDtC46WVZY2S`
|
||||
- Browse URL: `http://localhost:8080/web/client/pubshares/CjmQfrkXY5qDtC46WVZY2S/browse`
|
||||
|
||||
**本地目錄驗證:**
|
||||
```
|
||||
/Users/accusys/sftpgo_test/demo/
|
||||
└── Old_Time_Movie_Show_-_Charade_1963.HD.mov (2,361,629,896 bytes)
|
||||
```
|
||||
|
||||
**結果:** ✅ 成功
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Momentry 註冊
|
||||
|
||||
### Step 2.1: 健康檢查
|
||||
|
||||
**API 呼叫:**
|
||||
```bash
|
||||
curl -X GET "http://localhost:3002/health"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```
|
||||
(待填寫)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2.2: 註冊影片
|
||||
|
||||
**API 呼叫:**
|
||||
```bash
|
||||
curl -X POST "http://localhost:3002/api/v1/register" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"path": "/Users/accusys/sftpgo_test/demo/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
|
||||
}'
|
||||
```
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
POST /api/v1/register
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"path": "/Users/accusys/sftpgo_test/demo/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```
|
||||
(待填寫)
|
||||
{
|
||||
"uuid": "...",
|
||||
"video_id": ...,
|
||||
"file_name": "...",
|
||||
"duration": ...,
|
||||
"width": ...,
|
||||
"height": ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: 處理進度追蹤
|
||||
|
||||
### Step 3.1: 查詢處理進度 (新版 API)
|
||||
|
||||
**API 呼叫:**
|
||||
```bash
|
||||
curl -X GET "http://localhost:3002/api/v1/progress/{uuid}"
|
||||
```
|
||||
|
||||
**Response (新版 - 包含影片資訊與系統資源):**
|
||||
```json
|
||||
{
|
||||
"uuid": "a1b10138a6bbb0cd",
|
||||
"user": null,
|
||||
"group": null,
|
||||
"file_name": "Old_Time_Movie_Show_-_Charade_1963.HD.mov",
|
||||
"duration": 6879.33,
|
||||
"overall_progress": 28,
|
||||
"cpu_percent": 3.7,
|
||||
"gpu_percent": null,
|
||||
"memory_percent": 0.1,
|
||||
"memory_mb": 19328,
|
||||
"processors": [
|
||||
{"name": "asr", "status": "complete", "current": 1867, "total": 0, "progress": 100, "message": "1867 segments"},
|
||||
{"name": "cut", "status": "complete", "current": 1331, "total": 1331, "progress": 100, "message": "1331 scenes"},
|
||||
{"name": "asrx", "status": "error", "current": 0, "total": 0, "progress": 0, "message": "0 segments"},
|
||||
{"name": "yolo", "status": "progress", "current": 69400, "total": 412343, "progress": 16, "message": "frame 69400"},
|
||||
{"name": "ocr", "status": "pending", "current": 0, "total": 0, "progress": 0, "message": ""},
|
||||
{"name": "face", "status": "pending", "current": 0, "total": 0, "progress": 0, "message": ""},
|
||||
{"name": "pose", "status": "pending", "current": 0, "total": 0, "progress": 0, "message": ""}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**欄位說明:**
|
||||
| 欄位 | 說明 |
|
||||
|------|------|
|
||||
| uuid | 影片唯一識別碼 |
|
||||
| user | 處理所屬用戶 (如已設定) |
|
||||
| group | 處理所屬群組 (如已設定) |
|
||||
| file_name | 影片檔案名稱 |
|
||||
| duration | 影片時長 (秒) |
|
||||
| overall_progress | 整體進度 (百分比) |
|
||||
| cpu_percent | CPU 使用率 (%) |
|
||||
| gpu_percent | GPU 使用率 (%),無 GPU 則為 null |
|
||||
| memory_percent | 記憶體使用率 (%) |
|
||||
| memory_mb | 記憶體使用量 (MB) |
|
||||
| processors | 各處理器狀態陣列 |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: 自然語言檢索
|
||||
|
||||
### Step 4.1: RAG 搜尋
|
||||
|
||||
**API 呼叫:**
|
||||
```bash
|
||||
curl -X POST "http://localhost:3002/api/v1/search" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"query": "What is the movie about?",
|
||||
"limit": 10,
|
||||
"uuid": "..."
|
||||
}'
|
||||
```
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
POST /api/v1/search
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"query": "What is the movie about?",
|
||||
"limit": 10,
|
||||
"uuid": "<uuid from registration>"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```
|
||||
(待填寫)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4.2: n8n 搜尋 (含 file_path)
|
||||
|
||||
**API 呼叫:**
|
||||
```bash
|
||||
curl -X POST "http://localhost:3002/api/v1/n8n/search" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: demo_api_key_12345" \
|
||||
-d '{
|
||||
"query": "What is the movie about?",
|
||||
"limit": 10,
|
||||
"uuid": "..."
|
||||
}'
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```
|
||||
(待填寫)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 憑證彙整
|
||||
|
||||
| 服務 | 項目 | 值 |
|
||||
|------|------|------|
|
||||
| SFTPGo | API Base | `http://localhost:8080/api/v2` |
|
||||
| SFTPGo | Demo User | `demo` |
|
||||
| SFTPGo | Demo Password | `demopassword123` (已重設) |
|
||||
| SFTPGo | Demo Home | `/Users/accusys/sftpgo_test/demo` |
|
||||
| SFTPGo | Token Endpoint | `/api/v2/user/token` |
|
||||
| SFTPGo | Share ID | `CjmQfrkXY5qDtC46WVZY2S` |
|
||||
| Momentry | Server | `http://localhost:3002` |
|
||||
| Momentry | MEDIA_BASE_URL | `https://wp.momentry.ddns.net` |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 日期 | 版本 | 變更 |
|
||||
|------|------|------|
|
||||
| 2026-03-22 | v1.0 | 初始建立文件 |
|
||||
| 2026-03-22 | v1.1 | 成功取得 Demo Token |
|
||||
| 2026-03-22 | v1.2 | Phase 1 完成 (上傳 Charade 2.3GB) |
|
||||
@@ -1,269 +0,0 @@
|
||||
# n8n HTTP Request Node 設定指南
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-03-26 |
|
||||
| 文件版本 | V1.1 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-23 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
| V1.1 | 2026-03-26 | 新增 API Key 驗證說明,更新 curl 範例 | OpenCode | deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
> **API URL 說明**:
|
||||
> - **本地測試**: `http://localhost:3002`
|
||||
> - **n8n workflow**: `https://api.momentry.ddns.net`
|
||||
>
|
||||
> ⚠️ 在 n8n 中請使用 `api.momentry.ddns.net`,不要使用 `localhost:3002`
|
||||
|
||||
---
|
||||
|
||||
## 錯誤排除
|
||||
|
||||
### 錯誤訊息: "Your request is invalid or could not be processed by the service"
|
||||
|
||||
這通常表示 HTTP Request Node 的設定不正確。
|
||||
|
||||
---
|
||||
|
||||
## 正確的 Node 設定方式
|
||||
|
||||
### 方法 1: 使用 JSON Body (推薦)
|
||||
|
||||
```
|
||||
Node: HTTP Request
|
||||
├── URL: https://api.momentry.ddns.net/api/v1/n8n/search
|
||||
├── Method: POST
|
||||
├── Authentication: None
|
||||
├── Send Body: ✓ (checked)
|
||||
├── Content Type: JSON
|
||||
├── Body:
|
||||
│ {
|
||||
│ "query": "={{ $json.query }}",
|
||||
│ "limit": "={{ $json.limit }}"
|
||||
│ }
|
||||
├── Send Headers: ✓ (checked)
|
||||
└── Header Parameters:
|
||||
└── X-API-Key: {{ $env.MOMENTRY_API_KEY }}
|
||||
```
|
||||
|
||||
### 方法 2: 使用 Raw Body + Headers
|
||||
|
||||
```
|
||||
Node: HTTP Request
|
||||
├── URL: https://api.momentry.ddns.net/api/v1/n8n/search
|
||||
├── Method: POST
|
||||
├── Authentication: None
|
||||
├── Send Body: ✓ (checked)
|
||||
├── Specify Body: Using JSON
|
||||
├── JSON Body:
|
||||
│ {
|
||||
│ "query": "charade",
|
||||
│ "limit": 3
|
||||
│ }
|
||||
├── Send Headers: ✓ (checked)
|
||||
└── Header Parameters:
|
||||
├── Content-Type: application/json
|
||||
└── X-API-Key: {{ $env.MOMENTRY_API_KEY }}
|
||||
```
|
||||
|
||||
### 方法 3: 最簡單的 Hardcoded 測試
|
||||
|
||||
```
|
||||
Node: HTTP Request
|
||||
├── URL: https://api.momentry.ddns.net/api/v1/n8n/search
|
||||
├── Method: POST
|
||||
├── Send Body: ✓
|
||||
├── Content Type: JSON
|
||||
└── Body:
|
||||
{
|
||||
"query": "charade",
|
||||
"limit": 3
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常見錯誤與解決
|
||||
|
||||
### ❌ 錯誤 1: Body 格式錯誤
|
||||
|
||||
**錯誤設定:**
|
||||
```
|
||||
Body Parameters:
|
||||
query = {{ $json.query }}
|
||||
limit = {{ $json.limit }}
|
||||
```
|
||||
|
||||
**正確設定:**
|
||||
```
|
||||
Content Type: JSON
|
||||
Body:
|
||||
{
|
||||
"query": "={{ $json.query }}",
|
||||
"limit": "={{ $json.limit }}"
|
||||
}
|
||||
```
|
||||
|
||||
### ❌ 錯誤 2: 缺少引號
|
||||
|
||||
**錯誤:**
|
||||
```json
|
||||
{
|
||||
query: "charade",
|
||||
limit: 3
|
||||
}
|
||||
```
|
||||
|
||||
**正確:**
|
||||
```json
|
||||
{
|
||||
"query": "charade",
|
||||
"limit": 3
|
||||
}
|
||||
```
|
||||
|
||||
### ❌ 錯誤 3: URL 錯誤
|
||||
|
||||
**錯誤:**
|
||||
```
|
||||
URL: http://localhost:3002/api/v1/n8n/search
|
||||
```
|
||||
|
||||
**正確:**
|
||||
```
|
||||
URL: https://api.momentry.ddns.net/api/v1/n8n/search
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 測試步驟
|
||||
|
||||
### 步驟 1: 創建最簡單的測試
|
||||
|
||||
1. 新建工作流程
|
||||
2. 添加 **Manual Trigger** Node
|
||||
3. 添加 **HTTP Request** Node
|
||||
4. 設定如下:
|
||||
- URL: `https://api.momentry.ddns.net/api/v1/n8n/search`
|
||||
- Method: POST
|
||||
- Send Body: ✓
|
||||
- Content Type: JSON
|
||||
- Body: `{"query": "charade", "limit": 2}`
|
||||
|
||||
### 步驟 2: 執行測試
|
||||
|
||||
1. 點擊 **Execute Workflow**
|
||||
2. 查看 HTTP Request Node 的輸出
|
||||
3. 應該看到 JSON 回應
|
||||
|
||||
### 步驟 3: 確認成功
|
||||
|
||||
成功的回應應該包含:
|
||||
```json
|
||||
{
|
||||
"query": "charade",
|
||||
"count": 2,
|
||||
"hits": [...]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 直接複製使用的工作流程 JSON
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Video Search - Working Example",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [250, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "https://api.momentry.ddns.net/api/v1/n8n/search",
|
||||
"method": "POST",
|
||||
"sendBody": true,
|
||||
"contentType": "json",
|
||||
"body": {
|
||||
"query": "charade",
|
||||
"limit": 3
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"name": "Search Video API",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.1,
|
||||
"position": [450, 300]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Search Video API",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**導入方式:**
|
||||
1. 在 n8n UI 中,點擊左上角的 Menu
|
||||
2. 選擇 **Import from File**
|
||||
3. 選擇上面的 JSON 文件
|
||||
|
||||
---
|
||||
|
||||
## 驗證 API 可用性
|
||||
|
||||
在終端機測試:
|
||||
```bash
|
||||
# 需要 API Key 驗證 (設定環境變數或直接替換)
|
||||
export MOMENTRY_API_KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
|
||||
|
||||
curl -X POST https://api.momentry.ddns.net/api/v1/n8n/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: $MOMENTRY_API_KEY" \
|
||||
-d '{"query":"charade","limit":2}'
|
||||
```
|
||||
|
||||
如果 curl 成功但 n8n 失敗,問題在於 n8n HTTP Request Node 的設定。
|
||||
|
||||
---
|
||||
|
||||
## 需要幫助?
|
||||
|
||||
如果仍然無法工作:
|
||||
1. 開啟工作流程
|
||||
2. 點擊 HTTP Request Node
|
||||
3. 點擊右上角的 **Execute Node** 單獨執行
|
||||
4. 查看錯誤訊息的詳細內容
|
||||
5. 檢查 Network tab 中的 request/response
|
||||
|
||||
---
|
||||
|
||||
## 相關文件
|
||||
|
||||
- [API_INDEX.md](./API_INDEX.md) - 文件總覽(起點)
|
||||
- [API_N8N_GUIDE.md](./API_N8N_GUIDE.md) - n8n 快速使用指南
|
||||
- [API_ENDPOINTS.md](./API_ENDPOINTS.md) - 端點完整說明
|
||||
@@ -1,575 +0,0 @@
|
||||
# Momentry n8n 整合使用手冊
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-23 |
|
||||
| 文件版本 | V1.1 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-22 | 創建 n8n 整合手冊 | Warren | OpenCode |
|
||||
| V1.1 | 2026-03-23 | 新增 API Key 驗證與完整工作流範例 | Warren | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
**目標讀者**: n8n 使用者、DevOps
|
||||
|
||||
---
|
||||
|
||||
## 目錄
|
||||
|
||||
1. [概述](#1-概述)
|
||||
2. [前置作業](#2-前置作業)
|
||||
3. [建立 n8n API Key](#3-建立-n8n-api-key)
|
||||
4. [在 n8n 中使用 Momentry API](#4-在-n8n-中使用-momentry-api)
|
||||
5. [工作流範例](#5-工作流範例)
|
||||
6. [常見問題](#6-常見問題)
|
||||
|
||||
---
|
||||
|
||||
## 1. 概述
|
||||
|
||||
### 1.1 什麼是 n8n?
|
||||
|
||||
n8n 是一個開源的工作流自動化工具,可以連接各種服務和 API。
|
||||
|
||||
### 1.2 為什麼需要整合?
|
||||
|
||||
| 場景 | 說明 |
|
||||
|------|------|
|
||||
| 自動化影片處理 | 新影片上傳時自動觸發處理流程 |
|
||||
| 監控告警 | API Key 異常時發送通知 |
|
||||
| 定時備份 | 定期備份 API Key 資料 |
|
||||
| 跨系統同步 | 與其他系統同步 API Key 狀態 |
|
||||
|
||||
### 1.3 架構圖
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||
│ 觸發器 │────▶│ n8n 工作流 │────▶│ Momentry │
|
||||
│ (Webhook/ │ │ (處理邏輯) │ │ API │
|
||||
│ Cron) │ └─────────────┘ └─────────────┘
|
||||
└─────────────┘ │ │
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────────┐ ┌─────────────┐
|
||||
│ 通知 │ │ 動作 │
|
||||
│ (Slack/Email)│ │ (建立/查詢) │
|
||||
└─────────────┘ └─────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 1.4 API URL 選擇
|
||||
|
||||
| 環境 | URL | 說明 |
|
||||
|------|-----|------|
|
||||
| **本地測試** | `http://localhost:3002` | 直接訪問 API |
|
||||
| **n8n workflow** | `https://api.momentry.ddns.net` | 通過反向代理 |
|
||||
|
||||
> ⚠️ **重要**: 在 n8n HTTP Request Node 中,請使用 `https://api.momentry.ddns.net` 而非 `localhost:3002`,因為 n8n 需要從外部訪問 API。
|
||||
|
||||
**本地測試時**:
|
||||
```bash
|
||||
curl http://localhost:3002/health
|
||||
```
|
||||
|
||||
**n8n Workflow 中**:
|
||||
```
|
||||
URL: https://api.momentry.ddns.net/api/v1/n8n/search
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 前置作業
|
||||
|
||||
### 2.1 確認服務狀態
|
||||
|
||||
```bash
|
||||
# 檢查 Momentry API
|
||||
curl http://localhost:3002/health
|
||||
|
||||
# 檢查 n8n
|
||||
curl https://n8n.momentry.ddns.net/api/v1/workflows \
|
||||
-H "X-N8N-API-KEY: your-api-key"
|
||||
```
|
||||
|
||||
### 2.2 取得 n8n API Key
|
||||
|
||||
#### 方式 A: 透過 Momentry CLI
|
||||
|
||||
```bash
|
||||
# 建立 n8n API Key
|
||||
momentry n8n create \
|
||||
--api-key "your-existing-n8n-api-key" \
|
||||
--label "momentry-integration" \
|
||||
--expires-in-days 90
|
||||
|
||||
# 輸出:
|
||||
# ✅ n8n API Key created successfully!
|
||||
# API Key: eyJhbGciOiJIUzI1NiIs...
|
||||
```
|
||||
|
||||
#### 方式 B: 透過 n8n 介面
|
||||
|
||||
1. 登入 n8n: https://n8n.momentry.ddns.net
|
||||
2. 前往 Settings → n8n API
|
||||
3. 點擊「Create an API Key」
|
||||
4. 複製 API Key
|
||||
|
||||
### 2.3 設定環境變數
|
||||
|
||||
在 n8n 中設定以下環境變數:
|
||||
|
||||
| 變數名稱 | 值 | 說明 |
|
||||
|----------|-----|------|
|
||||
| `MOMENTRY_API_URL` | `http://localhost:3002` | Momentry API URL |
|
||||
| `MOMENTRY_API_KEY` | `your-api-key` | Momentry API Key |
|
||||
|
||||
---
|
||||
|
||||
## 3. 建立 n8n API Key
|
||||
|
||||
### 3.1 CLI 命令
|
||||
|
||||
```bash
|
||||
# 基本建立
|
||||
momentry n8n create \
|
||||
--api-key <existing-n8n-key> \
|
||||
--label <key-name>
|
||||
|
||||
# 設定過期時間
|
||||
momentry n8n create \
|
||||
--api-key <existing-n8n-key> \
|
||||
--label "ci-pipeline" \
|
||||
--expires-in-days 30
|
||||
```
|
||||
|
||||
### 3.2 範例:建立監控用 Key
|
||||
|
||||
```bash
|
||||
momentry n8n create \
|
||||
--api-key "n8n_api_1234567890" \
|
||||
--label "monitoring-key" \
|
||||
--expires-in-days 180
|
||||
|
||||
# 輸出:
|
||||
# ✅ n8n API Key created successfully!
|
||||
# Key ID: abc123-def456
|
||||
# Label: monitoring-key
|
||||
# API Key: eyJhbGciOiJIUz...
|
||||
```
|
||||
|
||||
### 3.3 列出已建立的 Key
|
||||
|
||||
```bash
|
||||
momentry n8n list --api-key <existing-n8n-key>
|
||||
|
||||
# 輸出:
|
||||
# 📋 n8n API Keys
|
||||
# ┌────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ Label │ ID │
|
||||
# ├────────────────────────────────────────────────────────────────────────────┤
|
||||
# │ monitoring-key │ abc123-def456 │
|
||||
# │ ci-pipeline │ xyz789-abc123 │
|
||||
# └────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 在 n8n 中使用 Momentry API
|
||||
|
||||
> ⚠️ **注意**: API Key 管理端點 (`/api/v1/api-keys/*`) 目前仍在規劃中,尚未實作。以下為規劃中的 API 說明。
|
||||
|
||||
### 4.1 設定 HTTP Request 節點
|
||||
|
||||
```
|
||||
Method: POST
|
||||
URL: {{ $env.MOMENTRY_API_URL }}/api/v1/api-keys
|
||||
Headers:
|
||||
X-API-Key: {{ $env.MOMENTRY_API_KEY }}
|
||||
Content-Type: application/json
|
||||
Body:
|
||||
{
|
||||
"name": "auto-generated-key",
|
||||
"key_type": "service",
|
||||
"ttl_days": 90
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 可用的 API 端點
|
||||
|
||||
> ⚠️ **API Key 管理端點為規劃功能,目前尚未實作**
|
||||
|
||||
#### 已實作端點
|
||||
|
||||
| 方法 | 端點 | 說明 |
|
||||
|------|------|------|
|
||||
| GET | `/health` | 健康檢查 |
|
||||
| POST | `/api/v1/search` | 語意搜尋 |
|
||||
| POST | `/api/v1/n8n/search` | n8n 格式搜尋 |
|
||||
| GET | `/api/v1/videos` | 列出所有影片 |
|
||||
| GET | `/api/v1/lookup` | 查詢影片 |
|
||||
| GET | `/api/v1/progress/:uuid` | 處理進度 |
|
||||
|
||||
#### 規劃中端點 *(尚未實作)*
|
||||
|
||||
| 方法 | 端點 | 說明 |
|
||||
|------|------|------|
|
||||
| GET | `/api/v1/api-keys` | 列出所有 API Keys |
|
||||
| POST | `/api/v1/api-keys` | 建立新的 API Key |
|
||||
| DELETE | `/api/v1/api-keys/{id}` | 刪除 API Key |
|
||||
| POST | `/api/v1/api-keys/{id}/rotate` | 請求 Key 輪換 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 工作流範例
|
||||
|
||||
### 範例 1:定時檢查 API Key 狀態
|
||||
|
||||
**目的**: 每天早上 9 點檢查即將過期的 API Keys
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||
│ Schedule │────▶│ HTTP │────▶│ Filter │────▶│ Slack │
|
||||
│ (每天 9AM) │ │ Request │ │ (7天內過期) │ │ 通知 │
|
||||
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
|
||||
```
|
||||
|
||||
**HTTP Request 設定**:
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "GET",
|
||||
"url": "{{ $env.MOMENTRY_API_URL }}/api/v1/api-keys",
|
||||
"headers": {
|
||||
"X-API-Key": "{{ $env.MOMENTRY_API_KEY }}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Filter 設定**:
|
||||
|
||||
```javascript
|
||||
// 檢查是否在 7 天內過期
|
||||
const expiresAt = new Date($json.expires_at);
|
||||
const now = new Date();
|
||||
const sevenDaysLater = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);
|
||||
|
||||
return expiresAt <= sevenDaysLater && expiresAt > now;
|
||||
```
|
||||
|
||||
**Slack 通知格式**:
|
||||
|
||||
```
|
||||
⚠️ API Key 即將過期提醒
|
||||
|
||||
以下 API Key 將在 7 天內過期:
|
||||
{{ $json.map(k => `• ${k.name} (${k.key_id}) - 過期時間: ${k.expires_at}`).join('\n') }}
|
||||
|
||||
請及時處理!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 範例 2:新影片上傳時自動建立 API Key
|
||||
|
||||
**目的**: 當有新影片上傳時,自動為該影片建立專用 API Key
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||
│ Webhook │────▶│ HTTP │────▶│ Set │────▶│ Respond │
|
||||
│ 觸發器 │ │ Request │ │ (組裝回應) │ │ Webhook │
|
||||
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
|
||||
```
|
||||
|
||||
**Webhook 設定**:
|
||||
|
||||
```
|
||||
Method: POST
|
||||
Path: /new-video
|
||||
```
|
||||
|
||||
**HTTP Request 設定**:
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "POST",
|
||||
"url": "{{ $env.MOMENTRY_API_URL }}/api/v1/api-keys",
|
||||
"headers": {
|
||||
"X-API-Key": "{{ $env.MOMENTRY_API_KEY }}"
|
||||
},
|
||||
"body": {
|
||||
"name": "video-{{ $json.video_id }}",
|
||||
"key_type": "service",
|
||||
"permissions": ["read"],
|
||||
"ttl_days": 30
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**回應格式**:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"video_id": "{{ $json.video_id }}",
|
||||
"api_key_id": "{{ $json.key_id }}",
|
||||
"message": "API Key 已建立"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 範例 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` | 整合測試文件 |
|
||||
@@ -1,227 +0,0 @@
|
||||
# OpenCode n8n MCP 整合設定
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-23 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-23 | 創建 n8n MCP 整合設定文件 | Warren | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
> 建立時間: 2026-03-23
|
||||
> 更新時間: 2026-03-23
|
||||
|
||||
---
|
||||
|
||||
## n8n MCP 工具列表 (43 個)
|
||||
|
||||
### Workflows (10)
|
||||
| 工具 | 說明 |
|
||||
|------|------|
|
||||
| `n8n_list_workflows` | 列出所有 workflows |
|
||||
| `n8n_get_workflow` | 取得 workflow 詳情 |
|
||||
| `n8n_create_workflow` | 建立新 workflow |
|
||||
| `n8n_update_workflow` | 更新 workflow |
|
||||
| `n8n_delete_workflow` | 刪除 workflow |
|
||||
| `n8n_activate_workflow` | 啟用 workflow |
|
||||
| `n8n_deactivate_workflow` | 停用 workflow |
|
||||
| `n8n_execute_workflow` | 執行 workflow |
|
||||
| `n8n_get_workflow_tags` | 取得 workflow 標籤 |
|
||||
| `n8n_update_workflow_tags` | 更新 workflow 標籤 |
|
||||
|
||||
### Executions (3)
|
||||
| 工具 | 說明 |
|
||||
|------|------|
|
||||
| `n8n_list_executions` | 列出執行記錄 |
|
||||
| `n8n_get_execution` | 取得執行詳情 |
|
||||
| `n8n_delete_execution` | 刪除執行記錄 |
|
||||
|
||||
### Data Tables (8)
|
||||
| 工具 | 說明 |
|
||||
|------|------|
|
||||
| `n8n_list_datatables` | 列出資料表 |
|
||||
| `n8n_create_datatable` | 建立資料表 |
|
||||
| `n8n_get_datatable` | 取得資料表結構 |
|
||||
| `n8n_get_datatable_rows` | 取得資料表列 |
|
||||
| `n8n_insert_datatable_rows` | 插入資料列 |
|
||||
| `n8n_update_datatable_rows` | 更新資料列 |
|
||||
| `n8n_upsert_datatable_row` | 插入或更新資料列 |
|
||||
| `n8n_delete_datatable_rows` | 刪除資料列 |
|
||||
|
||||
### Tags (5)
|
||||
| 工具 | 說明 |
|
||||
|------|------|
|
||||
| `n8n_list_tags` | 列出所有標籤 |
|
||||
| `n8n_get_tag` | 取得標籤 |
|
||||
| `n8n_create_tag` | 建立標籤 |
|
||||
| `n8n_update_tag` | 更新標籤 |
|
||||
| `n8n_delete_tag` | 刪除標籤 |
|
||||
|
||||
### Credentials (4)
|
||||
| 工具 | 說明 |
|
||||
|------|------|
|
||||
| `n8n_list_credentials` | 列出憑證 |
|
||||
| `n8n_create_credential` | 建立憑證 |
|
||||
| `n8n_delete_credential` | 刪除憑證 |
|
||||
| `n8n_get_credential_schema` | 取得憑證 schema |
|
||||
|
||||
### Users (3)
|
||||
| 工具 | 說明 |
|
||||
|------|------|
|
||||
| `n8n_list_users` | 列出使用者 |
|
||||
| `n8n_get_user` | 取得使用者 |
|
||||
| `n8n_delete_user` | 刪除使用者 |
|
||||
|
||||
### Variables (3)
|
||||
| 工具 | 說明 |
|
||||
|------|------|
|
||||
| `n8n_list_variables` | 列出變數 |
|
||||
| `n8n_create_variable` | 建立變數 |
|
||||
| `n8n_delete_variable` | 刪除變數 |
|
||||
|
||||
### 其他 (7)
|
||||
| 工具 | 說明 |
|
||||
|------|------|
|
||||
| `n8n_list_projects` | 列出專案 |
|
||||
| `n8n_create_project` | 建立專案 |
|
||||
| `n8n_update_project` | 更新專案 |
|
||||
| `n8n_delete_project` | 刪除專案 |
|
||||
| `n8n_generate_audit` | 產生安全審計報告 |
|
||||
| `n8n_health_check` | 健康檢查 |
|
||||
| `n8n_trigger_webhook` | 觸發 webhook |
|
||||
|
||||
---
|
||||
|
||||
## 安裝步驟
|
||||
|
||||
### 1. 安裝 n8n MCP Server
|
||||
|
||||
```bash
|
||||
npm install -g @nextoolsolutions/mcp-n8n
|
||||
```
|
||||
|
||||
驗證:
|
||||
```bash
|
||||
which mcp-n8n
|
||||
# /opt/homebrew/bin/mcp-n8n
|
||||
```
|
||||
|
||||
### 2. 取得 n8n API Key
|
||||
|
||||
1. 開啟 n8n UI: `http://localhost:5678`
|
||||
2. 登入後點擊右上角 **Settings** → **API**
|
||||
3. 點擊 **Create New API Key**
|
||||
4. 複製產生的 key
|
||||
|
||||
### 3. 設定 OpenCode MCP 設定檔
|
||||
|
||||
建立或編輯 `~/.config/opencode/opencode.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"mcp": {
|
||||
"gitea": {
|
||||
"type": "local",
|
||||
"enabled": true,
|
||||
"command": [
|
||||
"/opt/homebrew/bin/gitea-mcp-server",
|
||||
"-token", "<GITEA_TOKEN>",
|
||||
"-host", "http://localhost:3000"
|
||||
]
|
||||
},
|
||||
"n8n": {
|
||||
"type": "local",
|
||||
"enabled": true,
|
||||
"command": ["/opt/homebrew/bin/mcp-n8n"],
|
||||
"environment": {
|
||||
"N8N_BASE_URL": "http://localhost:5678",
|
||||
"N8N_API_KEY": "<N8N_API_KEY>"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 驗證 MCP 運作
|
||||
|
||||
重啟 OpenCode,確認 n8n MCP tools 可用。
|
||||
|
||||
---
|
||||
|
||||
## n8n API 端點
|
||||
|
||||
n8n v2 REST API 路徑為 `/rest/`(不是 `/api/v1/`)
|
||||
|
||||
| 端點 | 方法 | 說明 |
|
||||
|------|------|------|
|
||||
| `/rest/workflows` | GET | 列出 workflows |
|
||||
| `/rest/workflows/:id` | GET | 取得 workflow |
|
||||
| `/rest/workflows` | POST | 建立 workflow |
|
||||
| `/rest/workflows/:id` | PUT | 更新 workflow |
|
||||
| `/rest/workflows/:id` | DELETE | 刪除 workflow |
|
||||
| `/rest/workflows/:id/activate` | POST | 啟用 workflow |
|
||||
| `/rest/workflows/:id/deactivate` | POST | 停用 workflow |
|
||||
| `/rest/workflows/:id/execute` | POST | 執行 workflow |
|
||||
|
||||
**認證方式:**
|
||||
```bash
|
||||
curl -H "X-N8N-API-KEY: YOUR_API_KEY" \
|
||||
http://localhost:5678/rest/workflows
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 疑難排解
|
||||
|
||||
### API 404 問題
|
||||
|
||||
如果 API 傳回 404,檢查:
|
||||
|
||||
1. **n8n 是否運行中**
|
||||
```bash
|
||||
curl http://localhost:5678
|
||||
```
|
||||
|
||||
2. **n8n 初始設定(重要!)**
|
||||
- 第一次使用必須在瀏覽器完成初始化
|
||||
- 開啟 `http://localhost:5678`
|
||||
- 按照畫面指示建立管理員帳號
|
||||
- 完成後才能使用 API
|
||||
|
||||
3. **API Key 是否正確**
|
||||
```bash
|
||||
curl -H "X-N8N-API-KEY: YOUR_KEY" \
|
||||
http://localhost:5678/rest/workflows
|
||||
```
|
||||
|
||||
### n8n 初始設定(第一次使用)
|
||||
|
||||
1. 開啟瀏覽器: `http://localhost:5678`
|
||||
2. 輸入 email 和密碼建立管理員帳號
|
||||
3. 完成後進入 Settings → API
|
||||
4. 建立 API Key 並複製
|
||||
|
||||
### CLI Import vs PostgreSQL
|
||||
|
||||
n8n 使用 PostgreSQL 儲存資料:
|
||||
- CLI `n8n import:workflow` 可能寫入 SQLite
|
||||
- 手動在 UI import 會寫入 PostgreSQL
|
||||
|
||||
建議直接使用 UI 或 MCP import。
|
||||
|
||||
---
|
||||
|
||||
## 相關文件
|
||||
|
||||
- [OPENCODE_GUIDE.md](./OPENCODE_GUIDE.md) - OpenCode 使用規範
|
||||
- [INSTALL_N8N.md](./INSTALL_N8N.md) - n8n 安裝指南
|
||||
- [N8N_DEMO_WORKFLOW.md](./N8N_DEMO_WORKFLOW.md) - n8n Workflow 範例
|
||||
@@ -1,152 +0,0 @@
|
||||
# Momentry Video RAG - n8n 工作流程設定完成
|
||||
|
||||
## ✅ 最終成功版本
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| **工作流程名稱** | Video Search - Working v3 |
|
||||
| **ID** | 4vQo8I4SXEaR5E1A |
|
||||
| **狀態** | ✅ SUCCESS |
|
||||
| **執行 ID** | 1620 |
|
||||
|
||||
---
|
||||
|
||||
## 成功關鍵
|
||||
|
||||
### HTTP Request Node 正確設定
|
||||
```json
|
||||
{
|
||||
"url": "https://api.momentry.ddns.net/api/v1/n8n/search",
|
||||
"method": "POST",
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "{\"query\":\"charade\",\"limit\":3}",
|
||||
"options": {}
|
||||
}
|
||||
```
|
||||
|
||||
**重點**:
|
||||
- ✅ `specifyBody`: "json" (不是 "body")
|
||||
- ✅ `jsonBody`: 字串格式 (不是物件)
|
||||
- ✅ 使用 `"{\"query\":\"..."}` 轉義引號
|
||||
|
||||
---
|
||||
|
||||
## 所有可用工作流程
|
||||
|
||||
| 工作流程 | ID | 狀態 | 說明 |
|
||||
|---------|-----|------|------|
|
||||
| Video Search - Working v3 | 4vQo8I4SXEaR5E1A | ✅ 成功 | **推薦使用** |
|
||||
| Video Search - HTTP Only | tZbljQCFZDOJ4C0s | ❌ 失敗 | body 格式錯誤 |
|
||||
| Video Search - Debug Simple | e2CMjonwILMUYjp0 | ⚠️ 待測 | Code Node 版本 |
|
||||
| Video Search - Instant | zC5K3TbFzWGAh0la | ❌ 失敗 | `$httpRequest` 不可用 |
|
||||
|
||||
---
|
||||
|
||||
## 如何使用
|
||||
|
||||
### 方法 1: 直接執行
|
||||
```bash
|
||||
# 開啟工作流程
|
||||
open https://n8n.momentry.ddns.net/workflow/4vQo8I4SXEaR5E1A
|
||||
```
|
||||
|
||||
然後:
|
||||
1. 點擊 **"Execute Workflow"** ▶️
|
||||
2. 點擊 **"Show Result"** 節點
|
||||
3. 查看 JSON 結果
|
||||
|
||||
### 方法 2: 修改搜尋關鍵字
|
||||
1. 點擊 **"Search API"** 節點
|
||||
2. 修改 `jsonBody`:
|
||||
```json
|
||||
"{\"query\":\"您的關鍵字\",\"limit\":5}"
|
||||
```
|
||||
3. 儲存並重新執行
|
||||
|
||||
---
|
||||
|
||||
## API 端點
|
||||
|
||||
### Momentry Core API
|
||||
```
|
||||
POST https://api.momentry.ddns.net/api/v1/n8n/search
|
||||
Content-Type: application/json
|
||||
|
||||
Body:
|
||||
{
|
||||
"query": "charade",
|
||||
"limit": 3,
|
||||
"uuid": "可選的影片UUID"
|
||||
}
|
||||
```
|
||||
|
||||
### 直接測試
|
||||
```bash
|
||||
curl -X POST https://api.momentry.ddns.net/api/v1/n8n/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query":"charade","limit":3}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 已建立的文件
|
||||
|
||||
| 文件 | 路徑 | 內容 |
|
||||
|------|------|------|
|
||||
| API URL 範例 | `docs/API_URL_EXAMPLES.md` | 完整 URL 和 curl 指令 |
|
||||
| HTTP Request 指南 | `docs/N8N_HTTP_REQUEST_GUIDE.md` | Node 設定說明 |
|
||||
| 輸出查看指南 | `docs/N8N_VIEW_OUTPUT_GUIDE.md` | 如何查看結果 |
|
||||
| MCP 測試報告 | `docs/N8N_MCP_TEST_REPORT.md` | 43 個 MCP 工具 |
|
||||
| API 修復總結 | `docs/N8N_API_FIX_SUMMARY.md` | 問題修復過程 |
|
||||
| 工作流程 JSON | `docs/n8n_workflow_video_rag_mcp.json` | 原始工作流程 |
|
||||
| 測試腳本 | `docs/test_all.sh` | 自動測試腳本 |
|
||||
|
||||
---
|
||||
|
||||
## 服務狀態
|
||||
|
||||
✅ **Momentry Core**: https://api.momentry.ddns.net (Port 3002)
|
||||
✅ **n8n**: https://n8n.momentry.ddns.net (Port 5678)
|
||||
✅ **MCP 整合**: 43 個工具可用
|
||||
|
||||
---
|
||||
|
||||
## 下一步建議
|
||||
|
||||
### 1. 建立帶有參數的工作流程
|
||||
修改現有工作流程,讓 query 和 limit 可以動態輸入:
|
||||
- 添加 Webhook Node 接收外部請求
|
||||
- 或使用 Set Node 設定變數
|
||||
|
||||
### 2. 建立完整的 RAG 流程
|
||||
結合 OpenAI:
|
||||
- 搜尋影片片段
|
||||
- 使用 GPT 生成回答
|
||||
- 回傳格式化的 RAG 結果
|
||||
|
||||
### 3. 自動化監控
|
||||
- 建立定時執行的工作流程
|
||||
- 監控 API 健康狀態
|
||||
- 發送 Telegram/Email 通知
|
||||
|
||||
---
|
||||
|
||||
## 問題排除
|
||||
|
||||
如果再次遇到 "Your request is invalid":
|
||||
1. 檢查 `specifyBody` 必須設為 `"json"`
|
||||
2. `jsonBody` 必須是字串格式,不是物件
|
||||
3. 確保使用正確的 JSON 轉義: `{\"key\":\"value\"}`
|
||||
|
||||
---
|
||||
|
||||
## 完成!🎉
|
||||
|
||||
所有設定已完成:
|
||||
- ✅ n8n REST API 修復並運作正常
|
||||
- ✅ MCP 整合完成 (43 個工具)
|
||||
- ✅ Momentry Core API 可外部存取
|
||||
- ✅ 成功的工作流程已創建並測試
|
||||
|
||||
您可以開始使用 n8n 自動化管理 Momentry Core 了!
|
||||
@@ -1,141 +0,0 @@
|
||||
# n8n 工作流程輸出查看指南
|
||||
|
||||
## 問題:"Node executed successfully but no output data"
|
||||
|
||||
這是正常的!在 n8n 中,你需要**點擊節點**才能看到輸出資料。
|
||||
|
||||
---
|
||||
|
||||
## 如何查看輸出資料
|
||||
|
||||
### 方法 1: 點擊節點查看
|
||||
|
||||
1. 執行工作流程後(點擊 Execute Workflow)
|
||||
2. **點擊任何一個節點**(Node)
|
||||
3. 在右側面板會顯示該節點的輸出
|
||||
4. 查看 **JSON** 分頁看到完整資料
|
||||
|
||||
### 方法 2: 查看執行記錄
|
||||
|
||||
1. 執行工作流程
|
||||
2. 點擊左側的 **"Executions"** 選單
|
||||
3. 找到最近的執行記錄
|
||||
4. 點擊展開查看每個節點的輸出
|
||||
|
||||
### 方法 3: 使用 Respond to Webhook
|
||||
|
||||
如果你想直接看到結果,添加一個 Respond to Webhook 節點:
|
||||
|
||||
```javascript
|
||||
// 在最後一個節點之後添加:
|
||||
Node: Respond to Webhook
|
||||
├── Response Mode: Last Node
|
||||
└── Response Body: {{ JSON.stringify($json) }}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 新增的工作流程
|
||||
|
||||
### ✅ Video Search - Debug Simple
|
||||
- **ID**: e2CMjonwILMUYjp0
|
||||
- **URL**: https://n8n.momentry.ddns.net/workflow/e2CMjonwILMUYjp0
|
||||
|
||||
這個版本保證有輸出!
|
||||
|
||||
### 執行步驟
|
||||
|
||||
1. 開啟工作流程
|
||||
2. 點擊 **"Execute Workflow"** ▶️
|
||||
3. 等待執行完成(約 3-5 秒)
|
||||
4. **點擊最後一個節點** "Step 3 - Show Results"
|
||||
5. 查看右側的 **JSON** 分頁
|
||||
|
||||
### 預期看到的輸出
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "SUCCESS",
|
||||
"query": "charade",
|
||||
"totalResults": 2,
|
||||
"firstHit": {
|
||||
"text": "fun plot twists, Woody Dialog and charming perform...",
|
||||
"time": "48.8s - 55.44s",
|
||||
"score": "53%"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
或如果失敗:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "FAILED",
|
||||
"error": "error message here"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 截圖說明
|
||||
|
||||
### 執行後的畫面
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ When clicking "Execute Workflow" │ ✅ (綠色勾)
|
||||
├─────────────────────────────────────┤
|
||||
│ Step 1 - Set Query │ ✅ (綠色勾)
|
||||
├─────────────────────────────────────┤
|
||||
│ Step 2 - Call API │ ✅ (綠色勾) ← 點擊這裡
|
||||
├─────────────────────────────────────┤
|
||||
│ Step 3 - Show Results │ ✅ (綠色勾) ← 或這個
|
||||
└─────────────────────────────────────┘
|
||||
|
||||
[右側面板 - 點擊節點後顯示]
|
||||
┌─────────────────────────────────────┐
|
||||
│ Node: Step 2 - Call API │
|
||||
│ │
|
||||
│ [JSON] [Table] [Schema] │ ← 點擊 JSON
|
||||
│ │
|
||||
│ { │
|
||||
│ "success": true, │
|
||||
│ "query": "charade", │
|
||||
│ ... │
|
||||
│ } │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 快速測試
|
||||
|
||||
如果不想用瀏覽器,直接在這裡執行:
|
||||
|
||||
```bash
|
||||
# 開啟簡化版工作流程
|
||||
open https://n8n.momentry.ddns.net/workflow/e2CMjonwILMUYjp0
|
||||
```
|
||||
|
||||
然後:
|
||||
1. 點擊 **Execute Workflow**
|
||||
2. 點擊 **"Step 3 - Show Results"** 節點
|
||||
3. 看右側 JSON 面板
|
||||
|
||||
---
|
||||
|
||||
## 如果仍然看不到資料
|
||||
|
||||
檢查:
|
||||
1. ✅ 工作流程已儲存
|
||||
2. ✅ 點擊了正確的節點(有綠色勾的)
|
||||
3. ✅ 右側面板已展開(點擊 JSON 分頁)
|
||||
4. ✅ 沒有紅色錯誤訊息
|
||||
|
||||
---
|
||||
|
||||
## 聯絡支援
|
||||
|
||||
如果還是有問題,請告訴我:
|
||||
1. 你點擊的是哪個節點?
|
||||
2. 右側面板顯示什麼?
|
||||
3. 有沒有紅色錯誤訊息?
|
||||
@@ -1,430 +0,0 @@
|
||||
# OpenCode 使用規範
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-21 |
|
||||
| 文件版本 | V1.1 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 |
|
||||
|------|------|------|--------|
|
||||
| V1.0 | 2026-03-21 | 創建文件 | Warren |
|
||||
| V1.1 | 2026-03-21 | 新增 MCP 設定章節 | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
本文檔定義使用 OpenCode 進行專案開發的最佳實踐,確保開發效率和程式碼品質。
|
||||
|
||||
---
|
||||
|
||||
## 任務管理
|
||||
|
||||
### 任務批次策略
|
||||
|
||||
**原則**: 一次處理 1-2 個功能,完成後驗證,再繼續下一個。
|
||||
|
||||
| 批次大小 | 適用場景 | 說明 |
|
||||
|----------|----------|------|
|
||||
| 1 個 | 緊急修復、簡單任務 | 快速完成 |
|
||||
| 2-3 個 | 一般功能開發 | 平衡效率與品質 |
|
||||
| 3+ 個 | 大型重構 | 需要更詳細的追蹤 |
|
||||
|
||||
### 驗證流程
|
||||
|
||||
每個任務完成後必須執行:
|
||||
|
||||
```bash
|
||||
# 1. 編譯檢查
|
||||
cargo check
|
||||
|
||||
# 2. Lint 檢查
|
||||
cargo clippy --lib
|
||||
|
||||
# 3. 單元測試
|
||||
cargo test --lib
|
||||
|
||||
# 4. 格式化檢查
|
||||
cargo fmt -- --check
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 溝通模式
|
||||
|
||||
### 有效的任務描述
|
||||
|
||||
**建議格式**:
|
||||
```
|
||||
執行 [功能名稱]
|
||||
- 優先級: 高/中/低
|
||||
- 驗收標準: [明確的標準]
|
||||
- 約束: [限制條件]
|
||||
```
|
||||
|
||||
**範例**:
|
||||
```
|
||||
實作 monitor_jobs 表
|
||||
- 優先級: 高
|
||||
- 驗收標準: CRUD 操作可用,單元測試通過
|
||||
- 約束: 使用現有架構
|
||||
```
|
||||
|
||||
### 明確的暫停點
|
||||
|
||||
在每個階段完成後主動詢問:
|
||||
|
||||
```
|
||||
[任務] 完成。要繼續:
|
||||
1. 實作功能 A
|
||||
2. 添加測試
|
||||
3. 更新文檔
|
||||
```
|
||||
|
||||
### 不建議的溝通
|
||||
|
||||
| 模式 | 問題 | 建議 |
|
||||
|------|------|------|
|
||||
| 一次指定太多項目 | 增加複雜度,難以追蹤 | 分批處理 |
|
||||
| 模糊的任務描述 | 難以評估進度 | 使用明確的驗收標準 |
|
||||
| 從不驗證 | 累積問題 | 每步驟完成後驗證 |
|
||||
|
||||
---
|
||||
|
||||
## 決策點
|
||||
|
||||
### 常見決策點
|
||||
|
||||
| 階段 | 問題 | 選項 |
|
||||
|------|------|------|
|
||||
| 開始 | 如何開始? | 先了解現況 / 直接實作 |
|
||||
| 實作 | 實作方式? | 保持現有架構 / 重構 |
|
||||
| 驗證 | 通過了嗎? | 繼續 / 修復問題 |
|
||||
| 完成 | 還有什麼? | 下一個任務 / 結束 |
|
||||
|
||||
### 決策準則
|
||||
|
||||
1. **安全優先**: 破壞性變更需要明確確認
|
||||
2. **驗證後繼續**: 每步驟完成後驗證
|
||||
3. **文檔同步**: 變更後更新文檔
|
||||
|
||||
---
|
||||
|
||||
## 文檔使用
|
||||
|
||||
### 必讀文檔
|
||||
|
||||
| 文檔 | 用途 | 查閱時機 |
|
||||
|------|------|----------|
|
||||
| `AGENTS.md` | 專案規範 | 每次對話開始 |
|
||||
| `docs/*.md` | 技術規格 | 功能實作前 |
|
||||
|
||||
### 文檔更新時機
|
||||
|
||||
| 變更類型 | 需要更新 |
|
||||
|----------|----------|
|
||||
| 新功能 | `AGENTS.md` + 相關技術文檔 |
|
||||
| 架構變更 | `ARCHITECTURE_EVALUATION.md` |
|
||||
| 問題修復 | `PENDING_ISSUES.md` |
|
||||
| 環境變更 | `INSTALL_*.md` |
|
||||
|
||||
---
|
||||
|
||||
## 審查清單
|
||||
|
||||
### 實作完成後檢查
|
||||
|
||||
- [ ] `cargo clippy --lib` 通過
|
||||
- [ ] `cargo test --lib` 通過
|
||||
- [ ] `cargo fmt -- --check` 通過
|
||||
- [ ] 文檔已更新
|
||||
- [ ] 新功能有單元測試
|
||||
- [ ] Pre-commit hook 通過
|
||||
|
||||
### 對話結束前
|
||||
|
||||
- [ ] 所有變更已驗證
|
||||
- [ ] 文檔已同步
|
||||
- [ ] 下一步計劃明確
|
||||
|
||||
---
|
||||
|
||||
## 範例流程
|
||||
|
||||
### 範例 1: 實作新功能
|
||||
|
||||
```
|
||||
用戶: 實作使用者認證功能
|
||||
|
||||
OpenCode:
|
||||
1. 分析現有架構
|
||||
2. 創建任務清單
|
||||
3. 實作核心功能
|
||||
4. 添加單元測試
|
||||
5. 更新文檔
|
||||
6. 驗證通過
|
||||
|
||||
用戶: 完成,繼續下一個
|
||||
```
|
||||
|
||||
### 範例 2: 修復問題
|
||||
|
||||
```
|
||||
用戶: 修復登入超時問題
|
||||
|
||||
OpenCode:
|
||||
1. 重現問題
|
||||
2. 分析根因
|
||||
3. 實作修復
|
||||
4. 驗證修復
|
||||
5. 添加測試防止回歸
|
||||
|
||||
用戶: 確認修復完成
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常見問題
|
||||
|
||||
### Q: 如何避免一次處理太多?
|
||||
|
||||
**A**: 使用 `/todo` 追蹤任務,分批處理。
|
||||
|
||||
### Q: 如何確保文檔同步?
|
||||
|
||||
**A**: 每個任務完成後檢查是否需要更新文檔。
|
||||
|
||||
### Q: 何時應該結束對話?
|
||||
|
||||
**A**: 當主要任務完成,且沒有緊急的後續步驟時。
|
||||
|
||||
---
|
||||
|
||||
## MCP 設定
|
||||
|
||||
### 設定檔案
|
||||
|
||||
OpenCode MCP 設定位於 `~/.config/opencode/opencode.json`
|
||||
|
||||
### 已啟用的 MCP Servers
|
||||
|
||||
| Server | 用途 | 命令 |
|
||||
|--------|------|------|
|
||||
| gitea | Gitea API 操作 | `/opt/homebrew/bin/gitea-mcp-server` |
|
||||
| n8n | n8n Workflow 操作 | `/opt/homebrew/bin/mcp-n8n` |
|
||||
| postgres | PostgreSQL 資料庫查詢 | `/opt/homebrew/bin/mcp-server-postgres` |
|
||||
| redis | Redis 快取操作 | `/opt/homebrew/bin/mcp-server-redis` |
|
||||
| qdrant | Qdrant 向量搜尋 | `/opt/homebrew/bin/mcp-server-qdrant` |
|
||||
| filesystem | 檔案系統操作 | `/opt/homebrew/bin/mcp-server-filesystem` |
|
||||
|
||||
### MCP 設定格式
|
||||
|
||||
**Schema 參考**: `https://opencode.ai/config.json`
|
||||
|
||||
**必要欄位**:
|
||||
| 欄位 | 類型 | 說明 |
|
||||
|------|------|------|
|
||||
| `type` | string | `"local"` 或 `"remote"` |
|
||||
| `command` | array | 命令和參數(local 必要) |
|
||||
| `url` | string | 遠端 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 安裝指南
|
||||
@@ -1,535 +0,0 @@
|
||||
# OpenCode MCP Servers 安裝指南
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-03-24 |
|
||||
| 文件版本 | V1.1 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-24 | 創建文件 | OpenCode | OpenCode / big-pickle |
|
||||
| V1.1 | 2026-03-24 | 新增 sentry, context7, playwright MCP | OpenCode | OpenCode / big-pickle |
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
本文檔說明如何在 macOS 上為 OpenCode 安裝和配置 MCP (Model Context Protocol) Servers,透過標準化的協定讓 AI 助手能夠存取各種外部服務和工具。
|
||||
|
||||
MCP Servers 提供:
|
||||
- 標準化的工具調用介面
|
||||
- 安全的資源存取控制
|
||||
- 統一的配置和管理方式
|
||||
|
||||
---
|
||||
|
||||
## 已安裝的 MCP Servers
|
||||
|
||||
| Server | 安裝方式 | 用途 | 狀態 |
|
||||
|--------|----------|------|------|
|
||||
| gitea | Homebrew | Gitea API 操作 | ✅ |
|
||||
| n8n | NPM (@nextoolsolutions/mcp-n8n) | n8n Workflow 管理 | ✅ |
|
||||
| postgres | NPM (@modelcontextprotocol/server-postgres) | PostgreSQL 資料庫查詢 | ✅ |
|
||||
| redis | NPM (@modelcontextprotocol/server-redis) | Redis 快取操作 | ✅ |
|
||||
| mongodb | NPM (mongodb-mcp-server) | MongoDB 文件資料庫 | ✅ |
|
||||
| qdrant | Python (qdrant/mcp-server-qdrant) | Qdrant 向量搜尋 | ✅ |
|
||||
| filesystem | NPM (@modelcontextprotocol/server-filesystem) | 檔案系統操作 | ✅ |
|
||||
| sentry | NPM (@sentry/mcp-server) | 錯誤追蹤和監控 | ⏳ |
|
||||
| context7 | NPM (@upstash/context7-mcp) | 技術文檔搜尋 | ✅ |
|
||||
| playwright | NPM (@playwright/mcp) | 瀏覽器自動化 | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 前置條件
|
||||
|
||||
- OpenCode 已安裝
|
||||
- Node.js (用於 NPM 套件)
|
||||
- Python 3.11+ (用於 qdrant-mcp)
|
||||
- 相關服務已運行 (PostgreSQL, Redis, Qdrant, Gitea, n8n, MongoDB)
|
||||
|
||||
---
|
||||
|
||||
## 安裝步驟
|
||||
|
||||
### Step 1: 安裝 NPM MCP Servers
|
||||
|
||||
```bash
|
||||
npm install -g @modelcontextprotocol/server-postgres
|
||||
npm install -g @modelcontextprotocol/server-redis
|
||||
npm install -g @modelcontextprotocol/server-filesystem
|
||||
npm install -g @modelcontextprotocol/server-everything
|
||||
npm install -g @nextoolsolutions/mcp-n8n
|
||||
npm install -g mongodb-mcp-server
|
||||
npm install -g @sentry/mcp-server
|
||||
npm install -g @upstash/context7-mcp
|
||||
npm install -g @playwright/mcp
|
||||
```
|
||||
|
||||
**驗證**:
|
||||
|
||||
```bash
|
||||
which mcp-server-postgres
|
||||
which mcp-server-redis
|
||||
which mcp-server-filesystem
|
||||
which mcp-n8n
|
||||
which mongodb-mcp-server
|
||||
which sentry-mcp
|
||||
which context7-mcp
|
||||
which playwright-mcp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 安裝 gitea-mcp-server
|
||||
|
||||
```bash
|
||||
brew install gitea-mcp-server
|
||||
```
|
||||
|
||||
**驗證**:
|
||||
|
||||
```bash
|
||||
which gitea-mcp-server
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 安裝 MongoDB MCP Server (已包含在 Step 1)
|
||||
|
||||
MongoDB MCP 已透過 NPM 安裝:
|
||||
|
||||
```bash
|
||||
npm install -g mongodb-mcp-server
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 安裝 Qdrant MCP Server (Python)
|
||||
|
||||
```bash
|
||||
cd /tmp
|
||||
git clone https://github.com/qdrant/mcp-server-qdrant.git
|
||||
cd mcp-server-qdrant
|
||||
/opt/homebrew/bin/python3.11 -m pip install -e .
|
||||
```
|
||||
|
||||
**驗證**:
|
||||
|
||||
```bash
|
||||
which mcp-server-qdrant
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 5: 配置 OpenCode MCP 設定
|
||||
|
||||
編輯 `~/.config/opencode/opencode.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"mcp": {
|
||||
"gitea": {
|
||||
"type": "local",
|
||||
"enabled": true,
|
||||
"command": [
|
||||
"/opt/homebrew/bin/gitea-mcp-server",
|
||||
"-token",
|
||||
"<GITEA_TOKEN>",
|
||||
"-host",
|
||||
"http://localhost:3000"
|
||||
]
|
||||
},
|
||||
"n8n": {
|
||||
"type": "local",
|
||||
"enabled": true,
|
||||
"command": ["/opt/homebrew/bin/mcp-n8n"],
|
||||
"environment": {
|
||||
"N8N_BASE_URL": "http://localhost:5678",
|
||||
"N8N_API_KEY": "<N8N_API_KEY>"
|
||||
}
|
||||
},
|
||||
"postgres": {
|
||||
"type": "local",
|
||||
"enabled": true,
|
||||
"command": [
|
||||
"/opt/homebrew/bin/mcp-server-postgres",
|
||||
"postgresql://accusys:accusys@localhost:5432/momentry"
|
||||
]
|
||||
},
|
||||
"redis": {
|
||||
"type": "local",
|
||||
"enabled": true,
|
||||
"command": [
|
||||
"/opt/homebrew/bin/mcp-server-redis",
|
||||
"redis://:accusys@localhost:6379"
|
||||
]
|
||||
},
|
||||
"mongodb": {
|
||||
"type": "local",
|
||||
"enabled": true,
|
||||
"command": ["/opt/homebrew/bin/mongodb-mcp-server"],
|
||||
"environment": {
|
||||
"MONGODB_URI": "mongodb://localhost:27017"
|
||||
}
|
||||
},
|
||||
"qdrant": {
|
||||
"type": "local",
|
||||
"enabled": true,
|
||||
"command": ["/opt/homebrew/bin/mcp-server-qdrant"],
|
||||
"environment": {
|
||||
"QDRANT_URL": "http://localhost:6333",
|
||||
"QDRANT_API_KEY": ""
|
||||
}
|
||||
},
|
||||
"filesystem": {
|
||||
"type": "local",
|
||||
"enabled": true,
|
||||
"command": [
|
||||
"/opt/homebrew/bin/mcp-server-filesystem",
|
||||
"/Users/accusys/momentry"
|
||||
]
|
||||
},
|
||||
"sentry": {
|
||||
"type": "local",
|
||||
"enabled": true,
|
||||
"command": [
|
||||
"/opt/homebrew/bin/sentry-mcp",
|
||||
"--access-token",
|
||||
"<SENTRY_ACCESS_TOKEN>"
|
||||
]
|
||||
},
|
||||
"context7": {
|
||||
"type": "local",
|
||||
"enabled": true,
|
||||
"command": ["/opt/homebrew/bin/context7-mcp"]
|
||||
},
|
||||
"playwright": {
|
||||
"type": "local",
|
||||
"enabled": true,
|
||||
"command": ["/opt/homebrew/bin/playwright-mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 6: 驗證 MCP Servers
|
||||
|
||||
```bash
|
||||
# 列出所有 MCP servers
|
||||
opencode mcp ls
|
||||
|
||||
# 或在 OpenCode 中執行
|
||||
/mcps
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## MCP Server 工具說明
|
||||
|
||||
### gitea
|
||||
|
||||
| 工具 | 說明 |
|
||||
|------|------|
|
||||
| list_repos | 列出倉庫 |
|
||||
| get_repo | 取得倉庫詳情 |
|
||||
| list_issues | 列出 Issues |
|
||||
| create_issue | 建立 Issue |
|
||||
| list_pulls | 列出 Pull Requests |
|
||||
|
||||
---
|
||||
|
||||
### n8n (43 個工具)
|
||||
|
||||
#### Workflows (10)
|
||||
| 工具 | 說明 |
|
||||
|------|------|
|
||||
| n8n_list_workflows | 列出所有 workflows |
|
||||
| n8n_get_workflow | 取得 workflow 詳情 |
|
||||
| n8n_create_workflow | 建立新 workflow |
|
||||
| n8n_update_workflow | 更新 workflow |
|
||||
| n8n_delete_workflow | 刪除 workflow |
|
||||
| n8n_activate_workflow | 啟用 workflow |
|
||||
| n8n_deactivate_workflow | 停用 workflow |
|
||||
| n8n_execute_workflow | 執行 workflow |
|
||||
| n8n_get_workflow_tags | 取得 workflow 標籤 |
|
||||
| n8n_update_workflow_tags | 更新 workflow 標籤 |
|
||||
|
||||
#### Executions (3)
|
||||
| 工具 | 說明 |
|
||||
|------|------|
|
||||
| n8n_list_executions | 列出執行記錄 |
|
||||
| n8n_get_execution | 取得執行詳情 |
|
||||
| n8n_delete_execution | 刪除執行記錄 |
|
||||
|
||||
#### Data Tables (8)
|
||||
| 工具 | 說明 |
|
||||
|------|------|
|
||||
| n8n_list_datatables | 列出資料表 |
|
||||
| n8n_create_datatable | 建立資料表 |
|
||||
| n8n_get_datatable | 取得資料表結構 |
|
||||
| n8n_get_datatable_rows | 取得資料表列 |
|
||||
| n8n_insert_datatable_rows | 插入資料列 |
|
||||
| n8n_update_datatable_rows | 更新資料列 |
|
||||
| n8n_upsert_datatable_row | 插入或更新資料列 |
|
||||
| n8n_delete_datatable_rows | 刪除資料列 |
|
||||
|
||||
#### Tags (5)
|
||||
| 工具 | 說明 |
|
||||
|------|------|
|
||||
| n8n_list_tags | 列出所有標籤 |
|
||||
| n8n_get_tag | 取得標籤 |
|
||||
| n8n_create_tag | 建立標籤 |
|
||||
| n8n_update_tag | 更新標籤 |
|
||||
| n8n_delete_tag | 刪除標籤 |
|
||||
|
||||
#### 其他 (17)
|
||||
| 工具 | 說明 |
|
||||
|------|------|
|
||||
| n8n_list_credentials | 列出憑證 |
|
||||
| n8n_create_credential | 建立憑證 |
|
||||
| n8n_delete_credential | 刪除憑證 |
|
||||
| n8n_get_credential_schema | 取得憑證 schema |
|
||||
| n8n_list_users | 列出使用者 |
|
||||
| n8n_get_user | 取得使用者 |
|
||||
| n8n_delete_user | 刪除使用者 |
|
||||
| n8n_list_variables | 列出變數 |
|
||||
| n8n_create_variable | 建立變數 |
|
||||
| n8n_delete_variable | 刪除變數 |
|
||||
| n8n_list_projects | 列出專案 |
|
||||
| n8n_create_project | 建立專案 |
|
||||
| n8n_update_project | 更新專案 |
|
||||
| n8n_delete_project | 刪除專案 |
|
||||
| n8n_generate_audit | 產生安全審計報告 |
|
||||
| n8n_health_check | 健康檢查 |
|
||||
| n8n_trigger_webhook | 觸發 webhook |
|
||||
|
||||
---
|
||||
|
||||
### postgres
|
||||
|
||||
| 工具 | 說明 |
|
||||
|------|------|
|
||||
| query | 執行唯讀 SQL 查詢 |
|
||||
|
||||
**範例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"sql": "SELECT * FROM users LIMIT 10;"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### redis
|
||||
|
||||
| 工具 | 說明 |
|
||||
|------|------|
|
||||
| set | 設定 key-value 配對 |
|
||||
| get | 取得 key 的值 |
|
||||
| delete | 刪除 key(s) |
|
||||
| list | 列出符合模式的 keys |
|
||||
|
||||
---
|
||||
|
||||
### mongodb
|
||||
|
||||
| 工具 | 說明 |
|
||||
|------|------|
|
||||
| mongodb_list_databases | 列出所有資料庫 |
|
||||
| mongodb_list_collections | 列出資料庫中的集合 |
|
||||
| mongodb_find | 查詢文件 |
|
||||
| mongodb_insert_one | 插入單個文件 |
|
||||
| mongodb_insert_many | 插入多個文件 |
|
||||
| mongodb_update_one | 更新單個文件 |
|
||||
| mongodb_update_many | 更新多個文件 |
|
||||
| mongodb_delete_one | 刪除單個文件 |
|
||||
| mongodb_delete_many | 刪除多個文件 |
|
||||
| mongodb_aggregate | 聚合查詢 |
|
||||
|
||||
**範例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"database": "momentry",
|
||||
"collection": "videos",
|
||||
"filter": {"uuid": "abc123"}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### qdrant
|
||||
|
||||
| 工具 | 說明 |
|
||||
|------|------|
|
||||
| 查詢工具 | 向量搜尋和集合管理 |
|
||||
|
||||
---
|
||||
|
||||
### filesystem
|
||||
|
||||
| 工具 | 說明 |
|
||||
|------|------|
|
||||
| read_file | 讀取檔案 |
|
||||
| write_file | 寫入檔案 |
|
||||
| list_directory | 列出目錄 |
|
||||
| create_directory | 建立目錄 |
|
||||
|
||||
---
|
||||
|
||||
### sentry
|
||||
|
||||
| 工具 | 說明 |
|
||||
|------|------|
|
||||
| sentry_list_issues | 列出 issues |
|
||||
| sentry_get_issue | 取得 issue 詳情 |
|
||||
| sentry_create_issue | 建立 issue |
|
||||
| sentry_update_issue | 更新 issue |
|
||||
| sentry_list_projects | 列出專案 |
|
||||
| sentry_search_docs | 搜尋 Sentry 文件 |
|
||||
|
||||
**注意**: 需要設定 `SENTRY_ACCESS_TOKEN`
|
||||
|
||||
**取得 Token**:
|
||||
1. 登入 https://sentry.io
|
||||
2. Settings → Developer Tools → API Keys
|
||||
3. 建立新 Token (需要 `event:read`, `project:read` 權限)
|
||||
|
||||
---
|
||||
|
||||
### context7
|
||||
|
||||
| 工具 | 說明 |
|
||||
|------|------|
|
||||
| context7_resolve-library-id | 解析函式庫 ID |
|
||||
| context7_query-docs | 查詢函式庫文件 |
|
||||
|
||||
**用途**: 即時技術文件和函式庫查詢,獲取最新的 API 語法和範例。
|
||||
|
||||
---
|
||||
|
||||
### playwright
|
||||
|
||||
| 工具 | 說明 |
|
||||
|------|------|
|
||||
| playwright_navigate | 導航到 URL |
|
||||
| playwright_screenshot | 截圖 |
|
||||
| playwright_click | 點擊元素 |
|
||||
| playwright_type | 輸入文字 |
|
||||
| playwright_evaluate | 執行 JavaScript |
|
||||
|
||||
**用途**: 瀏覽器自動化測試、網頁截圖、網頁內容抓取。
|
||||
|
||||
---
|
||||
|
||||
## 管理指令
|
||||
|
||||
### 新增 MCP Server
|
||||
|
||||
```bash
|
||||
opencode mcp add
|
||||
```
|
||||
|
||||
### 列出 MCP Servers
|
||||
|
||||
```bash
|
||||
opencode mcp ls
|
||||
```
|
||||
|
||||
### 認證 OAuth Server
|
||||
|
||||
```bash
|
||||
opencode mcp auth <server-name>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 故障排除
|
||||
|
||||
### MCP Server 連線失敗
|
||||
|
||||
1. 檢查服務是否運行:
|
||||
```bash
|
||||
# PostgreSQL
|
||||
pg_isready -h localhost -p 5432
|
||||
|
||||
# Redis
|
||||
redis-cli -a accusys ping
|
||||
|
||||
# Qdrant
|
||||
curl -s http://localhost:6333/
|
||||
```
|
||||
|
||||
2. 檢查 MCP Server 是否安裝:
|
||||
```bash
|
||||
which mcp-server-postgres
|
||||
which mcp-server-redis
|
||||
```
|
||||
|
||||
3. 驗證配置格式正確
|
||||
|
||||
---
|
||||
|
||||
### Token 或 API Key 無效
|
||||
|
||||
1. PostgreSQL: 確認使用者名稱和密碼
|
||||
2. Redis: 確認密碼
|
||||
3. n8n: 確認 API Key 未過期
|
||||
4. Gitea: 確認 Token 有效
|
||||
|
||||
---
|
||||
|
||||
## 檔案位置
|
||||
|
||||
| 類型 | 路徑 | 說明 |
|
||||
|------|------|------|
|
||||
| OpenCode 配置 | ~/.config/opencode/opencode.json | MCP 設定檔 |
|
||||
| Gitea MCP | /opt/homebrew/bin/gitea-mcp-server | 安裝路徑 |
|
||||
| n8n MCP | /opt/homebrew/bin/mcp-n8n | 安裝路徑 |
|
||||
| Postgres MCP | /opt/homebrew/bin/mcp-server-postgres | 安裝路徑 |
|
||||
| Redis MCP | /opt/homebrew/bin/mcp-server-redis | 安裝路徑 |
|
||||
| Qdrant MCP | /opt/homebrew/bin/mcp-server-qdrant | 安裝路徑 |
|
||||
| Filesystem MCP | /opt/homebrew/bin/mcp-server-filesystem | 安裝路徑 |
|
||||
|
||||
---
|
||||
|
||||
## 常用指令
|
||||
|
||||
```bash
|
||||
# 列出所有 MCP servers
|
||||
opencode mcp ls
|
||||
|
||||
# 測試 PostgreSQL MCP
|
||||
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"query","arguments":{"sql":"SELECT 1;"}}}' | \
|
||||
/opt/homebrew/bin/mcp-server-postgres postgresql://accusys:accusys@localhost:5432/momentry
|
||||
|
||||
# 測試 Redis MCP
|
||||
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"ping","arguments":{}}}' | \
|
||||
/opt/homebrew/bin/mcp-server-redis redis://:accusys@localhost:6379
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 版本資訊
|
||||
|
||||
- 版本: 1.0
|
||||
- 安裝日期: 2026-03-24
|
||||
- 文件更新: 2026-03-24
|
||||
|
||||
---
|
||||
|
||||
## 相關文件
|
||||
|
||||
- [OpenCode Guide](./OPENCODE_GUIDE.md) - OpenCode 使用指南
|
||||
- [Gitea MCP 安裝](./INSTALL_GITEA_MCP.md) - Gitea MCP 詳細設定
|
||||
- [n8n MCP 設定](./N8N_MCP_SETUP.md) - n8n MCP 詳細設定
|
||||
- [服務總覽](./SERVICES.md) - 所有服務狀態總覽
|
||||
@@ -1,102 +0,0 @@
|
||||
# YOLO Resume 功能整合規劃
|
||||
|
||||
## 現有資源
|
||||
|
||||
### 1. video_yolo_player 專案
|
||||
**位置**: `/Users/accusys/video_yolo_player/video_yolo_object_prescan.py`
|
||||
|
||||
**功能**:
|
||||
- ✅ Ctrl+C 暫停並保存進度
|
||||
- ✅ 自動從上一次繼續 (自動偵測 last_processed_frame)
|
||||
- ✅ 可配置自動保存間隔 (預設 30 秒)
|
||||
- ✅ 完整 metadata 追蹤 (處理時間、檢測數量等)
|
||||
- ✅ 互動式詢問是否繼續
|
||||
|
||||
### 2. momentry_core_0.1 專案
|
||||
**位置**: `/Users/accusys/momentry_core_0.1/scripts/yolo_processor.py`
|
||||
|
||||
## 整合狀態
|
||||
|
||||
| 項目 | 狀態 | 完成日期 |
|
||||
|------|------|----------|
|
||||
| Python script 整合 | ✅ 已完成 | 2026-03-22 |
|
||||
| Rust JSON 格式支援 | ✅ 已完成 | 2026-03-22 |
|
||||
| Auto-save 功能 (時間) | ✅ 已完成 | 2026-03-22 |
|
||||
| Auto-save 功能 (frame) | ✅ 已完成 | 2026-03-22 |
|
||||
| Ctrl+C 信號處理 | ✅ 已完成 | 2026-03-22 |
|
||||
| --force 參數 | ✅ 已完成 | 2026-03-22 |
|
||||
| --auto-save-frames 參數 | ✅ 已完成 | 2026-03-22 |
|
||||
|
||||
## 已實作功能
|
||||
|
||||
### momentry_core_0.1/scripts/yolo_processor.py
|
||||
|
||||
```python
|
||||
# 新增功能:
|
||||
def load_existing_data(output_file) # 載入現有資料
|
||||
def save_detection_data(output_file) # 保存進度
|
||||
def signal_handler(signum, frame) # Ctrl+C 處理
|
||||
|
||||
# 新增參數:
|
||||
--auto-save 30 # 自動保存間隔 (秒)
|
||||
--auto-save-frames 300 # 每 N frames 保存一次 (先到為準)
|
||||
--force # 強制從頭開始
|
||||
```
|
||||
|
||||
### 輸出格式
|
||||
|
||||
```json
|
||||
{
|
||||
"metadata": {
|
||||
"video_path": "...",
|
||||
"fps": 24.0,
|
||||
"total_frames": 1000,
|
||||
"status": "in_progress" | "completed" | "interrupted",
|
||||
"total_detections": 5000,
|
||||
"processing_time": 120.5,
|
||||
"auto_save_count": 4,
|
||||
"auto_save_interval": 30,
|
||||
"auto_save_frames": 300,
|
||||
"last_saved_frame": 12500,
|
||||
"last_saved_at": "ISO timestamp"
|
||||
},
|
||||
"frames": {
|
||||
"1": { "frame_number": 1, "time_seconds": 0.0, "detections": [...] },
|
||||
"2": { "frame_number": 2, "time_seconds": 0.042, "detections": [...] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Auto-save 觸發條件
|
||||
|
||||
**滿足以下任一條件就會寫入磁碟:**
|
||||
1. 距離上次儲存已超過 `--auto-save` 秒數
|
||||
2. 距離上次儲存已處理超過 `--auto-save-frames` 個 frames
|
||||
|
||||
### 使用方式
|
||||
|
||||
```bash
|
||||
# 第一次執行 (預設: 30秒 或 300 frames)
|
||||
python yolo_processor.py video.mp4 output.json --uuid "abc123"
|
||||
|
||||
# 中斷後繼續 (自動偵測)
|
||||
python yolo_processor.py video.mp4 output.json --uuid "abc123"
|
||||
|
||||
# 強制從頭開始
|
||||
python yolo_processor.py video.mp4 output.json --force
|
||||
|
||||
# 自訂自動保存間隔
|
||||
python yolo_processor.py video.mp4 output.json --auto-save 10 --auto-save-frames 100
|
||||
```
|
||||
|
||||
### Rust 端支援
|
||||
|
||||
`check_json_completeness()` 已更新以支援新格式:
|
||||
- 讀取 `frames` dict 的最後一個 key 作為 `last_processed_frame`
|
||||
- 檢查 `metadata.status` 判斷是否完成
|
||||
|
||||
## 待測試項目
|
||||
|
||||
- [ ] 實際執行中斷後繼續
|
||||
- [ ] 驗證 large video (如 Old_Time_Movie_Show) 繼續處理
|
||||
- [ ] 驗證 Rust --resume 參數正確傳遞
|
||||
@@ -1,523 +0,0 @@
|
||||
---
|
||||
document_type: "implementation_guide"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "SFTPGo Demo 用戶指南"
|
||||
date: "2026-04-25"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "demo"
|
||||
- "sftpgo"
|
||||
- "用戶指南"
|
||||
ai_query_hints:
|
||||
- "查詢 SFTPGo Demo 用戶指南 的內容"
|
||||
- "SFTPGo Demo 用戶指南 的主要目的是什麼?"
|
||||
- "如何操作或實施 SFTPGo Demo 用戶指南?"
|
||||
---
|
||||
|
||||
# SFTPGo Demo 用戶指南
|
||||
|
||||
## Web 管理介面
|
||||
|
||||
**URL**: https://sftpgo.momentry.ddns.net
|
||||
|
||||
### 登入方式
|
||||
|
||||
| 角色 | 用戶名 | 密碼 |
|
||||
|------|--------|------|
|
||||
| **Demo 用戶** | `demo` | `demopassword123` |
|
||||
|
||||
### 可用功能
|
||||
|
||||
- 瀏覽個人目錄結構
|
||||
- 上傳、下載檔案
|
||||
- 查看上傳記錄
|
||||
|
||||
---
|
||||
|
||||
## 快速連線資訊
|
||||
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| **主機** | `sftpgo.momentry.ddns.net` |
|
||||
| **SFTP 連接埠** | `2022` |
|
||||
| **用戶名** | `demo` |
|
||||
| **密碼** | `demopassword123` |
|
||||
| **主目錄** | `/demo` |
|
||||
|
||||
---
|
||||
|
||||
## 連線方式
|
||||
|
||||
### 1. 命令列 SFTP
|
||||
|
||||
```bash
|
||||
# 使用密碼連線
|
||||
sshpass -p "demopassword123" sftp -P 2022 demo@sftpgo.momentry.ddns.net
|
||||
|
||||
# 使用金鑰連線 (需先設定)
|
||||
sftp -P 2022 -i ~/.ssh/id_rsa demo@sftpgo.momentry.ddns.net
|
||||
```
|
||||
|
||||
### 2. FileZilla
|
||||
|
||||
1. **主機**: `sftp://sftpgo.momentry.ddns.net`
|
||||
2. **連接埠**: `2022`
|
||||
3. **協定**: `SFTP`
|
||||
4. **登入類型**: `一般`
|
||||
5. **用戶名**: `demo`
|
||||
6. **密碼**: `demopassword123`
|
||||
|
||||
### 3. Cyberduck (macOS)
|
||||
|
||||
1. 選擇 **連線 > 新連線**
|
||||
2. 協定選擇 **SFTP (SSH File Transfer Protocol)**
|
||||
3. 伺服器: `sftpgo.momentry.ddns.net`
|
||||
4. 連接埠: `2022`
|
||||
5. 使用者名稱: `demo`
|
||||
6. 密碼: `demopassword123`
|
||||
|
||||
### 4. curl 上傳
|
||||
|
||||
```bash
|
||||
curl -u demo:demopassword123 \
|
||||
-T /path/to/video.mp4 \
|
||||
sftp://sftpgo.momentry.ddns.net:2022/demo/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## SFTP 基本操作
|
||||
|
||||
### 連線後常用指令
|
||||
|
||||
```bash
|
||||
# 進入互動式模式
|
||||
sftp demo@sftpgo.momentry.ddns.net -P 2022
|
||||
|
||||
# 常用指令
|
||||
sftp> pwd # 顯示目前目錄
|
||||
sftp> ls # 列出檔案
|
||||
sftp> ls -la # 詳細列表
|
||||
sftp> cd uploads # 切換目錄
|
||||
sftp> mkdir videos # 建立目錄
|
||||
sftp> put local.mp4 # 上傳檔案
|
||||
sftp> get remote.mp4 # 下載檔案
|
||||
sftp> rm old.mp4 # 刪除檔案
|
||||
sftp> exit # 斷線
|
||||
```
|
||||
|
||||
### 批次上傳
|
||||
|
||||
```bash
|
||||
# 上傳多個檔案
|
||||
sshpass -p "demopassword123" sftp -P 2022 demo@sftpgo.momentry.ddns.net <<EOF
|
||||
cd uploads
|
||||
put video1.mp4
|
||||
put video2.mp4
|
||||
put video3.mp4
|
||||
bye
|
||||
EOF
|
||||
|
||||
# 使用 glob 上傳
|
||||
sshpass -p "demopassword123" sftp -P 2022 demo@sftpgo.momentry.ddns.net <<EOF
|
||||
mput /path/to/videos/*.mp4
|
||||
bye
|
||||
EOF
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 自動上傳腳本
|
||||
|
||||
### Bash 腳本
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# upload.sh - 上傳視頻到 Momentry
|
||||
|
||||
HOST="sftpgo.momentry.ddns.net"
|
||||
PORT="2022"
|
||||
USER="demo"
|
||||
PASS="demopassword123"
|
||||
REMOTE_DIR="/demo/uploads"
|
||||
|
||||
# 要上傳的檔案
|
||||
FILE="$1"
|
||||
|
||||
if [ -z "$FILE" ]; then
|
||||
echo "用法: $0 <檔案路徑>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sshpass -p "$PASS" sftp -P $PORT $USER@$HOST <<EOF
|
||||
mkdir $REMOTE_DIR
|
||||
cd $REMOTE_DIR
|
||||
put "$FILE"
|
||||
bye
|
||||
EOF
|
||||
|
||||
echo "上傳完成: $FILE"
|
||||
```
|
||||
|
||||
使用方式:
|
||||
```bash
|
||||
chmod +x upload.sh
|
||||
./upload.sh /path/to/video.mp4
|
||||
```
|
||||
|
||||
### Python 腳本
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""上傳檔案到 Momentry SFTP"""
|
||||
|
||||
import paramiko
|
||||
import sys
|
||||
import os
|
||||
|
||||
def upload_file(local_path, remote_dir="/demo/uploads"):
|
||||
host = "sftpgo.momentry.ddns.net"
|
||||
port = 2022
|
||||
username = "demo"
|
||||
password = "demopassword123"
|
||||
|
||||
transport = paramiko.Transport((host, port))
|
||||
transport.connect(username=username, password=password)
|
||||
sftp = paramiko.SFTPClient.from_transport(transport)
|
||||
|
||||
filename = os.path.basename(local_path)
|
||||
remote_path = f"{remote_dir}/{filename}"
|
||||
|
||||
sftp.put(local_path, remote_path)
|
||||
print(f"已上傳: {filename} -> {remote_path}")
|
||||
|
||||
sftp.close()
|
||||
transport.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print("用法: python upload_sftp.py <檔案路徑>")
|
||||
sys.exit(1)
|
||||
|
||||
upload_file(sys.argv[1])
|
||||
```
|
||||
|
||||
安裝依賴:
|
||||
```bash
|
||||
pip install paramiko
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## WebDAV 替代方案
|
||||
|
||||
如果 SFTP 連線有問題,可使用 WebDAV:
|
||||
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| **URL** | `https://momentry.ddns.net/webdav/` |
|
||||
| **用戶名** | `demo` |
|
||||
| **密碼** | `demopassword123` |
|
||||
|
||||
### curl 使用 WebDAV
|
||||
|
||||
```bash
|
||||
# 上傳
|
||||
curl -u demo:demopassword123 \
|
||||
-T video.mp4 \
|
||||
"https://momentry.ddns.net/webdav/demo/uploads/"
|
||||
|
||||
# 下載
|
||||
curl -u demo:demopassword123 \
|
||||
-o video.mp4 \
|
||||
"https://momentry.ddns.net/webdav/demo/uploads/video.mp4"
|
||||
|
||||
# 列出目錄
|
||||
curl -u demo:demopassword123 \
|
||||
-X PROPFIND \
|
||||
"https://momentry.ddns.net/webdav/demo/" \
|
||||
-H "Depth: 1"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 連線被拒絕
|
||||
|
||||
```bash
|
||||
# 檢查 SFTPGo 是否運行
|
||||
curl -s http://localhost:8080/api/v2/status | jq .status
|
||||
|
||||
# 檢查連接埠
|
||||
nc -zv momentry.ddns.net 2022
|
||||
```
|
||||
|
||||
### 認證失敗
|
||||
|
||||
確認密碼是否正確:
|
||||
```bash
|
||||
# 測試認證
|
||||
curl -u demo:demopassword123 \
|
||||
"https://momentry.ddns.net/webdav/" -I
|
||||
```
|
||||
|
||||
### 權限不足
|
||||
|
||||
上傳目錄可能需要先建立:
|
||||
```bash
|
||||
sshpass -p "demopassword123" sftp -P 2022 demo@sftpgo.momentry.ddns.net <<EOF
|
||||
mkdir uploads
|
||||
mkdir videos
|
||||
bye
|
||||
EOF
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 檔案上傳後自動化
|
||||
|
||||
上傳後,SFTPGo 會自動:
|
||||
1. 觸發 Hook 腳本
|
||||
2. 記錄上傳事件到 `/Users/accusys/sftpgo_test/hook.log`
|
||||
3. 呼叫 Momentry Core API 註冊視頻
|
||||
|
||||
查看上傳日誌:
|
||||
```bash
|
||||
tail -f /Users/accusys/sftpgo_test/hook.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 管理手冊
|
||||
|
||||
### 管理員帳戶
|
||||
|
||||
| 角色 | 用戶名 | 密碼 | 說明 |
|
||||
|------|--------|------|------|
|
||||
| **WebAdmin** | `admin` | `Test3200Test3200` | SFTPGo 管理介面 |
|
||||
|
||||
**WebAdmin URL**: https://sftpgo.momentry.ddns.net
|
||||
|
||||
### Admin 創建方式
|
||||
|
||||
根據官方文檔,SFTPGo 有兩種方式創建管理員:
|
||||
|
||||
#### 方式 1: Web UI (首次設定)
|
||||
|
||||
1. 訪問 `http://localhost:8080/web/admin`
|
||||
2. 如果沒有管理員,會顯示設定畫面
|
||||
3. 輸入用戶名和密碼創建第一個管理員
|
||||
|
||||
#### 方式 2: 自動創建 (推薦)
|
||||
|
||||
需要同時滿足以下條件:
|
||||
1. 配置文件中設定 `"create_default_admin": true`
|
||||
2. 設定環境變數 `SFTPGO_DEFAULT_ADMIN_USERNAME` 和 `SFTPGO_DEFAULT_ADMIN_PASSWORD`
|
||||
|
||||
### 設定步驟
|
||||
|
||||
#### Step 1: 確保配置文件正確
|
||||
|
||||
確認 `/Users/accusys/momentry/etc/sftpgo/sftpgo.json` 中有:
|
||||
```json
|
||||
{
|
||||
"data_provider": {
|
||||
"create_default_admin": true
|
||||
},
|
||||
"httpd": {
|
||||
"setup": {
|
||||
"installation_code": "Test3200Test3200"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Step 2: 更新 plist 加入環境變數
|
||||
|
||||
編輯 `/Library/LaunchDaemons/com.momentry.sftpgo.plist`,加入:
|
||||
```xml
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>SFTPGO_DEFAULT_ADMIN_USERNAME</key>
|
||||
<string>admin</string>
|
||||
<key>SFTPGO_DEFAULT_ADMIN_PASSWORD</key>
|
||||
<string>Test3200Test3200</string>
|
||||
</dict>
|
||||
```
|
||||
|
||||
#### Step 3: 重啟 SFTPGo
|
||||
|
||||
```bash
|
||||
launchctl unload homebrew.mxcl.sftpgo
|
||||
launchctl load homebrew.mxcl.sftpgo
|
||||
```
|
||||
|
||||
#### Step 4: 驗證管理員
|
||||
|
||||
```bash
|
||||
curl -s -X GET "http://localhost:8080/api/v2/token" \
|
||||
-u "admin:Test3200Test3200"
|
||||
```
|
||||
|
||||
成功回應:
|
||||
```json
|
||||
{
|
||||
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"token_type": "bearer",
|
||||
"expires_in": 1200
|
||||
}
|
||||
```
|
||||
|
||||
### REST API 認證流程
|
||||
|
||||
#### 1. 獲取 Token
|
||||
|
||||
```bash
|
||||
curl -s -X GET "http://localhost:8080/api/v2/token" \
|
||||
-u "admin:Test3200Test3200"
|
||||
```
|
||||
|
||||
#### 2. 使用 Token 訪問 API
|
||||
|
||||
```bash
|
||||
TOKEN=$(curl -s -X GET "http://localhost:8080/api/v2/token" \
|
||||
-u "admin:Test3200Test3200" | jq -r '.access_token')
|
||||
|
||||
# 查看所有用戶
|
||||
curl -s http://localhost:8080/api/v2/users \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
|
||||
# 查看系統狀態
|
||||
curl -s http://localhost:8080/api/v2/status \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
### 常用管理操作
|
||||
|
||||
#### 查看所有用戶
|
||||
|
||||
```bash
|
||||
TOKEN=$(curl -s -X GET "http://localhost:8080/api/v2/token" \
|
||||
-u "admin:Test3200Test3200" | jq -r '.access_token')
|
||||
|
||||
curl -s http://localhost:8080/api/v2/users \
|
||||
-H "Authorization: Bearer $TOKEN" | jq .
|
||||
```
|
||||
|
||||
#### 建立新用戶
|
||||
|
||||
```bash
|
||||
TOKEN=$(curl -s -X GET "http://localhost:8080/api/v2/token" \
|
||||
-u "admin:Test3200Test3200" | jq -r '.access_token')
|
||||
|
||||
curl -s -X POST http://localhost:8080/api/v2/users \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"username": "newuser",
|
||||
"password": "userpassword123",
|
||||
"email": "user@example.com",
|
||||
"status": 1,
|
||||
"home_dir": "/Users/accusys/momentry/var/sftpgo/data/newuser",
|
||||
"uid": 501,
|
||||
"gid": 20,
|
||||
"permissions": {
|
||||
"/": ["*"]
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
#### 建立用戶組
|
||||
|
||||
```bash
|
||||
TOKEN=$(curl -s -X GET "http://localhost:8080/api/v2/token" \
|
||||
-u "admin:Test3200Test3200" | jq -r '.access_token')
|
||||
|
||||
curl -s -X POST http://localhost:8080/api/v2/groups \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "editors",
|
||||
"description": "Editor group with upload permissions"
|
||||
}'
|
||||
```
|
||||
|
||||
#### 刪除用戶
|
||||
|
||||
```bash
|
||||
TOKEN=$(curl -s -X GET "http://localhost:8080/api/v2/token" \
|
||||
-u "admin:Test3200Test3200" | jq -r '.access_token')
|
||||
|
||||
curl -s -X DELETE http://localhost:8080/api/v2/users/username \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
#### 修改用戶密碼
|
||||
|
||||
```bash
|
||||
TOKEN=$(curl -s -X GET "http://localhost:8080/api/v2/token" \
|
||||
-u "admin:Test3200Test3200" | jq -r '.access_token')
|
||||
|
||||
curl -s -X PUT http://localhost:8080/api/v2/users/demo \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"password": "newpassword456"
|
||||
}'
|
||||
```
|
||||
|
||||
### 重要設定
|
||||
|
||||
| 設定項目 | 值 | 說明 |
|
||||
|----------|-----|------|
|
||||
| **SFTP 連接埠** | `2022` | SSH 檔案傳輸協定 |
|
||||
| **HTTP/WebDAV 連接埠** | `8080` | 內部 HTTP 服務 |
|
||||
| **WebAdmin 連接埠** | `8080` | 管理介面 (`/web/admin`) |
|
||||
| **WebClient 連接埠** | `8080` | 用戶介面 (`/web/client`) |
|
||||
| **資料庫** | PostgreSQL | 用戶和設定儲存 |
|
||||
| **Hook 腳本** | `/Users/accusys/sftpgo_test/register_hook.sh` | 上傳後自動化處理 |
|
||||
| **安裝碼** | `Test3200Test3200` | 首次設定管理員所需 |
|
||||
| **create_default_admin** | `true` | 自動創建管理員 |
|
||||
| **Token 有效期** | 1200 秒 (20分鐘) | JWT 過期時間 |
|
||||
|
||||
### 用戶目錄結構
|
||||
|
||||
所有 SFTPGo 用戶資料統一存放在 `/Users/accusys/momentry/var/sftpgo/data/` 目錄下:
|
||||
|
||||
| 用戶 | 資料夾路徑 | 密碼 | 說明 |
|
||||
|------|------------|------|------|
|
||||
| **demo** | `/Users/accusys/momentry/var/sftpgo/data/demo` | `demopassword123` | Demo 用戶上傳目錄 |
|
||||
| **momentry** | `/Users/accusys/momentry/var/sftpgo/data/momentry` | `momentry123` | Momentry 系統用戶 |
|
||||
| **warren** | `/Users/accusys/momentry/var/sftpgo/data/warren` | `warren123` | 其他用戶 |
|
||||
|
||||
### API Token 獲取方式
|
||||
|
||||
```bash
|
||||
# 注意:使用 GET 而非 POST
|
||||
curl -s -X GET "http://localhost:8080/api/v2/token" \
|
||||
-u "admin:Test3200Test3200" | jq .access_token
|
||||
```
|
||||
|
||||
### 故障排除
|
||||
|
||||
| 問題 | 解決方案 |
|
||||
|------|----------|
|
||||
| 無法獲取 Token | 確認環境變數已正確設定並重啟 SFTPGo |
|
||||
| SFTP 連線被拒絕 | 檢查 SFTPGo 服務: `launchctl list \| grep sftpgo` |
|
||||
| 無法登入 WebAdmin | 確認 admin 用戶存在: 檢查 plist 中環境變數是否正確 |
|
||||
| 上傳失敗 | 檢查 Hook 腳本: `tail -f /Users/accusys/momentry/log/sftpgo.error.log` |
|
||||
| 權限不足 | 檢查用戶權限或更新 `permissions` 設定 |
|
||||
| API 返回 401 | Token 過期,需重新獲取: `curl -X POST .../token -u "admin:pass"` | |
|
||||
|
||||
---
|
||||
|
||||
## 安全注意事項
|
||||
|
||||
- **密碼保護**: `demopassword123` 為 demo 帳戶密碼
|
||||
- **限制存取**: Demo 用戶只能訪問 `/demo` 目錄
|
||||
- **監控**: 所有上傳都有日誌記錄
|
||||
- **生產環境**: 正式環境應使用更強的密碼和金鑰認證
|
||||
@@ -1,246 +0,0 @@
|
||||
# Video Registration
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-25 |
|
||||
| 文件版本 | V1.1 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-25 | 創建文件 | Warren | OpenCode |
|
||||
| V1.1 | 2026-03-26 | 修正 curl 範例,新增 API Key 驗證標頭 | OpenCode | deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
影片註冊 API (`POST /api/v1/register`) 用於將影片加入 Momentry Core 系統進行處理。
|
||||
|
||||
## 路徑格式
|
||||
|
||||
### 支援的路徑格式
|
||||
|
||||
| 格式 | 範例 | 說明 |
|
||||
|------|------|------|
|
||||
| 相對路徑 | `./demo/video.mp4` | 推薦格式 |
|
||||
| 相對路徑(無 ./) | `demo/video.mp4` | 自動加上 `./` |
|
||||
| 絕對路徑 | `/Users/.../sftpgo/data/demo/video.mp4` | 支援但不推薦 |
|
||||
|
||||
### 路徑結構
|
||||
|
||||
```
|
||||
./username/filepath
|
||||
│ │ │
|
||||
│ │ └── 檔案路徑(可以是多層目錄)
|
||||
│ └── 使用者名稱(SFTPgo 用戶目錄名稱)
|
||||
└── 相對路徑前綴
|
||||
```
|
||||
|
||||
**範例**:
|
||||
- `./demo/video.mp4` → username=`demo`, filepath=`video.mp4`
|
||||
- `./demo/movies/2024/video.mp4` → username=`demo`, filepath=`movies/2024/video.mp4`
|
||||
- `./warren/project1/interview.mp4` → username=`warren`, filepath=`project1/interview.mp4`
|
||||
|
||||
## UUID 計算
|
||||
|
||||
### 計算規則
|
||||
|
||||
```
|
||||
UUID = SHA256(username/filepath)[0:16]
|
||||
```
|
||||
|
||||
**範例**:
|
||||
```rust
|
||||
// 路徑: ./demo/video.mp4
|
||||
// username: "demo"
|
||||
// filepath: "video.mp4"
|
||||
// key: "demo/video.mp4"
|
||||
// UUID: SHA256("demo/video.mp4")[0:16]
|
||||
```
|
||||
|
||||
### 特性
|
||||
|
||||
| 特性 | 說明 |
|
||||
|------|------|
|
||||
| 用戶隔離 | 不同用戶的相同檔名會產生不同 UUID |
|
||||
| 一致性 | 相同相對路徑一定產生相同 UUID |
|
||||
| 遷移安全 | SFTPgo 資料路徑變更後 UUID 保持一致 |
|
||||
|
||||
### 範例
|
||||
|
||||
```rust
|
||||
// 用戶 demo 的影片
|
||||
compute_uuid_from_relative_path("./demo/video.mp4")
|
||||
// → "9760d0820f0cf9a7"
|
||||
|
||||
// 用戶 warren 的相同檔名影片
|
||||
compute_uuid_from_relative_path("./warren/video.mp4")
|
||||
// → "a1b2c3d4e5f6g7h8" (不同的 UUID)
|
||||
```
|
||||
|
||||
## 重複註冊檢查
|
||||
|
||||
### 行為
|
||||
|
||||
1. 系統檢查 UUID 是否已存在於資料庫
|
||||
2. 如果存在,返回 `already_exists: true` 和現有影片資訊
|
||||
3. 如果不存在,創建新的影片記錄
|
||||
|
||||
### API 回應
|
||||
|
||||
**新註冊**:
|
||||
```json
|
||||
{
|
||||
"uuid": "9760d0820f0cf9a7",
|
||||
"video_id": 18,
|
||||
"job_id": 2,
|
||||
"file_name": "video.mp4",
|
||||
"duration": 159.637188,
|
||||
"width": 640,
|
||||
"height": 360,
|
||||
"already_exists": false
|
||||
}
|
||||
```
|
||||
|
||||
**重複註冊**:
|
||||
```json
|
||||
{
|
||||
"uuid": "9760d0820f0cf9a7",
|
||||
"video_id": 18,
|
||||
"job_id": 2,
|
||||
"file_name": "video.mp4",
|
||||
"duration": 159.637188,
|
||||
"width": 640,
|
||||
"height": 360,
|
||||
"already_exists": true
|
||||
}
|
||||
```
|
||||
|
||||
## SFTPgo 整合
|
||||
|
||||
### 目錄結構
|
||||
|
||||
SFTPgo 的用戶目錄結構:
|
||||
|
||||
```
|
||||
/Users/accusys/momentry/var/sftpgo/data/
|
||||
├── demo/ ← 用戶目錄
|
||||
│ ├── video.mp4
|
||||
│ └── movies/
|
||||
│ └── movie1.mp4
|
||||
├── warren/ ← 用戶目錄
|
||||
│ └── project1/
|
||||
│ └── interview.mp4
|
||||
└── momentry/ ← 用戶目錄
|
||||
└── presentation.mp4
|
||||
```
|
||||
|
||||
### 註冊流程
|
||||
|
||||
1. SFTPgo 用戶上傳檔案到各自的目錄
|
||||
2. n8n 或其他服務調用註冊 API
|
||||
3. 使用相對路徑格式:`./username/filepath`
|
||||
4. 系統計算 UUID 並檢查重複
|
||||
5. 創建處理任務
|
||||
|
||||
## 程式碼範例
|
||||
|
||||
### 註冊影片
|
||||
|
||||
```bash
|
||||
# 使用相對路徑註冊
|
||||
curl -X POST http://localhost:3002/api/v1/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"path": "./demo/video.mp4"}'
|
||||
|
||||
# 或使用多層目錄
|
||||
curl -X POST http://localhost:3002/api/v1/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"path": "./demo/movies/2024/video.mp4"}'
|
||||
```
|
||||
|
||||
### UUID 計算函數
|
||||
|
||||
```rust
|
||||
// 使用相對路徑計算 UUID
|
||||
pub fn compute_uuid_from_relative_path(relative_path: &str) -> String {
|
||||
let (username, filepath) = extract_user_from_relative_path(relative_path);
|
||||
compute_uuid(&username, &filepath)
|
||||
}
|
||||
|
||||
// 從相對路徑提取用戶名和檔案路徑
|
||||
pub fn extract_user_from_relative_path(relative_path: &str) -> (String, String) {
|
||||
let path = relative_path.strip_prefix("./").unwrap_or(relative_path);
|
||||
let path_buf = PathBuf::from(path);
|
||||
|
||||
let mut components = path_buf.components();
|
||||
let username = components
|
||||
.next()
|
||||
.map(|c| c.as_os_str().to_string_lossy().to_string())
|
||||
.unwrap_or_default();
|
||||
|
||||
let filepath: String = components
|
||||
.map(|c| c.as_os_str().to_string_lossy().to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("/");
|
||||
|
||||
(username, filepath)
|
||||
}
|
||||
```
|
||||
|
||||
## 相關 API
|
||||
|
||||
### Probe API(僅探測,不註冊)
|
||||
|
||||
如果只需要取得影片資訊而不註冊,可以使用 Probe API:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/probe \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"path": "./demo/video.mp4"}'
|
||||
```
|
||||
|
||||
**回應範例**:
|
||||
```json
|
||||
{
|
||||
"uuid": "a1b10138a6bbb0cd",
|
||||
"file_name": "video.mp4",
|
||||
"duration": 120.5,
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"fps": 30.0,
|
||||
"cached": false,
|
||||
"format": {...},
|
||||
"streams": [...]
|
||||
}
|
||||
```
|
||||
|
||||
**與 Register API 的差異**:
|
||||
|
||||
| 功能 | Probe API | Register API |
|
||||
|------|-----------|---------------|
|
||||
| 計算 UUID | ✓ | ✓ |
|
||||
| 執行 ffprobe | ✓ | ✓ |
|
||||
| 儲存 probe.json | ✓ | ✓ |
|
||||
| 寫入 videos 表 | ✗ | ✓ |
|
||||
| 建立 monitor_job | ✗ | ✓ |
|
||||
| 返回 job_id | ✗ | ✓ |
|
||||
| 適用場景 | 預覽影片資訊 | 註冊並處理影片 |
|
||||
|
||||
## 相關檔案
|
||||
|
||||
| 檔案 | 說明 |
|
||||
|------|------|
|
||||
| `src/core/storage/uuid.rs` | UUID 計算邏輯 |
|
||||
| `src/api/server.rs` | 註冊與 Probe API 實現 |
|
||||
| `src/core/probe/ffprobe.rs` | ffprobe 整合 |
|
||||
| `docs/SFTPGO_DEMO_USER.md` | SFTPgo 用戶設置 |
|
||||
| `docs/API_ENDPOINTS.md` | API 端點總覽 |
|
||||
@@ -1,540 +0,0 @@
|
||||
# Momentry Core 開發日誌
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-18 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-18 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
|
||||
---
|
||||
|
||||
> **文檔維護開始**:2026-03-18
|
||||
> **⚠️ 補充說明**:事後補記(2026-03-18 以前),僅供參考。未來紀錄將即時記錄,參考價值較高。
|
||||
|
||||
---
|
||||
|
||||
## 開發工具
|
||||
|
||||
### Coding LLM 模型
|
||||
|
||||
| 階段 | 工具 | 模型 | ID | 說明 |
|
||||
|------|------|------|-----|------|
|
||||
| **初期** | Claude CLI | - | - | 初始專案架構建立 |
|
||||
| **中期** | OpenCode | big-pickle | opencode/big-pickle | 主要開發協作者 |
|
||||
|
||||
**切換記錄**:
|
||||
- 初期使用 Claude CLI 建立專案基本架構
|
||||
- 中期切換至 OpenCode (big-pickle) 進行主要功能開發
|
||||
|
||||
---
|
||||
|
||||
## 2026-03-17
|
||||
|
||||
### ML 模型選用
|
||||
|
||||
| Processor | 模型 | 版本/大小 | 說明 |
|
||||
|----------|------|-----------|------|
|
||||
| **ASR** | WhisperX (faster-whisper) | base, int8 | 語音識別 + 對話分段 |
|
||||
| **CUT** | PySceneDetect | 0.6.7.1 | ContentDetector 場景檢測 |
|
||||
| **YOLO** | YOLOv8n | yolov8n.pt (6.2MB) | 物體檢測(nano 版本最快) |
|
||||
| **OCR** | EasyOCR | 1.7.2 | 文字識別 |
|
||||
| **Face** | OpenCV Haar Cascade | built-in | 人臉檢測(無需額外下載) |
|
||||
| **Pose** | YOLOv8n-Pose | yolov8n-pose.pt (6.5MB) | 姿態估計(nano 版本) |
|
||||
|
||||
**模型下載**:
|
||||
- YOLOv8n: `yolov8n.pt` (6.2MB)
|
||||
- YOLOv8n-Pose: `yolov8n-pose.pt` (6.5MB)
|
||||
|
||||
**Python 依賴**:
|
||||
```
|
||||
torch==2.8.0
|
||||
whisperx==3.8.2
|
||||
ultralytics==8.4.23
|
||||
scenedetect==0.6.7.1
|
||||
easyocr==1.7.2
|
||||
opencv-python==4.13.0.92
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### ASR 實作完成
|
||||
- 完成 Python ML processor scripts(使用本地模型)
|
||||
- `asrx_processor.py` - whisperx for speaker diarization
|
||||
- `cut_processor.py` - PySceneDetect for scene detection
|
||||
- `yolo_processor.py` - YOLOv8 for object detection
|
||||
- `ocr_processor.py` - EasyOCR for text recognition
|
||||
- `face_processor.py` - OpenCV Haar Cascade for face detection
|
||||
- `pose_processor.py` - YOLOv8 Pose for pose estimation
|
||||
|
||||
- 更新 `requirements.txt` with all dependencies
|
||||
- 安裝完成:torch 2.8.0, whisperx 3.8.2, ultralytics 8.4.23, scenedetect 0.6.7.1, easyocr 1.7.2, opencv-python 4.13.0.92
|
||||
- 下載模型:YOLOv8n.pt (6.2MB), YOLOv8n-Pose.pt (6.5MB)
|
||||
|
||||
### Async Streaming 實作
|
||||
- 更新 Rust processor modules 使用 async streaming 進行 real-time progress
|
||||
- `src/core/processor/asr.rs`
|
||||
- `src/core/processor/cut.rs`
|
||||
- `src/core/processor/yolo.rs`
|
||||
- `src/core/processor/ocr.rs`
|
||||
- `src/core/processor/face.rs`
|
||||
- `src/core/processor/pose.rs`
|
||||
|
||||
### 測試結果
|
||||
- 測試影片:BigBuckBunny_320x180.mp4
|
||||
- ASR: 4 segments
|
||||
- CUT: 134 scenes
|
||||
- YOLO: 14315 frames(每幀處理耗時)
|
||||
- OCR: 40 frames with text
|
||||
- Face: 44 frames with faces
|
||||
- Pose: Timeout
|
||||
|
||||
---
|
||||
|
||||
### Warning 清理
|
||||
修復 clippy warnings:
|
||||
- 移除未使用的 imports (HashMap in mongodb_db.rs, postgres_db.rs)
|
||||
- 新增 `#[allow(dead_code)]` 標註未使用變數
|
||||
- 新增 `Default` implementation for MongoDb, QdrantDb
|
||||
- 將 `probe` module 重新命名為 `ffprobe`
|
||||
- 新增 `player` feature in Cargo.toml
|
||||
- 修復 `format_in_format_args` 警告
|
||||
|
||||
---
|
||||
|
||||
### TUI Progress Window 實作
|
||||
建立新的 UI module:
|
||||
- 建立 `src/ui/mod.rs`
|
||||
- 建立 `src/ui/progress/mod.rs`
|
||||
|
||||
實作功能:
|
||||
- ProcessorProgress 結構(追蹤每個 processor 狀態)
|
||||
- ProgressState 結構(管理所有 processors)
|
||||
- ProgressUi 結構(ratatui TUI 渲染)
|
||||
- 整合到 `src/main.rs` 的 process 命令
|
||||
|
||||
TUI 顯示:
|
||||
```
|
||||
┌ Processing: BigBuckBunny_320x180.mp4 ────────────────────────────────────────┐
|
||||
│ ASR [████████████] 100% (4 segs) │
|
||||
│ CUT [████████████] 100% (134 scenes) │
|
||||
│ ASRX [████████████] 100% (0 segs) │
|
||||
│ YOLO [██░░░░░░░░░░░] 30% (4200/14315) ETA 2:30 │
|
||||
│ OCR [---------] 0% │
|
||||
│ Face [---------] 0% │
|
||||
│ Pose [---------] 0% │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 輸出位置討論
|
||||
討論 stdout vs stderr vs TUI 的輸出配置:
|
||||
- 最終結果 → stdout
|
||||
- Python progress → 需改用 Redis Pub/Sub
|
||||
- TUI Progress → stderr (ratatui)
|
||||
|
||||
---
|
||||
|
||||
## 2026-03-18
|
||||
|
||||
### Redis Message Bus 設計
|
||||
討論使用 Redis 作為消息總線,分離 Python 輸出與 Rust TUI 顯示。
|
||||
|
||||
設計重點:
|
||||
1. 頻道命名:`momentry:progress:{uuid}`
|
||||
2. 本地 Redis:`localhost:6379`
|
||||
3. 失敗策略:完全失效(因 stdout 問題未解決)
|
||||
|
||||
### UUID 使用時機分析
|
||||
分析 Redis Key 上使用 UUID 的時機:
|
||||
|
||||
**全局 Keys(無 UUID)**:
|
||||
- health, stats, jobs 管理
|
||||
|
||||
**Per-Video Keys(UUID 必要)**:
|
||||
- job:{uuid}, progress:{uuid}, metrics:{uuid}
|
||||
|
||||
**Per-Processor Keys(UUID + Processor 必要)**:
|
||||
- job:{uuid}:processor:{name}
|
||||
|
||||
### 備份系統整合
|
||||
參考 `docs/SERVICE_ADDITION_GUIDE.md` 設計規範,規劃 OutputDir 模組:
|
||||
|
||||
1. **環境變數**:
|
||||
- `MOMENTRY_OUTPUT_DIR` - JSON 輸出目錄
|
||||
- `MOMENTRY_BACKUP_DIR` - 備份目錄(預設:`/Users/accusys/momentry/backup/momentry`)
|
||||
- `MOMENTRY_BACKUP_ENABLED` - 啟用備份
|
||||
|
||||
2. **命名格式**:
|
||||
- 備份格式:`momentry_data_{YYYYMMDD}_{HHMMSS}_{uuid}.{ext}`
|
||||
- 校驗和:`{filename}.sha256`
|
||||
|
||||
3. **CLI 命令**:
|
||||
- `cargo run -- backup list` - 列出備份
|
||||
- `cargo run -- backup cleanup` - 清理舊備份
|
||||
- `cargo run -- backup verify` - 驗證備份
|
||||
|
||||
---
|
||||
|
||||
### 監控系統整合
|
||||
討論將 momentry_core 納入監控系統:
|
||||
|
||||
1. **Layer 2: Service 監控**
|
||||
- 新增 momentry_core CLI 檢查
|
||||
|
||||
2. **Layer 7: Backup 監控**
|
||||
- 新增 momentry 備份配置
|
||||
|
||||
3. **Redis 監控**
|
||||
- 健康檢查
|
||||
- Job 狀態監控
|
||||
- 即時進度監控
|
||||
|
||||
---
|
||||
|
||||
## 實作完成項目
|
||||
|
||||
### 程式碼變更
|
||||
|
||||
| 日期 | 檔案 | 變更 |
|
||||
|------|------|------|
|
||||
| 2026-03-17 | `src/core/processor/*.rs` | Async streaming 更新 |
|
||||
| 2026-03-17 | `src/ui/mod.rs` | 新增 UI module |
|
||||
| 2026-03-17 | `src/ui/progress/mod.rs` | 新增 Progress TUI |
|
||||
| 2026-03-17 | `src/main.rs` | 整合 Progress UI |
|
||||
| 2026-03-18 | `src/core/storage/output_dir.rs` | 新增 OutputDir 模組 |
|
||||
| 2026-03-18 | `src/core/storage/mod.rs` | 新增 output_dir export |
|
||||
| 2026-03-18 | `src/core/db/redis_client.rs` | 新增 Redis 客戶端(Hash + Pub/Sub) |
|
||||
| 2026-03-18 | `src/core/db/mod.rs` | 新增 redis_client export |
|
||||
|
||||
### 新增檔案
|
||||
|
||||
| 日期 | 檔案 | 說明 |
|
||||
|------|------|------|
|
||||
| 2026-03-18 | `docs/MOMENTRY_CORE_REDIS_KEYS.md` | Redis Key 設計規範 |
|
||||
| 2026-03-18 | `docs/MOMENTRY_CORE_MONITORING.md` | 監控規範(暫定) |
|
||||
| 2026-03-18 | `scripts/redis_publisher.py` | Redis 訊息發布模組 |
|
||||
|
||||
### 更新檔案
|
||||
|
||||
| 日期 | 檔案 | 說明 |
|
||||
|------|------|------|
|
||||
| 2026-03-17 | `Cargo.toml` | 新增 player feature |
|
||||
| 2026-03-17 | `src/lib.rs` | 新增 ui module exports |
|
||||
| 2026-03-18 | `docs/PENDING_ISSUES.md` | 新增問題 #2, #3 |
|
||||
| 2026-03-18 | `src/core/storage/output_dir.rs` | 預設改為 `./output` |
|
||||
| 2026-03-18 | `scripts/yolo_processor.py` | 新增 --uuid 參數 + Redis |
|
||||
| 2026-03-18 | `scripts/cut_processor.py` | 新增 Redis |
|
||||
| 2026-03-18 | `scripts/ocr_processor.py` | 新增 Redis |
|
||||
| 2026-03-18 | `scripts/face_processor.py` | 新增 --uuid 參數 + Redis |
|
||||
| 2026-03-18 | `scripts/pose_processor.py` | 新增 --uuid 參數 + Redis |
|
||||
| 2026-03-18 | `scripts/asr_processor.py` | 新增 --uuid 參數 + Redis |
|
||||
| 2026-03-18 | `scripts/asrx_processor.py` | 新增 --uuid 參數 + Redis |
|
||||
| 2026-03-18 | `requirements.txt` | 新增 redis>=5.0.0 |
|
||||
| 2026-03-18 | `src/core/processor/yolo.rs` | 新增 uuid 參數 |
|
||||
| 2026-03-18 | `src/core/processor/cut.rs` | 新增 uuid 參數 |
|
||||
| 2026-03-18 | `src/core/processor/ocr.rs` | 新增 uuid 參數 |
|
||||
| 2026-03-18 | `src/core/processor/face.rs` | 新增 uuid 參數 |
|
||||
| 2026-03-18 | `src/core/processor/pose.rs` | 新增 uuid 參數 |
|
||||
| 2026-03-18 | `src/core/processor/asr.rs` | 新增 uuid 參數 |
|
||||
| 2026-03-18 | `src/core/processor/asrx.rs` | 新增 uuid 參數 |
|
||||
| 2026-03-18 | `src/main.rs` | 更新所有 processor 調用傳入 uuid |
|
||||
| 2026-03-18 | `Cargo.toml` | 新增 futures-util 依賴 |
|
||||
| 2026-03-18 | `src/core/db/redis_client.rs` | 新增 subscribe_and_callback 方法,密碼認證 |
|
||||
| 2026-03-18 | `src/ui/progress/mod.rs` | 新增 update_from_redis 方法 |
|
||||
| 2026-03-18 | `scripts/redis_publisher.py` | 新增密碼認證支援 |
|
||||
| 2026-03-18 | 測試 | Redis Pub/Sub 成功運作 |
|
||||
|
||||
---
|
||||
|
||||
## 待解決問題
|
||||
|
||||
### 問題 #1: sqlx async INSERT 不會實際寫入數據庫
|
||||
- 狀態:待解決
|
||||
- 影響:`store_vector` 函數,PVector 存儲
|
||||
|
||||
### 問題 #2: TUI 與 stdout 輸出混合
|
||||
- 狀態:已解決
|
||||
- 解決方案:使用 Redis Message Bus
|
||||
- 進度:
|
||||
- ✅ Redis 客戶端 (`src/core/db/redis_client.rs`)
|
||||
- ✅ Python redis_publisher.py
|
||||
- ✅ 所有 Python processors 更新完成
|
||||
- ✅ 所有 Rust processor 函數更新完成
|
||||
- ✅ main.rs 調用更新完成
|
||||
- ✅ Rust TUI Redis 訂閱已完成
|
||||
|
||||
### 問題 #3: Redis Message Bus 尚未實作
|
||||
- 狀態:已解決
|
||||
- 詳細設計:參考 `docs/MOMENTRY_CORE_REDIS_KEYS.md`
|
||||
- 進度:Python 端 + Rust 端均已完成
|
||||
|
||||
---
|
||||
|
||||
## 環境變數
|
||||
|
||||
```bash
|
||||
# 輸出目錄
|
||||
MOMENTRY_OUTPUT_DIR=./output # 預設
|
||||
|
||||
# 備份
|
||||
MOMENTRY_BACKUP_ENABLED=false # 預設
|
||||
MOMENTRY_BACKUP_DIR=/Users/accusys/momentry/backup/momentry
|
||||
|
||||
# Redis(未來實作)
|
||||
REDIS_URL=redis://localhost:6379
|
||||
REDIS_PASSWORD=accusys
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 數據庫
|
||||
|
||||
- PostgreSQL: `postgres://accusys@localhost:5432/momentry`
|
||||
- Redis: `localhost:6379`(待實作)
|
||||
- Qdrant: `localhost:6333`
|
||||
|
||||
---
|
||||
|
||||
## 指令範例
|
||||
|
||||
```bash
|
||||
# 註冊視頻
|
||||
cargo run -- register /path/to/video.mp4
|
||||
|
||||
# 處理視頻
|
||||
cargo run -- process <uuid>
|
||||
|
||||
# 列出備份
|
||||
cargo run -- backup list
|
||||
|
||||
# 清理備份
|
||||
cargo run -- backup cleanup
|
||||
|
||||
# 驗證備份
|
||||
cargo run -- backup verify
|
||||
|
||||
# 查看狀態
|
||||
cargo run -- status
|
||||
|
||||
# API Server
|
||||
cargo run -- server --host 0.0.0.0 --port 3000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2026-03-18 (進行中)
|
||||
|
||||
### Redis Message Bus 實作
|
||||
|
||||
**問題**:TUI 與 Python stdout 輸出混合,導致 TUI 顯示混亂
|
||||
|
||||
**解決方案**:使用 Redis Pub/Sub 作為訊息匯流排
|
||||
|
||||
**實作內容**:
|
||||
|
||||
| 元件 | 檔案 | 狀態 |
|
||||
|------|------|------|
|
||||
| Redis 客戶端 | `src/core/db/redis_client.rs` | ✅ |
|
||||
| Progress 訂閱 | `src/main.rs` | ✅ |
|
||||
| UI 更新 | `src/ui/progress/mod.rs` | ✅ |
|
||||
| Python Publisher | `scripts/redis_publisher.py` | ✅ |
|
||||
| Python Processors | 7 個 `scripts/*_processor.py` | ✅ |
|
||||
| Rust 函數 | `src/core/processor/*.rs` | ✅ |
|
||||
|
||||
**流程**:
|
||||
```
|
||||
Python Processor ──(Redis Pub)──> Redis ──(Subscribe)──> Rust TUI
|
||||
```
|
||||
|
||||
**測試結果**:
|
||||
- Redis 連線 ✅
|
||||
- 密碼認證 ✅
|
||||
- 即時進度發布 ✅
|
||||
- TUI 即時更新 ✅
|
||||
|
||||
**新增依賴**:
|
||||
- `futures-util = "0.3"` (Cargo.toml)
|
||||
- `redis >= 5.0.0` (requirements.txt)
|
||||
|
||||
---
|
||||
|
||||
## 2026-03-18 (HTTP API)
|
||||
|
||||
### HTTP API 實作
|
||||
|
||||
**問題**:TUI 運作正常但使用者偏好 HTTP API 來查詢進度
|
||||
|
||||
**解決方案**:建立 HTTP 端點 + Redis Hash 儲存
|
||||
|
||||
**實作內容**:
|
||||
|
||||
| 元件 | 檔案 | 變更 |
|
||||
|------|------|------|
|
||||
| HTTP 端點 | `src/api/server.rs` | 新增 `/api/v1/progress/:uuid` |
|
||||
| Redis Hash 查詢 | `src/core/db/redis_client.rs` | 新增 `get_processor_status` 方法 |
|
||||
| Progress 儲存 | `src/main.rs` | 新增 Redis HSET 儲存進度 |
|
||||
|
||||
**API 端點**:
|
||||
```
|
||||
GET /api/v1/progress/:uuid
|
||||
|
||||
Response:
|
||||
{
|
||||
"uuid": "5dea6618a606e7c7",
|
||||
"processors": [
|
||||
{"name": "asr", "status": "complete", "current": 0, "total": 0, "message": "7 segments"},
|
||||
{"name": "cut", "status": "complete", "current": 134, "total": 134, "message": "134 scenes"},
|
||||
{"name": "yolo", "status": "complete", "current": 14300, "total": 14315, "message": "..."},
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**流程**:
|
||||
```
|
||||
Python Processor ──(Redis Pub)──> Redis ──(Subscribe)──> Rust TUI
|
||||
└──(HSET)──> Redis Hash
|
||||
│
|
||||
HTTP Client ──(GET /progress/:uuid)──> Rust API ─(HGETALL)──> Redis Hash
|
||||
```
|
||||
|
||||
**測試結果**:
|
||||
- ✅ 編譯成功
|
||||
- ✅ API 伺服器啟動 (port 3002)
|
||||
- ✅ 即時進度查詢
|
||||
- ✅ 完整流程測試 (BigBuckBunny_320x180.mp4)
|
||||
|
||||
**除錯記錄**:
|
||||
1. 語法錯誤:main.rs 有重複程式碼區塊 (lines 297-322),已移除
|
||||
2. DB 連線池:從 5 增加到 10 個連線
|
||||
3. PostgreSQL 狀態:處理 shutdown 狀態,殺掉 stale 連線
|
||||
|
||||
**新增變更**:
|
||||
- `src/api/server.rs` - 新增進度端點
|
||||
- `src/core/db/redis_client.rs` - 新增 `get_processor_status` 方法
|
||||
- `src/core/db/postgres_db.rs` - 連線池 5→10
|
||||
- `src/main.rs` - Redis Hash 儲存 + 語法修復
|
||||
|
||||
**使用方式**:
|
||||
```bash
|
||||
# 啟動 API 伺服器
|
||||
cargo run --bin momentry -- server --host 127.0.0.1 --port 3002
|
||||
|
||||
# 註冊影片
|
||||
cargo run --bin momentry -- register ~/test_video/BigBuckBunny_320x180.mp4
|
||||
|
||||
# 處理影片
|
||||
cargo run --bin momentry -- process <uuid>
|
||||
|
||||
# 查詢進度
|
||||
curl http://127.0.0.1:3002/api/v1/progress/<uuid>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2026-03-18 (Dashboard)
|
||||
|
||||
### Web Dashboard 實作
|
||||
|
||||
**目標**:建立 Web 介面監控 momentry_core 處理進度
|
||||
|
||||
**技術選擇**:Static HTML + JavaScript (非 WASM)
|
||||
|
||||
**實作內容**:
|
||||
|
||||
| 元件 | 檔案 | 說明 |
|
||||
|------|------|------|
|
||||
| Dashboard | `momentry_dashboard/dist/index.html` | 靜態 HTML 頁面 |
|
||||
| API 代理 | Caddyfile port 3200 | 反向代理到 API server |
|
||||
|
||||
**功能**:
|
||||
- 影片列表顯示
|
||||
- 即時進度條 (每 5 秒自動刷新)
|
||||
- 搜尋功能
|
||||
- 處理器狀態 (ASR/CUT/YOLO/OCR/Face/Pose)
|
||||
|
||||
**訪問**:
|
||||
- Dashboard: http://localhost:3200
|
||||
- API: http://localhost:3200/api/v1/*
|
||||
|
||||
---
|
||||
|
||||
## 發生問題記錄
|
||||
|
||||
### HTTP API 問題
|
||||
|
||||
1. **語法錯誤** (main.rs)
|
||||
- 位置:lines 297-322
|
||||
- 原因:重複的程式碼區塊
|
||||
- 解決:移除重複區塊
|
||||
|
||||
2. **DB 連線池耗盡**
|
||||
- 原因:預設 5 個連線不足
|
||||
- 解決:增加到 10 個連線
|
||||
|
||||
3. **PostgreSQL shutdown 狀態**
|
||||
- 原因:共享記憶體未釋放
|
||||
- 解決:殺掉 stale 連線
|
||||
|
||||
### WASM Dashboard 問題
|
||||
|
||||
1. **Yew 版本問題**
|
||||
- 嘗試:yew 0.21 → 0.23
|
||||
- 問題:feature 名稱變更 (`web-sys` → `web_sys` → `csr`)
|
||||
- 解決:放棄 WASM,改用靜態 HTML
|
||||
|
||||
2. **編譯錯誤**
|
||||
- `wasm32-unknown-unknown` target 未安裝
|
||||
- 解決:`rustup target add wasm32-unknown-unknown`
|
||||
|
||||
3. **Yew 0.23 API 變更**
|
||||
- Properties 需要 PartialEq derive
|
||||
- 多處 API 語法變更
|
||||
- 放棄 WASM 方案
|
||||
|
||||
### Gitea Push 問題
|
||||
|
||||
1. **Remote URL 錯誤**
|
||||
- 原因:使用 localhost:3000 而非 gitea.momentry.ddns.net
|
||||
- 解決:建立新 repo `momentry_core_0_1`
|
||||
|
||||
2. **認證問題**
|
||||
- SSH key 未授權
|
||||
- 密碼認證成功推送
|
||||
|
||||
### Caddy 設定問題
|
||||
|
||||
1. **API 代理順序**
|
||||
- 問題:try_files 在 reverse_proxy 之前導致 API 回傳 HTML
|
||||
- 解決:使用 `handle` 區塊明確定義順序
|
||||
|
||||
```caddyfile
|
||||
:3200 {
|
||||
handle /api/* {
|
||||
reverse_proxy localhost:3002
|
||||
}
|
||||
handle {
|
||||
root * /Users/accusys/momentry_dashboard/dist
|
||||
try_files {path} /index.html
|
||||
file_server
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 未來工作
|
||||
|
||||
- [ ] 修復 WASM Dashboard (Yew 0.23 相容性)
|
||||
- [ ] 新增影片播放器整合
|
||||
- [ ] WebSocket 實時推送
|
||||
- [ ] 移動端響應式設計
|
||||
@@ -1,283 +0,0 @@
|
||||
# Momentry Core Redis Key 設計規範
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-17 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-17 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
| V1.1 | 2026-03-25 | 新增可配置 Redis Key Prefix | Warren | OpenCode / GLM-5 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 概述
|
||||
|
||||
本文檔說明 momentry_core 如何使用 Redis 作為監控和狀態管理系統。
|
||||
|
||||
## 2. 可配置 Redis Key Prefix
|
||||
|
||||
### 2.1 環境變數
|
||||
|
||||
從 V1.1 開始,所有 Redis Keys 都支援自定義前綴:
|
||||
|
||||
```bash
|
||||
MOMENTRY_REDIS_PREFIX=momentry:
|
||||
```
|
||||
|
||||
此設定允許多個 momentry 實例共用同一個 Redis 伺服器,例如:
|
||||
- **生產環境**: `MOMENTRY_REDIS_PREFIX=momentry:`
|
||||
- **開發環境**: `MOMENTRY_REDIS_PREFIX=momentry_dev:`
|
||||
|
||||
### 2.2 Key 格式
|
||||
|
||||
所有 Key 都遵循以下格式:
|
||||
|
||||
```
|
||||
{prefix}{key_type}:{uuid}
|
||||
```
|
||||
|
||||
範例 (生產環境):
|
||||
```
|
||||
momentry:job:5dea6618a606e7c7
|
||||
momentry:jobs:active
|
||||
momentry:health:current
|
||||
```
|
||||
|
||||
範例 (開發環境):
|
||||
```
|
||||
momentry_dev:job:5dea6618a606e7c7
|
||||
momentry_dev:jobs:active
|
||||
momentry_dev:health:current
|
||||
```
|
||||
|
||||
### 2.3 預設值
|
||||
|
||||
| Binary | 預設 Port | 預設 Redis Prefix |
|
||||
|--------|-----------|-------------------|
|
||||
| `momentry` (生產) | 3002 | `momentry:` |
|
||||
| `momentry_playground` (開發) | 3003 | `momentry_dev:` |
|
||||
|
||||
## 3. UUID 使用時機
|
||||
|
||||
### 3.1 全局 Keys(無 UUID)
|
||||
|
||||
- 單一實例全局狀態
|
||||
- 聚合統計數據
|
||||
|
||||
### 3.2 Per-Video Keys(UUID 必要)
|
||||
|
||||
- 每個視頻獨立處理狀態
|
||||
- 即時進度追蹤
|
||||
|
||||
### 3.3 Per-Processor Keys(UUID + Processor 必要)
|
||||
|
||||
- 每個 processor 獨立狀態
|
||||
|
||||
## 4. Key 命名空間
|
||||
|
||||
```
|
||||
momentry
|
||||
├── health: # 健康檢查
|
||||
│ ├── current # 當前狀態 (TTL: 60s)
|
||||
│ └── services # 依賴服務狀態
|
||||
├── config: # 配置
|
||||
├── stats: # 聚合統計
|
||||
│ ├── total_jobs # 總 Jobs 數
|
||||
│ ├── processed_today # 今日處理數
|
||||
│ ├── cpu:current # 當前 CPU 使用
|
||||
│ └── memory:current # 當前 Memory 使用
|
||||
├── jobs: # Jobs 管理
|
||||
│ ├── active # Set: 運行中 UUIDs
|
||||
│ ├── completed # Set: 完成 UUIDs
|
||||
│ └── failed # Set: 失敗 UUIDs
|
||||
├── job:{uuid} # Per-Video Job 狀態 (TTL: 24h)
|
||||
│ ├── status # 狀態 String
|
||||
│ ├── video_path # 視頻路徑
|
||||
│ ├── current_processor # 當前 processor
|
||||
│ ├── progress_total # 總進度
|
||||
│ ├── progress_current # 當前進度
|
||||
│ ├── started_at # 開始時間
|
||||
│ ├── updated_at # 最後更新
|
||||
│ └── processor:{name} # Per-Processor 狀態
|
||||
│ ├── status
|
||||
│ ├── progress
|
||||
│ ├── current
|
||||
│ ├── total
|
||||
│ └── started_at
|
||||
├── progress:{uuid} # Pub/Sub 頻道 (即時進度)
|
||||
├── result:{uuid} # 處理結果 Hash
|
||||
├── output:{uuid} # 輸出路徑
|
||||
├── metrics:{uuid} # Per-Video 指標
|
||||
│ ├── cpu # CPU 歷史 List (100條, TTL: 1h)
|
||||
│ ├── memory # Memory 歷史 List (100條, TTL: 1h)
|
||||
│ └── duration # 處理時長
|
||||
└── log:{uuid} # 處理日誌 String
|
||||
```
|
||||
|
||||
## 5. Key 詳細說明
|
||||
|
||||
### 全局 Keys
|
||||
|
||||
| Key | Type | TTL | 說明 |
|
||||
|-----|------|-----|------|
|
||||
| `momentry:health:current` | String | 60s | 當前健康狀態 |
|
||||
| `momentry:health:services` | Hash | 60s | 依賴服務健康狀態 |
|
||||
| `momentry:stats:total_jobs` | String | - | 總 Jobs 數 |
|
||||
| `momentry:stats:processed_today` | String | 86400s | 今日處理數 |
|
||||
| `momentry:stats:cpu:current` | String | 10s | 當前 CPU 使用 |
|
||||
| `momentry:stats:memory:current` | String | 10s | 當前 Memory 使用 |
|
||||
| `momentry:jobs:active` | Set | - | 運行中 Job UUIDs |
|
||||
| `momentry:jobs:completed` | Set | - | 完成 Job UUIDs |
|
||||
| `momentry:jobs:failed` | Set | - | 失敗 Job UUIDs |
|
||||
|
||||
### Per-Video Keys
|
||||
|
||||
| Key | Type | TTL | 說明 |
|
||||
|-----|------|-----|------|
|
||||
| `momentry:job:{uuid}` | Hash | 24h | Job 完整狀態 |
|
||||
| `momentry:job:{uuid}:status` | String | 24h | Job 狀態 |
|
||||
| `momentry:progress:{uuid}` | Pub/Sub | - | 即時進度頻道 |
|
||||
| `momentry:result:{uuid}` | Hash | 24h | 處理結果 |
|
||||
| `momentry:output:{uuid}` | String | 24h | 輸出路徑 |
|
||||
| `momentry:metrics:{uuid}:cpu` | List | 1h | CPU 歷史 (100條) |
|
||||
| `momentry:metrics:{uuid}:memory` | List | 1h | Memory 歷史 (100條) |
|
||||
| `momentry:metrics:{uuid}:duration` | String | 24h | 處理時長 |
|
||||
| `momentry:log:{uuid}` | String | 24h | 處理日誌 |
|
||||
|
||||
### Per-Processor Keys
|
||||
|
||||
| Key | Type | TTL | 說明 |
|
||||
|-----|------|-----|------|
|
||||
| `momentry:job:{uuid}:processor:{name}` | Hash | 24h | Processor 狀態 |
|
||||
| `momentry:job:{uuid}:processor:{name}:status` | String | 24h | 狀態 |
|
||||
| `momentry:job:{uuid}:processor:{name}:progress` | String | 24h | 進度百分比 |
|
||||
| `momentry:job:{uuid}:processor:{name}:current` | String | 24h | 當前項目 |
|
||||
| `momentry:job:{uuid}:processor:{name}:total` | String | 24h | 總項目數 |
|
||||
| `momentry:job:{uuid}:processor:{name}:started_at` | String | 24h | 開始時間 |
|
||||
|
||||
## 6. TTL 策略
|
||||
|
||||
| Key 類型 | TTL | 原因 |
|
||||
|----------|-----|------|
|
||||
| Health | 60s | 需要定期更新 |
|
||||
| Job | 24h | 處理完成後保留一天 |
|
||||
| Processor | 24h | 處理完成後保留一天 |
|
||||
| Metrics | 1h | 只保留近期歷史 |
|
||||
| Progress Pub/Sub | - | 不持久,僅即時訊息 |
|
||||
| Stats | 無 | 持久統計 |
|
||||
|
||||
## 7. 訊息格式
|
||||
|
||||
### Pub/Sub 訊息 (progress:{uuid})
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "info | progress | complete | error",
|
||||
"processor": "yolo | ocr | face | pose | cut | asr | asrx",
|
||||
"timestamp": 1700000000,
|
||||
"data": {
|
||||
"message": "Processing frame 5000",
|
||||
"current": 5000,
|
||||
"total": 14315
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Job 狀態 Hash
|
||||
|
||||
```json
|
||||
{
|
||||
"uuid": "5dea6618a606e7c7",
|
||||
"video_path": "/path/to/video.mp4",
|
||||
"status": "running",
|
||||
"current_processor": "yolo",
|
||||
"progress_total": 70,
|
||||
"progress_current": 50,
|
||||
"started_at": 1700000000,
|
||||
"updated_at": 1700000100,
|
||||
"error_count": 0,
|
||||
"last_error": ""
|
||||
}
|
||||
```
|
||||
|
||||
### Processor 狀態 Hash
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "yolo",
|
||||
"status": "running",
|
||||
"progress": 70,
|
||||
"current_frame": 10000,
|
||||
"total_frames": 14315,
|
||||
"started_at": 1700000000,
|
||||
"updated_at": 1700000100
|
||||
}
|
||||
```
|
||||
|
||||
## 8. 實作函數 (Rust)
|
||||
|
||||
所有 Redis Key 生成函數使用 `REDIS_KEY_PREFIX` 靜態變數:
|
||||
|
||||
```rust
|
||||
use crate::core::config::REDIS_KEY_PREFIX;
|
||||
|
||||
fn global_key(key: &str) -> String {
|
||||
format!("{}{}", REDIS_KEY_PREFIX, key)
|
||||
}
|
||||
|
||||
fn job_key(uuid: &str) -> String {
|
||||
format!("{}job:{}", REDIS_KEY_PREFIX, uuid)
|
||||
}
|
||||
|
||||
fn processor_key(uuid: &str, processor: &str) -> String {
|
||||
format!("{}job:{}:processor:{}", REDIS_KEY_PREFIX, uuid, processor)
|
||||
}
|
||||
|
||||
fn progress_channel(uuid: &str) -> String {
|
||||
format!("{}progress:{}", REDIS_KEY_PREFIX, uuid)
|
||||
}
|
||||
|
||||
fn metrics_key(uuid: &str, metric: &str) -> String {
|
||||
format!("{}metrics:{}:{}", REDIS_KEY_PREFIX, uuid, metric)
|
||||
}
|
||||
|
||||
fn jobs_set_key(status: &str) -> String {
|
||||
format!("{}jobs:{}", REDIS_KEY_PREFIX, status)
|
||||
}
|
||||
```
|
||||
|
||||
**注意**: `REDIS_KEY_PREFIX` 定義於 `src/core/config.rs`,由環境變數 `MOMENTRY_REDIS_PREFIX` 控制。
|
||||
|
||||
## 9. 環境變數
|
||||
|
||||
```bash
|
||||
# Redis 連接
|
||||
REDIS_URL=redis://localhost:6379
|
||||
REDIS_PASSWORD=accusys
|
||||
REDIS_DB=0
|
||||
|
||||
# Redis Key Prefix (可選,預設: momentry:)
|
||||
MOMENTRY_REDIS_PREFIX=momentry:
|
||||
|
||||
# 生產環境範例 (.env)
|
||||
MOMENTRY_SERVER_PORT=3002
|
||||
MOMENTRY_REDIS_PREFIX=momentry:
|
||||
|
||||
# 開發環境範例 (.env.development)
|
||||
MOMENTRY_SERVER_PORT=3003
|
||||
MOMENTRY_REDIS_PREFIX=momentry_dev:
|
||||
```
|
||||
|
||||
## 11. 監控腳本
|
||||
|
||||
使用 Redis 進行監控的腳本應參考:
|
||||
|
||||
- `monitor/service/momentry_redis_monitor.sh` - Redis 健康檢查
|
||||
- `monitor/service/momentry_job_monitor.sh` - Job 狀態監控
|
||||
@@ -1,334 +0,0 @@
|
||||
# Momentry Core 影片 RAG 系統說明稿
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-22 |
|
||||
| 文件版本 | V1.1 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-22 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
| V1.1 | 2026-03-25 | 更新API回應格式 (media_url→file_path) 與認證標頭 | OpenCode | deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
## 系統架構
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 使用者 │
|
||||
│ (marcom 團隊) │
|
||||
└─────────────────┬───────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ WordPress 入口 │
|
||||
│ (wp.momentry.ddns.net) │
|
||||
└─────────────────┬───────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ n8n 自動化 │
|
||||
│ (localhost:5678) │
|
||||
│ │
|
||||
│ [Webhook] → [HTTP Request] → [處理結果] → [回覆用戶] │
|
||||
└─────────────────┬───────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Momentry Core API │
|
||||
│ (localhost:3002) │
|
||||
│ │
|
||||
│ POST /api/v1/search → 語意搜尋 │
|
||||
│ POST /api/v1/n8n/search → n8n 專用格式 │
|
||||
│ GET /api/v1/videos → 影片列表 │
|
||||
└─────────────────┬───────────────────────────────────────────┘
|
||||
│
|
||||
┌─────────┴──────────┐
|
||||
▼ ▼
|
||||
┌───────────────┐ ┌───────────────┐
|
||||
│ PostgreSQL │ │ Qdrant │
|
||||
│ (chunks) │ │ (vectors) │
|
||||
└───────────────┘ └───────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 資料流程
|
||||
|
||||
```
|
||||
1. 上傳影片 → SFTPGo
|
||||
2. 影片註冊 → PostgreSQL
|
||||
3. ASR 處理 → 產生字幕區塊
|
||||
4. 儲存 chunks → PostgreSQL
|
||||
5. 向量化 → Qdrant
|
||||
6. 搜尋查詢 → API
|
||||
7. 回傳結果 → n8n → 用戶
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 示範影片
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 檔案名稱 | Old_Time_Movie_Show_-_Charade_1963.HD.mov |
|
||||
| UUID | a1b10138a6bbb0cd |
|
||||
| 時長 | 6879 秒(約 1.9 小時) |
|
||||
| 區塊數 | 3,886 個 |
|
||||
| 向量數 | 3,688 個 |
|
||||
|
||||
---
|
||||
|
||||
## API 端點
|
||||
|
||||
### 1. 語意搜尋
|
||||
|
||||
```
|
||||
POST http://localhost:3002/api/v1/search
|
||||
```
|
||||
|
||||
**請求:**
|
||||
```json
|
||||
{
|
||||
"query": "charade",
|
||||
"limit": 5,
|
||||
"uuid": "a1b10138a6bbb0cd"
|
||||
}
|
||||
```
|
||||
|
||||
> **注意**:
|
||||
> 1. **API 認證**: 所有 `/api/v1/*` 端點需要 `X-API-Key` 標頭
|
||||
> 2. **檔案路徑轉換**: API 現在返回 `file_path`(檔案系統路徑),需要轉換為可訪問的 URL(例如透過 SFTPGo 分享連結)
|
||||
|
||||
---
|
||||
|
||||
### 2. n8n 專用格式
|
||||
|
||||
```
|
||||
POST http://localhost:3002/api/v1/n8n/search
|
||||
```
|
||||
|
||||
**請求:**
|
||||
```json
|
||||
{
|
||||
"query": "charade",
|
||||
"limit": 5
|
||||
}
|
||||
```
|
||||
|
||||
**回應:**
|
||||
```json
|
||||
{
|
||||
"query": "charade",
|
||||
"count": 5,
|
||||
"hits": [
|
||||
{
|
||||
"id": "sentence_0006",
|
||||
"vid": "a1b10138a6bbb0cd",
|
||||
"start": 48.8,
|
||||
"end": 55.44,
|
||||
"title": "Chunk sentence_0006",
|
||||
"text": "fun plot twists...",
|
||||
"score": 0.526,
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 實作範例
|
||||
|
||||
### n8n Workflow 設計
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ Webhook │ ← 接收用戶搜尋請求
|
||||
└──────┬──────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ HTTP Request│ → POST /api/v1/n8n/search
|
||||
└──────┬──────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ Code │ → 處理回傳結果
|
||||
└──────┬──────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ Telegram │ → 回覆給用戶
|
||||
│ (或 LINE) │
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step-by-Step n8n Workflow
|
||||
|
||||
### Step 1: 建立 Webhook
|
||||
|
||||
1. n8n 開新 Workflow
|
||||
2. 新增 node: **Webhook**
|
||||
3. 設定 path: `video-search`
|
||||
4. 複製 Webhook URL
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 設定 HTTP Request
|
||||
|
||||
1. 新增 node: **HTTP Request**
|
||||
2. 設定:
|
||||
```
|
||||
Method: POST
|
||||
URL: http://localhost:3002/api/v1/n8n/search
|
||||
Body Content Type: JSON
|
||||
Headers: X-API-Key (需設定)
|
||||
```
|
||||
|
||||
3. Body:
|
||||
```json
|
||||
{
|
||||
"query": "={{ $json.body }}",
|
||||
"limit": 5
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 處理結果 (Code)
|
||||
|
||||
```javascript
|
||||
const hits = $input.first().json.hits;
|
||||
|
||||
if (!hits || hits.length === 0) {
|
||||
return {
|
||||
json: { message: "找不到相關結果" }
|
||||
};
|
||||
}
|
||||
|
||||
const results = hits.map((hit, index) => ({
|
||||
number: index + 1,
|
||||
text: hit.text,
|
||||
time: `${hit.start}s - ${hit.end}s`,
|
||||
score: Math.round(hit.score * 100) + "%",
|
||||
// 注意: API 現在返回 file_path(檔案系統路徑),需要轉換為可訪問的 URL
|
||||
url: hit.file_path + "#t=" + hit.start + "," + hit.end // 需實作檔案路徑轉換為 URL
|
||||
}));
|
||||
|
||||
return { json: { results } };
|
||||
```
|
||||
|
||||
> **注意**:
|
||||
> 1. **API 認證**: 所有 `/api/v1/*` 端點需要 `X-API-Key` 標頭
|
||||
> 2. **檔案路徑轉換**: API 現在返回 `file_path`(檔案系統路徑),需要轉換為可訪問的 URL(例如透過 SFTPGo 分享連結)
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 格式化輸出
|
||||
|
||||
**Telegram 格式:**
|
||||
```
|
||||
🎬 搜尋結果: "{{ $json.query }}"
|
||||
|
||||
1️⃣ "fun plot twists, Woody Dialog and charming performances..."
|
||||
⏱ 48.8s - 55.4s
|
||||
📊 相關度: 53%
|
||||
|
||||
2️⃣ "Don't you like me to say that a pretty girl..."
|
||||
⏱ 4745.6s - 4748.6s
|
||||
📊 相關度: 52%
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 測試指令
|
||||
|
||||
### curl 測試
|
||||
|
||||
```bash
|
||||
# 語意搜尋
|
||||
curl -X POST http://localhost:3002/api/v1/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "charade", "limit": 3}'
|
||||
|
||||
# n8n 格式
|
||||
curl -X POST http://localhost:3002/api/v1/n8n/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "charade", "limit": 3}'
|
||||
|
||||
# 影片列表
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos
|
||||
|
||||
# 特定影片區塊
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos/a1b10138a6bbb0cd/chunks
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 實際搜尋範例
|
||||
|
||||
| 搜尋詞 | 結果摘要 |
|
||||
|--------|----------|
|
||||
| `charade` | "fun plot twists, Woody Dialog and charming performances..." |
|
||||
| `woody` | "Well, you thick skull hair, brain half-witted..." |
|
||||
| `classic movie` | "Hello and welcome to the old-time movie show..." |
|
||||
| `charming` | "fun plot twists, Woody Dialog and charming performances..." |
|
||||
|
||||
---
|
||||
|
||||
## 資料庫狀態
|
||||
|
||||
| 資料庫 | 資料筆數 | 狀態 |
|
||||
|--------|----------|------|
|
||||
| PostgreSQL (videos) | 4 | ✅ |
|
||||
| PostgreSQL (chunks) | 3,950 | ✅ |
|
||||
| PostgreSQL (vectors) | 1,870 | ✅ |
|
||||
| Qdrant (vectors) | 3,688 | ✅ |
|
||||
| Redis (job cache) | 4 keys | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 下一步
|
||||
|
||||
1. **建立 SFTPGo 分享連結**
|
||||
- 開啟 http://localhost:8080
|
||||
- 登入 demo / demopassword123
|
||||
- 建立影片分享連結
|
||||
|
||||
2. **測試 n8n Workflow**
|
||||
- 匯入 Postman Collection
|
||||
- 建立 Webhook
|
||||
- 測試搜尋
|
||||
|
||||
3. **整合到 WordPress**
|
||||
- 建立表單接收用戶輸入
|
||||
- 呼叫 n8n Webhook
|
||||
- 顯示搜尋結果
|
||||
|
||||
---
|
||||
|
||||
## 快速開始
|
||||
|
||||
```bash
|
||||
# 1. 測試搜尋 API
|
||||
curl -X POST http://localhost:3002/api/v1/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "charade", "limit": 3}'
|
||||
|
||||
# 2. 查看影片列表
|
||||
curl http://localhost:3002/api/v1/videos
|
||||
|
||||
# 3. 查看 n8n 是否運行
|
||||
curl http://localhost:5678
|
||||
```
|
||||
@@ -1,813 +0,0 @@
|
||||
# 待解決問題追蹤
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-17 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-17 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
| V1.1 | 2026-03-21 | 更新問題狀態 | OpenCode | - |
|
||||
| V1.2 | 2026-03-21 | 添加備份機制優化待辦 | OpenCode | - |
|
||||
| V1.3 | 2026-03-21 | 完成清理硬編碼密碼 | OpenCode | - |
|
||||
| V1.4 | 2026-03-21 | 完成 OpenCode n8n MCP 整合 | OpenCode | - |
|
||||
| V1.5 | 2026-03-21 | 完成 API Key Management 核心模組 | OpenCode | - |
|
||||
| V1.6 | 2026-03-23 | 添加 Momentry Core API launchd 待辦 | OpenCode | - |
|
||||
| V1.7 | 2026-03-23 | 完成 Momentry Core API launchd 設定 | OpenCode | - |
|
||||
| V1.8 | 2026-03-24 | 完成服務統一遷移,所有服務使用自定義 plist | OpenCode | big-pickle |
|
||||
| V1.9 | 2026-03-24 | 建立統一會員系統實作計畫 | OpenCode | big-pickle |
|
||||
| V2.0 | 2026-03-24 | 建立 Job Worker 實作計畫 | OpenCode | big-pickle |
|
||||
|
||||
---
|
||||
|
||||
## 問題 #1: sqlx async INSERT 不會實際寫入數據庫
|
||||
|
||||
### 問題描述
|
||||
使用 sqlx async 執行 INSERT 時,報告成功(rows_affected=1),但數據沒有實際寫入數據庫。
|
||||
|
||||
### 嘗試過的解決方案
|
||||
| # | 嘗試方法 | 結果 |
|
||||
|---|---------|------|
|
||||
| 1 | 使用 `execute()` | 報告成功但未寫入 |
|
||||
| 2 | 使用 `fetch()` | 同樣問題 |
|
||||
| 3 | 使用交易 | 同樣問題 |
|
||||
| 4 | 使用連接池 `acquire()` | 同樣問題 |
|
||||
| 5 | 每個操作創建新連接池 | 同樣問題 |
|
||||
| 6 | 使用 `std::process::Command` 同步調用 psql | 同樣問題 |
|
||||
| 7 | 使用 `tokio::task::spawn_blocking` | 同樣問題 |
|
||||
|
||||
### 觀察到的現象
|
||||
- 直接用 psql 命令行可以成功寫入
|
||||
- 用另一個 PostgreSQL client 可以成功寫入
|
||||
- sqlx 查詢 COUNT(*) 可以正確讀取數據
|
||||
- 但 sqlx INSERT 報告成功卻不寫入
|
||||
|
||||
### 懷疑方向
|
||||
- sqlx 連接池與 PostgreSQL 的某種交互問題
|
||||
- Rust async runtime 與 PostgreSQL client 的問題
|
||||
- postgresql.conf 配置問題
|
||||
|
||||
### 臨時解決方案
|
||||
- Qdrant 向量存儲正常工作(不受影響)
|
||||
- 存儲狀態追蹤功能正常運作
|
||||
|
||||
### 負責人
|
||||
-
|
||||
|
||||
### 建立日期
|
||||
2026-03-17
|
||||
|
||||
### 備註
|
||||
影響 `store_vector` 函數,PVector 存儲無法正常工作,但 QVector 正常運作
|
||||
|
||||
### 2026-03-21 調查結果
|
||||
|
||||
#### 測試結果
|
||||
- 直接 psql INSERT: ✅ 成功
|
||||
- 資料寫入驗證: ✅ 成功
|
||||
|
||||
#### 發現的問題
|
||||
`store_vector` 函數 (`postgres_db.rs:819-860`) 存在以下問題:
|
||||
|
||||
```rust
|
||||
// 問題 1: 錯誤被靜默忽略
|
||||
match join_result {
|
||||
Ok((cid, Ok(output))) => {
|
||||
if !output.status.success() {
|
||||
let err = String::from_utf8_lossy(&output.stderr);
|
||||
tracing::error!("psql error for {}: {}", cid, err);
|
||||
// 沒有返回錯誤!只是記錄日誌
|
||||
}
|
||||
}
|
||||
// ...
|
||||
}
|
||||
Ok(()) // 即使失敗也返回 Ok
|
||||
```
|
||||
|
||||
#### 建議修復
|
||||
1. 讓 `store_vector` 返回實際結果
|
||||
2. 在失敗時返回 `Err`
|
||||
3. 添加單元測試驗證
|
||||
|
||||
#### 下一步
|
||||
- [x] 修復 `store_vector` 錯誤處理
|
||||
- [x] 添加單元測試
|
||||
- [ ] 重現並確認問題根因
|
||||
|
||||
### 2026-03-21 修復完成
|
||||
|
||||
已修復 `store_vector` 函數的錯誤處理:
|
||||
|
||||
```rust
|
||||
// 修復前:錯誤被靜默忽略
|
||||
match join_result {
|
||||
Ok((cid, Ok(output))) => {
|
||||
if !output.status.success() {
|
||||
tracing::error!("..."); // 只記錄,不返回錯誤
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(()) // 即使失敗也返回 Ok
|
||||
|
||||
// 修復後:正確傳播錯誤
|
||||
let (cid, output) = result;
|
||||
let output = output.map_err(|e| anyhow::anyhow!(...))?;
|
||||
if !output.status.success() {
|
||||
anyhow::bail!("psql INSERT failed...");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 問題 #2: TUI 與 stdout 輸出混合
|
||||
|
||||
### 問題描述
|
||||
Python processors 的 progress 輸出蓋過 TUI,導致使用者無法清楚看到處理進度。
|
||||
|
||||
### 解決方案
|
||||
~~使用 TUI 渲染到 stderr~~ → 使用 Redis Pub/Sub 作為消息總線
|
||||
|
||||
### 當前狀態
|
||||
| 子項目 | 狀態 |
|
||||
|--------|------|
|
||||
| RedisPublisher Python 端 | ✅ 已實作 |
|
||||
| Rust Redis 客戶端 | ✅ 已實作 (`redis_client.rs`) |
|
||||
| Rust 訂閱更新 TUI | ✅ 已實作 (main.rs) |
|
||||
| 中斷後繼續/重做 | 🔜 待實作 |
|
||||
|
||||
### 負責人
|
||||
-
|
||||
|
||||
### 建立日期
|
||||
2026-03-17
|
||||
|
||||
### 更新日期
|
||||
2026-03-21
|
||||
|
||||
### 參考文檔
|
||||
- `docs/MOMENTRY_CORE_REDIS_KEYS.md`
|
||||
- `scripts/redis_publisher.py`
|
||||
- `src/core/db/redis_client.rs`
|
||||
|
||||
---
|
||||
|
||||
## 問題 #3: Redis Message Bus 尚未實作
|
||||
|
||||
### 問題描述
|
||||
根據設計規範,需要使用 Redis 作為監控和狀態管理系統。
|
||||
|
||||
### 當前狀態
|
||||
|
||||
| 實作項目 | 狀態 | 說明 |
|
||||
|---------|------|------|
|
||||
| Redis 客戶端 (Hash) | ✅ | `redis_client.rs` |
|
||||
| Redis 客戶端 (Pub/Sub) | ✅ | `redis_client.rs::subscribe_progress()` |
|
||||
| Python RedisPublisher | ✅ | `scripts/redis_publisher.py` |
|
||||
| Rust 訂閱頻道 | ✅ | `main.rs` 中的 redis 訂閱邏輯 |
|
||||
| 監控腳本 | ✅ | `monitor/service/health_check.sh` |
|
||||
|
||||
### 負責人
|
||||
-
|
||||
|
||||
### 建立日期
|
||||
2026-03-17
|
||||
|
||||
### 更新日期
|
||||
2026-03-21
|
||||
|
||||
### 優先級
|
||||
~~高~~ → 中 - 核心功能已完成
|
||||
|
||||
### 參考文檔
|
||||
- `docs/MOMENTRY_CORE_REDIS_KEYS.md`
|
||||
- `docs/MOMENTRY_CORE_MONITORING.md`
|
||||
|
||||
---
|
||||
|
||||
## 架構優化待評估
|
||||
|
||||
詳細內容請參考 [ARCHITECTURE_EVALUATION.md](./ARCHITECTURE_EVALUATION.md)
|
||||
|
||||
| 項目 | 複雜度 | 優先級 |
|
||||
|------|--------|--------|
|
||||
| PostgreSQL → Redis 故障轉移 | 中 | 待評估 |
|
||||
| 連接池監控 | 低 | 待評估 |
|
||||
| Processor 重試機制 | 中 | 待評估 |
|
||||
| PyO3 整合 | 高 | 低 |
|
||||
| HTTP 健康端點 | 低 | ✅ 已完成 |
|
||||
| Commit Message Lint | 低 | ✅ 已完成 |
|
||||
|
||||
---
|
||||
|
||||
## 備份機制優化 (2026-03-21)
|
||||
|
||||
### 問題分析
|
||||
|
||||
| 問題 | 嚴重性 | 說明 |
|
||||
|------|--------|------|
|
||||
| 溫冷分層未啟用 | 中 | weekly/monthly/archive 目錄為空 |
|
||||
| 清理策略未執行 | 高 | 每日備份保留過多,空間浪費 |
|
||||
| 無異地備份 | 高 | 無遠程備份(雲端/另一設備) |
|
||||
| 備份驗證未自動化 | 中 | 只檢查文件存在,沒驗證可恢復性 |
|
||||
| Gitea 大文件 | 中 | 每次備份 1GB,頻率過高 |
|
||||
| 密碼硬編碼 | 高 | 腳本中有多處明文密碼 |
|
||||
|
||||
### 待辦事項
|
||||
|
||||
| # | 任務 | 優先級 | 狀態 |
|
||||
|---|------|--------|------|
|
||||
| 1 | 啟用溫冷分層 (`backup_monitor.sh tier`) | 高 | 待辦 |
|
||||
| 2 | 啟用清理策略 (`backup_monitor.sh cleanup`) | 高 | 待辦 |
|
||||
| 3 | 添加備份驗證腳本 | 高 | 待辦 |
|
||||
| 4 | 優化 Gitea 備份頻率 (改為週備份) | 中 | 待辦 |
|
||||
| 5 | 創建外部備份腳本 (rsync/雲端) | 高 | 待辦 |
|
||||
| 6 | 清理腳本中的硬編碼密碼 | 高 | ✅ 已完成 |
|
||||
|
||||
### 推薦備份策略
|
||||
|
||||
| 層級 | 保留時間 | 頻率 | 目標 |
|
||||
|------|----------|------|------|
|
||||
| Hot | 7 天 | 每日 | 本地 SSD |
|
||||
| Warm | 30 天 | 每週 | 本地 HDD |
|
||||
| Cold | 90 天 | 每月 | 外部存儲 |
|
||||
| Archive | 1 年 | 每季 | 離線/雲端 |
|
||||
|
||||
### 建議的 Crontab
|
||||
|
||||
```bash
|
||||
# 每日備份 (排除 Gitea)
|
||||
0 3 * * 1-6 /Users/accusys/momentry/scripts/backup_all.sh all_except_gitea
|
||||
|
||||
# 每週完整備份 (含 Gitea)
|
||||
0 3 * * 0 /Users/accusys/momentry/scripts/backup_all.sh all
|
||||
|
||||
# 每週溫冷分層
|
||||
0 4 * * 0 /Users/accusys/momentry_core_0.1/monitor/storage/backup_monitor.sh tier
|
||||
|
||||
# 每週清理
|
||||
0 5 * * 0 /Users/accusys/momentry_core_0.1/monitor/storage/backup_monitor.sh cleanup
|
||||
|
||||
# 每月驗證
|
||||
0 6 1 * * /Users/accusys/momentry/scripts/verify_backup.sh
|
||||
```
|
||||
|
||||
### 負責人
|
||||
-
|
||||
|
||||
### 參考文件
|
||||
- `/Users/accusys/momentry/scripts/backup_all.sh`
|
||||
- `/Users/accusys/momentry_core_0.1/monitor/storage/backup_monitor.sh`
|
||||
- `/Users/accusys/momentry/backup/`
|
||||
|
||||
---
|
||||
|
||||
## OpenCode n8n MCP 整合 (2026-03-21)
|
||||
|
||||
### 完成狀態
|
||||
|
||||
| 項目 | 狀態 | 說明 |
|
||||
|------|------|------|
|
||||
| n8n REST API 啟用 | ✅ | `N8N_PUBLIC_API_ENABLED=true` |
|
||||
| API Key 生成 | ✅ | Settings → API |
|
||||
| MCP Server 安裝 | ✅ | `@nextoolsolutions/mcp-n8n` |
|
||||
| OpenCode 設定 | ✅ | `~/.config/opencode/opencode.json` |
|
||||
| 43 工具可用 | ✅ | workflows, executions, datatables, tags 等 |
|
||||
|
||||
### 設定檔案
|
||||
|
||||
`~/.config/opencode/opencode.json`:
|
||||
```json
|
||||
{
|
||||
"mcp": {
|
||||
"gitea": {
|
||||
"type": "local",
|
||||
"enabled": true,
|
||||
"command": [
|
||||
"/opt/homebrew/bin/gitea-mcp-server",
|
||||
"-token", "<GITEA_TOKEN>",
|
||||
"-host", "http://localhost:3000"
|
||||
]
|
||||
},
|
||||
"n8n": {
|
||||
"type": "local",
|
||||
"enabled": true,
|
||||
"command": ["/opt/homebrew/bin/mcp-n8n"],
|
||||
"environment": {
|
||||
"N8N_BASE_URL": "http://localhost:5678",
|
||||
"N8N_API_KEY": "<N8N_API_KEY>"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 重要提醒
|
||||
|
||||
1. **API Key 安全**: 避免提交到 Git
|
||||
2. **n8n 需通過反向代理**: localhost:5678 無法直接訪問 API,需通過 Caddy
|
||||
3. **重啟生效**: 修改 `opencode.json` 後需重啟 OpenCode
|
||||
|
||||
### 參考文件
|
||||
- `docs/OPENCODE_GUIDE.md` - MCP 設定章節
|
||||
- `docs/INSTALL_N8N.md` - n8n 安裝指南
|
||||
|
||||
---
|
||||
|
||||
## n8n API 備份腳本 (2026-03-21)
|
||||
|
||||
### 腳本位置
|
||||
|
||||
| 腳本 | 說明 | 依賴 |
|
||||
|------|------|------|
|
||||
| `monitor/workflow/backup_n8n_api.py` | REST API 備份 | requests |
|
||||
| `monitor/workflow/backup_n8n_mcp.py` | MCP 備份(開發中) | mcp SDK |
|
||||
|
||||
### 使用方式
|
||||
|
||||
```bash
|
||||
# 備份所有 workflows
|
||||
N8N_API_KEY="..." python3.11 backup_n8n_api.py
|
||||
|
||||
# 只顯示變更(不備份)
|
||||
N8N_API_KEY="..." python3.11 backup_n8n_api.py --diff
|
||||
|
||||
# 差異備份(只備份變更的 workflows)
|
||||
N8N_API_KEY="..." python3.11 backup_n8n_api.py --incremental
|
||||
|
||||
# 列出可用備份
|
||||
python3.11 backup_n8n_api.py --list
|
||||
|
||||
# 驗證最新備份
|
||||
python3.11 backup_n8n_api.py --verify
|
||||
|
||||
# 顯示備份統計
|
||||
python3.11 backup_n8n_api.py --stats
|
||||
|
||||
# 只備份啟用的 workflows
|
||||
N8N_API_KEY="..." python3.11 backup_n8n_api.py --active-only
|
||||
|
||||
# 備份失敗的執行記錄
|
||||
N8N_API_KEY="..." python3.11 backup_n8n_api.py --failed-only
|
||||
```
|
||||
|
||||
### 功能
|
||||
|
||||
- [x] REST API 備份
|
||||
- [x] 變更偵測
|
||||
- [x] SHA256 校驗
|
||||
- [x] 備份版本化
|
||||
- [x] 差異備份(`--incremental`)
|
||||
- [x] 備份驗證(`--verify`)
|
||||
- [x] 備份統計(`--stats`)
|
||||
- [x] 失敗執行記錄備份(`--failed-only`)
|
||||
- [ ] 選擇性備份(按 Tags)
|
||||
|
||||
### 備份位置
|
||||
|
||||
```
|
||||
/Users/accusys/momentry/backup/n8n_workflows/api/
|
||||
├── 20260321_122059/
|
||||
│ ├── workflows.json # 21 workflows
|
||||
│ ├── workflows.json.sha256 # SHA256 校驗
|
||||
│ ├── tags.json # Tags(若有)
|
||||
│ └── manifest.json # 元數據
|
||||
└── ...
|
||||
```
|
||||
|
||||
### Crontab 建議
|
||||
|
||||
```bash
|
||||
# 每日備份(下午 3 點)
|
||||
0 15 * * * N8N_API_KEY="..." /opt/homebrew/bin/python3.11 /Users/accusys/momentry_core_0.1/monitor/workflow/backup_n8n_api.py >> /Users/accusys/momentry/log/monitor/workflow_backup_api.log 2>&1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Key Management System (2026-03-21)
|
||||
|
||||
### 設計目標
|
||||
|
||||
為 Momentry Core 實現完整的 API Key 管理系統,包括:
|
||||
- API Key 生成(安全隨機)
|
||||
- Key 哈希(SHA256)
|
||||
- 異常檢測
|
||||
- 強制輪換機制
|
||||
- 審計日誌
|
||||
|
||||
### 模組架構
|
||||
|
||||
```
|
||||
src/core/api_key/
|
||||
├── mod.rs # 模組導出
|
||||
├── models.rs # 數據模型和類型
|
||||
├── service.rs # 核心服務邏輯
|
||||
├── anomaly.rs # 異常檢測
|
||||
└── rotation.rs # 輪換管理
|
||||
```
|
||||
|
||||
### Key 類型
|
||||
|
||||
| 類型 | 前綴 | 默認 TTL | 寬限期 |
|
||||
|------|------|----------|--------|
|
||||
| System | `msys_` | 365 天 | 72 小時 |
|
||||
| User | `muser_` | 90 天 | 24 小時 |
|
||||
| Service | `msvc_` | 180 天 | 48 小時 |
|
||||
| Integration | `mint_` | 30 天 | 24 小時 |
|
||||
| Emergency | `memg_` | 1 天 | 0 小時 |
|
||||
|
||||
### Key 格式
|
||||
|
||||
```
|
||||
{prefix}{uuid}_{timestamp}_{random}
|
||||
例如: muser_a1b2c3d4e5f6_1710998400_abc12345
|
||||
```
|
||||
|
||||
### 異常檢測閾值
|
||||
|
||||
| 指標 | 閾值 |
|
||||
|------|------|
|
||||
| 每分鐘請求數 | 1000 |
|
||||
| 每小時請求數 | 10000 |
|
||||
| 錯誤率 | 50% |
|
||||
| 每小時唯一 IP 數 | 5 |
|
||||
| 鎖定閾值 | 3 次觸發 |
|
||||
|
||||
### 實現狀態
|
||||
|
||||
| 組件 | 狀態 | 說明 |
|
||||
|------|------|------|
|
||||
| 數據模型 | ✅ 完成 | `models.rs` |
|
||||
| Key 生成/哈希 | ✅ 完成 | `service.rs` |
|
||||
| 異常檢測 | ✅ 完成 | `anomaly.rs` |
|
||||
| 輪換機制 | ✅ 完成 | `rotation.rs` |
|
||||
| CLI 命令 | ✅ 完成 | `main.rs` |
|
||||
| 數據庫集成 | ✅ 完成 | `postgres_db.rs` |
|
||||
| Redis 告警 | ✅ 完成 | `redis_client.rs` |
|
||||
| 數據庫遷移 | ✅ 完成 | `migrations/001_api_key_management.sql` |
|
||||
| 單元測試 | ✅ 完成 | 55 個測試通過 |
|
||||
|
||||
### CLI 命令
|
||||
|
||||
```bash
|
||||
# 創建 API Key
|
||||
momentry api-key create <name> --key-type service --ttl 90
|
||||
|
||||
# 列出所有 Keys
|
||||
momentry api-key list
|
||||
|
||||
# 驗證 Key
|
||||
momentry api-key validate --key <key>
|
||||
|
||||
# 撤銷 Key
|
||||
momentry api-key revoke --key <key>
|
||||
|
||||
# 請求輪換
|
||||
momentry api-key rotate --key <key>
|
||||
|
||||
# 顯示統計
|
||||
momentry api-key stats
|
||||
```
|
||||
|
||||
### 參考文件
|
||||
|
||||
- `src/core/api_key/` - API Key 模組
|
||||
- `docs/API_KEY_MANAGEMENT.md` - 設計文檔
|
||||
- `migrations/001_api_key_management.sql` - 數據庫遷移
|
||||
|
||||
---
|
||||
|
||||
## 問題 #5: Redis 用戶名問題 (2026-03-21)
|
||||
|
||||
### 問題描述
|
||||
|
||||
Redis 僅有 `default` 用戶,無 `accusys` 用戶。`.env` 檔案使用 `redis://accusys:accusys@localhost:6379` 格式會導致認證失敗。
|
||||
|
||||
### 測試結果
|
||||
|
||||
| URL 格式 | 結果 |
|
||||
|----------|------|
|
||||
| `redis://accusys:accusys@localhost:6379` | ❌ AUTH failed |
|
||||
| `redis://:accusys@localhost:6379` | ✅ PONG |
|
||||
|
||||
### Redis ACL 狀態
|
||||
|
||||
```
|
||||
user default on sanitize-payload #1bd51c... ~* &* +@all
|
||||
requirepass: accusys
|
||||
```
|
||||
|
||||
### 根本原因
|
||||
|
||||
1. Redis 啟動時僅設定 `--requirepass accusys`
|
||||
2. 未建立自訂用戶 `accusys`
|
||||
3. ACL 變更不會持久化(無 config file)
|
||||
|
||||
### 已執行修復
|
||||
|
||||
| 項目 | 修改 |
|
||||
|------|------|
|
||||
| `.env` | `redis://accusys:accusys@localhost:6379` → `redis://:accusys@localhost:6379` |
|
||||
|
||||
### 待解決問題
|
||||
|
||||
1. **ACL 持久化**:Redis 啟動後手動建立的用戶不會保留(重啟後消失)
|
||||
2. **需配置 ACL 文件**:建議建立 `users.acl` 並在 plist 中指定
|
||||
|
||||
### 建議解決方案
|
||||
|
||||
#### 方案 A:使用默認用戶(現行)
|
||||
|
||||
```bash
|
||||
# .env
|
||||
REDIS_URL=redis://:accusys@localhost:6379
|
||||
```
|
||||
|
||||
**優點**:簡單,無需修改 Redis 配置
|
||||
**缺點**:所有應用共享默認用戶
|
||||
|
||||
#### 方案 B:建立 ACL 配置文件
|
||||
|
||||
```bash
|
||||
# 1. 創建 ACL 文件
|
||||
cat > /Users/accusys/momentry/etc/redis/users.acl << 'EOF'
|
||||
user default on sanitize-payload ~* &* +@all >accusys
|
||||
user accusys on sanitize-payload ~* &* +@all >accusys
|
||||
EOF
|
||||
|
||||
# 2. 修改 plist 添加 --aclfile 參數
|
||||
--aclfile /Users/accusys/momentry/etc/redis/users.acl
|
||||
|
||||
# 3. 重啟 Redis
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.momentry.redis.plist
|
||||
sudo launchctl load /Library/LaunchDaemons/com.momentry.redis.plist
|
||||
```
|
||||
|
||||
**優點**:支持多用戶,ACL 持久化
|
||||
**缺點**:需修改 plist 並重啟
|
||||
|
||||
### 影響範圍
|
||||
|
||||
- `src/core/config.rs` - REDIS_URL 讀取
|
||||
- `src/core/db/redis_client.rs` - Redis 連線
|
||||
- `momentry api-key` 命令 - 異常告警
|
||||
|
||||
### 狀態
|
||||
|
||||
- [x] 已確認問題存在
|
||||
- [x] 已修改 `.env` 使用默認用戶
|
||||
- [ ] 待決定是否實施 ACL 方案
|
||||
|
||||
---
|
||||
|
||||
## 問題 #6: Momentry Core API 未開機自動啟動 (2026-03-23)
|
||||
|
||||
### 問題描述
|
||||
|
||||
Momentry Core API 服務 (`momentry server --port 3002`) 未設定 launchd,導致:
|
||||
1. 系統重啟後 API 服務不會自動啟動
|
||||
2. `api.momentry.ddns.net` 返回 502 Bad Gateway
|
||||
3. n8n workflow 呼叫 API 時失敗
|
||||
|
||||
### 發現過程
|
||||
|
||||
1. n8n workflow 呼叫 `https://api.momentry.ddns.net/api/v1/n8n/search` 返回 502
|
||||
2. 檢查發現 port 3002 無服務運行
|
||||
3. Caddy 配置正向確,但後端服務未啟動
|
||||
4. 手動啟動服務後 API 正常運作
|
||||
|
||||
### 配置需求
|
||||
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| 服務名稱 | `com.momentry.api` |
|
||||
| 二進位檔 | `/Users/accusys/momentry_core_0.1/target/release/momentry` |
|
||||
| 命令 | `server --port 3002` |
|
||||
| Port | 3002 |
|
||||
| 環境變數 | `DATABASE_URL`, `REDIS_URL` 等 |
|
||||
|
||||
### 待辦事項
|
||||
|
||||
- [x] 建立 `docs/INSTALL_MOMENTRY_API.md` 安裝文件
|
||||
- [x] 建立 `/Library/LaunchDaemons/com.momentry.api.plist`
|
||||
- [x] 設定環境變數 (`DATABASE_URL`, `REDIS_URL` 等)
|
||||
- [x] 測試 launchctl load/unload
|
||||
- [x] 驗證開機自動啟動 (launchd 載入成功)
|
||||
|
||||
### 完成日期
|
||||
2026-03-23
|
||||
|
||||
### 參考文件
|
||||
|
||||
- `/Library/LaunchDaemons/com.momentry.n8n.main.plist` - n8n plist 範例
|
||||
- `docs/INSTALL_N8N.md` - plist 配置說明
|
||||
|
||||
---
|
||||
|
||||
## 服務統一遷移 (2026-03-24)
|
||||
|
||||
### 問題描述
|
||||
|
||||
Reboot 後發現 n8n workflow 數量從 42 變成 41,確認是 PostgreSQL 資料庫問題。經過調查發現:
|
||||
|
||||
1. **兩組不同的 PostgreSQL 資料目錄**:
|
||||
- Homebrew plist: `/opt/homebrew/var/postgresql@18` (有最新資料)
|
||||
- Custom plist: `/Users/accusys/momentry/var/postgresql` (可能是舊資料)
|
||||
|
||||
2. **Reboot 時 custom plist 搶先啟動**,使用了錯誤的資料目錄
|
||||
|
||||
### 解決方案
|
||||
|
||||
1. **統一使用 custom plist**:
|
||||
- 刪除 homebrew plist (`~/Library/LaunchAgents/homebrew.mxcl.postgresql@18.plist`)
|
||||
- Custom plist 使用 `/Users/accusys/momentry/var/postgresql` 作為資料目錄
|
||||
- 將所有 14 個服務的 plist 註冊到 launchd
|
||||
|
||||
2. **所有已遷移的服務**:
|
||||
|
||||
| 服務 | Plist | 資料目錄 |
|
||||
|------|-------|----------|
|
||||
| PostgreSQL | ✅ | `/Users/accusys/momentry/var/postgresql` |
|
||||
| MariaDB | ✅ | `/Users/accusys/momentry/var/mariadb` |
|
||||
| MongoDB | ✅ | `/opt/homebrew/var/mongodb` |
|
||||
| Redis | ✅ | - |
|
||||
| Ollama | ✅ | - |
|
||||
| Qdrant | ✅ | - |
|
||||
| n8n Main | ✅ | - |
|
||||
| n8n Worker | ✅ | - |
|
||||
| Caddy | ✅ | - |
|
||||
| SFTPGo | ✅ | - |
|
||||
| Gitea | ✅ | - |
|
||||
| Gitea MCP | ✅ | - |
|
||||
| PHP | ✅ | - |
|
||||
| Momentry API | ✅ | - |
|
||||
| RustDesk HBBR | ✅ | - |
|
||||
| RustDesk HBBS | ✅ | - |
|
||||
|
||||
### 還發現的 Homebrew 服務 (未遷移)
|
||||
|
||||
| 服務 | 建議 |
|
||||
|------|------|
|
||||
| homebrew.mxcl.grafana | ⚠️ 考慮遷移 |
|
||||
| homebrew.mxcl.prometheus | ⚠️ 考慮遷移 |
|
||||
| homebrew.mxcl.openwebui | ⚠️ 考慮遷移 |
|
||||
| homebrew.mxcl.kafka | ⚠️ 考慮遷移 |
|
||||
| homebrew.mxcl.seaweedfs | ⚠️ 考慮遷移 |
|
||||
| homebrew.mxcl.netdata | ⚠️ 考慮遷移 |
|
||||
| homebrew.mxcl.ddclient | ⚠️ 動態 DNS |
|
||||
| homebrew.mxcl.shadowsocks-rust | ⚠️ VPN |
|
||||
|
||||
### 預防措施
|
||||
|
||||
1. **確保統一資料目錄**:所有服務只使用一個資料目錄
|
||||
2. **Reboot 測試**:遷移完成後需進行 Reboot 測試
|
||||
3. **文件同步**:plist 檔案同步到 repo
|
||||
|
||||
### 完成日期
|
||||
2026-03-24
|
||||
|
||||
### 參考文件
|
||||
|
||||
- `docs/SERVICES.md` - 服務管理文檔
|
||||
- `docs/SERVICE_ADDITION_GUIDE.md` - 服務添加規範
|
||||
- `momentry_runtime/plist/` - plist 檔案存放位置
|
||||
|
||||
---
|
||||
|
||||
## Job Worker 實作 (2026-03-24)
|
||||
|
||||
### 目標
|
||||
|
||||
實作輪詢式 Job Worker,實現檔案註冊後自動觸發處理:
|
||||
|
||||
1. **輪詢機制**:Worker 定期輪詢 jobs 佇列
|
||||
2. **並行處理**:最多 2 個 processor 同時執行
|
||||
3. **失敗容忍**:任一模組獨立,失敗可接續
|
||||
|
||||
### 設計決策
|
||||
|
||||
| 項目 | 決策 | 理由 |
|
||||
|------|------|------|
|
||||
| 觸發方式 | 輪詢(Job Worker) | 暫無可靠 API 觸發 |
|
||||
| 並行處理 | 最多 2 個 | 可根據 CPU/GPU 調整 |
|
||||
| 失敗處理 | 獨立模組,部分完成可接續 | 任何模組失敗都產出狀態 |
|
||||
| Worker 啟動 | 獨立進程 | 隔離、易管理 |
|
||||
| 並行上限 | 環境變數 + 預設值 | 靈活調整 |
|
||||
| 狀態同步 | PostgreSQL + Redis | 可靠 + 即時 |
|
||||
|
||||
### 環境變數
|
||||
|
||||
| 變數 | 預設值 | 說明 |
|
||||
|------|--------|------|
|
||||
| `MOMENTRY_MAX_CONCURRENT` | 2 | 最大並行 processor 數 |
|
||||
| `MOMENTRY_POLL_INTERVAL` | 5 | 輪詢間隔(秒) |
|
||||
| `MOMENTRY_WORKER_ENABLED` | true | 是否啟用 worker |
|
||||
|
||||
### 實作計畫
|
||||
|
||||
詳細內容請參考 [JOB_WORKER_IMPLEMENTATION_PLAN.md](./JOB_WORKER_IMPLEMENTATION_PLAN.md)
|
||||
|
||||
### Phase 規劃
|
||||
|
||||
| Phase | 任務 | 預估工時 |
|
||||
|-------|------|----------|
|
||||
| 1 | 資料庫遷移 | 2h |
|
||||
| 2 | Worker 框架 | 4h |
|
||||
| 3 | Register API 整合 | 2h |
|
||||
| 4 | Processor 執行 | 4h |
|
||||
| 5 | 進度追蹤 | 2h |
|
||||
| 6 | API 端點 | 3h |
|
||||
| 7 | CLI 命令 | 2h |
|
||||
| 8 | 測試 | 4h |
|
||||
|
||||
**總預估**: ~23h
|
||||
|
||||
### 實作結構
|
||||
|
||||
```
|
||||
src/
|
||||
├── worker/
|
||||
│ ├── mod.rs # Worker 模組導出
|
||||
│ ├── config.rs # Worker 配置
|
||||
│ ├── worker.rs # Worker 主邏輯
|
||||
│ ├── processor.rs # Processor 執行器
|
||||
│ ├── queue.rs # Job 佇列管理
|
||||
│ └── progress.rs # 進度追蹤
|
||||
├── api/
|
||||
│ └── server.rs # 更新 Register API
|
||||
└── main.rs # 新增 worker 命令
|
||||
```
|
||||
|
||||
### 狀態
|
||||
|
||||
- [x] 系統分析完成
|
||||
- [x] 實作計畫文件建立
|
||||
- [ ] Phase 1: 資料庫遷移
|
||||
- [ ] Phase 2: Worker 框架
|
||||
- [ ] Phase 3: Register API 整合
|
||||
- [ ] Phase 4: Processor 執行
|
||||
- [ ] Phase 5-8: 依序實作
|
||||
|
||||
### 參考文件
|
||||
|
||||
- `docs/JOB_WORKER_IMPLEMENTATION_PLAN.md` - 完整實作計畫
|
||||
- `docs/PROCESSING_PIPELINE.md` - 處理流程
|
||||
- `docs/MOMENTRY_CORE_REDIS_KEYS.md` - Redis Key 設計
|
||||
|
||||
---
|
||||
|
||||
## 統一會員系統 + 影片歸屬追蹤 (2026-03-24)
|
||||
|
||||
### 目標
|
||||
|
||||
建立統一的會員系統:
|
||||
1. WordPress 作為唯一登入入口
|
||||
2. 每個影片關聯到 user_id(追蹤歸屬)
|
||||
3. Per-user 配額管理
|
||||
4. API 端點啟用認證
|
||||
|
||||
### 實作計畫
|
||||
|
||||
詳細內容請參考 [USER_MANAGEMENT_PLAN.md](./USER_MANAGEMENT_PLAN.md)
|
||||
|
||||
### Phase 規劃
|
||||
|
||||
| Phase | 任務 | 複雜度 | 優先級 | 預估工時 |
|
||||
|-------|------|--------|--------|----------|
|
||||
| 1 | WordPress Application Passwords 測試 | 低 | P0 | 1.5h |
|
||||
| 2 | 資料庫遷移 (users 表) | 中 | P0 | 3h |
|
||||
| 3 | API auth middleware | 中 | P0 | 4h |
|
||||
| 4 | Register API 更新 | 低 | P0 | 2h |
|
||||
| 5 | Admin users API | 中 | P1 | 4h |
|
||||
| 6 | n8n workflow | 中 | P1 | 6h |
|
||||
| 7 | 配額管理 | 中 | P2 | 4h |
|
||||
| 8 | 測試驗證 | 中 | P2 | 4h |
|
||||
|
||||
**總預估**: ~28.5h
|
||||
|
||||
### 待確認事項
|
||||
|
||||
- [ ] WordPress 用戶建立方式(手動/Elementor表單)
|
||||
- [ ] API Key 格式確認
|
||||
- [ ] SFTPGo 整合方式
|
||||
- [ ] 配額管理策略
|
||||
- [ ] 用戶刪除同步流程
|
||||
|
||||
### 狀態
|
||||
|
||||
- [x] 系統分析完成
|
||||
- [x] 實作計畫文件建立
|
||||
- [ ] Phase 1: WordPress 認證測試
|
||||
- [ ] Phase 2: 資料庫遷移
|
||||
- [ ] Phase 3-8: 依序實作
|
||||
|
||||
### 參考文件
|
||||
|
||||
- `docs/USER_MANAGEMENT_PLAN.md` - 完整實作計畫
|
||||
- `docs/API_KEY_MANAGEMENT.md` - API Key 管理
|
||||
- `docs/SFTPGO_DEMO_USER.md` - SFTPGo 用戶設定
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user