From a028b44c84a8f1ce708c16a2d261fa99d820f604 Mon Sep 17 00:00:00 2001 From: OG T Date: Tue, 31 Mar 2026 19:09:25 +0800 Subject: [PATCH] =?UTF-8?q?fix(web):=20Y/n=20=E6=8C=89=E9=88=95=20CSRF=20T?= =?UTF-8?q?oken=20=E7=BC=BA=E5=A4=B1=E4=BF=AE=E5=BE=A9=20(Phase=2022=20P0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修復問題: - 按鈕點擊無反應:CSRF token 載入中或失敗時,buttons 現在會被 disabled - 增加 toast.error() 提示:當 token 缺失時,顯示「安全驗證失敗」提示 變更: - handleSign: 新增 toast.error() 當 csrfToken 為 null - confirmReject: 新增 toast.error() 當 csrfToken 為 null - ApprovalCard isLoading: 擴展為 signing || csrfLoading || csrfError Co-Authored-By: Claude Opus 4.5 --- apps/web/src/components/approval/live-approval-panel.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/web/src/components/approval/live-approval-panel.tsx b/apps/web/src/components/approval/live-approval-panel.tsx index 30264c2e..f3c06af7 100644 --- a/apps/web/src/components/approval/live-approval-panel.tsx +++ b/apps/web/src/components/approval/live-approval-panel.tsx @@ -28,6 +28,7 @@ import { GlassCardTitle, GlassCardContent, } from '@/components/ui/glass-card' +import { toast } from '@/components/ui/toast' import { StatusOrb } from '@/components/ui/status-orb' import { cn } from '@/lib/utils' import { ShieldX, Lock, AlertTriangle } from 'lucide-react' @@ -154,6 +155,7 @@ export function LiveApprovalPanel({ // Phase 20: CSRF 保護 - 必須有 Token 才能簽核 if (!csrfToken) { console.error('[HITL] CSRF token not available, cannot sign') + toast.error('安全驗證失敗,請重新整理頁面後再試') return } @@ -209,6 +211,7 @@ export function LiveApprovalPanel({ // Phase 20: CSRF 保護 - 必須有 Token 才能拒絕 if (!csrfToken) { console.error('[HITL] CSRF token not available, cannot reject') + toast.error('安全驗證失敗,請重新整理頁面後再試') return } @@ -334,7 +337,7 @@ export function LiveApprovalPanel({ onApprove={() => handleSign(approval.id, approval.riskLevel)} onReject={() => handleReject(approval.id)} holdDuration={2000} - isLoading={signingStates[approval.id] === 'signing'} + isLoading={signingStates[approval.id] === 'signing' || csrfLoading || !!csrfError} readOnly={isResolved} />