feat: add all application source code

- apps/api: FastAPI backend with Dockerfile
- apps/web: Next.js frontend with Dockerfile
- apps/sensor: Signal collection agent
- packages: shared packages

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
OG T
2026-03-22 18:57:44 +08:00
parent a840bf975b
commit 196d269b92
245 changed files with 42207 additions and 6 deletions

9
apps/sensor/.env.example Normal file
View File

@@ -0,0 +1,9 @@
# AWOOOI Sensor Agent Configuration
# ===================================
# 複製此檔案為 .env 並填入正確的值
# 188 基地 Redis URL (必填)
AWOOOI_REDIS_URL=redis://192.168.68.188:6379/0
# 如果 Redis 有密碼
# AWOOOI_REDIS_URL=redis://:your_password@192.168.68.188:6379/0

49
apps/sensor/README.md Normal file
View File

@@ -0,0 +1,49 @@
# AWOOOI Sensor Agent
> Phase 6.5 神經末梢 - 極度輕量的告警採集代理
## 設計原則
```
嚴禁邏輯:
├── Incident 聚合 → 188 大腦負責
├── GraphRAG 分析 → 188 大腦負責
└── 任何決策邏輯 → 188 大腦負責
唯一職責:
└── 採集本地告警 → 無腦 XADD → 188 Event Bus
```
## 快速開始
```bash
# 1. 安裝依賴 (僅需 redis-py)
pip install -r requirements.txt
# 2. 設定 188 基地連線
export AWOOOI_REDIS_URL="redis://192.168.68.188:6379/0"
# 3. 發射測試告警
python agent.py
# 4. 持續監控模式
python agent.py --loop --interval 30
```
## 部署架構
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Host 118 │ │ Host 119 │ │ Host 120 │
│ Sensor │ │ Sensor │ │ Sensor │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
│ XADD (跨網段) │
└───────────────────┼───────────────────┘
┌────────────────────────┐
│ Host 188 (基地) │
│ Redis Event Bus │
│ stream:awoooi_signals│
└────────────────────────┘
```

302
apps/sensor/agent.py Normal file
View File

@@ -0,0 +1,302 @@
#!/usr/bin/env python3
"""
AWOOOI Sensor Agent - Phase 6.5 神經末梢
=========================================
極度輕量的告警採集代理,部署於各主機。
唯一職責:採集本地告警 → 無腦轉發至 188 基地 Event Bus
設計鐵律:
- 嚴禁 Incident/GraphRAG 邏輯 (防腦分裂)
- 零依賴 AWOOOI 核心資料庫
- 純 Python + Redis 即可運行
使用方式:
# 設定環境變數
export AWOOOI_REDIS_URL="redis://192.168.68.188:6379/0"
# 執行代理 (發送模擬告警)
python agent.py
# 持續監控模式 (每 30 秒發送一次)
python agent.py --loop --interval 30
Version: 1.0.0
Date: 2026-03-22
"""
import argparse
import json
import os
import random
import socket
import sys
import time
from datetime import datetime, timezone
from typing import Any
from uuid import uuid4
# ============================================================================
# 唯一外部依賴redis-py (pip install redis)
# ============================================================================
try:
import redis
except ImportError:
print("[FATAL] redis-py not installed. Run: pip install redis")
sys.exit(1)
# ============================================================================
# 常量定義
# ============================================================================
STREAM_NAME = "stream:awoooi_signals"
DEFAULT_REDIS_URL = "redis://192.168.68.188:6379/0"
# 模擬告警模板 (實際部署時會讀取 Prometheus/Alertmanager)
MOCK_ALERTS = [
{
"alert_name": "PodCrashLoopBackOff",
"severity": "critical",
"source": "prometheus",
"namespace": "production",
"target": "payment-service",
},
{
"alert_name": "HighLatencyP99",
"severity": "warning",
"source": "prometheus",
"namespace": "production",
"target": "api-gateway",
},
{
"alert_name": "HighErrorRate",
"severity": "critical",
"source": "prometheus",
"namespace": "staging",
"target": "order-service",
},
{
"alert_name": "MemoryPressure",
"severity": "warning",
"source": "node-exporter",
"namespace": "infra",
"target": "k3s-worker-01",
},
{
"alert_name": "FINAL_PHASE_6_TEST",
"severity": "critical",
"source": "sensor-agent",
"namespace": "production",
"target": "awoooi-brain",
},
]
# ============================================================================
# Sensor Agent Core
# ============================================================================
class SensorAgent:
"""
神經末梢 - 極簡告警採集代理
職責:
1. 採集本地告警 (或模擬生成)
2. 格式化為標準 Signal
3. 透過 Redis XADD 打入 188 基地 Event Bus
嚴禁邏輯:
- Incident 聚合 (由 188 大腦負責)
- GraphRAG 分析 (由 188 大腦負責)
- 任何決策邏輯 (由 188 大腦負責)
"""
def __init__(self, redis_url: str | None = None) -> None:
self.redis_url = redis_url or os.getenv("AWOOOI_REDIS_URL", DEFAULT_REDIS_URL)
self.hostname = socket.gethostname()
self.sensor_id = f"sensor-{self.hostname}"
self._redis: redis.Redis | None = None
def connect(self) -> bool:
"""連線至 188 基地 Redis"""
try:
self._redis = redis.from_url(
self.redis_url,
decode_responses=True,
socket_connect_timeout=5,
)
# 測試連線
self._redis.ping()
print(f"[OK] Connected to 188 Event Bus: {self._mask_url(self.redis_url)}")
return True
except redis.ConnectionError as e:
print(f"[FATAL] Cannot connect to 188 Event Bus: {e}")
return False
def _mask_url(self, url: str) -> str:
"""遮蔽密碼"""
if "@" in url:
parts = url.split("@")
return f"redis://***@{parts[-1]}"
return url
def send_signal(self, alert: dict[str, Any]) -> str | None:
"""
發送單一 Signal 至 Event Bus
無腦轉發邏輯:
1. 補齊必要欄位 (fingerprint, timestamp, sensor_id)
2. 直接 XADD 到 stream:awoooi_signals
3. 返回 message_id 或 None
Args:
alert: 告警字典 (至少需 alert_name, severity, source)
Returns:
Redis Stream message ID or None
"""
if not self._redis:
print("[ERROR] Not connected to Redis")
return None
# 建立標準 Signal 格式
now = datetime.now(timezone.utc)
signal = {
"alert_name": alert.get("alert_name", "UnknownAlert"),
"severity": alert.get("severity", "warning"),
"source": alert.get("source", "sensor-agent"),
"namespace": alert.get("namespace", "default"),
"target": alert.get("target", "unknown"),
"fingerprint": alert.get("fingerprint", f"fp_{uuid4().hex[:12]}"),
"labels": json.dumps(alert.get("labels", {"sensor_id": self.sensor_id})),
"annotations": json.dumps(alert.get("annotations", {})),
"received_at": now.isoformat(),
"sensor_id": self.sensor_id,
"sensor_host": self.hostname,
}
try:
# 無腦 XADD - 直接打入 188 基地
message_id = self._redis.xadd(STREAM_NAME, signal)
return message_id
except redis.RedisError as e:
print(f"[ERROR] XADD failed: {e}")
return None
def fire_mock_alert(self, alert_name: str | None = None) -> str | None:
"""
發射模擬告警 (測試用)
Args:
alert_name: 指定告警名稱,或隨機選擇
Returns:
message_id or None
"""
if alert_name:
# 尋找指定告警
alert = next(
(a for a in MOCK_ALERTS if a["alert_name"] == alert_name),
MOCK_ALERTS[-1], # 預設使用 FINAL_PHASE_6_TEST
)
else:
alert = random.choice(MOCK_ALERTS)
print(f"\n[FIRE] Sending alert: {alert['alert_name']}")
print(f" Severity: {alert['severity']}")
print(f" Target: {alert['namespace']}/{alert['target']}")
print(f" Sensor: {self.sensor_id}")
message_id = self.send_signal(alert)
if message_id:
print(f"[OK] Signal delivered to 188 Event Bus")
print(f" Stream: {STREAM_NAME}")
print(f" Message ID: {message_id}")
else:
print(f"[FAIL] Signal delivery failed!")
return message_id
def close(self) -> None:
"""關閉連線"""
if self._redis:
self._redis.close()
print("[OK] Disconnected from 188 Event Bus")
# ============================================================================
# CLI Entry Point
# ============================================================================
def main() -> int:
parser = argparse.ArgumentParser(
description="AWOOOI Sensor Agent - 神經末梢告警採集代理"
)
parser.add_argument(
"--alert",
type=str,
default="FINAL_PHASE_6_TEST",
help="告警名稱 (預設: FINAL_PHASE_6_TEST)",
)
parser.add_argument(
"--loop",
action="store_true",
help="持續監控模式",
)
parser.add_argument(
"--interval",
type=int,
default=30,
help="監控間隔秒數 (預設: 30)",
)
parser.add_argument(
"--redis-url",
type=str,
help="Redis URL (預設讀取 AWOOOI_REDIS_URL 環境變數)",
)
args = parser.parse_args()
print("=" * 70)
print("AWOOOI Sensor Agent - Phase 6.5 神經末梢")
print("=" * 70)
print(f"Time: {datetime.now().isoformat()}")
print(f"Host: {socket.gethostname()}")
print()
# 初始化 Agent
agent = SensorAgent(redis_url=args.redis_url)
if not agent.connect():
return 1
try:
if args.loop:
# 持續監控模式
print(f"\n[LOOP] Continuous mode: sending random alert every {args.interval}s")
print("[LOOP] Press Ctrl+C to stop\n")
while True:
agent.fire_mock_alert()
time.sleep(args.interval)
else:
# 單發模式
message_id = agent.fire_mock_alert(alert_name=args.alert)
if not message_id:
return 1
except KeyboardInterrupt:
print("\n[STOP] Interrupted by user")
finally:
agent.close()
print("\n" + "=" * 70)
print("Sensor Agent terminated")
print("=" * 70)
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,5 @@
# AWOOOI Sensor Agent Dependencies
# ==================================
# 極度輕量:僅需 redis-py
redis>=5.0.0