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.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Topbit
4
4
 
5
- [中文文档](README.cn.md)
5
+ #### [🪭 中文文档 ☯️](README.cn.md)
6
6
 
7
7
  Topbit is a server-side Web framework based on Node.js. It has no third-party dependencies and is optimized for extreme performance with a unique routing and middleware grouping execution mechanism.
8
8
 
@@ -23,7 +23,7 @@ Topbit is a server-side Web framework based on Node.js. It has no third-party de
23
23
  * **Auto-Scaling:** Optional automatic load mode: creates new child processes to handle requests based on load and restores the initial state when idle.
24
24
  * **Security:** Default settings related to network security to avoid DDoS attacks and other network security issues at the software service level.
25
25
 
26
- ## Installation
26
+ ## 📦Installation
27
27
 
28
28
  ```javascript
29
29
  npm i topbit
@@ -418,7 +418,7 @@ Specifying multiple middlewares in this way can be somewhat complex; you can use
418
418
 
419
419
  const Topbit = require('topbit')
420
420
  // Import ToFile extension
421
- const {ToFile} = require('topbit-toolkit')
421
+ const {ToFile} = Topbit.extensions
422
422
 
423
423
  const app = new Topbit({
424
424
  debug: true
@@ -485,7 +485,7 @@ It supports adding via return values, so passing a callback function is not mand
485
485
 
486
486
  const Topbit = require('topbit')
487
487
  // Import ToFile extension
488
- const {ToFile} = require('topbit-toolkit')
488
+ const {ToFile} = Topbit.extensions
489
489
 
490
490
  const app = new Topbit({
491
491
  debug: true
@@ -552,10 +552,6 @@ app.post('/upload', async c => {
552
552
 
553
553
  let f = c.getFile('image')
554
554
 
555
- // Helper functions: makeName generates a name based on timestamp by default,
556
- // extName parses the file extension.
557
- // let fname = `${c.ext.makeName()}${c.ext.extName(f.filename)}`
558
-
559
555
  // Generate a unique filename based on original extension + timestamp + random number.
560
556
  let fname = c.ext.makeName(f.filename)
561
557
 
@@ -582,8 +578,6 @@ This structure is designed based on the data construction of HTTP protocol file
582
578
  {
583
579
  image : [
584
580
  {
585
- 'content-type': CONTENT_TYPE,
586
- // Available since 23.2.6, alias for content-type for easier access
587
581
  type: CONTENT_TYPE,
588
582
  filename: ORIGIN_FILENAME,
589
583
  start : START,
@@ -597,8 +591,6 @@ This structure is designed based on the data construction of HTTP protocol file
597
591
 
598
592
  video : [
599
593
  {
600
- 'content-type': CONTENT_TYPE,
601
- // Available since 23.2.6, alias for content-type
602
594
  type: CONTENT_TYPE,
603
595
  filename: ORIGIN_FILENAME,
604
596
  start : START,
@@ -728,7 +720,7 @@ app.use(setbodysize, {pre: true});
728
720
 
729
721
  ```
730
722
 
731
- Using `pre` allows for more complex processing, including intercepting and not executing the next layer. For example, the `proxy` module in the `topbit-toolkit` extension uses this feature to implement a high-performance proxy service directly as a framework middleware. Its main operation is to set the request's `data` event to receive data at this layer, handle other logic, and return directly.
723
+ Using `pre` allows for more complex processing, including intercepting and not executing the next layer. For example, the `proxy` extension uses this feature to implement a high-performance proxy service directly as a framework middleware. Its main operation is to set the request's `data` event to receive data at this layer, handle other logic, and return directly.
732
724
 
733
725
  **Dynamically limiting request body size based on different request types**
734
726
 
@@ -1059,7 +1051,9 @@ To add extension support to the request context object, use `app.httpServ.contex
1059
1051
 
1060
1052
  ```javascript
1061
1053
  'use strict'
1054
+
1062
1055
  const Topbit = require('topbit')
1056
+
1063
1057
  const app = new Topbit({
1064
1058
  debug: true
1065
1059
  })
@@ -1186,7 +1180,7 @@ const app = new Topbit({
1186
1180
  if (cluster.isMaster) {
1187
1181
  app.setMsgEvent('test-msg', (worker, msg, handle) => {
1188
1182
  // Child process receives message via message event
1189
- worker.to({
1183
+ worker.send({
1190
1184
  id : worker.id,
1191
1185
  data : 'ok'
1192
1186
  })
@@ -1200,7 +1194,7 @@ if (cluster.isMaster) {
1200
1194
  })
1201
1195
 
1202
1196
  setIneterval(() => {
1203
- process.to({
1197
+ process.send({
1204
1198
  type : 'test-msg',
1205
1199
  pid : process.pid,
1206
1200
  time : (new Date()).toLocaleString()
@@ -1213,7 +1207,7 @@ if (cluster.isMaster) {
1213
1207
 
1214
1208
  Sending messages from worker processes is complex. Since version 22.4.0, a `send` method is provided for quick message sending. It only sends to master from a worker process, so no extra worker check is needed.
1215
1209
 
1216
- ## app.to and app.workerMsg
1210
+ ## app.send and app.workerMsg
1217
1211
 
1218
1212
  Let's rewrite the worker message sending part of the code above:
1219
1213
 
@@ -1229,7 +1223,7 @@ const app = new Topbit({
1229
1223
  // Master process registers message event type. Worker process ignores this.
1230
1224
  app.setMsgEvent('test-msg', (worker, msg, handle) => {
1231
1225
  // Child process receives message via message event
1232
- worker.to({
1226
+ worker.send({
1233
1227
  id : worker.id,
1234
1228
  data : 'ok'
1235
1229
  })
@@ -1246,7 +1240,7 @@ cluster.isWorker
1246
1240
  &&
1247
1241
  setInterval(() => {
1248
1242
  // Only worker executes.
1249
- app.to('test-msg', {
1243
+ app.send('test-msg', {
1250
1244
  pid: process.pid,
1251
1245
  time: (new Date).toLocaleString()
1252
1246
  })
@@ -1287,7 +1281,7 @@ When load is high, child processes are created. After a period of idleness, proc
1287
1281
 
1288
1282
  ----
1289
1283
 
1290
- ## Strong Mode
1284
+ ## 🛡️Strong Mode
1291
1285
 
1292
1286
  Enable strong mode via the `strong` option. This monitors `uncaughtException` and `unhandledRejection` events to ensure stable operation. Simplest usage: set `strong` to `true`.
1293
1287
 
@@ -1343,11 +1337,11 @@ const app = new Topbit({
1343
1337
  ]
1344
1338
 
1345
1339
  }
1346
- });
1340
+ })
1347
1341
 
1348
1342
  ```
1349
1343
 
1350
- ## Simultaneous HTTP and HTTPS?
1344
+ ## 🔔Simultaneous HTTP and HTTPS?
1351
1345
 
1352
1346
  Note the question mark. You shouldn't do this in production. If HTTPS is enabled, HTTP isn't needed, and some frontend features won't work without HTTPS.
1353
1347
 
@@ -1476,6 +1470,869 @@ app.daemon(8008, 2);
1476
1470
 
1477
1471
  ```
1478
1472
 
1479
- **Note: Requires `loadMonitor` option (enabled by default unless set to false).**
1473
+ **Note: Requires `loadMonitor` option (enabled by default).**
1474
+
1475
+ Here is the English translation of the document:
1476
+
1477
+ ---
1478
+
1479
+ # 🧩 Middleware Extensions and Other Extensions 📜
1480
+
1481
+ ---
1482
+
1483
+ As a Web framework, providing only basic functionality is not enough. Many basic functions are used across all Web services, which inevitably leads to the creation of universal extension modules.
1484
+
1485
+ In Topbit's global middleware design mechanism, most web service-related extensions can be completed by developing a middleware extension. You can refer to the `Middleware` section; this plugin mechanism is extremely flexible.
1486
+
1487
+ **Topbit provides several universal middleware extensions that developers can use as needed.**
1488
+
1489
+ **It also provides other extension modules: Route Loader, Token Session Verification, Command Argument Parsing, Automatic Error Log Processing, and ZIP compression/decompression.**
1490
+
1491
+ ## Importing Middleware Extensions
1492
+
1493
+ All middleware extensions are located under `Topbit.extensions`. Example:
1494
+
1495
+ ```javascript
1496
+ 'use strict'
1497
+
1498
+ process.chdir(__dirname)
1499
+
1500
+ const Topbit = require('Topbit')
1501
+
1502
+ // Import extensions via destructuring assignment
1503
+ const {Cors, ToFile} = Topbit.extensions
1504
+
1505
+ const app = new Topbit({
1506
+ debug: true,
1507
+ // Enable logging, alias for globalLog
1508
+ log: true
1509
+ })
1510
+
1511
+ // CORS extension runs before body parsing
1512
+ // Middleware added via 'use' runs after body data parsing
1513
+ app.pre(new Cors())
1514
+ .use(new ToFile, {method: ['PUT', 'POST']})
1515
+
1516
+ app.post('/file', async ctx => {
1517
+ let f = ctx.getFile('file')
1518
+ if (!f) return ctx.status(400).to('file not found')
1519
+
1520
+ ctx.to(
1521
+ await f.toFile('./uploads')
1522
+ )
1523
+ })
1524
+
1525
+ app.run(1235)
1526
+ ```
1527
+
1528
+ ## ☲ Middleware Extensions
1529
+
1530
+ | Middleware | Description |
1531
+ | :--- | :--- |
1532
+ | **Cors** | Middleware to support Cross-Origin Resource Sharing (CORS). |
1533
+ | **Resource** | Static resource processing; a powerful service for serving static files. |
1534
+ | **ToFile** | Equips uploaded file objects with a `toFile` method. |
1535
+ | **SSE** | SSE (Server-Sent Events) protocol middleware for HTTP-based data streaming. |
1536
+ | **Proxy** | Reverse proxy for the HTTP/1.1 protocol, supporting load balancing and alive checks. |
1537
+ | **Http2Proxy** | Reverse proxy for the HTTP/2 protocol, supporting load balancing and alive checks. |
1538
+ | **SNI** | Support for multi-domain HTTPS (Server Name Indication). |
1539
+ | **ParamCheck** | Request parameter validation, supporting checks for `param`, `query`, and `body`. |
1540
+ | **JWT** | Token verification using the JWT protocol. (Note: `Topbit.Token` is recommended instead as it is much stronger than JWT). |
1541
+ | **Cookie** | Parses cookies from the frontend request headers. |
1542
+ | **Session** | Session management using a local file mechanism. |
1543
+
1544
+ ---
1545
+
1546
+ The following documentation is reorganized based on the original `titbit-toolkit` documentation you provided, adapted for the new list order.
1547
+
1548
+ **Note**:
1549
+ * The documentation has been adjusted for the **Topbit** framework context (no longer emphasizing standalone installation of extensions).
1550
+ * Components not included in the list above have been removed.
1551
+
1552
+ ---
1553
+
1554
+ ### 1. Cors (Cross-Origin Support)
1555
+
1556
+ Middleware that supports cross-origin requests. It supports cross-origin communication via the `origin` header as well as resource inclusion via `referer`, allowing for flexible selection and configuration.
1557
+
1558
+ **Basic Usage:**
1559
+
1560
+ ```javascript
1561
+ const Topbit = require('topbit')
1562
+ const {Cors} = Topbit.extensions
1563
+
1564
+ let cr = new Cors({
1565
+ // Defaults to *, meaning cross-origin is enabled for all.
1566
+ // If you want to specify supported domains, pass an array.
1567
+ // Note: This specifies the domain of the application page accessing the API.
1568
+ allow : [
1569
+ 'https://a.com',
1570
+ 'https://www.a.com',
1571
+ ],
1572
+
1573
+ // Defaults to content-type only
1574
+ allowHeaders : 'authorization,content-type',
1575
+
1576
+ // Caches OPTIONS requests for 60 seconds; the browser will read from cache during this period.
1577
+ optionsCache: 60,
1578
+
1579
+ // Defaults to '*', sets allowed request methods.
1580
+ // requestHeaders : '*'
1581
+
1582
+ // Defaults to '', indicating which headers can be exposed to the requester. Multiple headers are separated by commas.
1583
+ // exposeHeaders: 'x-test-key,x-type'
1584
+ })
1585
+
1586
+ app.pre(cr)
1587
+
1588
+ // Supports OPTIONS requests. Browsers send an OPTIONS preflight request before processing POST/PUT/DELETE.
1589
+ // You must handle OPTIONS requests to fully support CORS.
1590
+ app.options('/*', async c => {})
1591
+ ```
1592
+
1593
+ **Advanced Configuration (Referer Check & Grouping):**
1594
+
1595
+ ```javascript
1596
+ let cr = new cors({
1597
+ // Do not allow empty referer
1598
+ allowEmptyReferer: false,
1599
+
1600
+ // Route groups that allow empty referer.
1601
+ // Group names are custom; refer to the framework's route grouping feature.
1602
+ // This is mainly used when API development also needs to serve pages.
1603
+ emptyRefererGroup: [
1604
+ '@webapp', '@pages'
1605
+ ],
1606
+
1607
+ allow: [
1608
+ // Default string format allows both origin cross-domain and referer external linking
1609
+ 'https://w.a.com',
1610
+ 'https://w.x.cn',
1611
+
1612
+ {url: 'https://servicewechat.com/wx234hiohr23', referer: true},
1613
+ // Not applied to referer check; if a request submits referer as self-define, it will not pass
1614
+ {url: 'self-define', referer: false}
1615
+ ],
1616
+ })
1617
+ ```
1618
+
1619
+ ---
1620
+
1621
+ ### 2. Resource (Static Resource Processing)
1622
+
1623
+ Static resource processing service, mainly used for hosting JS, CSS, images, audio, and other static files. Supports cache control, route prefix correction, and more.
1624
+
1625
+ **Basic Usage:**
1626
+
1627
+ ```javascript
1628
+ const Topbit = require('topbit')
1629
+ const {Resource} = Topbit.extensions
1630
+
1631
+ let st = new Resource({
1632
+ // Set the directory where static resources are located
1633
+ staticPath: './public'
1634
+ })
1635
+
1636
+ // Execute middleware only for the 'static' group.
1637
+ app.use(st, {group: 'static'})
1638
+
1639
+ // Add static resource route. Assuming 'css/a.css' exists in the 'public' directory.
1640
+ // Access via /static/css/a.css
1641
+ app.get('/static/*', async c => {
1642
+ // Request is in group 'static'
1643
+ }, '@static')
1644
+ ```
1645
+
1646
+ **Quick Example & Configuration Details:**
1647
+
1648
+ ```javascript
1649
+ let st = new Resource({
1650
+ // Set the directory where static resources are located
1651
+ staticPath: './public',
1652
+
1653
+ // Route path: Frontend must start with /static/, mapping to files in ./public
1654
+ routePath : '/static/*',
1655
+
1656
+ // Specify route group
1657
+ routeGroup: '_static',
1658
+
1659
+ // Whether to support decoding of Chinese paths (default false)
1660
+ decodePath: true,
1661
+
1662
+ // Prefix path correction. If set to xyz, it automatically corrects to /xyz.
1663
+ // prepath : '',
1664
+
1665
+ // Maximum file size to cache (in bytes); files larger than this will not be cached.
1666
+ maxFileSize: 12_000_000,
1667
+
1668
+ // Set the value of the cache-control header, defaults to null
1669
+ cacheControl: 'max-age=3600',
1670
+
1671
+ // Maximum total cache size (default 120MB)
1672
+ maxCacheSize: 120_000_000,
1673
+
1674
+ // Failure limit and cache release probability
1675
+ failedLimit: 50,
1676
+ prob: 6
1677
+ })
1678
+
1679
+ st.init(app)
1680
+
1681
+ // Automatically added to the _static group.
1682
+ // Example: If favicon.ico is in the public directory, it can be accessed via this request.
1683
+ app.get('/favicon.ico', async c => {}, {group: '_static'})
1684
+ ```
1685
+
1686
+ **Interface Description:**
1687
+ * `addType(obj)`: Add mapping from extension to content-type.
1688
+ * `clearCache()`: Clear the cache.
1689
+
1690
+ ---
1691
+
1692
+ ### 3. ToFile (File Upload Objectification)
1693
+
1694
+ Equips uploaded file objects with a `toFile` method, facilitating file saving in an object-oriented style.
1695
+
1696
+ ```javascript
1697
+ const Topbit = require('topbit')
1698
+ const {ToFile} = Topbit.extensions
1699
+
1700
+ app.use( new ToFile() )
1701
+
1702
+ app.post('/upload', async c => {
1703
+ // Get the uploaded file object
1704
+ let f = c.getFile('image')
1705
+
1706
+ if (!f) {
1707
+ return c.status(400).oo('image file not found')
1708
+ }
1709
+
1710
+ // Move the file to the 'images' directory (ensure the directory exists).
1711
+ // The second parameter is optional to specify a filename; defaults to a unique name based on timestamp and random number.
1712
+ let fname = await f.toFile('./images')
1713
+
1714
+ // Return filename
1715
+ c.ok(fname)
1716
+ })
1717
+
1718
+ app.run(1234)
1719
+ ```
1720
+
1721
+ ---
1722
+
1723
+ ### 4. SSE (Server-Side Messaging)
1724
+
1725
+ Server-Sent Events (SSE) middleware based on the HTTP protocol, used for the server to proactively push data streams to the client.
1726
+
1727
+ **Basic Usage:**
1728
+
1729
+ ```javascript
1730
+ const Topbit = require('topbit')
1731
+ const {SSE} = Topbit.extensions
1732
+
1733
+ let s = new SSE({
1734
+ // Timeout 20 seconds; connection closes automatically after this time
1735
+ timeout: 20000,
1736
+
1737
+ // Reconnection time; default is 0 meaning do not reconnect
1738
+ retry: 5000,
1739
+
1740
+ // Timer interval; execute the handler function every 2 seconds
1741
+ timeSlice: 2000
1742
+ })
1743
+
1744
+ // Set handler function, ctx is the request context
1745
+ s.handle = (ctx) => {
1746
+ // Send a single message
1747
+ ctx.sendmsg({event: 'eat', data: 'Rice'})
1748
+
1749
+ if (Date.now() % 5 === 0) {
1750
+ // Send multiple messages. Strings or numbers are automatically converted to message format.
1751
+ // Default event is 'message'.
1752
+ ctx.sendmsg([
1753
+ {event: 'clock', data: Date.now()},
1754
+ 'May we all be blessed with longevity.',
1755
+ Math.random()
1756
+ ])
1757
+ }
1758
+ }
1759
+
1760
+ // Enable middleware only for route group 'sse'
1761
+ app.use( s, {group: 'sse'} )
1762
+
1763
+ app.get('/sse', async ctx => {}, {group: 'sse'})
1764
+
1765
+ app.run(1234)
1766
+ ```
1767
+
1768
+ **Generator Mode:**
1769
+ Suitable for scenarios requiring dynamic interval adjustment or precise control.
1770
+
1771
+ ```javascript
1772
+ let s = new SSE({
1773
+ timeout: 20000,
1774
+ retry: 5000,
1775
+ timeSlice: 10,
1776
+ mode: 'yield' // Enable generator mode
1777
+ })
1778
+
1779
+ s.handle = (ctx) => {
1780
+ ctx.sendmsg({event: 'eat', data: 'Rice'})
1781
+ // Use await ctx.sse.moment(ms) for delay
1782
+ await ctx.sse.moment(100)
1783
+ }
1784
+ ```
1785
+
1786
+ ---
1787
+
1788
+ ### 5. Proxy (HTTP/1.1 Reverse Proxy)
1789
+
1790
+ Reverse proxy for HTTP/1.1 protocol, supporting load balancing and Alive Check.
1791
+
1792
+ **Basic Proxy Configuration:**
1793
+
1794
+ ```javascript
1795
+ const Topbit = require('topbit')
1796
+ const {Proxy} = Topbit.extensions
1797
+
1798
+ let hostcfg = {
1799
+ // Simple mapping: default path is /
1800
+ 'a.com' : 'http://localhost:8001',
1801
+
1802
+ // Specific path mapping
1803
+ 'b.com' : {
1804
+ path : '/xyz',
1805
+ url : 'http://localhost:8002'
1806
+ },
1807
+
1808
+ // Complex config and rewrite
1809
+ 'c.com' : [
1810
+ {
1811
+ path : '/',
1812
+ url : 'http://localhost:8004',
1813
+ rewrite: (ctx, path) => {
1814
+ // Custom route rewrite rules
1815
+ if (path.indexOf('/xyz') === 0) {
1816
+ return path.replace('/xyz', '/w')
1817
+ }
1818
+ }
1819
+ }
1820
+ ]
1821
+ };
1822
+
1823
+ const pxy = new Proxy({
1824
+ timeout: 10000,
1825
+ config: hostcfg
1826
+ })
1827
+
1828
+ pxy.init(app)
1829
+
1830
+ app.run(1234)
1831
+ ```
1832
+
1833
+ **Load Balancing Configuration:**
1834
+ Implemented via configuration arrays, supporting `weight` and alive checks.
1835
+
1836
+ ```javascript
1837
+ let load_balance_cfg = {
1838
+ 'a.com' : [
1839
+ {
1840
+ path : '/',
1841
+ url : 'http://localhost:1234',
1842
+ // Check service liveness every 3 seconds
1843
+ aliveCheckInterval : 3,
1844
+ aliveCheckPath : '/alive-check',
1845
+ // Weight: larger numbers mean higher weight
1846
+ weight: 3
1847
+ },
1848
+ {
1849
+ path : '/',
1850
+ url : 'http://localhost:1235',
1851
+ aliveCheckInterval : 3,
1852
+ aliveCheckPath : '/ok',
1853
+ weight: 2
1854
+ }
1855
+ ]
1856
+ }
1857
+
1858
+ const pxy = new Proxy({
1859
+ timeout: 10000,
1860
+ starPath : true, // If enabled, forwards the string after the proxy path as the path
1861
+ config: load_balance_cfg
1862
+ })
1863
+
1864
+ pxy.init(app)
1865
+ ```
1866
+
1867
+ ---
1868
+
1869
+ ### 6. Http2Proxy (HTTP/2 Reverse Proxy)
1870
+
1871
+ Reverse proxy for HTTP/2 protocol. Parameters are basically the same as `Proxy`, using HTTP/2 connection mechanisms for keep-alive.
1872
+
1873
+ ```javascript
1874
+ const Topbit = require('topbit')
1875
+ const {Http2Proxy} = Topbit.extensions
1876
+
1877
+ let hxy = new Http2Proxy({
1878
+ config: {
1879
+ 'a.com' : [
1880
+ {
1881
+ url: 'http://localhost:3001',
1882
+ weight: 10,
1883
+ path : '/',
1884
+ reconnDelay: 200, // Reconnection delay
1885
+ max: 2,
1886
+ headers: {
1887
+ 'x-test-key': `${Date.now()}`
1888
+ },
1889
+ connectTimeout: 2000
1890
+ },
1891
+ {
1892
+ url: 'http://localhost:3002',
1893
+ weight: 4,
1894
+ path : '/'
1895
+ }
1896
+ ]
1897
+ },
1898
+ debug: true
1899
+ })
1900
+
1901
+ hxy.init(app)
1902
+
1903
+ app.run(1234)
1904
+ ```
1905
+
1906
+ ---
1907
+
1908
+ ### 7. SNI (HTTPS Multi-Domain Support)
1909
+
1910
+ **Description**: Middleware to support multiple HTTPS domain certificates on the same IP address and port. It uses the TLS protocol's Server Name Indication feature to dynamically load the corresponding SSL certificate based on the domain requested by the client.
1911
+
1912
+ **Note**: Certificates are read synchronously during initialization; please ensure paths are correct. If a certificate for a domain fails to read, an error will be output to the console, but it will not block the loading of other domains.
1913
+
1914
+ **Usage:**
1915
+
1916
+ ```javascript
1917
+ const Topbit = require('topbit')
1918
+ const {SNI} = Topbit.extensions
1919
+
1920
+ const app = new Topbit({
1921
+ // Enable HTTPS; it's recommended to configure a default key and cert as fallback
1922
+ https: true,
1923
+ key: './ssl/default.key',
1924
+ cert: './ssl/default.cert',
1925
+ debug: true
1926
+ })
1927
+
1928
+ // Configure multi-domain certificates
1929
+ // Key is the domain, Value is an object containing paths to key and cert
1930
+ let certs = {
1931
+ 'a.com': {
1932
+ key: './ssl/a.com.key',
1933
+ cert: './ssl/a.com.cert'
1934
+ },
1935
+ 'www.b.org': {
1936
+ key: '/etc/nginx/ssl/b.org.key',
1937
+ cert: '/etc/nginx/ssl/b.org.crt'
1938
+ }
1939
+ }
1940
+
1941
+ // Instantiate and initialize
1942
+ let sni = new SNI(certs)
1943
+
1944
+ // The init method injects SNICallback into app.config.server
1945
+ sni.init(app)
1946
+
1947
+ app.run(443)
1948
+ ```
1949
+
1950
+ **Parameters**:
1951
+ * The constructor accepts an object where keys are **domains** and values are configuration objects.
1952
+ * The configuration object must contain:
1953
+ * `key`: Path to the private key file.
1954
+ * `cert`: Path to the certificate file.
1955
+
1956
+ ---
1957
+
1958
+ ### 8. ParamCheck (Request Parameter Validation)
1959
+
1960
+ Supports declarative checks for `param` (route parameters), `query` (query strings), and `body` (request body).
1961
+
1962
+ **Query and Param Check:**
1963
+
1964
+ ```javascript
1965
+ const Topbit = require('topbit')
1966
+ const {ParamCheck} = Topbit.extensions
1967
+
1968
+ // Query parameter check
1969
+ let pck = new ParamCheck({
1970
+ key: 'query',
1971
+ data : {
1972
+ // Strictly restrict value
1973
+ say: 'hello',
1974
+ // Type conversion and range restriction
1975
+ offset: {
1976
+ default: 0,
1977
+ to: 'int',
1978
+ min: 0,
1979
+ max: 100
1980
+ }
1981
+ }
1982
+ })
1983
+
1984
+ // Param parameter check and custom callback
1985
+ let paramck = new ParamCheck({
1986
+ key: 'param',
1987
+ deny: ['x-key'], // Fields forbidden to submit
1988
+ deleteDeny: true,
1989
+ data : {
1990
+ errorMessage: 'Parameter Error', // Custom error message
1991
+ mobile: {
1992
+ callback: (obj, k, method) => {
1993
+ let preg = /^(12|13|15|16|17|18|19)[0-9]{9}$/
1994
+ return preg.test(obj[k])
1995
+ }
1996
+ }
1997
+ }
1998
+ })
1999
+
2000
+ app.use(pck, {method: 'GET'})
2001
+ .use(paramck, {method: 'GET'})
2002
+ ```
2003
+
2004
+ **Body Check:**
2005
+
2006
+ ```javascript
2007
+ let pmbody = new ParamCheck({
2008
+ key: 'body',
2009
+ data: {
2010
+ username: { must: true },
2011
+ passwd: { must: true }
2012
+ }
2013
+ })
2014
+
2015
+ app.use(pmbody, {method: ['POST', 'PUT'], name: 'login'})
2016
+ ```
2017
+
2018
+ ---
2019
+
2020
+ ### 9. JWT (Token Verification)
2021
+
2022
+ JWT protocol Token verification and issuance. It is recommended to use `Topbit.Token` instead.
2023
+
2024
+ ```javascript
2025
+ const Topbit = require('topbit')
2026
+ const {JWT} = Topbit.extensions
2027
+
2028
+ let j = new JWT({
2029
+ timeout: 7200000, // Timeout duration
2030
+ })
2031
+ j.alg = 'hs512' // Set algorithm
2032
+
2033
+ // Issue Token
2034
+ let token = j.make({
2035
+ id: '123we',
2036
+ username: 'long'
2037
+ })
2038
+
2039
+ // Verify Token
2040
+ // Returns object: { ok: true, data: {...} } or { ok: false, errcode: '...' }
2041
+ let r = j.verify(token)
2042
+
2043
+ // Use as middleware
2044
+ // On successful verification, data is mounted to ctx.box.user
2045
+ app.pre(j.mid())
2046
+ ```
2047
+
2048
+ ---
2049
+
2050
+ ### 10. Cookie (Cookie Parsing)
2051
+
2052
+ Parses Cookies from the frontend request headers and adds a `cookie` property to the request context.
2053
+
2054
+ ```javascript
2055
+ const Topbit = require('topbit')
2056
+ const {Cookie} = Topbit.extensions
2057
+
2058
+ app.use( new Cookie() )
2059
+
2060
+ app.get('/', async ctx => {
2061
+ // Get parsed cookie object
2062
+ console.log(ctx.cookie)
2063
+ })
2064
+
2065
+ app.run(1234)
2066
+ ```
2067
+
2068
+ ---
2069
+
2070
+ ### 11. Session (Session Management)
2071
+
2072
+ Session management based on local file mechanisms, dependent on the Cookie component.
2073
+ **Note**: This extension is mainly for testing and teaching; for production environments, it is recommended to use Redis or other database storage solutions.
2074
+
2075
+ ```javascript
2076
+ const Topbit = require('topbit')
2077
+ const {Cookie,Session} = Topbit.extensions
2078
+
2079
+ let sess = new Session()
2080
+ sess.init(app) // Initialize: Adds ctx prototype methods
2081
+
2082
+ // Must enable Cookie first, then Session
2083
+ app.use( new Cookie() ).use( sess )
2084
+
2085
+ app.get('/:key/:data', async ctx => {
2086
+ // Set Session
2087
+ ctx.setSession(ctx.param.key, ctx.param.data)
2088
+
2089
+ // Get Session (key defaults to null to get all)
2090
+ let data = ctx.getSession()
2091
+
2092
+ ctx.ok(data)
2093
+ })
2094
+
2095
+ app.run(1234)
2096
+
2097
+ // Other methods:
2098
+ // ctx.delSession(key) - Delete specific Session
2099
+ // ctx.clearSession() - Clear all Sessions
2100
+ ```
2101
+
2102
+ ---
2103
+
2104
+ ## ⌨️ Command Argument Parsing
2105
+
2106
+ **Used for quickly parsing `process.argv`.**
2107
+
2108
+ ### Usage Example
2109
+
2110
+ ```javascript
2111
+ 'use strict'
2112
+
2113
+ const Topbit = require('Topbit')
2114
+ const parseArgv = Topbit.npargv
2115
+
2116
+ let ret = parseArgv({
2117
+ '--port=' : {
2118
+ name: 'port',
2119
+ // Alias
2120
+ alias: '-p',
2121
+ type: 'int',
2122
+ min: 2000,
2123
+ max: 2100
2124
+ },
2125
+
2126
+ '--host=' : {
2127
+ name: 'host',
2128
+ match: /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/i
2129
+ },
2130
+
2131
+ '-w' : {
2132
+ name: 'worker',
2133
+ type: 'int',
2134
+ min: 1,
2135
+ max: 4,
2136
+ // If a default value is set, it will automatically use the default value instead of returning an error when the argument is invalid.
2137
+ default: 2
2138
+ },
2139
+
2140
+ '--https': {
2141
+ name: 'https'
2142
+ },
2143
+
2144
+ '--http2': {
2145
+ name: 'http2'
2146
+ },
2147
+
2148
+ '--test': {
2149
+ name: 'test'
2150
+ },
2151
+
2152
+ '--id' : {
2153
+ name : 'id',
2154
+ callback: (str) => {
2155
+ return str.split(',').filter(p => p.length > 0)
2156
+ }
2157
+ },
2158
+
2159
+ // Automatically converts to {type: 'bool', name: 'limit'}
2160
+ '--limit' : 'limit',
2161
+
2162
+ // Automatically converts to {type: 'bool', name: '-x'}
2163
+ '-x' : false
2164
+
2165
+ })
2166
+
2167
+ console.log(ret)
2168
+ ```
2169
+
2170
+ Run with command arguments:
2171
+
2172
+ ```shell
2173
+ $ node test.js x --host=1.2.3.4 --https --http2 --test --port=2010 --id 1,2,3,4,5 --local a b c
2174
+ ```
2175
+
2176
+ Output result:
2177
+
2178
+ ```javascript
2179
+ {
2180
+ ok: true,
2181
+ errmsg: '',
2182
+ args: {
2183
+ worker: 2,
2184
+ https: true,
2185
+ http2: true,
2186
+ test: true,
2187
+ limit: false,
2188
+ host: '1.2.3.4',
2189
+ port: 2010,
2190
+ id: [ '1', '2', '3', '4', '5' ],
2191
+ '--local': true,
2192
+ '-x': false
2193
+ },
2194
+ // Items that are not arguments and do not start with - are placed in 'list' for easy access.
2195
+ // If positional reference arguments exist, they are parsed first, so 'x' is not in the list here (assuming it was consumed).
2196
+ list: ['a', 'b', 'c']
2197
+ }
2198
+ ```
2199
+
2200
+ In the parsed result, if `ok` is false, `errmsg` will provide the error message. If there are no errors, `args` is the parsed argument object.
2201
+
2202
+ During parsing, if an argument is not in the passed object description, it will still appear in `args`, with the key being the argument name (see `--local` in the example).
2203
+
2204
+ If the description info for an argument is not an object, it converts to boolean type, using the argument name as `name`. See the `-x` parameter.
2205
+
2206
+ **If no `type` is specified to limit the scope, the `type` is automatically set based on the description. This may lead to incorrect judgments, so specify `type` whenever possible.**
2207
+
2208
+ **Type inference rules:**
2209
+
2210
+ - If the argument contains `=`, e.g., `--port=`, the type is `string`.
2211
+ - Otherwise, if the description object has `match` or `callback`, it is `string` type.
2212
+ - Otherwise, if the description object contains `min` or `max`, it is `int` type.
2213
+ - Otherwise, if the description object has `default`, and `default` is a number or string, the type matches the `default` type.
2214
+ - Otherwise, the type is `bool`.
2215
+
2216
+ **Supported types:**
2217
+
2218
+ - `int` or `number`: Integer type.
2219
+ - `float`: Floating-point type.
2220
+ - `string`: String type.
2221
+ - `bool` or `boolean`: Boolean type.
2222
+
2223
+ ### Argument Description Options
2224
+
2225
+ | Option | Required | Description |
2226
+ | :--- | :--- | :--- |
2227
+ | type | No | Argument type. If not given, it is automatically set based on description info. |
2228
+ | name | No | Name of the parsed argument (key in the parsed object). If not given, uses the argument name. |
2229
+ | min | No | Restricts minimum value. |
2230
+ | max | No | Restricts maximum value. |
2231
+ | default | No | Default value. If set, invalid arguments will default to this value without returning an error. `default` is invalid for bool types (defaults to false). |
2232
+ | match | No | Regular expression. If given, regex matching is performed. |
2233
+ | callback | No | Function. If given, passes the argument value to the function. If the return value is not undefined, it is used as the final parsed value. |
2234
+
2235
+ ### autoDefault (Automatic Default Values)
2236
+
2237
+ Sometimes the program requires parameters to be returned, using default values if invalid. This usually requires setting the `default` property for every option, or using `@autoDefault` to let `npargv` set defaults automatically.
2238
+
2239
+ ```javascript
2240
+ 'use strict'
2241
+
2242
+ const Topbit = require('Topbit')
2243
+ const parseArgv = Topbit.npargv
2244
+
2245
+ let opts = {
2246
+ // Enable automatic default setting.
2247
+ '@autoDefault' : true,
2248
+
2249
+ // No default set; automatically sets default to the value of min.
2250
+ '--port' : {
2251
+ min: 1234,
2252
+ max: 5678
2253
+ },
2254
+
2255
+ // No default set, type is number, no min limit; automatically sets to 0.
2256
+ '-x' : {
2257
+ type : 'int'
2258
+ }
2259
+ }
2260
+
2261
+ let {args} = parseArgv(opts)
2262
+ ```
2263
+
2264
+ Rules for automatic defaults:
2265
+
2266
+ - If `type` is `bool`, default is `false`.
2267
+ - If `type` is `string`, default is an empty string.
2268
+ - If `type` is `number`, `int`, or `float`, default is the value of `min`. If no `min`, default is `0`.
2269
+
2270
+ ### First Argument as Subcommand
2271
+
2272
+ ```javascript
2273
+ let argscfg = {
2274
+ // Define supported subcommands
2275
+ '@command' : [
2276
+ 'create', 'show', 'update', 'delete'
2277
+ ],
2278
+
2279
+ // Default command when none is entered
2280
+ '@defaultCommand': 'show'
2281
+ }
2282
+ ```
2283
+
2284
+ ### Positional Reference Arguments
2285
+
2286
+ Using `$` followed by a number as a key indicates a positional argument. These are parsed first.
2287
+
2288
+ However, the positional index differs from internal indexing:
2289
+
2290
+ - Index starts at 1.
2291
+ - Index refers to argument values, excluding the command name.
2292
+ - If `@command` is set, position 1 automatically starts after the command.
2293
+
2294
+ Note: `@command` represents the first argument, used for script subcommand functionality.
2295
+
2296
+ ```javascript
2297
+ const Topbit = require('Topbit')
2298
+ const npargv = Topbit.npargv
2299
+
2300
+ let {args} = npargv({
2301
+ '@autoDefault': true,
2302
+ '$1': {
2303
+ type: 'string',
2304
+ },
2305
+ '$2': {
2306
+ type: 'string',
2307
+ callback: (v) => {
2308
+ return ['i', 'o', 'v'].indexOf(v) >= 0 ? v : 'o'
2309
+ }
2310
+ }
2311
+ })
2312
+ ```
2313
+
2314
+ ---
2315
+
2316
+ ## 🤖 Loader (Service Loader)
2317
+
2318
+ TopbitLoader is the official "automatic loader" extension recommended by the Topbit framework, completely eliminating the tedious manual writing of `app.get()` and `app.use()`.
2319
+
2320
+ It implements a true **MCM Pattern** (Middleware → Controller → Model), similar to MVC but lighter and more aligned with Topbit's extreme performance philosophy.
2321
+
2322
+ [🌐 View Detailed Documentation ☛](./docs/en/topbit-loader.md)
2323
+
2324
+ ---
2325
+
2326
+ ## 🔐 Token (Session Verification) 🪙
2327
+
2328
+ TopbitToken is a zero-dependency, minimalist, high-security encrypted user credential (Token) system built specifically for the Topbit framework.
2329
+
2330
+ It is implemented entirely based on Node.js native `crypto`, supporting:
2331
+
2332
+ - AES-256-GCM (Default, Recommended)
2333
+ - AES-192-GCM / AES-128-CBC / AES-256-CBC
2334
+ - SM4-CBC (National Standard)
2335
+
2336
+ [🌐 View Detailed Documentation ☛](./docs/en/topbit-token.md)
1480
2337
 
1481
- Service initialization automatically sets configuration based on available system memory. Unless necessary, stick to default configurations.
2338
+ ---