@quicktvui/web-cli 2.2.0 → 2.4.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
|
@@ -369,7 +369,13 @@ class DevServer {
|
|
|
369
369
|
|
|
370
370
|
// dist/dev/ 下的文件 - 映射到项目的 dist/dev 目录
|
|
371
371
|
if (pathname.startsWith('/dist/dev/')) {
|
|
372
|
-
|
|
372
|
+
let relativePath = pathname.replace('/dist/dev/', '')
|
|
373
|
+
// 剥离 bundles-dev 中间段:web-runtime 的 publicPath 包含 bundles-dev,
|
|
374
|
+
// 导致 webpack 管理的资源(如图片)URL 会变成 /dist/dev/bundles-dev/assets/xxx.png
|
|
375
|
+
// 但实际文件在 dist/dev/assets/xxx.png,bundles-dev 只是 Hippy debug server 的路径约定
|
|
376
|
+
if (relativePath.startsWith('bundles-dev/')) {
|
|
377
|
+
relativePath = relativePath.replace('bundles-dev/', '')
|
|
378
|
+
}
|
|
373
379
|
const filePath = path.join(this.distDir, relativePath)
|
|
374
380
|
this._serveStaticFile(res, filePath, this.distDir)
|
|
375
381
|
return
|
|
@@ -494,42 +500,97 @@ class DevServer {
|
|
|
494
500
|
|
|
495
501
|
/**
|
|
496
502
|
* 处理代理请求
|
|
503
|
+
*
|
|
504
|
+
* URL 格式: /proxy/{http|https}/{host}[:port]/{path}[?query]
|
|
505
|
+
* 示例:
|
|
506
|
+
* /proxy/https/api.example.com/v1/data → https://api.example.com/v1/data
|
|
507
|
+
* /proxy/https/api.example.com:8443/v1/data → https://api.example.com:8443/v1/data
|
|
497
508
|
*/
|
|
498
509
|
_handleProxy(req, res, pathname) {
|
|
499
|
-
//
|
|
510
|
+
// 从完整 URL 中提取查询参数(pathname 只有路径部分)
|
|
511
|
+
const fullUrl = url.parse(req.url, true)
|
|
512
|
+
const queryString = fullUrl.search || ''
|
|
513
|
+
|
|
500
514
|
const match = pathname.match(/^\/proxy\/(https?)\/([^/]+)(\/.*)?$/)
|
|
501
515
|
if (!match) {
|
|
502
516
|
res.writeHead(400, { 'Content-Type': 'text/plain' })
|
|
503
|
-
res.end('Invalid proxy URL format. Use: /proxy/{http|https}/{host}/{path}')
|
|
517
|
+
res.end('Invalid proxy URL format. Use: /proxy/{http|https}/{host}[:port]/{path}')
|
|
504
518
|
return
|
|
505
519
|
}
|
|
506
520
|
|
|
507
521
|
const protocol = match[1]
|
|
508
|
-
const
|
|
509
|
-
const proxyPath = match[3] || '/'
|
|
522
|
+
const hostPort = match[2]
|
|
523
|
+
const proxyPath = (match[3] || '/') + queryString
|
|
524
|
+
|
|
525
|
+
// 分离 host 和 port
|
|
526
|
+
let hostname = hostPort
|
|
527
|
+
let port = protocol === 'https' ? 443 : 80
|
|
528
|
+
if (hostPort.includes(':')) {
|
|
529
|
+
const parts = hostPort.split(':')
|
|
530
|
+
hostname = parts[0]
|
|
531
|
+
port = parseInt(parts[1], 10) || port
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// 过滤掉 hop-by-hop 头,避免转发问题
|
|
535
|
+
const headers = { ...req.headers }
|
|
536
|
+
delete headers['host']
|
|
537
|
+
delete headers['connection']
|
|
538
|
+
delete headers['keep-alive']
|
|
539
|
+
delete headers['transfer-encoding']
|
|
540
|
+
delete headers['te']
|
|
541
|
+
delete headers['trailer']
|
|
542
|
+
delete headers['upgrade']
|
|
543
|
+
delete headers['proxy-connection']
|
|
544
|
+
headers['host'] = hostPort
|
|
545
|
+
// 标记请求来自代理(方便调试)
|
|
546
|
+
headers['x-forwarded-for'] = req.socket.remoteAddress
|
|
547
|
+
headers['x-forwarded-proto'] = protocol
|
|
510
548
|
|
|
511
549
|
const options = {
|
|
512
|
-
hostname
|
|
513
|
-
port
|
|
550
|
+
hostname,
|
|
551
|
+
port,
|
|
514
552
|
path: proxyPath,
|
|
515
553
|
method: req.method,
|
|
516
|
-
headers
|
|
517
|
-
|
|
518
|
-
host: host,
|
|
519
|
-
},
|
|
554
|
+
headers,
|
|
555
|
+
rejectUnauthorized: false, // 允许自签名证书
|
|
520
556
|
}
|
|
521
557
|
|
|
522
558
|
const httpModule = protocol === 'https' ? require('https') : require('http')
|
|
523
559
|
const proxyReq = httpModule.request(options, (proxyRes) => {
|
|
524
|
-
|
|
560
|
+
// 代理响应添加 CORS 头
|
|
561
|
+
const responseHeaders = { ...proxyRes.headers }
|
|
562
|
+
responseHeaders['access-control-allow-origin'] = this.corsOrigin
|
|
563
|
+
responseHeaders['access-control-allow-methods'] = 'GET, POST, PUT, DELETE, OPTIONS, PATCH'
|
|
564
|
+
responseHeaders['access-control-allow-headers'] = '*'
|
|
565
|
+
responseHeaders['access-control-allow-credentials'] = 'true'
|
|
566
|
+
// 移除可能导致问题的安全头
|
|
567
|
+
delete responseHeaders['x-frame-options']
|
|
568
|
+
delete responseHeaders['content-security-policy']
|
|
569
|
+
|
|
570
|
+
res.writeHead(proxyRes.statusCode, responseHeaders)
|
|
525
571
|
proxyRes.pipe(res)
|
|
526
572
|
})
|
|
527
573
|
|
|
528
574
|
proxyReq.on('error', (err) => {
|
|
529
|
-
|
|
530
|
-
|
|
575
|
+
console.error(
|
|
576
|
+
`[Proxy] Error: ${err.message} -> ${protocol}://${hostname}:${port}${proxyPath}`
|
|
577
|
+
)
|
|
578
|
+
if (!res.headersSent) {
|
|
579
|
+
res.writeHead(502, {
|
|
580
|
+
'Content-Type': 'application/json',
|
|
581
|
+
'Access-Control-Allow-Origin': this.corsOrigin,
|
|
582
|
+
})
|
|
583
|
+
}
|
|
584
|
+
res.end(
|
|
585
|
+
JSON.stringify({
|
|
586
|
+
error: 'Proxy error',
|
|
587
|
+
message: err.message,
|
|
588
|
+
target: `${protocol}://${hostname}:${port}${proxyPath}`,
|
|
589
|
+
})
|
|
590
|
+
)
|
|
531
591
|
})
|
|
532
592
|
|
|
593
|
+
// 转发请求体(POST/PUT 等)
|
|
533
594
|
req.pipe(proxyReq)
|
|
534
595
|
}
|
|
535
596
|
|
|
@@ -538,8 +599,9 @@ class DevServer {
|
|
|
538
599
|
*/
|
|
539
600
|
_setCORSHeaders(res) {
|
|
540
601
|
res.setHeader('Access-Control-Allow-Origin', this.corsOrigin)
|
|
541
|
-
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS')
|
|
542
|
-
res.setHeader('Access-Control-Allow-Headers', '
|
|
602
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS, PATCH')
|
|
603
|
+
res.setHeader('Access-Control-Allow-Headers', '*')
|
|
604
|
+
res.setHeader('Access-Control-Allow-Credentials', 'true')
|
|
543
605
|
}
|
|
544
606
|
|
|
545
607
|
/**
|