Files
hr-performance-system/CORS_FIX_GUIDE.md
2025-12-04 00:07:38 +08:00

9.0 KiB
Raw Permalink Blame History

CORS 錯誤解決方案

🔴 您遇到的錯誤

錯誤訊息 1: CORS Policy

Access to fetch at 'https://api.anthropic.com/v1/messages' from origin 'http://127.0.0.1:5000'
has been blocked by CORS policy: Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header is present on the requested resource.

錯誤訊息 2: Storage Access

Uncaught (in promise) Error: Access to storage is not allowed from this context.

錯誤訊息 3: Failed to Fetch

POST https://api.anthropic.com/v1/messages net::ERR_FAILED
TypeError: Failed to fetch

🤔 為什麼會發生這些錯誤?

1. CORS跨來源資源共用限制

  • 瀏覽器的安全機制,防止網頁向不同來源的伺服器發送請求
  • Anthropic API 不允許直接從瀏覽器呼叫(沒有設定 CORS 標頭)
  • 這是為了保護 API 金鑰不被暴露

2. API 金鑰安全問題

// ❌ 危險!前端直接呼叫會暴露 API 金鑰
const apiKey = 'sk-ant-xxxxx'; // 所有人都能看到
fetch('https://api.anthropic.com/v1/messages', {
  headers: { 'x-api-key': apiKey }
});

3. Storage 限制

  • 使用 file:// 協定開啟的 HTML 檔案無法使用 localStorage
  • 必須透過 HTTP 伺服器運行

正確的解決方案

架構圖

┌─────────────┐         ┌─────────────┐         ┌─────────────┐
│             │         │             │         │             │
│   前端      │ ─────▶  │   後端      │ ─────▶  │  Claude API │
│  (瀏覽器)   │  HTTP   │  (Express)  │  HTTPS  │  (Anthropic)│
│             │         │             │         │             │
└─────────────┘         └─────────────┘         └─────────────┘
                         ↑
                         │ API 金鑰
                         │ 安全儲存

步驟 1: 安裝依賴

npm install

這會安裝:

  • express - Web 伺服器框架
  • cors - CORS 中介層
  • axios - HTTP 客戶端
  • dotenv - 環境變數管理
  • 其他依賴...

步驟 2: 設定環境變數

編輯 .env 檔案,加入您的 Claude API 金鑰:

# Claude API
CLAUDE_API_KEY=sk-ant-api03-xxxxxxxxxxxxx
CLAUDE_API_URL=https://api.anthropic.com/v1
CLAUDE_MODEL=claude-3-5-sonnet-20241022

⚠️ 重要.env 檔案已被 .gitignore 排除,不會被提交到 Git

步驟 3: 啟動後端伺服器

# 開發模式(自動重啟)
npm run dev

# 或生產模式
npm start

您應該會看到:

==================================================
🚀 HR Performance System API Server
==================================================
📡 Server running on: http://localhost:3000
🌍 Environment: development
📅 Started at: 2025-12-03 ...
==================================================

📚 Available endpoints:
   GET  /               - API information
   GET  /health         - Health check
   POST /api/llm/test/* - Test LLM connections
   POST /api/llm/generate - Generate content with LLM

✨ Server is ready to accept connections!

步驟 4: 測試 API

開啟瀏覽器訪問:

http://localhost:3000/api-proxy-example.html

或使用 curl 測試:

# 測試 Claude 連線
curl -X POST http://localhost:3000/api/llm/test/claude

# 生成內容
curl -X POST http://localhost:3000/api/llm/generate \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "介紹 HR 績效評核系統",
    "provider": "claude",
    "options": {
      "temperature": 0.7,
      "maxTokens": 200
    }
  }'

📝 前端程式碼範例

錯誤的做法(直接呼叫)

// 不要這樣做!會被 CORS 阻擋
async function callClaudeAPI() {
  const response = await fetch('https://api.anthropic.com/v1/messages', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-api-key': 'sk-ant-xxxxx', // ❌ API 金鑰暴露!
      'anthropic-version': '2023-06-01'
    },
    body: JSON.stringify({
      model: 'claude-3-5-sonnet-20241022',
      messages: [{ role: 'user', content: 'Hello' }]
    })
  });
}

正確的做法(透過後端代理)

// 透過後端 API 呼叫
async function generateContent(prompt) {
  try {
    const response = await fetch('http://localhost:3000/api/llm/generate', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        prompt: prompt,
        provider: 'claude',
        options: {
          temperature: 0.7,
          maxTokens: 2000
        }
      })
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const result = await response.json();

    if (result.success) {
      console.log('生成的內容:', result.content);
      return result.content;
    } else {
      throw new Error(result.error?.message || '生成失敗');
    }
  } catch (error) {
    console.error('錯誤:', error);
    // 顯示錯誤訊息給使用者
    showErrorModal({
      title: 'API 錯誤',
      message: error.message
    });
  }
}

// 使用範例
generateContent('請介紹 HR 績效評核系統的四卡循環');

🎯 API 端點說明

測試連線

// 測試 Claude API
POST http://localhost:3000/api/llm/test/claude

// 測試所有 LLM
POST http://localhost:3000/api/llm/test/all

// 回應格式
{
  "success": true,
  "message": "Claude API connection successful",
  "provider": "claude",
  "model": "claude-3-5-sonnet-20241022"
}

生成內容

POST http://localhost:3000/api/llm/generate
Content-Type: application/json

{
  "prompt": "你的提示內容",
  "provider": "claude",  // 可選: claude, gemini, deepseek, openai
  "options": {
    "temperature": 0.7,  // 可選: 0.0-1.0
    "maxTokens": 2000    // 可選: 最大生成長度
  }
}

// 回應格式
{
  "success": true,
  "content": "生成的內容...",
  "provider": "claude"
}

Help Me AI智能填寫

POST http://localhost:3000/api/llm/help-me-fill
Content-Type: application/json

{
  "cardType": "performance",
  "cardId": "PF-2024-001",
  "filledFields": {
    "kra1_title": "產品上市時程",
    "kra1_weight": 40
  },
  "emptyFields": [
    "kra1_output",
    "kra1_self_note"
  ],
  "context": {
    "roleCard": {...},
    "competencyCard": {...}
  }
}

// 回應格式
{
  "success": true,
  "filledCount": 2,
  "suggestions": {
    "kra1_output": "Q1 完成 MVP 開發...",
    "kra1_self_note": "超額達成目標..."
  }
}

🔒 安全性考量

1. API 金鑰保護

  • 儲存在 .env 檔案
  • 不提交到 Git已在 .gitignore 中)
  • 只在後端使用
  • 絕不在前端程式碼中暴露

2. CORS 設定

// server.js 中的 CORS 設定
app.use(cors({
  origin: process.env.FRONTEND_URL || '*',  // 生產環境應限制來源
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true
}));

3. 錯誤處理

  • 統一的錯誤格式
  • 不洩露敏感資訊
  • 記錄錯誤日誌

🐛 常見問題

Q1: 為什麼還是顯示 CORS 錯誤?

A: 確認以下事項:

  1. 後端伺服器是否正在運行?(npm run dev
  2. 前端是否呼叫正確的 API 地址?(http://localhost:3000
  3. 瀏覽器是否快取了舊的錯誤?(清除快取或使用無痕模式)

Q2: API 回傳 "Claude API key not configured"

A: 檢查:

  1. .env 檔案中是否設定了 CLAUDE_API_KEY
  2. 環境變數的值是否正確
  3. 重新啟動伺服器以載入新的環境變數

Q3: 連線超時

A: 可能原因:

  1. 網路連線問題
  2. API 金鑰無效
  3. Anthropic 服務暫時無法使用
  4. 超時設定太短(預設 30 秒)

Q4: 如何在生產環境部署?

A: 部署步驟:

  1. 設定環境變數(不要使用 .env 檔案)
  2. 限制 CORS 來源為您的網域
  3. 使用 HTTPS
  4. 設定適當的速率限制
  5. 記錄和監控 API 使用情況

📚 相關檔案

🎉 完成!

現在您可以安全地透過後端 API 呼叫 Claude 和其他 LLM 服務,不再有 CORS 錯誤!

如有任何問題,請參考:


最後更新: 2025-12-03 問題回報: https://gitea.theaken.com/donald/hr-performance-system/issues