docs: file_uuid generation rules for M4

This commit is contained in:
Accusys
2026-05-17 02:26:09 +08:00
parent 3a6c186575
commit eec2eea880
79 changed files with 23293 additions and 0 deletions

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,818 @@
{
"id": "o9MZ3XaJ5Vyf4kJ9",
"name": "Momentry Search API - Core v1.2",
"active": true,
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "search",
"responseMode": "responseNode",
"options": {}
},
"id": "b5f92603-1071-42e0-85b1-6c1655f9c992",
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
-384,
1408
],
"webhookId": "79d6584e-c37c-49c4-9e83-c72896cef416"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"name": "query",
"value": "={{$json.body.query || $json.query}}",
"type": "string",
"id": "f9a4b19c-b478-4fd3-9969-cd78298d2138"
},
{
"name": "mode",
"value": "={{ $json.body?.mode || $json.mode || \"mock\" }}",
"type": "string",
"id": "59066dec-1e8e-4339-ab5e-ce538c0dc216"
},
{
"name": "request_source",
"value": "portal",
"type": "string",
"id": "1ff615c2-23b2-45af-a580-9d27a6a8d4bc"
},
{
"name": "limit",
"value": 10,
"type": "number",
"id": "16adb464-079b-4a91-bbb9-bd9b44db83ed"
},
{
"id": "d298ad4d-4fab-41f0-a11a-7d089cf78ae3",
"name": "query_normalized",
"value": "={{ ($json.body.query || $json.query || \"\").toLowerCase().trim() }}",
"type": "string"
}
]
},
"options": {}
},
"id": "fadff1c5-038b-4aa7-ad18-957440d0942c",
"name": "Build Search Request",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
-160,
1408
]
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 1
},
"conditions": [
{
"leftValue": "={{$json.query_normalized}}",
"rightValue": "sun",
"operator": {
"type": "string",
"operation": "contains"
},
"id": "54556e33-0dff-4e3d-ada0-81ea89f422c2"
}
],
"combinator": "and"
}
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 1
},
"conditions": [
{
"leftValue": "={{$json.query_normalized}}",
"rightValue": "morning",
"operator": {
"type": "string",
"operation": "contains"
},
"id": "cca4c190-d840-486c-937f-221d62fcf05f"
}
],
"combinator": "and"
}
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 1
},
"conditions": [
{
"id": "88239dfd-773c-4337-9f2e-521e6368305b",
"leftValue": "={{$json.query_normalized}}",
"rightValue": "error",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
}
}
]
},
"options": {
"fallbackOutput": "extra"
}
},
"id": "613caa5d-d3f7-4099-a5f3-5c642264d32c",
"name": "Search Adapter (Mock Router)",
"type": "n8n-nodes-base.switch",
"typeVersion": 3,
"position": [
512,
1664
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"name": "query",
"value": "={{$json.query}}",
"type": "string",
"id": "7af70495-2dbb-4e7b-aed3-79cd7e970183"
},
{
"name": "search_results",
"value": "={{[\n {\n \"moment_id\": \"m001\",\n \"video_id\": \"v001\",\n \"t_start\": 3,\n \"t_end\": 8,\n \"title\": \"Sunset moment\",\n \"snippet\": \"The sun slowly sets over the sea\",\n \"score\": 0.92,\n \"video_url\": \"https://wp.momentry.ddns.net/wp-content/uploads/2026/03/H_moment3.mp4\"\n },\n {\n \"moment_id\": \"m002\",\n \"video_id\": \"v002\",\n \"t_start\": 3,\n \"t_end\": 7,\n \"title\": \"Morning sunlight\",\n \"snippet\": \"Sunlight enters the room\",\n \"score\": 0.88,\n \"video_url\": \"https://wp.momentry.ddns.net/wp-content/uploads/2026/03/O_moment3.mp4\"\n }\n]}}",
"type": "array",
"id": "e504493f-0dec-4a80-b434-7586059159f0"
}
]
},
"options": {}
},
"id": "38b6c814-3e60-45e1-a31f-b93fcf9b2fd5",
"name": "Mock Sun Results",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
736,
1456
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"name": "query",
"value": "={{$json.query}}",
"type": "string",
"id": "137b4cd7-5ef8-41f2-94bc-921c9040e0e5"
},
{
"name": "search_results",
"value": "={{[\n {\n \"moment_id\": \"m002\",\n \"video_id\": \"v002\",\n \"t_start\": 3,\n \"t_end\": 7,\n \"title\": \"Morning sunlight\",\n \"snippet\": \"Sunlight enters the room\",\n \"score\": 0.88,\n \"video_url\": \"https://wp.momentry.ddns.net/wp-content/uploads/2026/03/H_moment3.mp4\"\n }\n]}}",
"type": "array",
"id": "181df2c1-9469-4d3d-9f23-2ca839091583"
}
]
},
"options": {}
},
"id": "e0bc4873-b4a7-4cdc-a4e8-01aef82a79b8",
"name": "Mock Morning Results",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
736,
1648
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"name": "query",
"value": "={{$json.query}}",
"type": "string",
"id": "be01ea1f-f92f-41c7-8dab-f998e7c07bec"
},
{
"name": "search_results",
"value": "={{ [] }}",
"type": "array",
"id": "21e46d9d-0a35-4641-b936-806713ff021c"
}
]
},
"options": {}
},
"id": "e7c3a7b0-6cee-4832-b6ad-9cdc30f59131",
"name": "Mock Empty Results",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
736,
2032
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"name": "query",
"value": "={{$json.query}}",
"type": "string"
},
{
"name": "total",
"value": "={{$json.search_results.length}}",
"type": "number"
},
{
"name": "results",
"value": "={{$json.search_results}}",
"type": "array"
}
]
},
"options": {}
},
"id": "e746705c-ddfc-4c6b-904b-a5a8ed5d7420",
"name": "Format Search Response",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
960,
1648
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { \"query\": $json.query, \"total\": $json.total, \"results\": $json.results } }}",
"options": {}
},
"id": "d7f9806c-ad9f-4e56-bbdc-a943f1d416fd",
"name": "Respond Search",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
1184,
1168
]
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "99d7d111-05ae-42aa-8b86-a5de7a7fdb52",
"leftValue": "={{ $json.mode }}",
"rightValue": "mock",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "mock"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "d24d518d-bd6b-4b99-a4c9-cc98c49027ca",
"leftValue": "={{ $json.mode }}",
"rightValue": "core_stub",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "core_stub"
}
]
},
"options": {
"fallbackOutput": "none"
}
},
"type": "n8n-nodes-base.switch",
"typeVersion": 3.4,
"position": [
64,
1408
],
"id": "550273ba-8d33-4653-a26b-2b83218ce993",
"name": "Mode Router"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "c2c253a2-689c-4d8f-92d1-017dacbf1eca",
"name": "error",
"value": true,
"type": "boolean"
},
{
"id": "523b04a9-9843-4542-846d-d027f697ceff",
"name": "message",
"value": "mock backend error",
"type": "string"
},
{
"id": "ef30be3a-cdf7-4461-90b9-6d3ee6a88ba3",
"name": "query",
"value": "={{$json.query}}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
736,
1840
],
"id": "be069c6a-da6b-4a1b-a930-b535818b9ae2",
"name": "Mock Error Response"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { \"error\": true, \"message\": $json.message, \"query\": $json.query } }}",
"options": {
"responseCode": 500
}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
960,
1888
],
"id": "6777eded-6c8e-426d-82b2-550580f74591",
"name": "Respond Search Error"
},
{
"parameters": {
"jsCode": "const query = $json.query ?? \"\";\nconst hits = Array.isArray($json.hits) ? $json.hits : [];\n\nreturn [\n {\n json: {\n query,\n total: typeof $json.count === \"number\" ? $json.count : hits.length,\n results: hits.map(hit => ({\n moment_id: hit.id ?? \"\",\n video_id: hit.vid ?? \"\",\n t_start: Number(hit.start ?? 0),\n t_end: Number(hit.end ?? 0),\n title: hit.title ?? \"\",\n snippet: hit.text ?? \"\",\n score: Number(hit.score ?? 0),\n video_url: hit.media_url ?? \"\"\n }))\n }\n }\n];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
960,
1120
],
"id": "34662909-6e7a-4cf4-967b-1f474748777a",
"name": "Normalize Core Response"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "c51bf73f-e1e0-4811-985c-4de5ec70b9d8",
"leftValue": "={{ $json.error ? \"yes\" : \"no\" }}",
"rightValue": "=yes",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
736,
1168
],
"id": "cb6a5372-05a5-4f1b-ae5e-3b33bf009d4c",
"name": "Check Core Error"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "fad95504-6182-4d4c-bc95-8927c25c7f31",
"name": "ok",
"value": false,
"type": "boolean"
},
{
"id": "d267371c-90d8-401e-8180-a753e807441b",
"name": "error_code",
"value": "={{ $json.error?.code === \"ECONNABORTED\" ? \"SEARCH_TIMEOUT\" : \"SEARCH_BACKEND_ERROR\" }}",
"type": "string"
},
{
"id": "4992c4d9-e61d-453a-a826-8895374f56b8",
"name": "message",
"value": "={{ $json.error?.code === \"ECONNABORTED\" ? \"Search service timeout\" : \"Search service unavailable\" }}",
"type": "string"
},
{
"id": "56b9fbfa-4c98-4265-b992-498e3edf733b",
"name": "query",
"value": "={{ $node[\"Build Search Request\"].json.query || \"unknown\" }}",
"type": "string"
},
{
"id": "c50010c1-a0c8-47b9-a763-e7212a8db93c",
"name": "results",
"value": "={{ [] }}",
"type": "array"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
960,
1312
],
"id": "1c811ef9-91da-465c-9121-6247549d16fc",
"name": "Map Search Error"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ {\n \"ok\": $json.ok,\n \"error_code\": $json.error_code,\n \"message\": $json.message,\n \"query\": $json.query,\n \"results\": $json.results\n} }}",
"options": {
"responseCode": 500
}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
1184,
1360
],
"id": "797824a7-85ac-40d9-bd2b-42ea11dfd1a1",
"name": "Respond Search Error1"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "c08206fb-5d74-40f0-a682-f2116c290af1",
"name": "query",
"value": "={{ $json.query }}",
"type": "string"
},
{
"id": "85e83fbc-b4f0-4b22-8d37-516609387918",
"name": "limit",
"value": "={{ Number($json.limit || 10) }}",
"type": "number"
},
{
"id": "823706fe-288a-4a71-a498-0ad3daad5081",
"name": "mode",
"value": "={{ $json.mode }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
288,
1168
],
"id": "89366f65-c36a-4b19-9970-706a5d729594",
"name": "Prepare Core Request Context"
},
{
"parameters": {
"method": "POST",
"url": "https://api.momentry.ddns.net/api/v1/n8n/search",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ {\n \"query\": $json.query,\n \"limit\": $json.limit,\n \"request_query\": $json.query\n} }}",
"options": {
"timeout": 3000
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
512,
1168
],
"id": "d5ea8f1b-5bd7-4c88-9c38-70e5b14ca1d8",
"name": "Call Momentry Core API",
"onError": "continueRegularOutput"
}
],
"connections": {
"Webhook": {
"main": [
[
{
"node": "Build Search Request",
"type": "main",
"index": 0
}
]
]
},
"Build Search Request": {
"main": [
[
{
"node": "Mode Router",
"type": "main",
"index": 0
}
]
]
},
"Search Adapter (Mock Router)": {
"main": [
[
{
"node": "Mock Sun Results",
"type": "main",
"index": 0
}
],
[
{
"node": "Mock Morning Results",
"type": "main",
"index": 0
}
],
[
{
"node": "Mock Error Response",
"type": "main",
"index": 0
}
],
[
{
"node": "Mock Empty Results",
"type": "main",
"index": 0
}
]
]
},
"Mock Sun Results": {
"main": [
[
{
"node": "Format Search Response",
"type": "main",
"index": 0
}
]
]
},
"Mock Morning Results": {
"main": [
[
{
"node": "Format Search Response",
"type": "main",
"index": 0
}
]
]
},
"Mock Empty Results": {
"main": [
[
{
"node": "Format Search Response",
"type": "main",
"index": 0
}
]
]
},
"Format Search Response": {
"main": [
[
{
"node": "Respond Search",
"type": "main",
"index": 0
}
]
]
},
"Mode Router": {
"main": [
[
{
"node": "Search Adapter (Mock Router)",
"type": "main",
"index": 0
}
],
[
{
"node": "Prepare Core Request Context",
"type": "main",
"index": 0
}
]
]
},
"Mock Error Response": {
"main": [
[
{
"node": "Respond Search Error",
"type": "main",
"index": 0
}
]
]
},
"Normalize Core Response": {
"main": [
[
{
"node": "Respond Search",
"type": "main",
"index": 0
}
]
]
},
"Check Core Error": {
"main": [
[
{
"node": "Map Search Error",
"type": "main",
"index": 0
}
],
[
{
"node": "Normalize Core Response",
"type": "main",
"index": 0
}
]
]
},
"Map Search Error": {
"main": [
[
{
"node": "Respond Search Error1",
"type": "main",
"index": 0
}
]
]
},
"Prepare Core Request Context": {
"main": [
[
{
"node": "Call Momentry Core API",
"type": "main",
"index": 0
}
]
]
},
"Call Momentry Core API": {
"main": [
[
{
"node": "Check Core Error",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"availableInMCP": false,
"binaryMode": "separate"
},
"staticData": null,
"pinData": {
"Webhook": [
{
"json": {
"headers": {
"host": "n8n.momentry.ddns.net",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36 Edg/145.0.0.0",
"content-length": "15",
"accept": "*/*",
"accept-encoding": "gzip, deflate, br, zstd",
"accept-language": "zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6,zh-HK;q=0.5",
"content-type": "application/json",
"origin": "https://wp.momentry.ddns.net",
"priority": "u=1, i",
"referer": "https://wp.momentry.ddns.net/",
"sec-ch-ua": "\"Not:A-Brand\";v=\"99\", \"Microsoft Edge\";v=\"145\", \"Chromium\";v=\"145\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"macOS\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site",
"via": "3.0 Caddy",
"x-forwarded-for": "111.243.8.96",
"x-forwarded-host": "n8n.momentry.ddns.net",
"x-forwarded-proto": "https"
},
"params": {},
"query": {},
"body": {
"query": "sun"
},
"webhookUrl": "https://n8n.momentry.ddns.net/webhook/search",
"executionMode": "production"
},
"pairedItem": {
"item": 0
}
}
]
},
"triggerCount": 1,
"createdAt": "2026-03-23T19:06:43.592+08:00",
"updatedAt": "2026-03-23T20:06:51.588+08:00"
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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