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:
Warren
2026-05-04 01:31:21 +08:00
parent ee81e343ce
commit e75c4d6f07
3270 changed files with 35190 additions and 53367 deletions

View File

@@ -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
View File

@@ -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/

View File

@@ -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
View File

@@ -2369,6 +2369,7 @@ dependencies = [
"qdrant-client",
"ratatui",
"redis",
"regex",
"reqwest",
"sdl2",
"serde",

View File

@@ -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"

View File

@@ -1 +0,0 @@
/Users/accusys/momentry_core_0.1/output/a1b10138a6bbb0cd.cut.json

View File

@@ -1 +0,0 @@
/Users/accusys/momentry_core_0.1/output/a1b10138a6bbb0cd.face.json

View File

@@ -1 +0,0 @@
/Users/accusys/momentry_core_0.1/output/a1b10138a6bbb0cd.ocr.json

View File

@@ -1 +0,0 @@
/Users/accusys/momentry_core_0.1/output/a1b10138a6bbb0cd.pose.json

View File

@@ -1 +0,0 @@
/Users/accusys/momentry_core_0.1/output/a1b10138a6bbb0cd.story.json

View File

@@ -1 +0,0 @@
/Users/accusys/momentry_core_0.1/output/a1b10138a6bbb0cd.yolo.json

View File

@@ -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}")

View File

@@ -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}")

View File

@@ -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);
}

View File

@@ -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}")

View File

@@ -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()

View File

@@ -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>

View File

@@ -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(())
}

View File

@@ -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.

View File

@@ -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])

View File

@@ -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")

View File

@@ -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

View File

@@ -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
```

View 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 |

View 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 |

View 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
```

View 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` | 敘事 chunkparent | 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 |

View 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`)

View 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 |

View 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 6879sASR 以 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 量化) |
| 依賴 | 無 |

View 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 本地化為 Moondream2HuggingFace transformers, ~1.8GB)。備援方案為 YOLO + OCR + Scene 結果串接。
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-05-02 | 初始版本 | OpenCode | deepseek-chat |
## 資源預估
| 資源 | 值 |
|------|-----|
| CPU | - |
| 記憶體 | ~1.8 GB模型載入後 |
| GPU | 不使用 |
| 依賴 | Scene |

View 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 | 不使用 |
| 依賴 | 無 |

View File

@@ -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 |

View 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` |
| 依賴 | 無 |
---

View 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 不使用 GPUCPU 使用率 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 | 不使用 |
| 依賴 | 無 |

View 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 Posepose_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` |
| 依賴 | 無 |

View 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.3s5.2 分鐘) |
## 資源預估
| 資源 | 值 |
|------|-----|
| CPU | 0.3 |
| 記憶體 | 512 MB |
| GPU | 不使用 |
| 依賴 | CUT, ASR |

View 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 |

View File

@@ -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 |

View File

@@ -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 |

View 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 ShadersApple 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 倍) |
| 依賴 | 無 |

View 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_durationScene 移除 ASR 依賴;長影片 Face 動態調度Job 完成條件放寬 | OpenCode | deepseek-chat |

View 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 |

View File

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

View File

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

View File

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

View File

@@ -1,461 +0,0 @@
# Momentry API 使用流程
> **目標**: 從影片上傳到搜尋的完整流程
> **適用**: WordPress / n8n 整合
> **版本**: V1.0 | **日期**: 2026-03-25
---
## 流程總覽
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 1. 上傳 │ → │ 2. 註冊 │ → │ 3. 確認 │ → │ 4. 處理 │ → │ 5. 搜尋 │
│ SFTPGo │ │ 自動完成 │ │ UUID │ │ 查詢進度 │ │ 測試 │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
```
---
## Step 1: 上傳影片
### 方式 A: SFTP 上傳(推薦)
```bash
# 連線資訊
主機: sftpgo.momentry.ddns.net
連接埠: 2022
用戶名: demo
密碼: demopassword123
```
使用 FileZilla 或 SFTP 客戶端上傳到 `/` 目錄
### 方式 B: SFTP 命令列
```bash
sshpass -p "demopassword123" sftp -P 2022 demo@sftpgo.momentry.ddns.net
```
上傳後確認檔案在 SFTPGo 中的位置
---
## Step 2: 自動註冊
上傳後,系統會自動:
1. 偵測新檔案
2. 計算 UUIDSHA256
3. 建立資料庫記錄
**無需手動操作**
---
## Step 3: 確認註冊成功
### 查詢所有影片
```bash
curl -s -H "X-API-Key: YOUR_API_KEY" \
"https://api.momentry.ddns.net/api/v1/videos" | jq '.videos | length'
```
### 查詢特定檔案
```bash
curl -s -H "X-API-Key: YOUR_API_KEY" \
"https://api.momentry.ddns.net/api/v1/videos" | jq '.videos[] | select(.file_name | contains("你的檔案名"))'
```
### 預期回應
```json
{
"uuid": "952f5854b9febad1",
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/你的檔案.mp4",
"file_name": "你的檔案.mp4",
"duration": 123.45,
"width": 1920,
"height": 1080
}
```
**確認要點**:
- ✅ UUID 已產生16位 hex
-`file_path` 正確
-`duration` > 0
---
## Step 4: 查詢處理進度
### 取得任務 UUID
```bash
# 從影片資訊取得 job_id
curl -s -H "X-API-Key: YOUR_API_KEY" \
"https://api.momentry.ddns.net/api/v1/videos" | \
jq '.videos[] | select(.file_name == "你的檔案.mp4") | {uuid, job_id}'
```
### 查詢任務狀態
```bash
curl -s -H "X-API-Key: YOUR_API_KEY" \
"https://api.momentry.ddns.net/api/v1/jobs/{uuid}"
```
### 任務狀態說明
| status | 說明 | 動作 |
|--------|------|------|
| `pending` | 等待處理 | 等待中 |
| `processing` | 處理中 | 繼續輪詢 |
| `completed` | 已完成 | 可進入 Step 5 |
| `failed` | 處理失敗 | 檢查錯誤 |
### n8n 輪詢範例
```javascript
// n8n Workflow: 檢查處理狀態
const jobUuid = $input.item.json.job_uuid;
const response = await fetch(
`https://api.momentry.ddns.net/api/v1/jobs/${jobUuid}`,
{
headers: {
"X-API-Key": "YOUR_API_KEY"
}
}
);
const job = await response.json();
// 狀態檢查
if (job.status === 'completed') {
return [{ json: { done: true, 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 |

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,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` | 伺服器內部錯誤 |

View File

@@ -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` | 伺服器內部錯誤 |

View File

@@ -1,391 +0,0 @@
# Momentry Core API 教育訓練手冊
> **對象**: marcom 團隊
> **版本**: V1.4 | **日期**: 2026-03-25
---
## 1. 快速開始
### 基本資訊
| 項目 | 值 |
|------|-----|
| API 網址 | `https://api.momentry.ddns.net` |
| 認證方式 | Header `X-API-Key` |
| 格式 | JSON |
### Demo 測試帳號
#### API Key用於 API 認證)
```
X-API-Key: muser_68600856036340bcafc01930eb4bd839
```
#### SFTPGo用於影片上傳
| 項目 | 值 |
|------|-----|
| SFTP 主機 | `sftpgo.momentry.ddns.net` |
| SFTP 連接埠 | `2022` |
| 用戶名 | `demo` |
| 密碼 | `demopassword123` |
| Web 管理介面 | `https://sftpgo.momentry.ddns.net` |
**使用方式**:透過 SFTP 上傳影片,系統會自動註冊並處理。
---
## 2. 快速範例
### 查詢所有影片
```bash
curl -s -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
"https://api.momentry.ddns.net/api/v1/videos"
```
### 查詢單一影片
```bash
curl -s -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
"https://api.momentry.ddns.net/api/v1/videos/{uuid}"
```
### 查詢處理任務狀態
```bash
curl -s -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
"https://api.momentry.ddns.net/api/v1/jobs/{uuid}"
```
---
## 3. API 端點說明
### 3.1 影片相關
#### GET /api/v1/videos
取得所有影片列表
**回應範例**:
```json
{
"videos": [
{
"uuid": "5dea6618a606e7c7",
"filename": "demo_video.mp4",
"duration": 123.45,
"status": "ready",
"created_at": "2026-03-25T10:00:00Z"
}
]
}
```
#### GET /api/v1/videos/:uuid
取得單一影片詳情
### 3.2 搜尋與分段查詢
#### POST /api/v1/search
向量搜尋查詢分段Chunk詳情
**請求參數**:
| 參數 | 類型 | 必填 | 說明 |
|------|------|------|------|
| `query` | string | 是 | 搜尋關鍵字 |
| `limit` | number | 否 | 回傳數量(預設 10 |
| `uuid` | string | 否 | 只搜尋指定影片 |
**請求範例**:
```json
{
"query": "天氣",
"limit": 10,
"uuid": "5dea6618a606e7c7"
}
```
**回應範例**:
```json
{
"results": [
{
"uuid": "39567a0eb16f39fd",
"chunk_id": "sentence_1471",
"chunk_type": "sentence",
"start_time": 5309.08,
"end_time": 5311.08,
"text": "influenced by a vital way,",
"score": 0.68
}
],
"query": "天氣"
}
```
**Chunk 欄位說明**:
| 欄位 | 說明 | 範例 |
|------|------|------|
| `uuid` | 影片唯一識別碼 | `39567a0eb16f39fd` |
| `chunk_id` | 分段識別碼 | `sentence_1471` |
| `chunk_type` | 分段類型 | `sentence` / `cut` / `time` / `trace` / `story` |
| `start_time` | 開始時間(秒) | `5309.08` |
| `end_time` | 結束時間(秒) | `5311.08` |
| `text` | 內容文字 | `influenced by a vital way` |
| `score` | 相似度分數0-1 | `0.68` |
**Chunk 類型說明**:
| 類型 | 說明 | 來源 |
|------|------|------|
| `sentence` | 語音轉文字片段 | ASR 處理 |
| `cut` | 場景變化片段 | CUT 處理 |
| `time` | 固定時間分段 | 系統自動切割 |
| `trace` | 軌跡追蹤片段 | YOLO 追蹤 |
| `story` | 故事線片段(父子關係) | Story 分析 |
#### POST /api/v1/n8n/search
n8n 專用搜尋(包含完整影片檔案路徑 file_path
**請求參數**: 與 `/search` 相同
**回應範例**:
```json
{
"query": "天氣",
"count": 2,
"hits": [
{
"id": "sentence_1471",
"vid": "39567a0eb16f39fd",
"chunk_type": "sentence",
"start_frame": 318545,
"end_frame": 318665,
"fps": 59.94,
"start_time": 5314.31,
"end_time": 5316.32,
"text": "influenced by a vital way,",
"score": 0.68
}
]
}
```
**與 /search 的差異**:
| 欄位 | `/search` | `/n8n/search` |
|------|-----------|----------------|
| 影片 UUID | `uuid` | `vid` |
| Chunk ID | `chunk_id` | `id` |
| 開始時間 | `start_time` | `start_time` |
| 結束時間 | `end_time` | `end_time` |
| 相似度分數 | `score` | `score` |
| **檔案路徑** | ❌ | ✅ `file_path` |
> **注意**: `file_path` 是影片的實際路徑,可用於本地播放。
### 3.3 任務相關
### 3.4 任務相關
#### GET /api/v1/jobs/:uuid
查詢處理任務狀態
**回應範例**:
```json
{
"uuid": "9760d0820f0cf9a7",
"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 |

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,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')*
\`\`\`
```

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,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` 目錄
- **監控**: 所有上傳都有日誌記錄
- **生產環境**: 正式環境應使用更強的密碼和金鑰認證

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,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