fix(web): openclaw-state-machine 補上 CSRF Token (P0 根本原因)
All checks were successful
E2E Health Check / e2e-health (push) Successful in 15s

根本原因: 首頁用的是 openclaw-state-machine.tsx 而非 LiveApprovalPanel
該元件的 handleApprove 完全沒有 CSRF token 和 credentials: include
導致後端回傳 "CSRF token cookie missing" → 按鈕沒有任何反應

修復:
- import useCSRF hook
- handleApprove 加上 X-CSRF-Token header
- fetch 加上 credentials: 'include'
- useCallback deps 加上 csrfToken

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-03-31 21:50:34 +08:00
parent 6da240219d
commit 589f2fc4c7

View File

@@ -19,6 +19,7 @@
import { useState, useEffect, useCallback, useRef } from 'react'
import { useTranslations } from 'next-intl'
import { cn } from '@/lib/utils'
import { useCSRF } from '@/hooks/useCSRF'
import { OpenClawPanel, type OpenClawStatus } from './openclaw-panel'
import type { ThinkingStream as _ThinkingStream, DEFAULT_THINKING_MESSAGES as _DEFAULT_THINKING_MESSAGES } from './thinking-stream'
import { ApprovalCard, type ApprovalRequest } from '@/components/approval/approval-card'
@@ -94,6 +95,7 @@ export function OpenClawStateMachine({
className,
}: OpenClawStateMachineProps) {
const t = useTranslations()
const { csrfToken } = useCSRF()
// State
const [machineState, setMachineState] = useState<MachineState>('idle')
@@ -199,9 +201,13 @@ export function OpenClawStateMachine({
signerCounter.current++
try {
const headers: Record<string, string> = { 'Content-Type': 'application/json' }
if (csrfToken) headers['X-CSRF-Token'] = csrfToken
const response = await fetch(`${apiBaseUrl}/api/v1/approvals/${approvalId}/sign`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
headers,
credentials: 'include',
body: JSON.stringify({
signer_id: signer.id,
signer_name: signer.name,
@@ -223,7 +229,7 @@ export function OpenClawStateMachine({
setError(err instanceof Error ? err.message : 'Sign failed')
return -1
}
}, [fetchPendingApprovals])
}, [fetchPendingApprovals, csrfToken])
// ==========================================================================
// API: Reject Request