diff --git a/.claude/scheduled_tasks.lock b/.claude/scheduled_tasks.lock new file mode 100644 index 00000000..eee9a99d --- /dev/null +++ b/.claude/scheduled_tasks.lock @@ -0,0 +1 @@ +{"sessionId":"412c1507-44d4-4702-bb80-f37e97b804a7","pid":5408,"acquiredAt":1774326092203} \ No newline at end of file diff --git a/.claude/settings.json b/.claude/settings.json index 69b88af3..063101fb 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -370,7 +370,44 @@ "Bash(sshpass -p '0936223270' ssh -o StrictHostKeyChecking=no wooo@192.168.0.110 'echo \"\"=== 日誌行數 ===\"\" && wc -l /tmp/sentry-install.log && echo \"\"\"\" && echo \"\"=== 最近 20 行 ===\"\" && tail -20 /tmp/sentry-install.log')", "Bash(sshpass -p '0936223270' ssh -o StrictHostKeyChecking=no wooo@192.168.0.110 'echo \"\"=== 日誌行數 ===\"\" && wc -l /tmp/sentry-install.log && echo \"\"\"\" && echo \"\"=== 關鍵階段 ===\"\" && grep -E \"\"^▶|✓|Error|Creating|Starting|Building|DONE\"\" /tmp/sentry-install.log | tail -30')", "Bash(sshpass -p '0936223270' ssh -o StrictHostKeyChecking=no wooo@192.168.0.110 'echo \"\"=== 日誌行數 ===\"\" && wc -l /tmp/sentry-install.log && echo \"\"\"\" && echo \"\"=== 最近關鍵階段 ===\"\" && grep -E \"\"^▶|✓|Error|Creating|Starting|DONE|Completed|success\"\" /tmp/sentry-install.log | tail -25')", - "Bash(sshpass -p '0936223270' ssh -o StrictHostKeyChecking=no wooo@192.168.0.110 'grep -E \"\"^▶|✓|Error|Completed|success|fail\"\" /tmp/sentry-install.log | tail -15')" + "Bash(sshpass -p '0936223270' ssh -o StrictHostKeyChecking=no wooo@192.168.0.110 'grep -E \"\"^▶|✓|Error|Completed|success|fail\"\" /tmp/sentry-install.log | tail -15')", + "Bash(redis-cli -h 192.168.0.188 -p 6380 KEYS incident:*)", + "Bash(sshpass -p '0936223270' ssh -o StrictHostKeyChecking=no ollama@192.168.0.188 \"cat /home/ollama/momo-pro/monitoring/alertmanager.yml 2>/dev/null || cat /etc/alertmanager/alertmanager.yml 2>/dev/null || echo ''Config not found''\")", + "Bash(sshpass -p '0936223270' ssh -o StrictHostKeyChecking=no ollama@192.168.0.188 \"docker logs clawbot --tail 30 2>&1\")", + "Bash(sshpass -p '0936223270' ssh -o StrictHostKeyChecking=no ollama@192.168.0.188 \"docker logs clawbot --tail 20 2>&1 | grep -iE ''telegram|send|alert|incident|error''\")", + "Bash(sshpass -p '0936223270' ssh -o StrictHostKeyChecking=no ollama@192.168.0.188 \"cat /home/ollama/clawbot-v5/.env | grep -E ''TELEGRAM|TG_'' | head -5\")", + "Bash(sshpass -p '0936223270' ssh -o StrictHostKeyChecking=no ollama@192.168.0.188 \"cat /home/ollama/clawbot-v5/.env | grep -E ''REDIS|POSTGRES|DATABASE'' | head -5\")", + "Bash(ssh ollama@192.168.0.188 'curl -s \"\"http://localhost:9093/api/v2/alerts?active=true\"\" | python3 -c \"\"import sys,json; alerts=json.load\\(sys.stdin\\); print\\(f\\\\\"\"Active alerts: {len\\(alerts\\)}\\\\\"\"\\)\"\"')", + "Bash(ssh ollama@192.168.0.188 'curl -s \"\"http://localhost:9093/api/v2/alerts\"\" | python3 -c \"\"import sys,json; alerts=json.load\\(sys.stdin\\); print\\(f\\\\\"\"Total alerts: {len\\(alerts\\)}\\\\\"\"\\); [print\\(a[\\\\\"\"labels\\\\\"\"][\\\\\"\"alertname\\\\\"\"]\\) for a in alerts[:5]]\"\"')", + "Bash(ssh ollama@192.168.0.188 'redis-cli -p 6380 -n 0 GET incident:INC-20260324-36AF55 | python3 -c \"\"import sys,json; d=json.load\\(sys.stdin\\); print\\(f\\\\\"\"Status: {d.get\\(\\\\\"\"status\\\\\"\"\\)}\\\\\"\"\\); print\\(f\\\\\"\"message_id: {d.get\\(\\\\\"\"message_id\\\\\"\", \\\\\"\"NONE\\\\\"\"\\)}\\\\\"\"\\); print\\(f\\\\\"\"chat_id: {d.get\\(\\\\\"\"chat_id\\\\\"\", \\\\\"\"NONE\\\\\"\"\\)}\\\\\"\"\\)\"\"')", + "Bash(ssh ollama@192.168.0.188 'redis-cli -p 6380 -n 0 GET incident:INC-20260324-36AF55 | python3 -c \"\"import sys,json; d=json.load\\(sys.stdin\\); print\\(f\\\\\"\"status: {d.get\\('status'\\)}\\\\\"\"\\); print\\(f\\\\\"\"message_id: {d.get\\('message_id'\\)}\\\\\"\"\\); print\\(f\\\\\"\"created_at: {d.get\\('created_at'\\)}\\\\\"\"\\)\"\"')", + "Bash(redis-cli -h 192.168.0.188 -p 6380 -n 0 KEYS *approval*)", + "Bash(redis-cli -h 192.168.0.188 -p 6380 -n 0 KEYS *incident*)", + "Bash(redis-cli -h 192.168.0.188 -p 6380 -n 0 KEYS *pending*)", + "Bash(redis-cli -h 192.168.0.188 -p 6380 -n 0 KEYS *)", + "Bash(KUBECONFIG=/Users/ogt/awoooi/k3s-prod.yaml kubectl get pods -n awoooi-prod -o wide)", + "Bash(KUBECONFIG=/Users/ogt/awoooi/k3s-prod.yaml kubectl get deployment awoooi-api -n awoooi-prod -o jsonpath='{.spec.template.spec.containers[0].image}')", + "Bash(kubectl --kubeconfig=/Users/ogt/awoooi/k3s-prod.yaml get deployment awoooi-api -n awoooi-prod -o jsonpath='{.spec.template.spec.containers[0].image}')", + "Bash(python3 -c \":*)", + "Bash(/tmp/awoooi-tg-secret.yaml:*)", + "Bash(KUBECONFIG=/Users/ogt/awoooi/k3s-prod.yaml kubectl apply -f /tmp/awoooi-tg-secret.yaml)", + "Bash(for pod:*)", + "Bash(sshpass -p '0936223270' ssh -o StrictHostKeyChecking=no wooo@192.168.0.188 \"curl -fsSL https://ollama.com/install.sh | sh\")", + "Bash(sshpass -p '0936223270' ssh -o StrictHostKeyChecking=no -o PreferredAuthentications=password wooo@192.168.0.188 \"echo connected && ollama --version\")", + "Bash(sshpass -p '0936223270' ssh -o StrictHostKeyChecking=no -o PreferredAuthentications=password ollama@192.168.0.188 \"curl -fsSL https://ollama.com/install.sh | sh\")", + "Bash(sshpass -p '0936223270' ssh -o StrictHostKeyChecking=no ollama@192.168.0.188 \"echo ''0936223270'' | sudo -S curl -fsSL https://ollama.com/install.sh | sudo -S sh\")", + "Bash(sshpass -p '0936223270' ssh -o StrictHostKeyChecking=no ollama@192.168.0.188 \"ollama --version\")", + "Bash(__NEW_LINE_95e9df111552805b__ echo:*)", + "Bash(sshpass -p '0936223270' scp /Users/ogt/awoooi/k8s/nginx/awoooi-prod.conf ollama@192.168.0.188:/tmp/awoooi-prod.conf)", + "Bash(sshpass -p '0936223270' ssh ollama@192.168.0.188 \"echo ''0936223270'' | sudo -S cp /tmp/awoooi-prod.conf /etc/nginx/conf.d/awoooi-prod.conf && echo ''0936223270'' | sudo -S nginx -t 2>&1\")", + "Bash(sshpass -p '0936223270' ssh ollama@192.168.0.188 \"echo ''0936223270'' | sudo -S ls -la /etc/nginx/ssl/ 2>/dev/null || echo ''No ssl dir''; echo ''0936223270'' | sudo -S ls -la /etc/nginx/conf.d/ 2>/dev/null | head -10\")", + "Bash(sshpass -p '0936223270' ssh ollama@192.168.0.188 \"echo ''0936223270'' | sudo -S grep -r ''ssl_certificate'' /etc/nginx/ 2>/dev/null | head -5\")", + "Bash(sshpass -p '0936223270' ssh ollama@192.168.0.188 \"echo ''0936223270'' | sudo -S grep -A 20 ''server_name awoooi'' /etc/nginx/sites-enabled/all-sites.conf 2>/dev/null | head -30\")", + "Bash(sshpass -p '0936223270' ssh ollama@192.168.0.188 \"echo ''0936223270'' | sudo -S ls -la /etc/nginx/sites-enabled/ 2>/dev/null\")", + "Bash(sshpass -p '0936223270' ssh ollama@192.168.0.188 \"echo ''0936223270'' | sudo -S cat /etc/nginx/sites-available/awoooi.wooo.work.conf 2>/dev/null\")", + "Bash(sshpass -p '0936223270' ssh ollama@192.168.0.188 \"echo ''0936223270'' | sudo -S rm /etc/nginx/conf.d/awoooi-prod.conf && echo ''0936223270'' | sudo -S nginx -t 2>&1\")", + "Bash(sshpass -p '0936223270' ssh ollama@192.168.0.188 \"echo ''0936223270'' | sudo -S nginx -s reload 2>&1\")", + "Bash(sshpass -p '0936223270' ssh ollama@192.168.0.188 \"echo ''0936223270'' | sudo -S systemctl reload nginx 2>&1\")" ], "deny": [ "Bash(rm -rf *)", diff --git a/.claude/worktrees/reverent-gauss b/.claude/worktrees/reverent-gauss new file mode 160000 index 00000000..9f353343 --- /dev/null +++ b/.claude/worktrees/reverent-gauss @@ -0,0 +1 @@ +Subproject commit 9f353343c9baa2bc61e1534bd9f9214edf45b7ab diff --git a/Gemini_Generated_Image_sxbfrvsxbfrvsxbf (2).png b/Gemini_Generated_Image_sxbfrvsxbfrvsxbf (2).png new file mode 100644 index 00000000..5f5159de Binary files /dev/null and b/Gemini_Generated_Image_sxbfrvsxbfrvsxbf (2).png differ diff --git a/Gemini_Generated_Image_sxbfrvsxbfrvsxbf.png b/Gemini_Generated_Image_sxbfrvsxbfrvsxbf.png new file mode 100644 index 00000000..3515049b Binary files /dev/null and b/Gemini_Generated_Image_sxbfrvsxbfrvsxbf.png differ diff --git a/apps/api/src/api/v1/incidents.py b/apps/api/src/api/v1/incidents.py index dfd2aa52..f5416f12 100644 --- a/apps/api/src/api/v1/incidents.py +++ b/apps/api/src/api/v1/incidents.py @@ -622,3 +622,153 @@ async def debug_resolve_incident(incident_id: str) -> dict[str, Any]: "updates": {"redis": redis_updated, "db": db_updated}, "error": error_msg, } + + +# ============================================================================= +# POST /api/v1/incidents/sync-from-approvals - 補建 Incidents +# ============================================================================= + +class SyncResult(BaseModel): + """同步結果""" + synced: int + skipped: int + errors: list[str] + details: list[dict[str, Any]] + + +@router.post( + "/sync-from-approvals", + response_model=SyncResult, + summary="從現有 Approvals 補建 Incidents", + description=""" + 修復歷史數據:為沒有對應 Incident 的 Approvals 補建。 + + 用途: + - 修復 b645981 之前創建的 Approvals(無 Incident) + - 確保「活躍事件」與「待簽核」數量一致 + """, +) +async def sync_incidents_from_approvals() -> SyncResult: + """ + 掃描所有 pending Approvals,為缺少 Incident 的補建 + """ + from uuid import UUID + + from src.models.incident import Signal + from src.services.approval_db import get_approval_service + + redis_client = get_redis() + approval_service = get_approval_service() + + synced = 0 + skipped = 0 + errors: list[str] = [] + details: list[dict[str, Any]] = [] + + # 取得所有 pending approvals + pending_approvals = await approval_service.get_pending_approvals() + + for approval in pending_approvals: + approval_id = str(approval.id) + + # 檢查是否已有 Incident 關聯此 Approval + has_incident = False + cursor = 0 + while True: + cursor, keys = await redis_client.scan( + cursor=cursor, + match="incident:INC-*", + count=100, + ) + for key in keys: + try: + data = await redis_client.get(key) + if data: + incident = Incident.model_validate_json(data) + if UUID(approval_id) in incident.proposal_ids: + has_incident = True + break + except Exception: + pass + if has_incident or cursor == 0: + break + + if has_incident: + skipped += 1 + continue + + # 補建 Incident + try: + # 映射 risk_level -> severity + risk_map = { + "critical": Severity.P0, + "high": Severity.P1, + "medium": Severity.P2, + "low": Severity.P3, + } + severity = risk_map.get(approval.risk_level.value.lower(), Severity.P2) + + # 解析 action 取得資源名稱 + action_parts = approval.action.split("|") + resource_name = action_parts[0].strip() if action_parts else "unknown" + + signal = Signal( + alert_name="sync_from_approval", + severity=severity, + source="backfill", + fired_at=approval.created_at, + labels={"approval_id": approval_id}, + annotations={"description": approval.description or ""}, + ) + + incident = Incident( + status=IncidentStatus.INVESTIGATING, + severity=severity, + signals=[signal], + affected_services=[resource_name], + proposal_ids=[UUID(approval_id)], + created_at=approval.created_at, + updated_at=datetime.now(UTC), + ) + + key = f"incident:{incident.incident_id}" + await redis_client.set( + key, + incident.model_dump_json(), + ex=604800, # 7 days + ) + + synced += 1 + details.append({ + "approval_id": approval_id, + "incident_id": incident.incident_id, + "severity": severity.value, + }) + + logger.info( + "incident_synced_from_approval", + approval_id=approval_id, + incident_id=incident.incident_id, + ) + + except Exception as e: + errors.append(f"{approval_id}: {str(e)}") + logger.error( + "incident_sync_error", + approval_id=approval_id, + error=str(e), + ) + + logger.info( + "sync_from_approvals_completed", + synced=synced, + skipped=skipped, + errors=len(errors), + ) + + return SyncResult( + synced=synced, + skipped=skipped, + errors=errors, + details=details, + ) diff --git a/apps/api/src/api/v1/sentry_webhook.py b/apps/api/src/api/v1/sentry_webhook.py new file mode 100644 index 00000000..71ca74ca --- /dev/null +++ b/apps/api/src/api/v1/sentry_webhook.py @@ -0,0 +1,247 @@ +""" +AWOOOI API - Sentry Webhook Handler +==================================== +接收 Sentry Issue Alert,轉發給 OpenClaw 進行 AI 分析 + +整合流程: +1. Sentry Alert → AWOOOI API Webhook +2. 組裝錯誤上下文 +3. 呼叫 OpenClaw Error Analyzer Agent +4. 結果回寫 Sentry Issue Comment +""" + +import logging +from typing import Any +from datetime import datetime + +from fastapi import APIRouter, Request, HTTPException, BackgroundTasks +from pydantic import BaseModel +import httpx + +logger = logging.getLogger(__name__) + +router = APIRouter(prefix="/webhooks/sentry", tags=["Sentry Webhook"]) + +# OpenClaw 配置 +OPENCLAW_URL = "http://192.168.0.188:8088" +SENTRY_API_URL = "http://192.168.0.110:9000" + + +class SentryIssuePayload(BaseModel): + """Sentry Issue Alert Payload (簡化版)""" + action: str # created, resolved, etc. + data: dict + actor: dict | None = None + + +class ErrorAnalysisResult(BaseModel): + """錯誤分析結果""" + root_cause: str + impact: str + fix_suggestion: str + prevention: str + confidence: float + analyzed_by: str # ollama, claude + + +@router.post("/error") +async def handle_sentry_error( + request: Request, + background_tasks: BackgroundTasks +): + """ + Sentry Issue Webhook Handler + + 觸發條件: + - Issue 新建 (action=created) + - Level: error 或 fatal + + 處理流程: + 1. 解析 Sentry payload + 2. 組裝錯誤上下文 + 3. 背景執行 OpenClaw 分析 + 4. 回寫 Sentry Comment + """ + try: + payload = await request.json() + logger.info(f"Received Sentry webhook: action={payload.get('action')}") + + # 只處理新建的 issue + if payload.get("action") != "triggered": + return {"status": "ignored", "reason": "action is not triggered"} + + # 提取錯誤資訊 + issue_data = payload.get("data", {}).get("issue", {}) + event_data = payload.get("data", {}).get("event", {}) + + error_context = { + "issue_id": issue_data.get("id"), + "title": issue_data.get("title"), + "culprit": issue_data.get("culprit"), + "level": issue_data.get("level"), + "first_seen": issue_data.get("firstSeen"), + "count": issue_data.get("count"), + "project": issue_data.get("project", {}).get("slug"), + + # 事件詳情 + "message": event_data.get("message"), + "platform": event_data.get("platform"), + "tags": event_data.get("tags", []), + + # Stack trace (最後5個 frame) + "stacktrace": _extract_stacktrace(event_data), + } + + # 判斷是否需要 AI 分析 + level = issue_data.get("level", "error") + if level not in ["error", "fatal"]: + return {"status": "ignored", "reason": f"level {level} does not require analysis"} + + # 背景執行分析 + background_tasks.add_task( + analyze_and_comment, + error_context=error_context, + issue_id=issue_data.get("id"), + project_slug=issue_data.get("project", {}).get("slug"), + ) + + return { + "status": "accepted", + "issue_id": error_context["issue_id"], + "message": "Analysis scheduled" + } + + except Exception as e: + logger.exception("Sentry webhook processing failed") + raise HTTPException(status_code=500, detail=str(e)) + + +def _extract_stacktrace(event_data: dict) -> list[dict]: + """提取 Stack Trace (最後5個 frame)""" + try: + exception = event_data.get("exception", {}) + values = exception.get("values", []) + if not values: + return [] + + stacktrace = values[0].get("stacktrace", {}) + frames = stacktrace.get("frames", []) + + # 取最後5個 frame,只保留關鍵資訊 + return [ + { + "filename": f.get("filename"), + "function": f.get("function"), + "lineno": f.get("lineno"), + "context_line": f.get("context_line"), + } + for f in frames[-5:] + ] + except Exception: + return [] + + +async def analyze_and_comment( + error_context: dict, + issue_id: str, + project_slug: str +): + """ + 背景任務:分析錯誤並回寫 Sentry Comment + """ + try: + logger.info(f"Starting AI analysis for issue {issue_id}") + + # 呼叫 OpenClaw 分析 + analysis = await call_openclaw_analyzer(error_context) + + if analysis: + # 回寫 Sentry Comment + await post_sentry_comment( + project_slug=project_slug, + issue_id=issue_id, + analysis=analysis + ) + logger.info(f"Analysis completed for issue {issue_id}") + else: + logger.warning(f"Analysis returned empty for issue {issue_id}") + + except Exception as e: + logger.exception(f"Analysis failed for issue {issue_id}: {e}") + + +async def call_openclaw_analyzer(error_context: dict) -> ErrorAnalysisResult | None: + """ + 呼叫 OpenClaw Error Analyzer Agent + + 優先使用 Ollama (本地,零成本) + Fallback: Claude (高嚴重性) + """ + try: + async with httpx.AsyncClient(timeout=60.0) as client: + response = await client.post( + f"{OPENCLAW_URL}/api/v1/analyze/error", + json={ + "error_context": error_context, + "prefer_local": True, # 優先 Ollama + } + ) + + if response.status_code == 200: + data = response.json() + return ErrorAnalysisResult(**data) + else: + logger.warning(f"OpenClaw returned {response.status_code}") + return None + + except httpx.TimeoutException: + logger.warning("OpenClaw analysis timeout, trying fallback prompt") + # TODO: 直接呼叫 Ollama/Claude 作為 fallback + return None + except Exception as e: + logger.exception(f"OpenClaw call failed: {e}") + return None + + +async def post_sentry_comment( + project_slug: str, + issue_id: str, + analysis: ErrorAnalysisResult +): + """ + 回寫分析結果到 Sentry Issue Comment + + API: POST /api/0/issues/{issue_id}/comments/ + """ + comment_text = f"""## AI 錯誤分析 (by {analysis.analyzed_by}) + +**根本原因 (Root Cause)** +{analysis.root_cause} + +**影響範圍 (Impact)** +{analysis.impact} + +**建議修復 (Fix Suggestion)** +{analysis.fix_suggestion} + +**預防措施 (Prevention)** +{analysis.prevention} + +--- +*分析信心度: {analysis.confidence:.0%} | 分析時間: {datetime.now().isoformat()}* +""" + + try: + # TODO: 需要 Sentry API Token + # 目前先 log 出來 + logger.info(f"Would post comment to issue {issue_id}:\n{comment_text}") + + # async with httpx.AsyncClient() as client: + # response = await client.post( + # f"{SENTRY_API_URL}/api/0/issues/{issue_id}/comments/", + # headers={"Authorization": f"Bearer {SENTRY_API_TOKEN}"}, + # json={"text": comment_text} + # ) + + except Exception as e: + logger.exception(f"Failed to post Sentry comment: {e}") diff --git a/apps/web/playwright-report/data/0278a752e4d81bb2e4d865f8342a2663980f2f63.webm b/apps/web/playwright-report/data/0278a752e4d81bb2e4d865f8342a2663980f2f63.webm new file mode 100644 index 00000000..0748af9d Binary files /dev/null and b/apps/web/playwright-report/data/0278a752e4d81bb2e4d865f8342a2663980f2f63.webm differ diff --git a/apps/web/playwright-report/data/038be691f38e58a3cb2702f6432fc6a7732d05b2.png b/apps/web/playwright-report/data/038be691f38e58a3cb2702f6432fc6a7732d05b2.png new file mode 100644 index 00000000..eb6e3194 Binary files /dev/null and b/apps/web/playwright-report/data/038be691f38e58a3cb2702f6432fc6a7732d05b2.png differ diff --git a/apps/web/playwright-report/data/0ab6357444921c4fdafc23abfbe061d03057f17a.png b/apps/web/playwright-report/data/0ab6357444921c4fdafc23abfbe061d03057f17a.png new file mode 100644 index 00000000..b985c925 Binary files /dev/null and b/apps/web/playwright-report/data/0ab6357444921c4fdafc23abfbe061d03057f17a.png differ diff --git a/apps/web/playwright-report/data/12811b164edd9553309bb7706999888a4e4a2820.webm b/apps/web/playwright-report/data/12811b164edd9553309bb7706999888a4e4a2820.webm new file mode 100644 index 00000000..e00c923a Binary files /dev/null and b/apps/web/playwright-report/data/12811b164edd9553309bb7706999888a4e4a2820.webm differ diff --git a/apps/web/playwright-report/data/136d69dffadad984b563bbe9be717f34281757ad.png b/apps/web/playwright-report/data/136d69dffadad984b563bbe9be717f34281757ad.png new file mode 100644 index 00000000..cba0b936 Binary files /dev/null and b/apps/web/playwright-report/data/136d69dffadad984b563bbe9be717f34281757ad.png differ diff --git a/apps/web/playwright-report/data/145d1ce92578bb4bb9925dbe08f9c4cbe8a3eb9a.webm b/apps/web/playwright-report/data/145d1ce92578bb4bb9925dbe08f9c4cbe8a3eb9a.webm new file mode 100644 index 00000000..591b8682 Binary files /dev/null and b/apps/web/playwright-report/data/145d1ce92578bb4bb9925dbe08f9c4cbe8a3eb9a.webm differ diff --git a/apps/web/playwright-report/data/17b6a81e39d16d9a7165b0e92f03eeee12791cdd.webm b/apps/web/playwright-report/data/17b6a81e39d16d9a7165b0e92f03eeee12791cdd.webm new file mode 100644 index 00000000..c833a83d Binary files /dev/null and b/apps/web/playwright-report/data/17b6a81e39d16d9a7165b0e92f03eeee12791cdd.webm differ diff --git a/apps/web/playwright-report/data/25672f8d97a6d022a4da2af15e623ef65b5f9f5b.png b/apps/web/playwright-report/data/25672f8d97a6d022a4da2af15e623ef65b5f9f5b.png new file mode 100644 index 00000000..ab3967e1 Binary files /dev/null and b/apps/web/playwright-report/data/25672f8d97a6d022a4da2af15e623ef65b5f9f5b.png differ diff --git a/apps/web/playwright-report/data/25d4a7c7b1c73066faa0727a6cb6d72afda5f61e.webm b/apps/web/playwright-report/data/25d4a7c7b1c73066faa0727a6cb6d72afda5f61e.webm new file mode 100644 index 00000000..d7467d98 Binary files /dev/null and b/apps/web/playwright-report/data/25d4a7c7b1c73066faa0727a6cb6d72afda5f61e.webm differ diff --git a/apps/web/playwright-report/data/2961415fc8bbebe710b8d02430d0ef7a709078f4.png b/apps/web/playwright-report/data/2961415fc8bbebe710b8d02430d0ef7a709078f4.png new file mode 100644 index 00000000..7e26ace0 Binary files /dev/null and b/apps/web/playwright-report/data/2961415fc8bbebe710b8d02430d0ef7a709078f4.png differ diff --git a/apps/web/playwright-report/data/357fd986bdcc32fe3ed900a085100e401a1b0252.webm b/apps/web/playwright-report/data/357fd986bdcc32fe3ed900a085100e401a1b0252.webm new file mode 100644 index 00000000..6a755310 Binary files /dev/null and b/apps/web/playwright-report/data/357fd986bdcc32fe3ed900a085100e401a1b0252.webm differ diff --git a/apps/web/playwright-report/data/3a0cbb6219e70e7856e03ad6955a34c7cde00d97.webm b/apps/web/playwright-report/data/3a0cbb6219e70e7856e03ad6955a34c7cde00d97.webm new file mode 100644 index 00000000..2760baea Binary files /dev/null and b/apps/web/playwright-report/data/3a0cbb6219e70e7856e03ad6955a34c7cde00d97.webm differ diff --git a/apps/web/playwright-report/data/43d51cbc086d97d684b5eab42e86d4d60e2bb9cd.png b/apps/web/playwright-report/data/43d51cbc086d97d684b5eab42e86d4d60e2bb9cd.png new file mode 100644 index 00000000..43035a74 Binary files /dev/null and b/apps/web/playwright-report/data/43d51cbc086d97d684b5eab42e86d4d60e2bb9cd.png differ diff --git a/apps/web/playwright-report/data/4e49d731edd6cbad58f00175d83ee216d0e3998d.webm b/apps/web/playwright-report/data/4e49d731edd6cbad58f00175d83ee216d0e3998d.webm new file mode 100644 index 00000000..7d6e4663 Binary files /dev/null and b/apps/web/playwright-report/data/4e49d731edd6cbad58f00175d83ee216d0e3998d.webm differ diff --git a/apps/web/playwright-report/data/516b57fbef8c19f7d2eb5d653146f086c7d644dc.webm b/apps/web/playwright-report/data/516b57fbef8c19f7d2eb5d653146f086c7d644dc.webm new file mode 100644 index 00000000..41fd4977 Binary files /dev/null and b/apps/web/playwright-report/data/516b57fbef8c19f7d2eb5d653146f086c7d644dc.webm differ diff --git a/apps/web/playwright-report/data/516e26842435367830955a1e765c91378131aac7.webm b/apps/web/playwright-report/data/516e26842435367830955a1e765c91378131aac7.webm new file mode 100644 index 00000000..50a1cdc1 Binary files /dev/null and b/apps/web/playwright-report/data/516e26842435367830955a1e765c91378131aac7.webm differ diff --git a/apps/web/playwright-report/data/5366c5ca31d96a3b2b0b89113adb5ee240d4d12a.webm b/apps/web/playwright-report/data/5366c5ca31d96a3b2b0b89113adb5ee240d4d12a.webm new file mode 100644 index 00000000..babf66b7 Binary files /dev/null and b/apps/web/playwright-report/data/5366c5ca31d96a3b2b0b89113adb5ee240d4d12a.webm differ diff --git a/apps/web/playwright-report/data/5fc3b60ac7d7c39e513df45e4488f03daca48b38.webm b/apps/web/playwright-report/data/5fc3b60ac7d7c39e513df45e4488f03daca48b38.webm new file mode 100644 index 00000000..e7acab28 Binary files /dev/null and b/apps/web/playwright-report/data/5fc3b60ac7d7c39e513df45e4488f03daca48b38.webm differ diff --git a/apps/web/playwright-report/data/67cc08c03597b1550dc9a7baaa888c9e1271869c.png b/apps/web/playwright-report/data/67cc08c03597b1550dc9a7baaa888c9e1271869c.png new file mode 100644 index 00000000..6347102e Binary files /dev/null and b/apps/web/playwright-report/data/67cc08c03597b1550dc9a7baaa888c9e1271869c.png differ diff --git a/apps/web/playwright-report/data/7ad96703d03f24c2386692cae54ba21c9eecf56d.png b/apps/web/playwright-report/data/7ad96703d03f24c2386692cae54ba21c9eecf56d.png new file mode 100644 index 00000000..7fb76b60 Binary files /dev/null and b/apps/web/playwright-report/data/7ad96703d03f24c2386692cae54ba21c9eecf56d.png differ diff --git a/apps/web/playwright-report/data/7c85ae1d9e38cc5df90ff15037ed91e2b8e80e7e.webm b/apps/web/playwright-report/data/7c85ae1d9e38cc5df90ff15037ed91e2b8e80e7e.webm new file mode 100644 index 00000000..2ce1ecc7 Binary files /dev/null and b/apps/web/playwright-report/data/7c85ae1d9e38cc5df90ff15037ed91e2b8e80e7e.webm differ diff --git a/apps/web/playwright-report/data/845d87b3102993fabce170c250aa167af5444e22.webm b/apps/web/playwright-report/data/845d87b3102993fabce170c250aa167af5444e22.webm new file mode 100644 index 00000000..17183aa4 Binary files /dev/null and b/apps/web/playwright-report/data/845d87b3102993fabce170c250aa167af5444e22.webm differ diff --git a/apps/web/playwright-report/data/8b28ba28a5a7a881f3e60c9c202310a48ad0452e.webm b/apps/web/playwright-report/data/8b28ba28a5a7a881f3e60c9c202310a48ad0452e.webm new file mode 100644 index 00000000..afc64aad Binary files /dev/null and b/apps/web/playwright-report/data/8b28ba28a5a7a881f3e60c9c202310a48ad0452e.webm differ diff --git a/apps/web/playwright-report/data/8c826519ff8657d0c01cdb118746fcdd76ccf254.png b/apps/web/playwright-report/data/8c826519ff8657d0c01cdb118746fcdd76ccf254.png new file mode 100644 index 00000000..bb002b46 Binary files /dev/null and b/apps/web/playwright-report/data/8c826519ff8657d0c01cdb118746fcdd76ccf254.png differ diff --git a/apps/web/playwright-report/data/97ff76a3a1cd9466bcc9a14bf990b126ecbacc02.png b/apps/web/playwright-report/data/97ff76a3a1cd9466bcc9a14bf990b126ecbacc02.png new file mode 100644 index 00000000..15961131 Binary files /dev/null and b/apps/web/playwright-report/data/97ff76a3a1cd9466bcc9a14bf990b126ecbacc02.png differ diff --git a/apps/web/playwright-report/data/998e5c81518b2f62abcf1bc5ef81c2c8ff3ab582.webm b/apps/web/playwright-report/data/998e5c81518b2f62abcf1bc5ef81c2c8ff3ab582.webm new file mode 100644 index 00000000..ed1e18db Binary files /dev/null and b/apps/web/playwright-report/data/998e5c81518b2f62abcf1bc5ef81c2c8ff3ab582.webm differ diff --git a/apps/web/playwright-report/data/9cc0b679da726f8bf3030dea4cebe84947f55014.png b/apps/web/playwright-report/data/9cc0b679da726f8bf3030dea4cebe84947f55014.png new file mode 100644 index 00000000..d72f1bad Binary files /dev/null and b/apps/web/playwright-report/data/9cc0b679da726f8bf3030dea4cebe84947f55014.png differ diff --git a/apps/web/playwright-report/data/9f4baf7290d7a05d1f21107b4e9389e7724daa19.webm b/apps/web/playwright-report/data/9f4baf7290d7a05d1f21107b4e9389e7724daa19.webm new file mode 100644 index 00000000..348ce571 Binary files /dev/null and b/apps/web/playwright-report/data/9f4baf7290d7a05d1f21107b4e9389e7724daa19.webm differ diff --git a/apps/web/playwright-report/data/a1993b883b38a80934775e357269ab6577b8b6fb.png b/apps/web/playwright-report/data/a1993b883b38a80934775e357269ab6577b8b6fb.png new file mode 100644 index 00000000..bef5ddad Binary files /dev/null and b/apps/web/playwright-report/data/a1993b883b38a80934775e357269ab6577b8b6fb.png differ diff --git a/apps/web/playwright-report/data/aff45a35b472100e10a8db1bcd79d9fa7aeb3188.png b/apps/web/playwright-report/data/aff45a35b472100e10a8db1bcd79d9fa7aeb3188.png new file mode 100644 index 00000000..067df910 Binary files /dev/null and b/apps/web/playwright-report/data/aff45a35b472100e10a8db1bcd79d9fa7aeb3188.png differ diff --git a/apps/web/playwright-report/data/b1aec01b5660a4d85b10dba0bb4c593abec2e169.webm b/apps/web/playwright-report/data/b1aec01b5660a4d85b10dba0bb4c593abec2e169.webm new file mode 100644 index 00000000..4e239bd9 Binary files /dev/null and b/apps/web/playwright-report/data/b1aec01b5660a4d85b10dba0bb4c593abec2e169.webm differ diff --git a/apps/web/playwright-report/data/b74bfad9aebbc8a76918908e3b1c4c5e6713bfa6.png b/apps/web/playwright-report/data/b74bfad9aebbc8a76918908e3b1c4c5e6713bfa6.png new file mode 100644 index 00000000..4a8c5351 Binary files /dev/null and b/apps/web/playwright-report/data/b74bfad9aebbc8a76918908e3b1c4c5e6713bfa6.png differ diff --git a/apps/web/playwright-report/data/bbdcb161ad33657ae50733eba844f232efa4d4f7.png b/apps/web/playwright-report/data/bbdcb161ad33657ae50733eba844f232efa4d4f7.png new file mode 100644 index 00000000..5d7d0bb7 Binary files /dev/null and b/apps/web/playwright-report/data/bbdcb161ad33657ae50733eba844f232efa4d4f7.png differ diff --git a/apps/web/playwright-report/data/c48e7a4dbcf2289d90ff0614a195732e97d154bd.webm b/apps/web/playwright-report/data/c48e7a4dbcf2289d90ff0614a195732e97d154bd.webm new file mode 100644 index 00000000..6536b578 Binary files /dev/null and b/apps/web/playwright-report/data/c48e7a4dbcf2289d90ff0614a195732e97d154bd.webm differ diff --git a/apps/web/playwright-report/data/dc4685864a8de2c4ce23ff5bad13b1e49c4a88f2.png b/apps/web/playwright-report/data/dc4685864a8de2c4ce23ff5bad13b1e49c4a88f2.png new file mode 100644 index 00000000..f0cef069 Binary files /dev/null and b/apps/web/playwright-report/data/dc4685864a8de2c4ce23ff5bad13b1e49c4a88f2.png differ diff --git a/apps/web/playwright-report/data/dd1d700c5bbba415f4459b1fd7e85f81fa25071f.png b/apps/web/playwright-report/data/dd1d700c5bbba415f4459b1fd7e85f81fa25071f.png new file mode 100644 index 00000000..968a8251 Binary files /dev/null and b/apps/web/playwright-report/data/dd1d700c5bbba415f4459b1fd7e85f81fa25071f.png differ diff --git a/apps/web/playwright-report/data/efaf06b749353a3ea3b11ae60c7ef9a0db7e64ef.png b/apps/web/playwright-report/data/efaf06b749353a3ea3b11ae60c7ef9a0db7e64ef.png new file mode 100644 index 00000000..c27a9c8b Binary files /dev/null and b/apps/web/playwright-report/data/efaf06b749353a3ea3b11ae60c7ef9a0db7e64ef.png differ diff --git a/apps/web/playwright-report/data/f3111f8da1f11251d3e81d0b8798a32a05308884.webm b/apps/web/playwright-report/data/f3111f8da1f11251d3e81d0b8798a32a05308884.webm new file mode 100644 index 00000000..6b47d193 Binary files /dev/null and b/apps/web/playwright-report/data/f3111f8da1f11251d3e81d0b8798a32a05308884.webm differ diff --git a/apps/web/playwright-report/data/ff47a4d142785ef6a7191d7488ef2805a4078a9f.png b/apps/web/playwright-report/data/ff47a4d142785ef6a7191d7488ef2805a4078a9f.png new file mode 100644 index 00000000..77d3c6f5 Binary files /dev/null and b/apps/web/playwright-report/data/ff47a4d142785ef6a7191d7488ef2805a4078a9f.png differ diff --git a/apps/web/test-results/html-report/index.html b/apps/web/playwright-report/index.html similarity index 93% rename from apps/web/test-results/html-report/index.html rename to apps/web/playwright-report/index.html index ee336ec1..45f3cbaa 100644 --- a/apps/web/test-results/html-report/index.html +++ b/apps/web/playwright-report/index.html @@ -82,4 +82,4 @@ Error generating stack: `+a.message+`