Skip to content

Commit d72a820

Browse files
committed
feat:
1 parent 0ad7c04 commit d72a820

5 files changed

Lines changed: 189 additions & 10 deletions

File tree

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* API 代理路由
3+
* 解决 HTTPS 前端无法请求 HTTP 后端的混合内容问题
4+
*/
5+
6+
import { NextRequest, NextResponse } from 'next/server'
7+
8+
// 后端服务器地址
9+
const BACKEND_URL = 'http://83.229.126.150:3001'
10+
11+
export async function GET(
12+
request: NextRequest,
13+
{ params }: { params: Promise<{ path: string[] }> }
14+
) {
15+
const { path } = await params
16+
const targetPath = path.join('/')
17+
const searchParams = request.nextUrl.searchParams.toString()
18+
const url = `${BACKEND_URL}/api/${targetPath}${searchParams ? `?${searchParams}` : ''}`
19+
20+
try {
21+
const response = await fetch(url, {
22+
headers: {
23+
'Content-Type': 'application/json',
24+
},
25+
})
26+
27+
const data = await response.json()
28+
return NextResponse.json(data, { status: response.status })
29+
} catch (error) {
30+
console.error('[API Proxy] GET error:', error)
31+
return NextResponse.json(
32+
{ error: 'Failed to fetch from backend' },
33+
{ status: 502 }
34+
)
35+
}
36+
}
37+
38+
export async function POST(
39+
request: NextRequest,
40+
{ params }: { params: Promise<{ path: string[] }> }
41+
) {
42+
const { path } = await params
43+
const targetPath = path.join('/')
44+
const url = `${BACKEND_URL}/api/${targetPath}`
45+
46+
try {
47+
const body = await request.json()
48+
const response = await fetch(url, {
49+
method: 'POST',
50+
headers: {
51+
'Content-Type': 'application/json',
52+
},
53+
body: JSON.stringify(body),
54+
})
55+
56+
const data = await response.json()
57+
return NextResponse.json(data, { status: response.status })
58+
} catch (error) {
59+
console.error('[API Proxy] POST error:', error)
60+
return NextResponse.json(
61+
{ error: 'Failed to fetch from backend' },
62+
{ status: 502 }
63+
)
64+
}
65+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* Socket.io 轮询代理
3+
* 通过 HTTP 轮询代理 Socket.io 请求,解决 HTTPS 混合内容问题
4+
*/
5+
6+
import { NextRequest, NextResponse } from 'next/server'
7+
8+
const BACKEND_URL = 'http://83.229.126.150:3001'
9+
10+
export async function GET(request: NextRequest) {
11+
const searchParams = request.nextUrl.searchParams.toString()
12+
const url = `${BACKEND_URL}/socket.io/${searchParams ? `?${searchParams}` : ''}`
13+
14+
try {
15+
const response = await fetch(url, {
16+
headers: {
17+
'Content-Type': 'application/json',
18+
},
19+
})
20+
21+
const contentType = response.headers.get('content-type') || ''
22+
23+
if (contentType.includes('application/json')) {
24+
const data = await response.json()
25+
return NextResponse.json(data, { status: response.status })
26+
} else {
27+
const text = await response.text()
28+
return new NextResponse(text, {
29+
status: response.status,
30+
headers: {
31+
'Content-Type': contentType,
32+
},
33+
})
34+
}
35+
} catch (error) {
36+
console.error('[Socket.io Proxy] GET error:', error)
37+
return NextResponse.json(
38+
{ error: 'Failed to connect to backend' },
39+
{ status: 502 }
40+
)
41+
}
42+
}
43+
44+
export async function POST(request: NextRequest) {
45+
const searchParams = request.nextUrl.searchParams.toString()
46+
const url = `${BACKEND_URL}/socket.io/${searchParams ? `?${searchParams}` : ''}`
47+
48+
try {
49+
const body = await request.text()
50+
const response = await fetch(url, {
51+
method: 'POST',
52+
headers: {
53+
'Content-Type': 'text/plain;charset=UTF-8',
54+
},
55+
body,
56+
})
57+
58+
const contentType = response.headers.get('content-type') || ''
59+
const text = await response.text()
60+
61+
return new NextResponse(text, {
62+
status: response.status,
63+
headers: {
64+
'Content-Type': contentType,
65+
},
66+
})
67+
} catch (error) {
68+
console.error('[Socket.io Proxy] POST error:', error)
69+
return NextResponse.json(
70+
{ error: 'Failed to connect to backend' },
71+
{ status: 502 }
72+
)
73+
}
74+
}

frontend/src/config/env.ts

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,46 @@
33
* 用于 ModelScope 等不支持环境变量的部署环境
44
*/
55

6-
export const ENV_CONFIG = {
7-
// 后端 API 地址
8-
API_URL: 'http://83.229.126.150:3001',
6+
// 后端服务器地址(用于服务端代理)
7+
export const BACKEND_URL = 'http://83.229.126.150:3001'
8+
9+
/**
10+
* 检测是否在 HTTPS 环境
11+
*/
12+
function isHttpsEnv(): boolean {
13+
if (typeof window === 'undefined') return false
14+
return window.location.protocol === 'https:'
15+
}
916

10-
// WebSocket 地址
11-
WS_URL: 'ws://83.229.126.150:3001',
17+
/**
18+
* 获取 API URL
19+
* HTTPS 环境使用相对路径(走 Next.js 代理),HTTP 环境直接请求
20+
*/
21+
export function getApiUrl(): string {
22+
return isHttpsEnv() ? '' : BACKEND_URL
23+
}
1224

13-
// 视觉模型 API 配置(阶跃星辰)
25+
/**
26+
* 获取 WebSocket URL
27+
* HTTPS 环境禁用(返回空),HTTP 环境直接连接
28+
*/
29+
export function getWsUrl(): string {
30+
return isHttpsEnv() ? '' : 'ws://83.229.126.150:3001'
31+
}
32+
33+
export const ENV_CONFIG = {
34+
// 视觉模型 API 配置(阶跃星辰)- 已经是 HTTPS,无需代理
1435
VISION_API_URL: 'https://api.stepfun.com/v1',
1536
VISION_API_KEY: '4FlSdYVfkMrcVqaex9PXjLUOg8b3MazJ8GdY993DymL6sN3DwEYW39HwuowYdn7xC',
1637
VISION_MODEL: 'step-1o-turbo-vision',
38+
39+
// 兼容旧代码的 getter
40+
get API_URL() {
41+
return getApiUrl()
42+
},
43+
get WS_URL() {
44+
return getWsUrl()
45+
},
1746
} as const
1847

1948
export default ENV_CONFIG

frontend/src/hooks/useWebSocket.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,15 +148,26 @@ export function useWebSocket({ url, roomId, enabled = true }: UseWebSocketOption
148148
useEffect(() => {
149149
if (!enabled || !roomId) return
150150

151-
const socketUrl = url || ENV_CONFIG.WS_URL
151+
// 检测是否在 HTTPS 环境
152+
const isHttps = typeof window !== 'undefined' && window.location.protocol === 'https:'
152153

153-
console.log('[WS] Connecting to:', socketUrl, 'Room:', roomId)
154+
// HTTPS 环境使用相对路径(走代理),强制使用轮询模式
155+
// HTTP 环境直接连接后端,优先使用 WebSocket
156+
const socketUrl = isHttps ? '' : (url || ENV_CONFIG.WS_URL)
157+
const transports: ('websocket' | 'polling')[] = isHttps ? ['polling'] : ['websocket', 'polling']
158+
159+
console.log('[WS] Connecting to:', socketUrl || '(relative path)', 'Room:', roomId, 'Transports:', transports)
154160

155161
socketRef.current = io(socketUrl, {
156-
transports: ['websocket', 'polling'],
162+
transports,
157163
reconnection: true,
158164
reconnectionDelay: 1000,
159165
reconnectionAttempts: 5,
166+
// HTTPS 环境下的额外配置
167+
...(isHttps && {
168+
path: '/socket.io',
169+
withCredentials: false,
170+
}),
160171
})
161172

162173
const socket = socketRef.current

frontend/tsconfig.tsbuildinfo

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)