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:
265
monitor/workflow/n8n_workflow_monitor.sh
Executable file
265
monitor/workflow/n8n_workflow_monitor.sh
Executable 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
|
||||
Reference in New Issue
Block a user