From 8c2983b70ab03b86f65ce7f65ee3ea651e9cfb7b Mon Sep 17 00:00:00 2001 From: OG T Date: Thu, 9 Apr 2026 19:50:48 +0800 Subject: [PATCH] =?UTF-8?q?fix(api+web):=20CORS=20=E8=A3=9C=20K3s=20NodePo?= =?UTF-8?q?rt=20origins=20+=20sign=20=E8=A3=9C=20signer=5Fid/name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CORS (config.py): - 補 http://192.168.0.125:32335 (K3s VIP NodePort) - 補 http://192.168.0.120:32335 + 121:32335 (K3s nodes) - 修前: 內網瀏覽器開 :32335 打 API 全 CORS blocked (incidents Failed to fetch / monitoring 無法連線根因) sign body (pending-approvals-card.tsx): - signer: 'web-ui' → signer_id: CURRENT_USER.id + signer_name: CURRENT_USER.name - 修前: POST /approvals/{id}/sign 回 403 (缺必填欄位 422 誤報為 403) — 實際是 422 Field required signer_id + signer_name Co-Authored-By: Claude Sonnet 4.6 --- apps/api/src/core/config.py | 3 +++ apps/web/src/components/shared/pending-approvals-card.tsx | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/api/src/core/config.py b/apps/api/src/core/config.py index 99e38a16..2ca78a20 100644 --- a/apps/api/src/core/config.py +++ b/apps/api/src/core/config.py @@ -123,6 +123,9 @@ class Settings(BaseSettings): "http://localhost:3333", "http://192.168.0.168:3000", # 168 MacBook 本機開發 "http://192.168.0.188:3000", # 188 本機開發 + "http://192.168.0.125:32335", # K3s VIP NodePort (staging/QA) + "http://192.168.0.120:32335", # K3s node-1 NodePort + "http://192.168.0.121:32335", # K3s node-2 NodePort "https://awoooi.wooo.work", ], description="Allowed CORS origins - NO wildcards allowed", diff --git a/apps/web/src/components/shared/pending-approvals-card.tsx b/apps/web/src/components/shared/pending-approvals-card.tsx index 09b2cb38..d8ea3eb4 100644 --- a/apps/web/src/components/shared/pending-approvals-card.tsx +++ b/apps/web/src/components/shared/pending-approvals-card.tsx @@ -11,6 +11,7 @@ import { useTranslations } from 'next-intl' import { useRouter } from 'next/navigation' import { useLocale } from 'next-intl' import { useCSRF } from '@/hooks/useCSRF' +import { CURRENT_USER } from '@/lib/constants/user' const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? '' @@ -85,7 +86,7 @@ export function PendingApprovalsCard() { onClick={() => { setActionError(null) setActioningId(ap.id) - fetch(`${API_BASE}/api/v1/approvals/${ap.id}/sign`, { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json', ...getHeaders() }, body: JSON.stringify({ signer: 'web-ui' }) }) + fetch(`${API_BASE}/api/v1/approvals/${ap.id}/sign`, { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json', ...getHeaders() }, body: JSON.stringify({ signer_id: CURRENT_USER.id, signer_name: CURRENT_USER.name }) }) .then(r => { if (!r.ok) throw new Error(`${r.status}`); return r }) .then(() => setApprovals(prev => prev.filter(x => x.id !== ap.id))) .catch(e => setActionError(`approve failed: ${e.message}`))