@quicktvui/web-cli 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/DevServer.js
CHANGED
|
@@ -494,42 +494,86 @@ class DevServer {
|
|
|
494
494
|
|
|
495
495
|
/**
|
|
496
496
|
* 处理代理请求
|
|
497
|
+
*
|
|
498
|
+
* URL 格式: /proxy/{http|https}/{host}[:port]/{path}[?query]
|
|
499
|
+
* 示例:
|
|
500
|
+
* /proxy/https/api.example.com/v1/data → https://api.example.com/v1/data
|
|
501
|
+
* /proxy/https/api.example.com:8443/v1/data → https://api.example.com:8443/v1/data
|
|
497
502
|
*/
|
|
498
503
|
_handleProxy(req, res, pathname) {
|
|
499
|
-
//
|
|
504
|
+
// 从完整 URL 中提取查询参数(pathname 只有路径部分)
|
|
505
|
+
const fullUrl = url.parse(req.url, true)
|
|
506
|
+
const queryString = fullUrl.search || ''
|
|
507
|
+
|
|
500
508
|
const match = pathname.match(/^\/proxy\/(https?)\/([^/]+)(\/.*)?$/)
|
|
501
509
|
if (!match) {
|
|
502
510
|
res.writeHead(400, { 'Content-Type': 'text/plain' })
|
|
503
|
-
res.end('Invalid proxy URL format. Use: /proxy/{http|https}/{host}/{path}')
|
|
511
|
+
res.end('Invalid proxy URL format. Use: /proxy/{http|https}/{host}[:port]/{path}')
|
|
504
512
|
return
|
|
505
513
|
}
|
|
506
514
|
|
|
507
515
|
const protocol = match[1]
|
|
508
|
-
const
|
|
509
|
-
const proxyPath = match[3] || '/'
|
|
516
|
+
const hostPort = match[2]
|
|
517
|
+
const proxyPath = (match[3] || '/') + queryString
|
|
518
|
+
|
|
519
|
+
// 分离 host 和 port
|
|
520
|
+
let hostname = hostPort
|
|
521
|
+
let port = protocol === 'https' ? 443 : 80
|
|
522
|
+
if (hostPort.includes(':')) {
|
|
523
|
+
const parts = hostPort.split(':')
|
|
524
|
+
hostname = parts[0]
|
|
525
|
+
port = parseInt(parts[1], 10) || port
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// 过滤掉 hop-by-hop 头,避免转发问题
|
|
529
|
+
const headers = { ...req.headers }
|
|
530
|
+
delete headers['host']
|
|
531
|
+
delete headers['connection']
|
|
532
|
+
delete headers['keep-alive']
|
|
533
|
+
delete headers['transfer-encoding']
|
|
534
|
+
delete headers['te']
|
|
535
|
+
delete headers['trailer']
|
|
536
|
+
delete headers['upgrade']
|
|
537
|
+
delete headers['proxy-connection']
|
|
538
|
+
headers['host'] = hostPort
|
|
539
|
+
// 标记请求来自代理(方便调试)
|
|
540
|
+
headers['x-forwarded-for'] = req.socket.remoteAddress
|
|
541
|
+
headers['x-forwarded-proto'] = protocol
|
|
510
542
|
|
|
511
543
|
const options = {
|
|
512
|
-
hostname
|
|
513
|
-
port
|
|
544
|
+
hostname,
|
|
545
|
+
port,
|
|
514
546
|
path: proxyPath,
|
|
515
547
|
method: req.method,
|
|
516
|
-
headers
|
|
517
|
-
|
|
518
|
-
host: host,
|
|
519
|
-
},
|
|
548
|
+
headers,
|
|
549
|
+
rejectUnauthorized: false, // 允许自签名证书
|
|
520
550
|
}
|
|
521
551
|
|
|
522
552
|
const httpModule = protocol === 'https' ? require('https') : require('http')
|
|
523
553
|
const proxyReq = httpModule.request(options, (proxyRes) => {
|
|
524
|
-
|
|
554
|
+
// 代理响应添加 CORS 头
|
|
555
|
+
const responseHeaders = { ...proxyRes.headers }
|
|
556
|
+
responseHeaders['access-control-allow-origin'] = this.corsOrigin
|
|
557
|
+
responseHeaders['access-control-allow-methods'] = 'GET, POST, PUT, DELETE, OPTIONS, PATCH'
|
|
558
|
+
responseHeaders['access-control-allow-headers'] = '*'
|
|
559
|
+
responseHeaders['access-control-allow-credentials'] = 'true'
|
|
560
|
+
// 移除可能导致问题的安全头
|
|
561
|
+
delete responseHeaders['x-frame-options']
|
|
562
|
+
delete responseHeaders['content-security-policy']
|
|
563
|
+
|
|
564
|
+
res.writeHead(proxyRes.statusCode, responseHeaders)
|
|
525
565
|
proxyRes.pipe(res)
|
|
526
566
|
})
|
|
527
567
|
|
|
528
568
|
proxyReq.on('error', (err) => {
|
|
529
|
-
|
|
530
|
-
res.
|
|
569
|
+
console.error(`[Proxy] Error: ${err.message} -> ${protocol}://${hostname}:${port}${proxyPath}`)
|
|
570
|
+
if (!res.headersSent) {
|
|
571
|
+
res.writeHead(502, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': this.corsOrigin })
|
|
572
|
+
}
|
|
573
|
+
res.end(JSON.stringify({ error: 'Proxy error', message: err.message, target: `${protocol}://${hostname}:${port}${proxyPath}` }))
|
|
531
574
|
})
|
|
532
575
|
|
|
576
|
+
// 转发请求体(POST/PUT 等)
|
|
533
577
|
req.pipe(proxyReq)
|
|
534
578
|
}
|
|
535
579
|
|
|
@@ -538,8 +582,9 @@ class DevServer {
|
|
|
538
582
|
*/
|
|
539
583
|
_setCORSHeaders(res) {
|
|
540
584
|
res.setHeader('Access-Control-Allow-Origin', this.corsOrigin)
|
|
541
|
-
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS')
|
|
542
|
-
res.setHeader('Access-Control-Allow-Headers', '
|
|
585
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS, PATCH')
|
|
586
|
+
res.setHeader('Access-Control-Allow-Headers', '*')
|
|
587
|
+
res.setHeader('Access-Control-Allow-Credentials', 'true')
|
|
543
588
|
}
|
|
544
589
|
|
|
545
590
|
/**
|