topbit 3.0.5 → 3.0.7

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/README.cn.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Topbit
4
4
 
5
- [English Documentation](README.en.md)
5
+ #### [English Documentation](README.en.md)
6
6
 
7
7
  topbit是基于Node.js的运行于服务端的Web框架,它没有任何第三方依赖,在独特的路由和中间件分组执行机制上进行了极致优化。
8
8
 
@@ -38,7 +38,7 @@ topbit是基于Node.js的运行于服务端的Web框架,它没有任何第三
38
38
  * 默认设定和网络安全相关的配置,避免软件服务层面的DDOS攻击和其他网络安全问题。
39
39
 
40
40
 
41
- ## 安装
41
+ ## 📦安装
42
42
 
43
43
  ```javascript
44
44
  npm i topbit
@@ -131,7 +131,7 @@ GET POST PUT PATCH DELETE OPTIONS TRACE HEAD
131
131
 
132
132
  'use strict'
133
133
 
134
- const Topbit = require('topibit')
134
+ const Topbit = require('topbit')
135
135
 
136
136
  const app = new Topbit({
137
137
  debug: true
@@ -176,7 +176,7 @@ app.run(8080)
176
176
  ``` JavaScript
177
177
  'use strict';
178
178
 
179
- const Topbit = require('topbit');
179
+ const Topbit = require('topbit')
180
180
 
181
181
  let app = new Topbit({
182
182
  debug: true
@@ -443,7 +443,7 @@ app.run(1234)
443
443
 
444
444
  const Topbit = require('topbit')
445
445
  //导入ToFile扩展
446
- const {ToFile} = require('topbit-toolkit')
446
+ const {ToFile} = Topbit.extensions
447
447
 
448
448
  const app = new Topbit({
449
449
  debug: true
@@ -509,7 +509,7 @@ app.run(1234)
509
509
 
510
510
  const Topbit = require('topbit')
511
511
  //导入ToFile扩展
512
- const {ToFile} = require('topbit-toolkit')
512
+ const {ToFile} = Topbit.extensions
513
513
 
514
514
  const app = new Topbit({
515
515
  debug: true
@@ -575,9 +575,6 @@ app.post('/upload', async c => {
575
575
 
576
576
  let f = c.getFile('image')
577
577
 
578
- //此函数是助手函数,makeName默认会按照时间戳生成名字,extName解析文件的扩展名。
579
- //let fname = `${c.ext.makeName()}${c.ext.extName(f.filename)}`
580
-
581
578
  //根据原始文件名解析扩展名并生成时间戳加随机数的唯一文件名。
582
579
 
583
580
  let fname = c.ext.makeName(f.filename)
@@ -607,8 +604,6 @@ app.run(1234)
607
604
  {
608
605
  image : [
609
606
  {
610
- 'content-type': CONTENT_TYPE,
611
- //23.2.6以上可用,是content-type的别名,方便程序访问
612
607
  type: CONTENT_TYPE,
613
608
  filename: ORIGIN_FILENAME,
614
609
  start : START,
@@ -622,8 +617,6 @@ app.run(1234)
622
617
 
623
618
  video : [
624
619
  {
625
- 'content-type': CONTENT_TYPE,
626
- //23.2.6以上可用,是content-type的别名,方便程序访问
627
620
  type: CONTENT_TYPE,
628
621
  filename: ORIGIN_FILENAME,
629
622
  start : START,
@@ -1056,18 +1049,18 @@ app.run(1234)
1056
1049
 
1057
1050
  'use strict';
1058
1051
 
1059
- const Topbit = require('topbit');
1052
+ const Topbit = require('topbit')
1060
1053
 
1061
1054
  let app = new Topbit({
1062
1055
  debug: true
1063
1056
  });
1064
1057
 
1065
1058
  //有则会覆盖,没有则添加。
1066
- app.addService('name', 'first');
1059
+ app.addService('name', 'first')
1067
1060
  app.addService('data', {
1068
1061
  id : 123,
1069
1062
  ip : '127.0.0.1'
1070
- });
1063
+ })
1071
1064
 
1072
1065
  /*
1073
1066
  这可能看不出什么作用,毕竟在一个文件中,直接访问变量都可以,如果要做模块分离,就变得非常重要了。
@@ -1091,7 +1084,9 @@ app.run(1234)
1091
1084
 
1092
1085
  ```javascript
1093
1086
  'use strict'
1087
+
1094
1088
  const Topbit = require('topbit')
1089
+
1095
1090
  const app = new Topbit({
1096
1091
  debug: true
1097
1092
  })
@@ -1221,7 +1216,7 @@ const app = new Topbit({
1221
1216
  if (cluster.isMaster) {
1222
1217
  app.setMsgEvent('test-msg', (worker, msg, handle) => {
1223
1218
  //子进程中会通过message事件收到消息
1224
- worker.to({
1219
+ worker.send({
1225
1220
  id : worker.id,
1226
1221
  data : 'ok'
1227
1222
  })
@@ -1235,7 +1230,7 @@ if (cluster.isMaster) {
1235
1230
  })
1236
1231
 
1237
1232
  setIneterval(() => {
1238
- process.to({
1233
+ process.send({
1239
1234
  type : 'test-msg',
1240
1235
  pid : process.pid,
1241
1236
  time : (new Date()).toLocaleString()
@@ -1248,7 +1243,7 @@ if (cluster.isMaster) {
1248
1243
 
1249
1244
  比较麻烦的地方在于,worker进程发送消息比较复杂,在22.4.0版本开始,提供了一个send方法用于快速发送消息。只有在worker进程中才会发送给master进程,所以不必额外进行worker进程检测。
1250
1245
 
1251
- ## app.to 和 app.workerMsg
1246
+ ## app.send 和 app.workerMsg
1252
1247
 
1253
1248
  现在让我们来改写上面代码的worker进程发送消息的部分:
1254
1249
 
@@ -1264,7 +1259,7 @@ const app = new Topbit({
1264
1259
  //master进程注册消息事件类型,worker进程不会执行。
1265
1260
  app.setMsgEvent('test-msg', (worker, msg, handle) => {
1266
1261
  //子进程中会通过message事件收到消息
1267
- worker.to({
1262
+ worker.send({
1268
1263
  id : worker.id,
1269
1264
  data : 'ok'
1270
1265
  })
@@ -1281,7 +1276,7 @@ cluster.isWorker
1281
1276
  &&
1282
1277
  setInterval(() => {
1283
1278
  //只有worker会执行。
1284
- app.to('test-msg', {
1279
+ app.send('test-msg', {
1285
1280
  pid: process.pid,
1286
1281
  time: (new Date).toLocaleString()
1287
1282
  })
@@ -1322,7 +1317,7 @@ app.daemon(1234, 2)
1322
1317
 
1323
1318
  ----
1324
1319
 
1325
- ## strong模式
1320
+ ## 🛡️strong模式
1326
1321
 
1327
1322
  通过strong选项可以开启strong模式,此模式会监听uncaughtException和unhandledRejection事件,保证程序稳定运行。最简单的情况,你只需要给strong设置为true即可。
1328
1323
 
@@ -1382,7 +1377,7 @@ const app = new Topbit({
1382
1377
 
1383
1378
  ```
1384
1379
 
1385
- ## 同时运行http和https?
1380
+ ## 🔔同时运行http和https?
1386
1381
 
1387
1382
  请注意这是打问号的,你最好不要在正式环境这样做,如果你已经开启了https,则不需要http,而且前端应用有些功能在不启用https是无法使用的。
1388
1383
 
@@ -1474,7 +1469,7 @@ app.daemon(1234, '0.0.0.0', 3)
1474
1469
 
1475
1470
  - 这一切你都可以通过配置选项或是中间件来进行扩展和重写,既有限制也有自由。
1476
1471
 
1477
- - 它很快,并且我们一直在都在关注优化。如果你需要和其他对比测试,请都添加多个中间件,并且都添加上百个路由,然后测试对比。
1472
+ - 它很快,并且我们一直在都在关注优化。
1478
1473
 
1479
1474
  - 提供了一个sched函数用来快速设置cluster模式的调度方式,支持参数为'rr'或'none',本质就是设置cluster.schedulingPolicy的值。
1480
1475
 
@@ -1482,12 +1477,12 @@ app.daemon(1234, '0.0.0.0', 3)
1482
1477
  框架在初始化会自动检测内存大小并设定相关上限,你可以在初始化后,通过更改secure中的属性来更改限制,这需要你使用daemon接口,也就是使用master管理子进程的模式。
1483
1478
 
1484
1479
 
1485
- ```
1480
+ ```javascript
1486
1481
  'use strict'
1487
1482
 
1488
- const Topbit = require('topbit');
1483
+ const Topbit = require('topbit')
1489
1484
 
1490
- let app = new Topbit();
1485
+ let app = new Topbit()
1491
1486
 
1492
1487
  /*
1493
1488
  以下操作可以通过选项memFactor控制,请参考上文的配置选项部分。
@@ -1505,13 +1500,911 @@ app.secure.diemem = 900_000_000;
1505
1500
  app.secure.maxrss = 800_000_000;
1506
1501
 
1507
1502
  app.get('/', async c => {
1508
- c.to('ok');
1503
+ c.to('ok')
1504
+ })
1505
+
1506
+ app.daemon(8008, 2)
1507
+
1508
+ ```
1509
+
1510
+ **注意,这需要你开启loadMonitor选项,这是默认开启的。**
1511
+
1512
+ ---
1513
+
1514
+ # 🧩 中间件扩展和其他扩展 📜
1515
+
1516
+ ---
1517
+
1518
+ 作为Web框架,仅仅提供基础功能是不够的,很多基础功能在所有Web服务上都会用到,所以必然会出现通用的扩展模块。
1519
+
1520
+ 在Topbit全局中间件设计机制上,大部分和Web服务有关的扩展都可以通过开发一个中间件扩展完成,可以参考 `中间件` 部分,这种插件机制十分灵活。
1521
+
1522
+ **Topbit提供了一些通用中间件扩展,开发者可以按需使用。**
1523
+
1524
+ **也提供了一些其他扩展模块:路由加载器、Token会话验证、命令参数解析、错误日志自动化处理、zip压缩和解压。**
1525
+
1526
+ ## 导入中间件扩展
1527
+
1528
+ 所有中间件扩展都在Topbit.extensions下,示例:
1529
+
1530
+ ```javascript
1531
+ 'use strict'
1532
+
1533
+ process.chdir(__dirname)
1534
+
1535
+ const Topbit = require('Topbit')
1536
+
1537
+ //通过解构赋值导入扩展
1538
+ const {Cors, ToFile} = Topbit.extensions
1539
+
1540
+ const app = new Topbit({
1541
+ debug: true,
1542
+ //开启日志,是globalLog的别名
1543
+ log: true
1544
+ })
1545
+
1546
+ // 跨域扩展在解析body之前运行
1547
+ // use添加的中间件解析body数据之后运行
1548
+ app.pre(new Cors())
1549
+ .use(new ToFile, {method: ['PUT', 'POST']})
1550
+
1551
+ app.post('/file', async ctx => {
1552
+ let f = ctx.getFile('file')
1553
+ if (!f) return ctx.status(400).to('file not found')
1554
+
1555
+ ctx.to(
1556
+ await f.toFile('./uploads')
1557
+ )
1558
+ })
1559
+
1560
+ app.run(1235)
1561
+ ```
1562
+
1563
+ ## ☲ 中间件扩展
1564
+
1565
+
1566
+ | 中间件 | 描述 |
1567
+ |----------------|----------------------------------------------------------------------|
1568
+ | Cors | 支持跨域请求的中间件。 |
1569
+ | Resource | 静态资源处理,是目前最强静态资源处理服务。 |
1570
+ | ToFile | 让上传的文件对象具备 `toFile` 方法。 |
1571
+ | SSE | SSE协议中间件,基于HTTP的数据流推送。 |
1572
+ | Proxy | 针对 HTTP/1.1 协议的反向代理,支持负载均衡和 alive 检测。 |
1573
+ | Http2Proxy | HTTP/2 协议的反向代理,支持负载均衡和 alive 检测。 |
1574
+ | SNI | HTTPS 多域名支持。 |
1575
+ | ParamCheck | 请求参数检测,支持 `param`、`query`、`body` 的检测。 |
1576
+ | JWT | JWT 协议的 token 验证,推荐使用 Topbit.Token,比 JWT 强太多。 |
1577
+ | Cookie | 解析前端请求的消息头 cookie。 |
1578
+ | Session | Session 会话,使用的是本地文件机制。 |
1579
+
1580
+ ---
1581
+
1582
+ 以下是根据你提供的 `titbit-toolkit` 原始文档内容,按照新的列表顺序重新整理的文档。
1583
+
1584
+ **注意**:
1585
+ * 根据指示,文档已针对新的 **Topbit** 框架语境进行了调整(即不再强调独立安装扩展)。
1586
+ * 列表中未包含的组件(如 `Timing`, `RealIP`, `MixLogger`, `SendType`, `SetFinal`, `Http2Limit`)已被移除。
1587
+ * 列表中包含但原文档未提供详细说明的组件(如 `SNI`),仅保留了描述占位符。
1588
+
1589
+ ---
1590
+
1591
+ ### 1. Cors (跨域支持)
1592
+
1593
+ 支持跨域请求的中间件。尽管是对跨域场景的支持,但在具备 `origin` 消息头的跨域通信和 `referer` 资源引入场景都做了支持,并且可以灵活选择和配置。
1594
+
1595
+ **基本使用:**
1596
+
1597
+ ```javascript
1598
+ const Topbit = require('topbit')
1599
+ const {Cors} = Topbit.extensions
1600
+
1601
+ let cr = new Cors({
1602
+ // 默认为 * 表示全部开启跨域,
1603
+ // 若要指定要支持的域名则需要传递一个数组。
1604
+ // 注意:这里指定的是用于访问接口的应用页面所在域。
1605
+ allow : [
1606
+ 'https://a.com',
1607
+ 'https://www.a.com',
1608
+ ],
1609
+
1610
+ // 默认只有 content-type
1611
+ allowHeaders : 'authorization,content-type',
1612
+
1613
+ // OPTIONS 请求缓存 60 秒,此期间浏览器请求会先去读取缓存。
1614
+ optionsCache: 60,
1615
+
1616
+ // 默认为 '*',可以设置允许的请求方法。
1617
+ // requestHeaders : '*'
1618
+
1619
+ // 默认为 '',表示哪些消息头可以暴露给请求端,多个消息头使用 , 分隔。
1620
+ // exposeHeaders: 'x-test-key,x-type'
1621
+ })
1622
+
1623
+ app.pre(cr)
1624
+
1625
+ // 支持 OPTIONS 请求,浏览器在处理 POST/PUT/DELETE 时会先发送 OPTIONS 预检请求。
1626
+ // 必须处理 OPTIONS 请求才能完整支持跨域。
1627
+ app.options('/*', async c => {})
1628
+ ```
1629
+
1630
+ **高级配置(Referer检测与分组):**
1631
+
1632
+ ```javascript
1633
+ let cr = new cors({
1634
+ // 不允许 referer 为空
1635
+ allowEmptyReferer: false,
1636
+
1637
+ // 允许 referer 为空的路由分组,
1638
+ // 分组名称是自定义的,参考框架的路由分组功能。
1639
+ // 此功能主要用于在 API 开发中还要兼顾页面的服务上。
1640
+ emptyRefererGroup: [
1641
+ '@webapp', '@pages'
1642
+ ],
1643
+
1644
+ allow: [
1645
+ // 默认的字符串形式表示 origin 跨域和 referer 外链接引用都被允许
1646
+ 'https://w.a.com',
1647
+ 'https://w.x.cn',
1648
+
1649
+ {url: 'https://servicewechat.com/wx234hiohr23', referer: true},
1650
+ // 不应用于 referer 检测,如果有请求提交了 referer 为 self-define 不会通过检测
1651
+ {url: 'self-define', referer: false}
1652
+ ],
1653
+ })
1654
+ ```
1655
+
1656
+ ---
1657
+
1658
+ ### 2. Resource (静态资源处理)
1659
+
1660
+ 静态资源处理服务,主要用于 JS、CSS、图片、音频等静态文件的托管。支持缓存控制、路由前缀修正等功能。
1661
+
1662
+ **基本使用:**
1663
+
1664
+ ```javascript
1665
+ const Topbit = require('topbit')
1666
+ const {Resource} = Topbit.extensions
1667
+
1668
+ let st = new Resource({
1669
+ // 设定静态资源所在目录
1670
+ staticPath: './public'
1671
+ })
1672
+
1673
+ // 只对分组为 static 执行中间件。
1674
+ app.use(st, {group: 'static'})
1675
+
1676
+ // 添加静态资源路由,假设 public 目录下有 css/a.css
1677
+ // 访问 /static/css/a.css 即可
1678
+ app.get('/static/*', async c => {
1679
+ // 请求分组为 static
1680
+ }, '@static')
1681
+ ```
1682
+
1683
+ **快速示例与配置详解:**
1684
+
1685
+ ```javascript
1686
+ let st = new Resource({
1687
+ // 设定静态资源所在目录
1688
+ staticPath: './public',
1689
+
1690
+ // 路由路径:前端必须以 /static/ 开头,映射到 ./public 目录下的文件
1691
+ routePath : '/static/*',
1692
+
1693
+ // 指定路由分组
1694
+ routeGroup: '_static',
1695
+
1696
+ // 是否支持中文路径解码(默认 false)
1697
+ decodePath: true,
1698
+
1699
+ // 前缀路径修正。如果设置为 xyz,会自动修正为 /xyz。
1700
+ // prepath : '',
1701
+
1702
+ // 最大缓存文件大小,超过此大小则不会缓存(单位字节)
1703
+ maxFileSize: 12_000_000,
1704
+
1705
+ // 设置消息头 cache-control 的值,默认为 null
1706
+ cacheControl: 'max-age=3600',
1707
+
1708
+ // 最大总缓存大小(默认 120MB)
1709
+ maxCacheSize: 120_000_000,
1710
+
1711
+ // 失败限制次数与缓存释放概率
1712
+ failedLimit: 50,
1713
+ prob: 6
1714
+ })
1715
+
1716
+ st.init(app)
1717
+
1718
+ // 自动添加到 _static 分组。
1719
+ // 例如:public 目录中有 favicon.ico,通过此请求即可获取。
1720
+ app.get('/favicon.ico', async c => {}, {group: '_static'})
1721
+ ```
1722
+
1723
+ **接口说明:**
1724
+ * `addType(obj)`: 添加扩展名到 content-type 的映射关系。
1725
+ * `clearCache()`: 清理缓存。
1726
+
1727
+ ---
1728
+
1729
+ ### 3. ToFile (文件上传对象化)
1730
+
1731
+ 让上传的文件对象具备 `toFile` 方法,便于按照面向对象的风格保存文件。
1732
+
1733
+ ```javascript
1734
+ const Topbit = require('topbit')
1735
+ const {ToFile} = Topbit.extensions
1736
+
1737
+ app.use( new ToFile() )
1738
+
1739
+ app.post('/upload', async c => {
1740
+ // 获取上传的文件对象
1741
+ let f = c.getFile('image')
1742
+
1743
+ if (!f) {
1744
+ return c.status(400).oo('image file not found')
1745
+ }
1746
+
1747
+ // 把文件移动到 images 目录(需确保目录存在)。
1748
+ // 第二个参数可选指定文件名,默认根据时间戳和随机数生成唯一文件名。
1749
+ let fname = await f.toFile('./images')
1750
+
1751
+ // 返回文件名
1752
+ c.ok(fname)
1753
+ })
1754
+
1755
+ app.run(1234)
1756
+
1757
+ ```
1758
+
1759
+ ---
1760
+
1761
+ ### 4. SSE (服务端消息推送)
1762
+
1763
+ 基于 HTTP 协议的 Server Sent Events (SSE) 中间件,用于实现服务端主动向客户端推送数据流。
1764
+
1765
+ **基本使用:**
1766
+
1767
+ ```javascript
1768
+ const Topbit = require('topbit')
1769
+ const {SSE} = Topbit.extensions
1770
+
1771
+ let s = new SSE({
1772
+ // 超时20秒,超过此时间后会自动关闭连接
1773
+ timeout: 20000,
1774
+
1775
+ // 重新发起连接时间,默认值为0表示不再发起连接
1776
+ retry: 5000,
1777
+
1778
+ // 定时器间隔,每隔2秒执行一次处理函数
1779
+ timeSlice: 2000
1780
+ })
1781
+
1782
+ // 设置处理函数,ctx 为请求上下文
1783
+ s.handle = (ctx) => {
1784
+ // 发送单条消息
1785
+ ctx.sendmsg({event: 'eat', data: '饭'})
1786
+
1787
+ if (Date.now() % 5 === 0) {
1788
+ // 发送多条消息,字符串或数字会自动转换为对应的消息格式,事件默认为 message
1789
+ ctx.sendmsg([
1790
+ {event: 'clock', data: Date.now()},
1791
+ '但愿人长久,千里共婵娟。',
1792
+ Math.random()
1793
+ ])
1794
+ }
1795
+ }
1796
+
1797
+ // 只针对路由分组 sse 启用中间件
1798
+ app.use( s, {group: 'sse'} )
1799
+
1800
+ app.get('/sse', async ctx => {}, {group: 'sse'})
1801
+
1802
+ app.run(1234)
1803
+
1804
+ ```
1805
+
1806
+ **生成器模式 (Generator Mode):**
1807
+ 适用于需要动态调整时间间隔或更精确控制的场景。
1808
+
1809
+ ```javascript
1810
+ let s = new SSE({
1811
+ timeout: 20000,
1812
+ retry: 5000,
1813
+ timeSlice: 10,
1814
+ mode: 'yield' // 开启生成器模式
1815
+ })
1816
+
1817
+ s.handle = (ctx) => {
1818
+ ctx.sendmsg({event: 'eat', data: '饭'})
1819
+ // 使用 await ctx.sse.moment(ms) 进行延迟
1820
+ await ctx.sse.moment(100)
1821
+ }
1822
+ ```
1823
+
1824
+ ---
1825
+
1826
+ ### 5. Proxy (HTTP/1.1 反向代理)
1827
+
1828
+ 针对 HTTP/1.1 协议的反向代理,支持负载均衡和存活检测 (Alive Check)。
1829
+
1830
+ **基本代理配置:**
1831
+
1832
+ ```javascript
1833
+ const Topbit = require('topbit')
1834
+ const {Proxy} = Topbit.extensions
1835
+
1836
+ let hostcfg = {
1837
+ // 简单映射:默认 path 为 /
1838
+ 'a.com' : 'http://localhost:8001',
1839
+
1840
+ // 指定路径映射
1841
+ 'b.com' : {
1842
+ path : '/xyz',
1843
+ url : 'http://localhost:8002'
1844
+ },
1845
+
1846
+ // 复杂配置与重写
1847
+ 'c.com' : [
1848
+ {
1849
+ path : '/',
1850
+ url : 'http://localhost:8004',
1851
+ rewrite: (ctx, path) => {
1852
+ // 自定义重写路由规则
1853
+ if (path.indexOf('/xyz') === 0) {
1854
+ return path.replace('/xyz', '/w')
1855
+ }
1856
+ }
1857
+ }
1858
+ ]
1859
+ };
1860
+
1861
+ const pxy = new Proxy({
1862
+ timeout: 10000,
1863
+ config: hostcfg
1864
+ })
1865
+
1866
+ pxy.init(app)
1867
+
1868
+ app.run(1234)
1869
+
1870
+ ```
1871
+
1872
+ **负载均衡配置:**
1873
+ 通过配置数组实现,支持权重 (`weight`) 和存活检测。
1874
+
1875
+ ```javascript
1876
+ let load_balance_cfg = {
1877
+ 'a.com' : [
1878
+ {
1879
+ path : '/',
1880
+ url : 'http://localhost:1234',
1881
+ // 每3秒检测服务存活
1882
+ aliveCheckInterval : 3,
1883
+ aliveCheckPath : '/alive-check',
1884
+ // 权重,数字越大权重越高
1885
+ weight: 3
1886
+ },
1887
+ {
1888
+ path : '/',
1889
+ url : 'http://localhost:1235',
1890
+ aliveCheckInterval : 3,
1891
+ aliveCheckPath : '/ok',
1892
+ weight: 2
1893
+ }
1894
+ ]
1895
+ }
1896
+
1897
+ const pxy = new Proxy({
1898
+ timeout: 10000,
1899
+ starPath : true, // 开启后将代理路径后的字符串作为路径转发
1900
+ config: load_balance_cfg
1901
+ })
1902
+
1903
+ pxy.init(app)
1904
+ ```
1905
+
1906
+ ---
1907
+
1908
+ ### 6. Http2Proxy (HTTP/2 反向代理)
1909
+
1910
+ HTTP/2 协议的反向代理,参数与 `Proxy` 基本一致,利用 HTTP/2 连接机制进行保活。
1911
+
1912
+ ```javascript
1913
+ const Topbit = require('topbit')
1914
+ const {Http2Proxy} = Topbit.extensions
1915
+
1916
+ let hxy = new Http2Proxy({
1917
+ config: {
1918
+ 'a.com' : [
1919
+ {
1920
+ url: 'http://localhost:3001',
1921
+ weight: 10,
1922
+ path : '/',
1923
+ reconnDelay: 200, // 重连延迟
1924
+ max: 2,
1925
+ headers: {
1926
+ 'x-test-key': `${Date.now()}`
1927
+ },
1928
+ connectTimeout: 2000
1929
+ },
1930
+ {
1931
+ url: 'http://localhost:3002',
1932
+ weight: 4,
1933
+ path : '/'
1934
+ }
1935
+ ]
1936
+ },
1937
+ debug: true
1509
1938
  })
1510
1939
 
1511
- app.daemon(8008, 2);
1940
+ hxy.init(app)
1512
1941
 
1942
+ app.run(1234)
1513
1943
  ```
1514
1944
 
1515
- **注意,这需要你开启loadMonitor选项,这是默认开启的,除非你设置为false**
1945
+ ---
1946
+
1947
+ ### 7. SNI (HTTPS 多域名支持)
1948
+
1949
+ **描述**:用于在同一 IP 地址和端口上支持多个 HTTPS 域名证书的中间件。
1950
+
1951
+ 感谢提供源代码。根据代码逻辑,`SNI` 扩展通过 `init` 方法将 `SNICallback` 注入到应用的 `config.server` 配置中,从而利用 Node.js 原生 TLS 的能力实现多域名证书支持。
1952
+
1953
+ 以下是补全后的 **SNI** 文档部分:
1954
+
1955
+ ---
1956
+
1957
+ ### 7. SNI (HTTPS 多域名支持)
1958
+
1959
+ **描述**:用于在同一 IP 地址和端口上支持多个 HTTPS 域名证书的中间件。它利用 TLS 协议的 Server Name Indication 特性,根据客户端请求的域名动态加载对应的 SSL 证书。
1960
+
1961
+ **注意**:初始化时会同步读取证书文件,请确保路径正确。如果某个域名的证书读取失败,会在控制台输出错误信息,但不会阻塞其他域名的加载。
1962
+
1963
+ **使用:**
1964
+
1965
+ ```javascript
1966
+ const Topbit = require('topbit')
1967
+ const {SNI} = Topbit.extensions
1968
+
1969
+ const app = new Topbit({
1970
+ // 启用 HTTPS,通常建议配置一组默认的 key 和 cert 作为回退
1971
+ https: true,
1972
+ key: './ssl/default.key',
1973
+ cert: './ssl/default.cert',
1974
+ debug: true
1975
+ })
1976
+
1977
+ // 配置多域名证书
1978
+ // Key 为域名,Value 为包含 key 和 cert 路径的对象
1979
+ let certs = {
1980
+ 'a.com': {
1981
+ key: './ssl/a.com.key',
1982
+ cert: './ssl/a.com.cert'
1983
+ },
1984
+ 'www.b.org': {
1985
+ key: '/etc/nginx/ssl/b.org.key',
1986
+ cert: '/etc/nginx/ssl/b.org.crt'
1987
+ }
1988
+ }
1989
+
1990
+ // 实例化并初始化
1991
+ let sni = new SNI(certs)
1992
+
1993
+ // init 方法会将 SNICallback 注入到 app.config.server 中
1994
+ sni.init(app)
1995
+
1996
+ app.run(443)
1997
+ ```
1998
+
1999
+ **参数说明**:
2000
+ * 构造函数接受一个对象,键名为**域名**,键值为配置对象。
2001
+ * 配置对象必须包含:
2002
+ * `key`: 私钥文件的路径。
2003
+ * `cert`: 证书文件的路径。
2004
+
2005
+ ---
2006
+
2007
+ ### 8. ParamCheck (请求参数检测)
2008
+
2009
+ 支持对 `param` (路由参数)、`query` (查询字符串)、`body` (请求体) 进行声明式检测。
2010
+
2011
+ **Query 和 Param 检测:**
2012
+
2013
+ ```javascript
2014
+ const Topbit = require('topbit')
2015
+ const {ParamCheck} = Topbit.extensions
2016
+
2017
+ // Query 参数检测
2018
+ let pck = new ParamCheck({
2019
+ key: 'query',
2020
+ data : {
2021
+ // 严格限制值
2022
+ say: 'hello',
2023
+ // 类型转换与范围限制
2024
+ offset: {
2025
+ default: 0,
2026
+ to: 'int',
2027
+ min: 0,
2028
+ max: 100
2029
+ }
2030
+ }
2031
+ })
2032
+
2033
+ // Param 参数检测与自定义回调
2034
+ let paramck = new ParamCheck({
2035
+ key: 'param',
2036
+ deny: ['x-key'], // 禁止提交的字段
2037
+ deleteDeny: true,
2038
+ data : {
2039
+ errorMessage: '参数错误', // 自定义错误信息
2040
+ mobile: {
2041
+ callback: (obj, k, method) => {
2042
+ let preg = /^(12|13|15|16|17|18|19)[0-9]{9}$/
2043
+ return preg.test(obj[k])
2044
+ }
2045
+ }
2046
+ }
2047
+ })
2048
+
2049
+ app.use(pck, {method: 'GET'})
2050
+ .use(paramck, {method: 'GET'})
2051
+ ```
2052
+
2053
+ **Body 检测:**
2054
+
2055
+ ```javascript
2056
+ let pmbody = new ParamCheck({
2057
+ key: 'body',
2058
+ data: {
2059
+ username: { must: true },
2060
+ passwd: { must: true }
2061
+ }
2062
+ })
2063
+
2064
+ app.use(pmbody, {method: ['POST', 'PUT'], name: 'login'})
2065
+ ```
2066
+
2067
+ ---
2068
+
2069
+ ### 9. JWT (Token 验证)
2070
+
2071
+ JWT 协议的 Token 验证与签发。推荐使用 `Topbit.Token` 替代。
2072
+
2073
+ ```javascript
2074
+ const Topbit = require('topbit')
2075
+ const {JWT} = Topbit.extensions
2076
+
2077
+ let j = new JWT({
2078
+ timeout: 7200000, // 超时时间
2079
+ })
2080
+ j.alg = 'hs512' // 设置算法
2081
+
2082
+ // 签发 Token
2083
+ let token = j.make({
2084
+ id: '123we',
2085
+ username: 'long'
2086
+ })
2087
+
2088
+ // 验证 Token
2089
+ // 返回对象:{ ok: true, data: {...} } 或 { ok: false, errcode: '...' }
2090
+ let r = j.verify(token)
2091
+
2092
+ // 作为中间件使用
2093
+ // 验证成功会将数据挂载到 ctx.box.user
2094
+ app.pre(j.mid())
2095
+ ```
2096
+
2097
+ ---
2098
+
2099
+ ### 10. Cookie (Cookie 解析)
2100
+
2101
+ 解析前端请求消息头中的 Cookie,并在请求上下文中添加 `cookie` 属性。
2102
+
2103
+ ```javascript
2104
+ const Topbit = require('topbit')
2105
+ const {Cookie} = Topbit.extensions
2106
+
2107
+ app.use( new Cookie() )
2108
+
2109
+ app.get('/', async ctx => {
2110
+ // 获取解析后的 cookie 对象
2111
+ console.log(ctx.cookie)
2112
+ })
2113
+
2114
+ app.run(1234)
2115
+
2116
+ ```
2117
+
2118
+ ---
2119
+
2120
+ ### 11. Session (会话管理)
2121
+
2122
+ 基于本地文件机制的 Session 会话管理,依赖 Cookie 组件。
2123
+ **注意**:此扩展主要用于测试和教学,生产环境建议使用 Redis 或其他数据库存储方案。
2124
+
2125
+ ```javascript
2126
+ const Topbit = require('topbit')
2127
+ const {Cookie,Session} = Topbit.extensions
2128
+
2129
+ let sess = new Session()
2130
+ sess.init(app) // 初始化:添加 ctx 原型方法
2131
+
2132
+ // 必须先启用 Cookie,再启用 Session
2133
+ app.use( new Cookie() ).use( sess )
2134
+
2135
+ app.get('/:key/:data', async ctx => {
2136
+ // 设置 Session
2137
+ ctx.setSession(ctx.param.key, ctx.param.data)
2138
+
2139
+ // 获取 Session (key 默认为 null 获取所有)
2140
+ let data = ctx.getSession()
2141
+
2142
+ ctx.ok(data)
2143
+ })
2144
+
2145
+ app.run(1234)
2146
+
2147
+ // 其他方法:
2148
+ // ctx.delSession(key) - 删除指定 Session
2149
+ // ctx.clearSession() - 清空 Session
2150
+ ```
2151
+
2152
+ ---
2153
+
2154
+ ## ⌨️命令参数解析
2155
+
2156
+ **用于快速解析process.argv。**
2157
+
2158
+
2159
+ ### 使用示例
2160
+
2161
+ ```javascript
2162
+
2163
+ 'use strict'
2164
+
2165
+ const Topbit = require('Topbit')
2166
+ const parseArgv = Topbit.npargv
2167
+
2168
+ let ret = parseArgv({
2169
+ '--port=' : {
2170
+ name: 'port',
2171
+ //别名
2172
+ alias: '-p',
2173
+ type: 'int',
2174
+ min: 2000,
2175
+ max: 2100
2176
+ },
2177
+
2178
+ '--host=' : {
2179
+ name: 'host',
2180
+ match: /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/i
2181
+ },
2182
+
2183
+ '-w' : {
2184
+ name: 'worker',
2185
+ type: 'int',
2186
+ min: 1,
2187
+ max: 4,
2188
+ //若设置了默认值,则在参数不合法时会自动使用默认值不会返回错误信息。
2189
+ default: 2
2190
+ },
2191
+
2192
+ '--https': {
2193
+ name: 'https'
2194
+ },
2195
+
2196
+ '--http2': {
2197
+ name: 'http2'
2198
+ },
2199
+
2200
+ '--test': {
2201
+ name: 'test'
2202
+ },
2203
+
2204
+ '--id' : {
2205
+ name : 'id',
2206
+ callback: (str) => {
2207
+ return str.split(',').filter(p => p.length > 0)
2208
+ }
2209
+ },
2210
+
2211
+ //自动转换为{type: 'bool', name: 'limit'}
2212
+ '--limit' : 'limit',
2213
+
2214
+ //自动转换为{type: 'bool', name: '-x'}
2215
+ '-x' : false
2216
+
2217
+ })
2218
+
2219
+ console.log(ret)
2220
+
2221
+ ```
2222
+
2223
+ 运行命令参数:
2224
+
2225
+ ```shell
2226
+ $ node test.js x --host=1.2.3.4 --https --http2 --test --port=2010 --id 1,2,3,4,5 --local a b c
2227
+ ```
2228
+
2229
+ 运行输出结果:
2230
+
2231
+ ```javascript
2232
+ {
2233
+ ok: true,
2234
+ errmsg: '',
2235
+ args: {
2236
+ worker: 2,
2237
+ https: true,
2238
+ http2: true,
2239
+ test: true,
2240
+ limit: false,
2241
+ host: '1.2.3.4',
2242
+ port: 2010,
2243
+ id: [ '1', '2', '3', '4', '5' ],
2244
+ '--local': true,
2245
+ '-x': false
2246
+ },
2247
+ // 不是参数,也不是-开头的会放在list中,方便获取。
2248
+ // 如果存在位置引用的参数,则会优先解析,所以x没有在list中。
2249
+ list: ['a', 'b', 'c']
2250
+ }
2251
+
2252
+ ```
2253
+
2254
+ 解析后的结果,若ok为false,则errmsg会给出错误提示信息。若参数无错误,则args是解析后的参数对象。
2255
+
2256
+ 解析过程中,若参数不在传递的对象描述信息内,仍然会在args中体现,key值即为参数的名字,可以参考示例中的--local。
2257
+
2258
+ 如果对参数的描述信息不是object类型,则会转换为boolean类型,此时name值即为参数的名字。参考-x参数。
2259
+
2260
+ **若没有给定type限定范围,则会根据描述自动设定type,这可能会有错误判断,所以尽可能给定type限定。**
2261
+
2262
+ **type推断规则如下:**
2263
+
2264
+ - 若参数含有=,比如--port=,则type为string类型。
2265
+
2266
+ - 否则,若描述对象具备match或callback,则为string类型。
2267
+
2268
+ - 否则,若描述对象含有min或max则为int类型。
2269
+
2270
+ - 否则,若描述对象具备default,则如果default是数字或字符串类型,type和default类型一致。
2271
+
2272
+ - 否则,type为bool。
2273
+
2274
+ **type支持类型如下:**
2275
+
2276
+ - int 或 number,整数类型。
2277
+
2278
+ - float,浮点数类型。
2279
+
2280
+ - string,字符串类型。
2281
+
2282
+ - bool或boolean,布尔类型。
2283
+
2284
+ ### 参数描述的选项
2285
+
2286
+ | 选项 | 是否必须 | 描述 |
2287
+ | ---- | ---- | ---- |
2288
+ | type | 否 | 参数类型,若不给定则会根据描述信息自动设定。 |
2289
+ | name | 否 | 解析后参数的名字,就是解析后参数对象的key值。若不给定则使用参数名称。 |
2290
+ | min | 否 | 限制最小值。 |
2291
+ | max | 否 | 限制最大值。 |
2292
+ | default | 否 | 默认值,若设置,则检测到传递的参数不合法,会采用默认值不会返回错误信息。对于bool来说,default无效。默认值为false。 |
2293
+ | match | 否 | 正则表达式,若给定,会进行正则匹配。 |
2294
+ | callback | 否 | 函数,若给定,会把参数值传递到函数,若函数返回值不是undefined,则作为最后解析的值。 |
2295
+
2296
+ ### autoDefault自动设定默认值
2297
+
2298
+ 在程序解析过程中,有时候需要必须返回参数,不合法则使用默认值,这需要对每个选项都使用default属性,或者使用\@autoDefault让npargv自动设定默认值。
2299
+
2300
+ ```javascript
2301
+ 'use strict'
2302
+
2303
+ const Topbit = require('Topbit')
2304
+ const parseArgv = Topbit.npargv
2305
+
2306
+ let opts = {
2307
+ //启用自动设定默认值。
2308
+ '@autoDefault' : true,
2309
+
2310
+ //没有设定默认值,会自动设定默认值为min的值。
2311
+ '--port' : {
2312
+ min: 1234,
2313
+ max: 5678
2314
+ },
2315
+
2316
+ //没有default设定默认值,类型为数字也没有min的限制,会自动设置为0
2317
+ '-x' : {
2318
+ type : 'int'
2319
+ }
2320
+
2321
+
2322
+ }
2323
+
2324
+ let {args} = parseArgv(opts)
2325
+
2326
+ ```
2327
+
2328
+ 自动设定默认值的规则:
2329
+
2330
+ - 若type为bool则默认值为false
2331
+
2332
+ - 若type为string,则默认值为 空字符串。
2333
+
2334
+ - 若type为number、int、float,则默认值为min属性给定的值,若没有min则默认值为0。
2335
+
2336
+ ### 第一个参数作为子命令
2337
+
2338
+ ```javascript
2339
+
2340
+ let argscfg = {
2341
+ //定义支持的子命令
2342
+ '@command' : [
2343
+ 'create', 'show', 'update', 'delete'
2344
+ ],
2345
+
2346
+ //当不输入时,默认的命令
2347
+ '@defaultCommand': 'show'
2348
+ }
2349
+
2350
+
2351
+ ```
2352
+
2353
+ ### 位置引用参数
2354
+
2355
+ 以 $ 加数字作为key值即表示这是一个位置相关的参数,在解析过程中会先进行位置引用参数的解析。
2356
+
2357
+ 但是位置的索引数字和实际内部的索引有差异:
2358
+
2359
+ - 索引位置从1开始。
2360
+
2361
+ - 索引位置是参数值,不会包括命令名称。
2362
+
2363
+ - 如果设定了@command,则位置1会自动在解析时从command后开始。
2364
+
2365
+ 注意:@command表示的是第一个参数,用于脚本的子命令功能。
2366
+
2367
+ ```javascript
2368
+ const Topbit = require('Topbit')
2369
+ const npargv = Topbit.npargv
2370
+
2371
+ let {args} = npargv({
2372
+ '@autoDefault': true,
2373
+ '$1': {
2374
+ type: 'string',
2375
+ },
2376
+ '$2': {
2377
+ type: 'string',
2378
+ callback: (v) => {
2379
+ return ['i', 'o', 'v'].indexOf(v) >= 0 ? v : 'o'
2380
+ }
2381
+ }
2382
+ })
2383
+
2384
+ ```
2385
+
2386
+ ---
2387
+
2388
+ ## 🤖Loader(服务加载器)
2389
+
2390
+ TopbitLoader 是 Topbit 框架官方推荐的「自动化加载器」扩展,彻底告别手动 `app.get()`、`app.use()` 的繁琐写法。
2391
+
2392
+ 它实现了真正的 **MCM 模式**(Middleware → Controller → Model),类似 MVC 但更轻量、更符合 Topbit 的极致性能哲学。
2393
+
2394
+ [🌐查看详细文档☛](./docs/topbit-loader.md)
2395
+
2396
+ ---
2397
+
2398
+ ## 🔐Token(会话验证)🪙
2399
+
2400
+ TopbitToken 是专为 Topbit 框架打造的零依赖、极简、高安全的加密用户凭证(Token)系统。
2401
+
2402
+ 它完全基于 Node.js 原生 `crypto` 实现,支持:
2403
+
2404
+ - AES-256-GCM(默认,推荐)
2405
+ - AES-192-GCM / AES-128-CBC / AES-256-CBC
2406
+ - 国密 SM4-CBC
2407
+
2408
+ [🌐查看详细文档☛](./docs/topbit-token.md)
1516
2409
 
1517
- 在服务始化时,会根据系统的可用内存来进行自动的设置,除非你必须要自己控制,否则最好是使用默认的配置。
2410
+ ---