fix: remove duplicate Verbose field and use raw JSON parsing for handlers

- Remove Verbose from Config struct (inherited from RestConf)
- Remove Verbose from yaml config to fix conflict
- Use raw JSON parsing in handlers for interface{} Content field
- Fix config tests
This commit is contained in:
王性驊 2026-04-04 12:49:32 +08:00
parent 9e2a10b614
commit 3dc49bfc7d
7 changed files with 58 additions and 69 deletions

View File

@ -1,40 +0,0 @@
# ──────────────────────────────────────────────────────────────
# cursor-api-proxy 設定範例
# 複製為 .env 後填入你的設定cp .env.example .env
# ──────────────────────────────────────────────────────────────
# ── 伺服器設定 ────────────────────────────────────────────────
# Docker 模式固定使用 0.0.0.0;本機直接執行時可改 127.0.0.1
CURSOR_BRIDGE_HOST=0.0.0.0
CURSOR_BRIDGE_PORT=8766
CURSOR_BRIDGE_API_KEY=
CURSOR_BRIDGE_TIMEOUT_MS=3600000
CURSOR_BRIDGE_MULTI_PORT=false
CURSOR_BRIDGE_VERBOSE=false
# ── Agent / 模型設定 ──────────────────────────────────────────
# Docker 模式:容器內 agent 路徑(預設掛載至 /usr/local/bin/agent
CURSOR_AGENT_BIN=/usr/local/bin/agent
CURSOR_BRIDGE_DEFAULT_MODEL=auto
CURSOR_BRIDGE_STRICT_MODEL=true
CURSOR_BRIDGE_MAX_MODE=false
CURSOR_BRIDGE_FORCE=false
CURSOR_BRIDGE_APPROVE_MCPS=false
# ── 工作區與帳號 ──────────────────────────────────────────────
CURSOR_BRIDGE_WORKSPACE=/workspace
CURSOR_BRIDGE_CHAT_ONLY_WORKSPACE=true
# 多帳號設定目錄(逗號分隔),留空則自動探索 ~/.cursor-api-proxy/accounts/
CURSOR_CONFIG_DIRS=
# ── TLS / HTTPS選用────────────────────────────────────────
CURSOR_BRIDGE_TLS_CERT=
CURSOR_BRIDGE_TLS_KEY=
# ── Docker 專用:宿主機路徑對映 ──────────────────────────────
# 宿主機上 Cursor agent 二進位檔的實際路徑
CURSOR_AGENT_HOST_BIN=/usr/local/bin/agent
# 宿主機上的帳號資料目錄(會掛載至容器的 /root/.cursor-api-proxy
CURSOR_ACCOUNTS_DIR=~/.cursor-api-proxy
# 宿主機上要掛載進容器的工作區目錄
WORKSPACE_DIR=/tmp/workspace

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
.idea/ .idea/
cursor-api-proxy* cursor-api-proxy*
.env .env
.opencode

View File

@ -1,12 +1,12 @@
Name: cursor-api-proxy Name: api-proxy
Host: 0.0.0.0 Host: 0.0.0.0
Port: 8080 Port: 8766
# Cursor Agent 配置 # Cursor Agent 配置
AgentBin: cursor AgentBin: cursor
DefaultModel: claude-3.5-sonnet DefaultModel: claude-4.5-sonnet
Provider: cursor Provider: cursor
TimeoutMs: 300000 TimeoutMs: 36000000
# 多帳號池配置 # 多帳號池配置
ConfigDirs: ConfigDirs:
@ -19,7 +19,7 @@ TLSKeyPath: ""
# 日誌 # 日誌
SessionsLogPath: "" SessionsLogPath: ""
Verbose: false # Verbose 使用 RestConf 預設值
# Gemini Web Provider 配置 # Gemini Web Provider 配置
GeminiAccountDir: ~/.cursor-api-proxy/gemini-accounts GeminiAccountDir: ~/.cursor-api-proxy/gemini-accounts

View File

@ -29,7 +29,7 @@ type Config struct {
// 日誌 // 日誌
SessionsLogPath string SessionsLogPath string
Verbose bool // Verbose is inherited from rest.RestConf
// Gemini // Gemini
GeminiAccountDir string GeminiAccountDir string

View File

@ -30,9 +30,7 @@ func TestConfigToBridgeConfigWithValues(t *testing.T) {
StrictModel: true, StrictModel: true,
Workspace: "/tmp/test", Workspace: "/tmp/test",
ChatOnlyWorkspace: true, ChatOnlyWorkspace: true,
Verbose: true,
GeminiAccountDir: "/tmp/gemini", GeminiAccountDir: "/tmp/gemini",
GeminiBrowserVisible: true,
GeminiMaxSessions: 5, GeminiMaxSessions: 5,
} }

View File

@ -4,20 +4,33 @@
package chat package chat
import ( import (
"encoding/json"
"io"
"net/http" "net/http"
"cursor-api-proxy/internal/logic/chat" "cursor-api-proxy/internal/logic/chat"
"cursor-api-proxy/internal/svc" "cursor-api-proxy/internal/svc"
"cursor-api-proxy/internal/types" "cursor-api-proxy/internal/types"
"github.com/zeromicro/go-zero/rest/httpx" "cursor-api-proxy/pkg/infrastructure/httputil"
) )
func AnthropicMessagesHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { func AnthropicMessagesHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
// Read raw body first
rawBody, err := io.ReadAll(r.Body)
if err != nil {
httputil.WriteJSON(w, 400, map[string]interface{}{
"error": map[string]string{"type": "invalid_request_error", "message": "failed to read body"},
}, nil)
return
}
var req types.AnthropicRequest var req types.AnthropicRequest
if err := httpx.Parse(r, &req); err != nil { if err := json.Unmarshal(rawBody, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err) httputil.WriteJSON(w, 400, map[string]interface{}{
"error": map[string]string{"type": "invalid_request_error", "message": "invalid JSON body"},
}, nil)
return return
} }
@ -31,7 +44,9 @@ func AnthropicMessagesHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
} else { } else {
err := l.AnthropicMessages(&req, w, r.Method, r.URL.Path) err := l.AnthropicMessages(&req, w, r.Method, r.URL.Path)
if err != nil { if err != nil {
httpx.ErrorCtx(r.Context(), w, err) httputil.WriteJSON(w, 500, map[string]interface{}{
"error": map[string]string{"type": "api_error", "message": err.Error()},
}, nil)
} }
} }
} }

View File

@ -4,20 +4,33 @@
package chat package chat
import ( import (
"encoding/json"
"io"
"net/http" "net/http"
"cursor-api-proxy/internal/logic/chat" "cursor-api-proxy/internal/logic/chat"
"cursor-api-proxy/internal/svc" "cursor-api-proxy/internal/svc"
"cursor-api-proxy/internal/types" "cursor-api-proxy/internal/types"
"github.com/zeromicro/go-zero/rest/httpx" "cursor-api-proxy/pkg/infrastructure/httputil"
) )
func ChatCompletionsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { func ChatCompletionsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
// Read raw body first
rawBody, err := io.ReadAll(r.Body)
if err != nil {
httputil.WriteJSON(w, 400, map[string]interface{}{
"error": map[string]string{"message": "failed to read body", "code": "bad_request"},
}, nil)
return
}
var req types.ChatCompletionRequest var req types.ChatCompletionRequest
if err := httpx.Parse(r, &req); err != nil { if err := json.Unmarshal(rawBody, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err) httputil.WriteJSON(w, 400, map[string]interface{}{
"error": map[string]string{"message": "invalid JSON body", "code": "bad_request"},
}, nil)
return return
} }
@ -31,9 +44,11 @@ func ChatCompletionsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
} else { } else {
resp, err := l.ChatCompletions(&req) resp, err := l.ChatCompletions(&req)
if err != nil { if err != nil {
httpx.ErrorCtx(r.Context(), w, err) httputil.WriteJSON(w, 500, map[string]interface{}{
"error": map[string]string{"message": err.Error(), "code": "internal_error"},
}, nil)
} else { } else {
httpx.OkJsonCtx(r.Context(), w, resp) httputil.WriteJSON(w, 200, resp, nil)
} }
} }
} }