@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
- const relativePath = pathname.replace('/dist/dev/', '')
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
- // /proxy/https/example.com/path https://example.com/path
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 host = match[2]
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: host,
513
- port: protocol === 'https' ? 443 : 80,
550
+ hostname,
551
+ port,
514
552
  path: proxyPath,
515
553
  method: req.method,
516
- headers: {
517
- ...req.headers,
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
- res.writeHead(proxyRes.statusCode, proxyRes.headers)
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
- res.writeHead(502, { 'Content-Type': 'text/plain' })
530
- res.end(`Proxy error: ${err.message}`)
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', 'Content-Type')
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
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quicktvui/web-cli",
3
- "version": "2.2.0",
3
+ "version": "2.4.0",
4
4
  "description": "CLI tool for QuickTVUI web development v2 - delegate build & bundle loading",
5
5
  "author": "QuickTVUI Team",
6
6
  "license": "Apache-2.0",