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:
82
monitor/database/mongodb_monitor.sh
Executable file
82
monitor/database/mongodb_monitor.sh
Executable file
@@ -0,0 +1,82 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Momentry MongoDB 監控 (Layer 5)
|
||||
# 路徑: /Users/accusys/momentry_core_0.1/monitor/database/mongodb_monitor.sh
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
LOG_DIR="/Users/accusys/momentry/log/monitor"
|
||||
|
||||
mkdir -p "$LOG_DIR"
|
||||
LOG_FILE="$LOG_DIR/mongodb_check.log"
|
||||
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
MONGO_USER="accusys"
|
||||
MONGO_PASS="Test3200Test3200"
|
||||
|
||||
record_metric() {
|
||||
psql -U accusys -h localhost -d momentry << EOF 2>/dev/null
|
||||
INSERT INTO monitor_databases (db_type, db_name, metric_name, metric_value, checked_at)
|
||||
VALUES ('mongodb', 'mongodb', '$1', '$2', NOW());
|
||||
EOF
|
||||
}
|
||||
|
||||
get_status() {
|
||||
mongosh --quiet --username "$MONGO_USER" --password "$MONGO_PASS" --authenticationDatabase admin --eval "JSON.stringify(db.adminCommand({ replSetGetStatus: 1 }))" 2>/dev/null || echo "{}"
|
||||
}
|
||||
|
||||
get_server_status() {
|
||||
mongosh --quiet --username "$MONGO_USER" --password "$MONGO_PASS" --authenticationDatabase admin --eval "JSON.stringify(db.serverStatus()))" 2>/dev/null || echo "{}"
|
||||
}
|
||||
|
||||
get_databases() {
|
||||
mongosh --quiet --username "$MONGO_USER" --password "$MONGO_PASS" --authenticationDatabase admin --eval "JSON.stringify(db.adminCommand({ listDatabases: 1 }))" 2>/dev/null || echo "{}"
|
||||
}
|
||||
|
||||
echo "========================================"
|
||||
echo "Layer 5: MongoDB Monitoring"
|
||||
echo "Time: $(date)"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
if ! mongosh --quiet --username "$MONGO_USER" --password "$MONGO_PASS" --authenticationDatabase admin --eval "db.adminCommand('ping')" > /dev/null 2>&1; then
|
||||
echo "MongoDB 不可用"
|
||||
log "MongoDB unavailable"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✓ MongoDB 連接正常"
|
||||
echo ""
|
||||
|
||||
echo "資料庫:"
|
||||
echo "----------------------------------------"
|
||||
databases=$(get_databases)
|
||||
echo "$databases" | jq -r '.databases[] | " \(.name): \(.sizeOnDisk / 1024 / 1024 | floor)MB"' 2>/dev/null || echo " 無法獲取資料庫列表"
|
||||
|
||||
echo ""
|
||||
echo "伺服器狀態:"
|
||||
echo "----------------------------------------"
|
||||
server_status=$(get_server_status)
|
||||
connections=$(echo "$server_status" | jq -r '.connections.current' 2>/dev/null || echo "N/A")
|
||||
active_connections=$(echo "$server_status" | jq -r '.connections.active' 2>/dev/null || echo "N/A")
|
||||
uptime=$(echo "$server_status" | jq -r '.uptime' 2>/dev/null || echo "N/A")
|
||||
mem_resident=$(echo "$server_status" | jq -r '.mem.resident' 2>/dev/null || echo "N/A")
|
||||
|
||||
echo " 當前連接: $connections"
|
||||
echo " 活躍連接: $active_connections"
|
||||
echo " 運行時間: ${uptime}秒"
|
||||
echo " 記憶體使用: ${mem_resident}MB"
|
||||
|
||||
record_metric "connections" "$connections"
|
||||
record_metric "active_connections" "$active_connections"
|
||||
record_metric "uptime" "$uptime"
|
||||
record_metric "mem_resident" "$mem_resident"
|
||||
|
||||
echo ""
|
||||
log "MongoDB check completed: connections=$connections"
|
||||
|
||||
echo "========================================"
|
||||
echo "完成"
|
||||
echo "========================================"
|
||||
130
monitor/database/postgres_monitor.sh
Executable file
130
monitor/database/postgres_monitor.sh
Executable file
@@ -0,0 +1,130 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Momentry PostgreSQL 監控 (Layer 5)
|
||||
# 路徑: /Users/accusys/momentry_core_0.1/monitor/database/postgres_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/postgres_check.log"
|
||||
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
# 記錄指標
|
||||
record_metric() {
|
||||
local db_type="postgresql"
|
||||
local db_name=$1
|
||||
local metric_name=$2
|
||||
local value=$3
|
||||
|
||||
psql -U accusys -h localhost -d momentry << EOF 2>/dev/null
|
||||
INSERT INTO monitor_databases (db_type, db_name, metric_name, metric_value, checked_at)
|
||||
VALUES ('$db_type', '$db_name', '$metric_name', '$value', NOW());
|
||||
EOF
|
||||
}
|
||||
|
||||
# 獲取資料庫列表
|
||||
get_databases() {
|
||||
psql -U accusys -h localhost -t -A -c "SELECT datname FROM pg_database WHERE datistemplate = false;" 2>/dev/null
|
||||
}
|
||||
|
||||
# 獲取表大小
|
||||
get_table_sizes() {
|
||||
local db=$1
|
||||
psql -U accusys -h localhost -d "$db" -t -A -c "
|
||||
SELECT
|
||||
schemaname,
|
||||
tablename,
|
||||
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as size,
|
||||
n_live_tup as rows
|
||||
FROM pg_stat_user_tables
|
||||
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC
|
||||
LIMIT 10;
|
||||
" 2>/dev/null
|
||||
}
|
||||
|
||||
# 獲取連線數
|
||||
get_connections() {
|
||||
psql -U accusys -h localhost -t -A -c "
|
||||
SELECT state, count(*)
|
||||
FROM pg_stat_activity
|
||||
WHERE datname = current_database()
|
||||
GROUP BY state;
|
||||
" 2>/dev/null
|
||||
}
|
||||
|
||||
# 獲取慢查詢
|
||||
get_slow_queries() {
|
||||
psql -U accusys -h localhost -t -A -c "
|
||||
SELECT query, calls, mean_time
|
||||
FROM pg_stat_statements
|
||||
WHERE query NOT LIKE '%pg_stat_statements%'
|
||||
ORDER BY mean_time DESC
|
||||
LIMIT 5;
|
||||
" 2>/dev/null
|
||||
}
|
||||
|
||||
# 主程序
|
||||
echo "========================================"
|
||||
echo "Layer 5: PostgreSQL Database Monitoring"
|
||||
echo "Time: $(date)"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
# 檢查 PostgreSQL 是否可用
|
||||
if ! pg_isready -h localhost -p 5432 -U accusys > /dev/null 2>&1; then
|
||||
echo "PostgreSQL 不可用"
|
||||
log "PostgreSQL unavailable"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 記錄連線數
|
||||
connections=$(get_connections)
|
||||
echo "連線狀態:"
|
||||
echo "$connections"
|
||||
echo ""
|
||||
|
||||
# 記錄指標
|
||||
record_metric "postgres" "connections" "'$connections'"
|
||||
|
||||
# 檢查各資料庫
|
||||
echo "資料庫表:"
|
||||
echo "----------------------------------------"
|
||||
|
||||
for db in $(get_databases); do
|
||||
echo ""
|
||||
echo "資料庫: $db"
|
||||
|
||||
table_count=$(psql -U accusys -h localhost -d "$db" -t -A -c "SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public';" 2>/dev/null || echo "0")
|
||||
echo " 表數量: $table_count"
|
||||
|
||||
record_metric "$db" "table_count" "$table_count"
|
||||
|
||||
# 顯示大表
|
||||
echo " 大表:"
|
||||
get_table_sizes "$db" | while read -r schema table size rows; do
|
||||
[ -z "$table" ] && continue
|
||||
echo " - $table: $size ($rows rows)"
|
||||
done
|
||||
done
|
||||
|
||||
# 檢查慢查詢(如果 pg_stat_statements 可用)
|
||||
echo ""
|
||||
echo "慢查詢 (如有):"
|
||||
slow_queries=$(get_slow_queries)
|
||||
if [ -n "$slow_queries" ]; then
|
||||
echo "$slow_queries" | while read -r query calls time; do
|
||||
[ -z "$query" ] && continue
|
||||
echo " - ${time}ms (調用 $calls 次)"
|
||||
done
|
||||
else
|
||||
echo " (pg_stat_statements 未啟用)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
log "PostgreSQL check completed"
|
||||
124
monitor/database/qdrant_monitor.sh
Executable file
124
monitor/database/qdrant_monitor.sh
Executable file
@@ -0,0 +1,124 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Momentry Qdrant 監控 (Layer 5)
|
||||
# 路徑: /Users/accusys/momentry_core_0.1/monitor/database/qdrant_monitor.sh
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
LOG_DIR="/Users/accusys/momentry/log/monitor"
|
||||
|
||||
mkdir -p "$LOG_DIR"
|
||||
LOG_FILE="$LOG_DIR/qdrant_check.log"
|
||||
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
QDRANT_HOST="http://localhost:6333"
|
||||
QDRANT_API_KEY="Test3200Test3200Test3200"
|
||||
|
||||
# 記錄指標
|
||||
record_metric() {
|
||||
local collection=$1
|
||||
local metric_name=$2
|
||||
local value=$3
|
||||
|
||||
psql -U accusys -h localhost -d momentry << EOF 2>/dev/null
|
||||
INSERT INTO monitor_databases (db_type, db_name, metric_name, metric_value, checked_at)
|
||||
VALUES ('qdrant', '$collection', '$metric_name', '$value', NOW());
|
||||
EOF
|
||||
}
|
||||
|
||||
# 記錄 Collection
|
||||
record_collection() {
|
||||
local name=$1
|
||||
local vectors=$2
|
||||
local points=$3
|
||||
local disk_size=$4
|
||||
local status=$5
|
||||
|
||||
psql -U accusys -h localhost -d momentry << EOF 2>/dev/null
|
||||
INSERT INTO monitor_qdrant_collections (collection_name, vectors_count, points_count, disk_size_bytes, status, snapshot_at)
|
||||
VALUES ('$name', $vectors, $points, $disk_size, '$status', NOW())
|
||||
ON CONFLICT (collection_name) DO UPDATE SET
|
||||
vectors_count = EXCLUDED.vectors_count,
|
||||
points_count = EXCLUDED.points_count,
|
||||
disk_size_bytes = EXCLUDED.disk_size_bytes,
|
||||
status = EXCLUDED.status,
|
||||
snapshot_at = NOW();
|
||||
EOF
|
||||
}
|
||||
|
||||
# 主程序
|
||||
echo "========================================"
|
||||
echo "Layer 5: Qdrant Vector Database Monitoring"
|
||||
echo "Time: $(date)"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
# 檢查 Qdrant 是否可用
|
||||
http_code=$(curl -s -o /dev/null -w "%{http_code}" \
|
||||
-H "api-key: $QDRANT_API_KEY" \
|
||||
"$QDRANT_HOST/collections" 2>/dev/null || echo "000")
|
||||
if [ "$http_code" = "000" ]; then
|
||||
echo "Qdrant 不可用"
|
||||
log "Qdrant unavailable"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Qdrant 狀態: HTTP $http_code"
|
||||
echo ""
|
||||
|
||||
# 獲取 Collection 列表
|
||||
collections=$(curl -s -H "api-key: $QDRANT_API_KEY" "$QDRANT_HOST/collections" 2>/dev/null)
|
||||
|
||||
if [ "$http_code" = "200" ]; then
|
||||
collection_count=$(echo "$collections" | jq '.result.collections | length' 2>/dev/null || echo "0")
|
||||
echo "Collection 數量: $collection_count"
|
||||
echo ""
|
||||
|
||||
# 遍歷每個 Collection
|
||||
echo "Collections:"
|
||||
echo "----------------------------------------"
|
||||
|
||||
for i in $(seq 0 $((collection_count - 1))); do
|
||||
name=$(echo "$collections" | jq -r ".result.collections[$i].name")
|
||||
|
||||
# 獲取 Collection 詳情
|
||||
details=$(curl -s -H "api-key: $QDRANT_API_KEY" "$QDRANT_HOST/collections/$name" 2>/dev/null)
|
||||
|
||||
vectors_count=$(echo "$details" | jq -r '.result.indexed_vectors_count // 0' 2>/dev/null || echo "0")
|
||||
points_count=$(echo "$details" | jq -r '.result.points_count // 0' 2>/dev/null || echo "0")
|
||||
disk_size=$(echo "$details" | jq -r '.result.disk_size_bytes // 0' 2>/dev/null || echo "0")
|
||||
status=$(echo "$details" | jq -r '.result.status // "unknown"' 2>/dev/null || echo "unknown")
|
||||
|
||||
# 格式化大小
|
||||
if [ "$disk_size" -gt 1073741824 ]; then
|
||||
size_str="$((disk_size / 1073741824))GB"
|
||||
elif [ "$disk_size" -gt 1048576 ]; then
|
||||
size_str="$((disk_size / 1048576))MB"
|
||||
elif [ "$disk_size" -gt 1024 ]; then
|
||||
size_str="$((disk_size / 1024))KB"
|
||||
else
|
||||
size_str="${disk_size}B"
|
||||
fi
|
||||
|
||||
echo " - $name"
|
||||
echo " 狀態: $status"
|
||||
echo " Vectors: $vectors_count"
|
||||
echo " Points: $points_count"
|
||||
echo " 大小: $size_str"
|
||||
|
||||
# 記錄到資料庫
|
||||
record_collection "$name" "$vectors_count" "$points_count" "$disk_size" "$status"
|
||||
record_metric "$name" "vectors_count" "$vectors_count"
|
||||
record_metric "$name" "points_count" "$points_count"
|
||||
record_metric "$name" "disk_size" "$disk_size"
|
||||
done
|
||||
else
|
||||
echo "無法獲取 Collection 列表"
|
||||
log "Failed to get collections: HTTP $http_code"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
log "Qdrant check completed"
|
||||
111
monitor/database/redis_monitor.sh
Executable file
111
monitor/database/redis_monitor.sh
Executable file
@@ -0,0 +1,111 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Momentry Redis 監控 (Layer 5)
|
||||
# 路徑: /Users/accusys/momentry_core_0.1/monitor/database/redis_monitor.sh
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
LOG_DIR="/Users/accusys/momentry/log/monitor"
|
||||
|
||||
mkdir -p "$LOG_DIR"
|
||||
LOG_FILE="$LOG_DIR/redis_check.log"
|
||||
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
REDIS_PASS="accusys"
|
||||
|
||||
# 記錄指標
|
||||
record_metric() {
|
||||
local value=$1
|
||||
|
||||
psql -U accusys -h localhost -d momentry << EOF 2>/dev/null
|
||||
INSERT INTO monitor_databases (db_type, db_name, metric_name, metric_value, checked_at)
|
||||
VALUES ('redis', 'redis', '$1', '$2', NOW());
|
||||
EOF
|
||||
}
|
||||
|
||||
# 獲取 Redis INFO
|
||||
get_info() {
|
||||
redis-cli -a "$REDIS_PASS" INFO 2>/dev/null
|
||||
}
|
||||
|
||||
# 主程序
|
||||
echo "========================================"
|
||||
echo "Layer 5: Redis Monitoring"
|
||||
echo "Time: $(date)"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
# 檢查 Redis 是否可用
|
||||
if ! redis-cli -a "$REDIS_PASS" ping > /dev/null 2>&1; then
|
||||
echo "Redis 不可用"
|
||||
log "Redis unavailable"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
info=$(get_info)
|
||||
|
||||
# 提取關鍵指標
|
||||
echo "關鍵指標:"
|
||||
echo "----------------------------------------"
|
||||
|
||||
# 內存使用
|
||||
used_memory=$(echo "$info" | grep "^used_memory_human:" | cut -d: -f2 | tr -d '\r')
|
||||
echo " 內存使用: $used_memory"
|
||||
|
||||
# 連線數
|
||||
connected_clients=$(echo "$info" | grep "^connected_clients:" | cut -d: -f2 | tr -d '\r')
|
||||
echo " 客戶端連線: $connected_clients"
|
||||
|
||||
# 命中率
|
||||
keyspace_hits=$(echo "$info" | grep "^keyspace_hits:" | cut -d: -f2 | tr -d '\r')
|
||||
keyspace_misses=$(echo "$info" | grep "^keyspace_misses:" | cut -d: -f2 | tr -d '\r')
|
||||
total_ops=$((keyspace_hits + keyspace_misses))
|
||||
if [ $total_ops -gt 0 ]; then
|
||||
hit_rate=$((keyspace_hits * 100 / total_ops))
|
||||
echo " 命中率: ${hit_rate}%"
|
||||
else
|
||||
echo " 命中率: N/A"
|
||||
fi
|
||||
|
||||
# 持久化
|
||||
rdb_changes=$(echo "$info" | grep "^rdb_changes_since_last_save:" | cut -d: -f2 | tr -d '\r')
|
||||
echo " RDB 變更: $rdb_changes"
|
||||
|
||||
# 總鍵數
|
||||
echo ""
|
||||
echo "鍵數據庫:"
|
||||
db0_info=$(echo "$info" | grep "^db0:" | head -1)
|
||||
if [ -n "$db0_info" ]; then
|
||||
keys=$(echo "$db0_info" | sed 's/.*keys=\([0-9]*\).*/\1/')
|
||||
expires=$(echo "$db0_info" | sed 's/.*expires=\([0-9]*\).*/\1/')
|
||||
echo " db0: $keys keys, $expires 有過期時間"
|
||||
fi
|
||||
|
||||
# 記錄到資料庫
|
||||
record_metric "used_memory" "'$used_memory'"
|
||||
record_metric "connected_clients" "$connected_clients"
|
||||
record_metric "keyspace_hits" "$keyspace_hits"
|
||||
record_metric "keyspace_misses" "$keyspace_misses"
|
||||
|
||||
# 檢查閾值
|
||||
echo ""
|
||||
echo "閾值檢查:"
|
||||
memory_percent=$(echo "$info" | grep "^used_memory:" | cut -d: -f2)
|
||||
maxmemory=$(redis-cli -a "$REDIS_PASS" CONFIG GET maxmemory 2>/dev/null | tail -1)
|
||||
if [ -n "$maxmemory" ] && [ "$maxmemory" -gt 0 ]; then
|
||||
mem_pct=$((memory_percent * 100 / maxmemory))
|
||||
echo " 內存使用: ${mem_pct}%"
|
||||
if [ $mem_pct -gt 80 ]; then
|
||||
echo " ⚠️ 內存使用超過 80%"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $connected_clients -gt 100 ]; then
|
||||
echo " ⚠️ 客戶端連線過多"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
log "Redis check completed"
|
||||
492
monitor/database/schema.sql
Normal file
492
monitor/database/schema.sql
Normal file
@@ -0,0 +1,492 @@
|
||||
-- Momentry 監控系統數據庫表
|
||||
-- 使用方式: psql -U accusys -h localhost -d momentry -f schema.sql
|
||||
|
||||
-- ============================================================
|
||||
-- Layer 2: Service 監控
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS monitor_services (
|
||||
id SERIAL PRIMARY KEY,
|
||||
service_name VARCHAR(50) NOT NULL,
|
||||
service_type VARCHAR(20),
|
||||
port INTEGER,
|
||||
status VARCHAR(20) CHECK (status IN ('up', 'down', 'degraded', 'unknown')),
|
||||
response_time_ms INTEGER,
|
||||
error_message TEXT,
|
||||
checked_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_monitor_services_name ON monitor_services(service_name);
|
||||
CREATE INDEX idx_monitor_services_time ON monitor_services(checked_at);
|
||||
|
||||
-- ============================================================
|
||||
-- Layer 3: n8n Workflow 監控
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS monitor_workflows (
|
||||
id SERIAL PRIMARY KEY,
|
||||
workflow_id VARCHAR(50) NOT NULL,
|
||||
workflow_name VARCHAR(255),
|
||||
workflow_type VARCHAR(50),
|
||||
is_active BOOLEAN DEFAULT FALSE,
|
||||
last_executed_at TIMESTAMP,
|
||||
execution_count INTEGER DEFAULT 0,
|
||||
success_count INTEGER DEFAULT 0,
|
||||
failure_count INTEGER DEFAULT 0,
|
||||
avg_duration_ms INTEGER,
|
||||
has_schedule BOOLEAN DEFAULT FALSE,
|
||||
has_webhook BOOLEAN DEFAULT FALSE,
|
||||
idle_days INTEGER,
|
||||
suggestion VARCHAR(100),
|
||||
checked_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_monitor_workflows_id ON monitor_workflows(workflow_id);
|
||||
CREATE INDEX idx_monitor_workflows_active ON monitor_workflows(is_active);
|
||||
CREATE INDEX idx_monitor_workflows_idle ON monitor_workflows(idle_days);
|
||||
|
||||
-- ============================================================
|
||||
-- Layer 4: WordPress Portal 監控
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS monitor_portal_pages (
|
||||
id SERIAL PRIMARY KEY,
|
||||
page_url VARCHAR(500) NOT NULL,
|
||||
page_type VARCHAR(20),
|
||||
is_accessible BOOLEAN,
|
||||
response_time_ms INTEGER,
|
||||
http_status INTEGER,
|
||||
error_message TEXT,
|
||||
checked_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS monitor_portal_users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id BIGINT,
|
||||
username VARCHAR(100),
|
||||
email VARCHAR(255),
|
||||
role VARCHAR(50),
|
||||
is_active BOOLEAN,
|
||||
last_login TIMESTAMP,
|
||||
created_at TIMESTAMP,
|
||||
detected_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_monitor_portal_pages_url ON monitor_portal_pages(page_url);
|
||||
CREATE INDEX idx_monitor_portal_users_username ON monitor_portal_users(username);
|
||||
|
||||
-- ============================================================
|
||||
-- Layer 5: Database 監控
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS monitor_databases (
|
||||
id SERIAL PRIMARY KEY,
|
||||
db_type VARCHAR(20) NOT NULL CHECK (db_type IN ('postgresql', 'redis', 'qdrant', 'mariadb', 'mongodb')),
|
||||
db_name VARCHAR(50),
|
||||
metric_name VARCHAR(50) NOT NULL,
|
||||
metric_value JSONB,
|
||||
checked_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_monitor_databases_type ON monitor_databases(db_type);
|
||||
CREATE INDEX idx_monitor_databases_time ON monitor_databases(checked_at);
|
||||
|
||||
-- PostgreSQL 表結構快照
|
||||
CREATE TABLE IF NOT EXISTS monitor_pg_tables (
|
||||
id SERIAL PRIMARY KEY,
|
||||
database_name VARCHAR(50),
|
||||
schema_name VARCHAR(50),
|
||||
table_name VARCHAR(100),
|
||||
table_type VARCHAR(20),
|
||||
row_count BIGINT,
|
||||
table_size_bytes BIGINT,
|
||||
index_size_bytes BIGINT,
|
||||
snapshot_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 表結構變更記錄
|
||||
CREATE TABLE IF NOT EXISTS monitor_pg_schema_changes (
|
||||
id SERIAL PRIMARY KEY,
|
||||
database_name VARCHAR(50),
|
||||
schema_name VARCHAR(50),
|
||||
table_name VARCHAR(100),
|
||||
change_type VARCHAR(20) CHECK (change_type IN ('table_created', 'table_dropped', 'column_added', 'column_removed', 'column_type_changed')),
|
||||
column_name VARCHAR(100),
|
||||
old_value TEXT,
|
||||
new_value TEXT,
|
||||
detected_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Qdrant Collection 監控
|
||||
CREATE TABLE IF NOT EXISTS monitor_qdrant_collections (
|
||||
id SERIAL PRIMARY KEY,
|
||||
collection_name VARCHAR(100),
|
||||
vectors_count BIGINT,
|
||||
points_count BIGINT,
|
||||
disk_size_bytes BIGINT,
|
||||
status VARCHAR(20),
|
||||
snapshot_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- Layer 6: 使用者監控
|
||||
-- ============================================================
|
||||
|
||||
-- 連線會話追蹤
|
||||
CREATE TABLE IF NOT EXISTS monitor_sessions (
|
||||
id SERIAL PRIMARY KEY,
|
||||
session_type VARCHAR(20) CHECK (session_type IN ('ssh', 'web', 'db', 'sftp', 'rdp')),
|
||||
service_name VARCHAR(50),
|
||||
username VARCHAR(100),
|
||||
source_ip VARCHAR(45),
|
||||
source_port INTEGER,
|
||||
connected_at TIMESTAMP,
|
||||
last_activity_at TIMESTAMP,
|
||||
disconnected_at TIMESTAMP,
|
||||
bytes_sent BIGINT,
|
||||
bytes_received BIGINT,
|
||||
status VARCHAR(20) CHECK (status IN ('active', 'disconnected', 'timeout'))
|
||||
);
|
||||
|
||||
-- 登入歷史
|
||||
CREATE TABLE IF NOT EXISTS monitor_logins (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_type VARCHAR(20) CHECK (user_type IN ('system', 'wordpress', 'n8n', 'gitea', 'sftpgo', 'database')),
|
||||
username VARCHAR(100),
|
||||
source_ip VARCHAR(45),
|
||||
user_agent TEXT,
|
||||
login_method VARCHAR(20),
|
||||
success BOOLEAN,
|
||||
failure_reason VARCHAR(200),
|
||||
login_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- sudo 命令記錄
|
||||
CREATE TABLE IF NOT EXISTS monitor_sudo_history (
|
||||
id SERIAL PRIMARY KEY,
|
||||
username VARCHAR(100),
|
||||
command TEXT,
|
||||
run_as VARCHAR(100),
|
||||
tty VARCHAR(50),
|
||||
source_ip VARCHAR(45),
|
||||
exit_code INTEGER,
|
||||
executed_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 資源使用追蹤
|
||||
CREATE TABLE IF NOT EXISTS monitor_resource_usage (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_type VARCHAR(20),
|
||||
username VARCHAR(100),
|
||||
service_name VARCHAR(50),
|
||||
cpu_percent DECIMAL(5,2),
|
||||
memory_mb INTEGER,
|
||||
disk_io_read_mb BIGINT,
|
||||
disk_io_write_mb BIGINT,
|
||||
network_rx_mb BIGINT,
|
||||
network_tx_mb BIGINT,
|
||||
recorded_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 異常檢測記錄
|
||||
CREATE TABLE IF NOT EXISTS monitor_anomalies (
|
||||
id SERIAL PRIMARY KEY,
|
||||
anomaly_type VARCHAR(50) CHECK (anomaly_type IN ('brute_force', 'privilege_escalation', 'unusual_access', 'unusual_time', 'excessive_queries', 'idle_session', 'schema_change')),
|
||||
severity VARCHAR(20) CHECK (severity IN ('low', 'medium', 'high', 'critical')),
|
||||
source_type VARCHAR(20),
|
||||
username VARCHAR(100),
|
||||
source_ip VARCHAR(45),
|
||||
description TEXT,
|
||||
details JSONB,
|
||||
detected_at TIMESTAMP DEFAULT NOW(),
|
||||
resolved BOOLEAN DEFAULT FALSE,
|
||||
resolved_at TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX idx_monitor_sessions_type ON monitor_sessions(session_type);
|
||||
CREATE INDEX idx_monitor_sessions_username ON monitor_sessions(username);
|
||||
CREATE INDEX idx_monitor_logins_type ON monitor_logins(user_type);
|
||||
CREATE INDEX idx_monitor_logins_time ON monitor_logins(login_at);
|
||||
CREATE INDEX idx_monitor_anomalies_type ON monitor_anomalies(anomaly_type);
|
||||
CREATE INDEX idx_monitor_anomalies_severity ON monitor_anomalies(severity);
|
||||
CREATE INDEX idx_monitor_anomalies_time ON monitor_anomalies(detected_at);
|
||||
|
||||
-- ============================================================
|
||||
-- Layer 7: Storage 監控
|
||||
-- ============================================================
|
||||
|
||||
-- 檔案註冊表
|
||||
CREATE TABLE IF NOT EXISTS file_registry (
|
||||
file_uuid UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
file_name VARCHAR(255) NOT NULL,
|
||||
file_path TEXT NOT NULL,
|
||||
file_path_hash VARCHAR(64) NOT NULL,
|
||||
file_size BIGINT NOT NULL,
|
||||
file_hash VARCHAR(64),
|
||||
mime_type VARCHAR(100),
|
||||
user_cluster VARCHAR(50) CHECK (user_cluster IN ('family', 'work', 'wordpress', 'shared', 'system')),
|
||||
owner_id VARCHAR(100),
|
||||
storage_tier VARCHAR(20) DEFAULT 'hot' CHECK (storage_tier IN ('hot', 'warm', 'cold')),
|
||||
storage_location VARCHAR(500),
|
||||
status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'temporary', 'archived', 'deleted')),
|
||||
is_registered BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW(),
|
||||
last_accessed_at TIMESTAMP,
|
||||
access_count INTEGER DEFAULT 0,
|
||||
archived_at TIMESTAMP,
|
||||
archive_location VARCHAR(500),
|
||||
retention_until TIMESTAMP,
|
||||
UNIQUE(file_path_hash)
|
||||
);
|
||||
|
||||
-- 存儲使用統計
|
||||
CREATE TABLE IF NOT EXISTS storage_usage_stats (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_cluster VARCHAR(50),
|
||||
storage_tier VARCHAR(20),
|
||||
file_count BIGINT,
|
||||
total_size_bytes BIGINT,
|
||||
record_time TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 文件訪問日誌
|
||||
CREATE TABLE IF NOT EXISTS storage_access_logs (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_cluster VARCHAR(50),
|
||||
owner_id VARCHAR(100),
|
||||
file_path TEXT,
|
||||
access_type VARCHAR(20) CHECK (access_type IN ('read', 'write', 'delete', 'download', 'move')),
|
||||
access_time TIMESTAMP DEFAULT NOW(),
|
||||
client_ip VARCHAR(45),
|
||||
access_method VARCHAR(20)
|
||||
);
|
||||
|
||||
-- 文件生命週期
|
||||
CREATE TABLE IF NOT EXISTS file_lifecycle (
|
||||
id SERIAL PRIMARY KEY,
|
||||
file_uuid UUID REFERENCES file_registry(file_uuid),
|
||||
file_path TEXT,
|
||||
user_cluster VARCHAR(50),
|
||||
storage_tier VARCHAR(20),
|
||||
created_at TIMESTAMP,
|
||||
last_accessed_at TIMESTAMP,
|
||||
last_modified_at TIMESTAMP,
|
||||
access_count INTEGER DEFAULT 0,
|
||||
current_status VARCHAR(20) DEFAULT 'active',
|
||||
tier_migration_count INTEGER DEFAULT 0,
|
||||
migrated_at TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX idx_file_registry_cluster ON file_registry(user_cluster);
|
||||
CREATE INDEX idx_file_registry_tier ON file_registry(storage_tier);
|
||||
CREATE INDEX idx_file_registry_status ON file_registry(status);
|
||||
CREATE INDEX idx_storage_usage_cluster ON storage_usage_stats(user_cluster);
|
||||
CREATE INDEX idx_storage_usage_time ON storage_usage_stats(record_time);
|
||||
|
||||
-- ============================================================
|
||||
-- 外部監控 (Layer 1)
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS monitor_external (
|
||||
id SERIAL PRIMARY KEY,
|
||||
target_name VARCHAR(50) NOT NULL,
|
||||
target_type VARCHAR(20) CHECK (target_type IN ('ddns', 'gateway', 'internet', 'api')),
|
||||
target_host VARCHAR(255),
|
||||
is_reachable BOOLEAN,
|
||||
response_time_ms INTEGER,
|
||||
dns_resolved_ip VARCHAR(45),
|
||||
error_message TEXT,
|
||||
checked_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_monitor_external_name ON monitor_external(target_name);
|
||||
CREATE INDEX idx_monitor_external_time ON monitor_external(checked_at);
|
||||
|
||||
-- ============================================================
|
||||
-- 監控配置表
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS monitor_config (
|
||||
id SERIAL PRIMARY KEY,
|
||||
config_key VARCHAR(50) UNIQUE NOT NULL,
|
||||
config_value TEXT,
|
||||
description VARCHAR(255),
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 插入默認配置
|
||||
INSERT INTO monitor_config (config_key, config_value, description) VALUES
|
||||
('check_interval', '300', '監控檢查間隔(秒)'),
|
||||
('retention_days', '30', '歷史數據保留天數'),
|
||||
('idle_threshold_days', '30', 'Workflow 閒置天數閾值'),
|
||||
('alert_threshold_bruteforce', '5', '暴力破解嘗試次數閾值'),
|
||||
('alert_threshold_slow_response', '3000', '響應時間閾值(毫秒)')
|
||||
ON CONFLICT (config_key) DO NOTHING;
|
||||
|
||||
-- ============================================================
|
||||
-- 視圖定義
|
||||
-- ============================================================
|
||||
|
||||
-- 服務健康狀態視圖
|
||||
CREATE OR REPLACE VIEW v_service_health AS
|
||||
SELECT
|
||||
service_name,
|
||||
status,
|
||||
COUNT(*) as check_count,
|
||||
COUNT(*) FILTER (WHERE status = 'up') as up_count,
|
||||
COUNT(*) FILTER (WHERE status = 'down') as down_count,
|
||||
AVG(response_time_ms) as avg_response_time,
|
||||
MAX(checked_at) as last_check
|
||||
FROM monitor_services
|
||||
WHERE checked_at > NOW() - INTERVAL '24 hours'
|
||||
GROUP BY service_name, status;
|
||||
|
||||
-- 最近異常視圖
|
||||
CREATE OR REPLACE VIEW v_recent_anomalies AS
|
||||
SELECT
|
||||
anomaly_type,
|
||||
severity,
|
||||
username,
|
||||
source_ip,
|
||||
description,
|
||||
detected_at
|
||||
FROM monitor_anomalies
|
||||
WHERE detected_at > NOW() - INTERVAL '24 hours'
|
||||
ORDER BY detected_at DESC;
|
||||
|
||||
-- 閒置 Workflow 視圖
|
||||
CREATE OR REPLACE VIEW v_idle_workflows AS
|
||||
SELECT
|
||||
workflow_name,
|
||||
idle_days,
|
||||
suggestion,
|
||||
last_executed_at
|
||||
FROM monitor_workflows
|
||||
WHERE idle_days > 30 AND is_active = TRUE
|
||||
ORDER BY idle_days DESC;
|
||||
|
||||
-- 存儲使用概況視圖
|
||||
CREATE OR REPLACE VIEW v_storage_overview AS
|
||||
SELECT
|
||||
user_cluster,
|
||||
storage_tier,
|
||||
COUNT(*) as file_count,
|
||||
SUM(file_size) as total_size
|
||||
FROM file_registry
|
||||
WHERE status = 'active'
|
||||
GROUP BY user_cluster, storage_tier;
|
||||
|
||||
-- ============================================================
|
||||
-- 備份監控 (Layer 7 Extension)
|
||||
-- ============================================================
|
||||
|
||||
-- 備份註冊表
|
||||
CREATE TABLE IF NOT EXISTS backup_registry (
|
||||
id SERIAL PRIMARY KEY,
|
||||
service_name VARCHAR(50) NOT NULL,
|
||||
backup_file VARCHAR(500) NOT NULL,
|
||||
backup_size_bytes BIGINT,
|
||||
backup_type VARCHAR(20) CHECK (backup_type IN ('daily', 'weekly', 'monthly', 'archive', 'full', 'incremental')),
|
||||
backup_method VARCHAR(20) CHECK (backup_method IN ('pg_dump', 'mysqldump', 'tar', 'snapshot', 'dump')),
|
||||
status VARCHAR(20) CHECK (status IN ('pending', 'running', 'completed', 'failed', 'verified')),
|
||||
compression_ratio DECIMAL(5,2),
|
||||
verification_result BOOLEAN,
|
||||
error_message TEXT,
|
||||
started_at TIMESTAMP DEFAULT NOW(),
|
||||
completed_at TIMESTAMP,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 備份存儲統計
|
||||
CREATE TABLE IF NOT EXISTS backup_storage_stats (
|
||||
id SERIAL PRIMARY KEY,
|
||||
tier VARCHAR(20) CHECK (tier IN ('daily', 'weekly', 'monthly', 'archive', 'total')),
|
||||
file_count BIGINT,
|
||||
total_size_bytes BIGINT,
|
||||
record_time TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 備份歷史
|
||||
CREATE TABLE IF NOT EXISTS backup_history (
|
||||
id SERIAL PRIMARY KEY,
|
||||
service_name VARCHAR(50) NOT NULL,
|
||||
operation VARCHAR(20) CHECK (operation IN ('backup', 'restore', 'tier_migration', 'cleanup', 'verify')),
|
||||
backup_file VARCHAR(500),
|
||||
backup_tier VARCHAR(20),
|
||||
source_tier VARCHAR(20),
|
||||
dest_tier VARCHAR(20),
|
||||
file_count BIGINT,
|
||||
size_bytes BIGINT,
|
||||
duration_seconds INTEGER,
|
||||
status VARCHAR(20) CHECK (status IN ('success', 'failed', 'partial')),
|
||||
error_message TEXT,
|
||||
executed_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_backup_registry_service ON backup_registry(service_name);
|
||||
CREATE INDEX idx_backup_registry_time ON backup_registry(created_at);
|
||||
CREATE INDEX idx_backup_storage_stats_tier ON backup_storage_stats(tier);
|
||||
CREATE INDEX idx_backup_storage_stats_time ON backup_storage_stats(record_time);
|
||||
CREATE INDEX idx_backup_history_service ON backup_history(service_name);
|
||||
CREATE INDEX idx_backup_history_time ON backup_history(executed_at);
|
||||
|
||||
-- ============================================================
|
||||
-- Node.js 版本基線監控
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS node_version_baseline (
|
||||
id SERIAL PRIMARY KEY,
|
||||
runtime_name VARCHAR(50) NOT NULL,
|
||||
required_version VARCHAR(20) NOT NULL,
|
||||
current_version VARCHAR(20),
|
||||
process_name VARCHAR(100),
|
||||
process_path TEXT,
|
||||
is_compliant BOOLEAN,
|
||||
locked_path VARCHAR(500),
|
||||
checked_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Node.js 進程追蹤
|
||||
CREATE TABLE IF NOT EXISTS node_process_tracking (
|
||||
id SERIAL PRIMARY KEY,
|
||||
process_name VARCHAR(100) NOT NULL,
|
||||
pid INTEGER,
|
||||
command VARCHAR(500),
|
||||
node_version VARCHAR(20),
|
||||
is_managed BOOLEAN DEFAULT FALSE,
|
||||
started_at TIMESTAMP,
|
||||
checked_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- Python 版本基線監控
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS python_version_baseline (
|
||||
id SERIAL PRIMARY KEY,
|
||||
runtime_name VARCHAR(50) NOT NULL,
|
||||
required_version VARCHAR(20) NOT NULL,
|
||||
current_version VARCHAR(20),
|
||||
interpreter_path VARCHAR(500),
|
||||
is_compliant BOOLEAN,
|
||||
checked_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Python 腳本追蹤
|
||||
CREATE TABLE IF NOT EXISTS python_script_tracking (
|
||||
id SERIAL PRIMARY KEY,
|
||||
script_path TEXT NOT NULL,
|
||||
shebang_version VARCHAR(20),
|
||||
actual_version VARCHAR(20),
|
||||
is_compliant BOOLEAN DEFAULT FALSE,
|
||||
last_run_at TIMESTAMP,
|
||||
exit_code INTEGER,
|
||||
error_output TEXT,
|
||||
checked_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_node_version_name ON node_version_baseline(runtime_name);
|
||||
CREATE INDEX idx_node_process_name ON node_process_tracking(process_name);
|
||||
CREATE INDEX idx_python_version_name ON python_version_baseline(runtime_name);
|
||||
CREATE INDEX idx_python_script_path ON python_script_tracking(script_path);
|
||||
Reference in New Issue
Block a user