Initial commit: Momentry Core v0.1

- Rust-based digital asset management system
- Video analysis: ASR, OCR, YOLO, Face, Pose
- RAG capabilities with Qdrant vector database
- Multi-database support: PostgreSQL, Redis, MongoDB
- Monitoring system with launchd plists
- n8n workflow automation integration
This commit is contained in:
accusys
2026-03-16 15:07:33 +08:00
parent ca24794853
commit 75edf0aa71
101 changed files with 19858 additions and 0 deletions

View File

@@ -0,0 +1,265 @@
#!/bin/bash
# Momentry n8n Workflow 監控 (Layer 3)
# 路徑: /Users/accusys/momentry_core_0.1/monitor/workflow/n8n_workflow_monitor.sh
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
MONITOR_DIR="$(dirname "$SCRIPT_DIR")"
LOG_DIR="/Users/accusys/momentry/log/monitor"
mkdir -p "$LOG_DIR"
LOG_FILE="$LOG_DIR/workflow_check.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# n8n API 配置
N8N_HOST="http://localhost:5678"
N8N_API_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJlNjdiY2UzOS1iY2RkLTRjMjEtYmMwYy0yODNhYmI3ZjVjMjMiLCJpc3MiOiJuOG4iLCJhdWQiOiJwdWJsaWMtYXBpIiwiaWF0IjoxNzczNjM5ODU4fQ.QOmOju2jLy07GrgXYvylM5AyFINPC06crKEsLLC988I"
# 從資料庫獲取 workflow
fetch_workflows_from_db() {
PGPASSWORD=accusys psql -U n8n -h localhost -d n8n -t -A <<'EOF'
SELECT json_agg(row_to_json(t)) FROM (
SELECT w.id, w.name, w.active, w."createdAt", w."updatedAt",
COALESCE(u.email, 'unknown') as owner_email
FROM workflow_entity w
LEFT JOIN shared_workflow sw ON w.id = sw."workflowId"
LEFT JOIN project p ON sw."projectId" = p.id
LEFT JOIN "user" u ON p."creatorId" = u.id
) t
EOF
}
# 記錄 workflow
record_workflow() {
local wf_id=$1
local wf_name=$2
local active=$3
local last_exec=$4
local exec_count=$5
local success=$6
local failure=$7
local avg_duration=$8
local has_schedule=$9
local has_webhook=${10}
local idle_days=${11}
local suggestion=${12}
psql -U accusys -h localhost -d momentry << EOF 2>/dev/null
INSERT INTO monitor_workflows
(workflow_id, workflow_name, is_active, last_executed_at, execution_count,
success_count, failure_count, avg_duration_ms, has_schedule, has_webhook,
idle_days, suggestion, checked_at)
VALUES
('$wf_id', '$wf_name', $active, $last_exec, $exec_count,
$success, $failure, $avg_duration, $has_schedule, $has_webhook,
$idle_days, '$suggestion', NOW())
ON CONFLICT (workflow_id) DO UPDATE SET
workflow_name = EXCLUDED.workflow_name,
is_active = EXCLUDED.is_active,
last_executed_at = EXCLUDED.last_executed_at,
execution_count = EXCLUDED.execution_count,
success_count = EXCLUDED.success_count,
failure_count = EXCLUDED.failure_count,
avg_duration_ms = EXCLUDED.avg_duration_ms,
has_schedule = EXCLUDED.has_schedule,
has_webhook = EXCLUDED.has_webhook,
idle_days = EXCLUDED.idle_days,
suggestion = EXCLUDED.suggestion,
checked_at = NOW();
EOF
}
# 獲取 workflow 列表
fetch_workflows() {
curl -s -H "Accept: application/json" \
-H "X-N8N-API-KEY: ${N8N_API_KEY}" \
"${N8N_HOST}/rest/workflows" 2>/dev/null || echo "[]"
}
# 獲取 workflow 執行統計
fetch_executions() {
local wf_id=$1
curl -s -H "Accept: application/json" \
-H "X-N8N-API-KEY: ${N8N_API_KEY}" \
"${N8N_HOST}/rest/executions?workflowId=${wf_id}&limit=50" 2>/dev/null || echo "{\"data\":[]}"
}
# 判斷是否有 schedule
has_schedule() {
local wf_data=$1
echo "$wf_data" | grep -q '"type":"schedule"' && echo "true" || echo "false"
}
# 判斷是否有 webhook
has_webhook() {
local wf_data=$1
echo "$wf_data" | grep -q '"type":"webhook"' && echo "true" || echo "false"
}
# 計算閒置天數
calc_idle_days() {
local last_exec=$1
if [ "$last_exec" = "null" ] || [ -z "$last_exec" ]; then
echo "999"
else
echo "0"
fi
}
# 生成建議
generate_suggestion() {
local has_schedule=$1
local has_webhook=$2
local idle_days=$3
local failure_rate=$4
if [ "$idle_days" -ge 90 ]; then
echo "建議刪除"
elif [ "$idle_days" -ge 30 ] && [ "$has_schedule" = "false" ] && [ "$has_webhook" = "false" ]; then
echo "建議停用"
elif [ "$failure_rate" -gt 20 ]; then
echo "建議優化"
else
echo ""
fi
}
# 主程序
echo "========================================"
echo "Layer 3: n8n Workflow Monitoring"
echo "Time: $(date)"
echo "========================================"
echo ""
# 檢查 n8n 是否可用 (檢查 PostgreSQL 中的 n8n 資料庫)
if ! PGPASSWORD=accusys psql -U n8n -h localhost -d n8n -c "SELECT 1" >/dev/null 2>&1; then
echo "n8n 資料庫不可用"
log "n8n database unavailable"
exit 1
fi
# 獲取 workflow 列表 (從資料庫)
workflows=$(fetch_workflows_from_db)
total_count=$(echo "$workflows" | jq 'length' 2>/dev/null || echo "0")
active_count=$(echo "$workflows" | jq '[.[] | select(.active == true)] | length' 2>/dev/null || echo "0")
echo "總 Workflow: $total_count"
echo "啟用中: $active_count"
echo ""
# 閒置閾值
IDLE_THRESHOLD=30
echo "Workflow 詳細:"
echo "----------------------------------------"
total_idle=0
for wf in $(echo "$workflows" | jq -r '.[] | @base64' 2>/dev/null); do
wf_decoded=$(echo "$wf" | base64 -d)
wf_id=$(echo "$wf_decoded" | jq -r '.id' 2>/dev/null)
wf_name=$(echo "$wf_decoded" | jq -r '.name' 2>/dev/null)
is_active=$(echo "$wf_decoded" | jq -r '.active' 2>/dev/null)
wf_owner=$(echo "$wf_decoded" | jq -r '.owner_email' 2>/dev/null)
# 從資料庫獲取執行數據
exec_data=$(PGPASSWORD=accusys psql -U n8n -h localhost -d n8n -t -A <<EOF
SELECT json_agg(row_to_json(t)) FROM (
SELECT status, "startedAt", "stoppedAt",
EXTRACT(EPOCH FROM ("stoppedAt" - "startedAt")) * 1000 as execution_time
FROM execution_entity
WHERE "workflowId" = '$wf_id'
ORDER BY "startedAt" DESC
LIMIT 50
) t
EOF
)
exec_count=$(echo "$exec_data" | jq '. | length' 2>/dev/null || echo "0")
# 計算成功/失敗
success_count=$(echo "$exec_data" | jq '[.[] | select(.status == "success")] | length' 2>/dev/null || echo "0")
failure_count=$(echo "$exec_data" | jq '[.[] | select(.status == "error")] | length' 2>/dev/null || echo "0")
# 平均執行時間
avg_duration=$(echo "$exec_data" | jq '[.[] | .execution_time] | map(select(. != null)) | add / length | floor' 2>/dev/null || echo "0")
# 檢查是否有 webhook
has_webh=$(PGPASSWORD=accusys psql -U n8n -h localhost -d n8n -t -A -c "
SELECT COUNT(*) FROM webhook_entity WHERE workflow_id = '$wf_id'
" 2>/dev/null || echo "0")
[ "$has_webh" -gt 0 ] && has_webh="true" || has_webh="false"
has_sched="false"
# 最後執行時間
last_exec=$(echo "$exec_data" | jq -r '.[0].startedAt // "null"' 2>/dev/null | head -1)
if [ "$last_exec" = "null" ] || [ -z "$last_exec" ]; then
idle_days=999
else
idle_days=0
fi
# 確保數值正確
exec_count=$(echo "$exec_count" | tr -d '[:space:]' || echo "0")
success_count=$(echo "$success_count" | tr -d '[:space:]' || echo "0")
failure_count=$(echo "$failure_count" | tr -d '[:space:]' || echo "0")
avg_duration=$(echo "$avg_duration" | tr -d '[:space:]' || echo "0")
# 計算失敗率
if [ -n "$exec_count" ] && [ "$exec_count" -gt 0 ] 2>/dev/null; then
failure_rate=$(( failure_count * 100 / exec_count ))
else
failure_rate=0
fi
# 生成建議
suggestion=$(generate_suggestion "$has_sched" "$has_webh" "$idle_days" "$failure_rate")
# 記錄到資料庫
if [ "$last_exec" = "null" ] || [ -z "$last_exec" ]; then
record_workflow "$wf_id" "$wf_name" "$is_active" "NULL" "$exec_count" "$success_count" "$failure_count" "$avg_duration" "$has_sched" "$has_webh" "$idle_days" "$suggestion"
else
record_workflow "$wf_id" "$wf_name" "$is_active" "'$last_exec'" "$exec_count" "$success_count" "$failure_count" "$avg_duration" "$has_sched" "$has_webh" "$idle_days" "$suggestion"
fi
# 顯示
status_icon="○"
if [ "$is_active" = "true" ]; then
status_icon="●"
fi
idle_info=""
if [ "$idle_days" -ge "$IDLE_THRESHOLD" ]; then
idle_info=" [閒置 $idle_days 天]"
total_idle=$((total_idle + 1))
fi
suggestion_info=""
if [ -n "$suggestion" ]; then
suggestion_info=" [$suggestion]"
fi
echo "$status_icon $wf_name (ID: $wf_id) [$wf_owner]$idle_info$suggestion_info"
echo " 執行: $exec_count (成功: $success_count, 失敗: $failure_count) | 平均: ${avg_duration}ms"
done
echo "----------------------------------------"
echo "閒置 Workflow (> $IDLE_THRESHOLD 天): $total_idle"
echo ""
log "Workflow check completed: $total_count total, $total_idle idle"
# 顯示閒置 workflow
if [ $total_idle -gt 0 ]; then
echo ""
echo "閒置 Workflow 建議:"
psql -U accusys -h localhost -d momentry -t -A -c "
SELECT ' - ' || workflow_name || ': ' || suggestion
FROM monitor_workflows
WHERE idle_days >= $IDLE_THRESHOLD AND suggestion != '';
" 2>/dev/null
fi