topbit 3.0.6 → 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 +923 -30
- package/README.md +879 -22
- package/demo/upserv.js +35 -0
- package/docs/README.md +2 -2
- package/docs/en/topbit-loader.md +1 -1
- package/docs/en/topbit-token.md +3 -1
- package/docs/topbit-loader.md +1 -1
- package/docs/topbit-token.md +3 -1
- package/images/topbit.webp +0 -0
- package/package.json +1 -1
- package/src/bodyparser.js +1 -3
- package/src/extends/cookie.js +4 -5
- package/src/extends/jwt.js +2 -3
- package/src/extends/session.js +1 -1
- package/src/extends/timing.js +2 -2
- package/src/extends/tofile.js +2 -2
- package/src/topbit.js +4 -1
package/README.md
CHANGED
|
@@ -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} =
|
|
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} =
|
|
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`
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
2338
|
+
---
|