soajs.controller 4.2.42 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +43 -0
- package/.github/workflows/release.yml +94 -0
- package/ARCHITECTURE.md +193 -0
- package/Gruntfile.js +1 -1
- package/docs/configuration-custom-registry.md +421 -0
- package/docs/configuration-services-config.md +275 -0
- package/docs/data-flow.md +138 -0
- package/docs/deployment.md +205 -0
- package/docs/external-integrations.md +159 -0
- package/docs/gateway-routes.md +259 -0
- package/docs/maintenance-routes.md +308 -0
- package/docs/middleware/awareness.md +120 -0
- package/docs/middleware/cors.md +88 -0
- package/docs/middleware/enhancer.md +75 -0
- package/docs/middleware/favicon.md +36 -0
- package/docs/middleware/gotoService.md +146 -0
- package/docs/middleware/ip2ban.md +70 -0
- package/docs/middleware/key.md +196 -0
- package/docs/middleware/keyACL.md +139 -0
- package/docs/middleware/lastSeen.md +111 -0
- package/docs/middleware/maintenanceMode.md +81 -0
- package/docs/middleware/mt.md +568 -0
- package/docs/middleware/oauth.md +173 -0
- package/docs/middleware/response.md +106 -0
- package/docs/middleware/soajs.md +71 -0
- package/docs/middleware/traffic.md +190 -0
- package/docs/middleware/url.md +102 -0
- package/docs/multi-tenant-security.md +183 -0
- package/docs/request-flow.md +230 -0
- package/docs/service-discovery.md +167 -0
- package/mw/key/index.js +16 -1
- package/mw/mt/urac.js +21 -1
- package/mw/mt/utils.js +2 -0
- package/package.json +13 -13
- package/test/unit/mw/key/index.js +150 -0
- package/test/unit/mw/mt/urac.js +157 -4
- package/.travis.yml +0 -72
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# OAuth Middleware
|
|
2
|
+
|
|
3
|
+
**File:** `mw/oauth/index.js`
|
|
4
|
+
|
|
5
|
+
**Order:** 15
|
|
6
|
+
|
|
7
|
+
The OAuth middleware handles authentication by validating OAuth2 tokens or JWT tokens.
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
|
|
11
|
+
- Validates OAuth2 access tokens via oauth2-server
|
|
12
|
+
- Validates JWT tokens via jsonwebtoken
|
|
13
|
+
- Supports tenant-specific OAuth configuration
|
|
14
|
+
- Sets `req.oauth` with token information
|
|
15
|
+
|
|
16
|
+
## Flow
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Incoming Request
|
|
20
|
+
│
|
|
21
|
+
▼
|
|
22
|
+
┌─────────────────────────┐
|
|
23
|
+
│ Determine OAuth Type │ tenantOauth.type or
|
|
24
|
+
│ │ registry.serviceConfig.oauth.type
|
|
25
|
+
└────────┬────────────────┘
|
|
26
|
+
│
|
|
27
|
+
├─── Type 0 (JWT) ──▶ JWT Validation
|
|
28
|
+
│
|
|
29
|
+
├─── Type 2 (OAuth2) ──▶ OAuth2 Server Validation
|
|
30
|
+
│
|
|
31
|
+
▼
|
|
32
|
+
┌─────────────────────────┐
|
|
33
|
+
│ Set req.oauth │ { bearerToken, type }
|
|
34
|
+
└────────┬────────────────┘
|
|
35
|
+
│
|
|
36
|
+
▼
|
|
37
|
+
next() or Error
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## OAuth Types
|
|
41
|
+
|
|
42
|
+
### Type 2: OAuth2 (Default)
|
|
43
|
+
|
|
44
|
+
Uses oauth2-server library for token validation.
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
Request with access_token
|
|
48
|
+
│
|
|
49
|
+
▼
|
|
50
|
+
┌─────────────────────────┐
|
|
51
|
+
│ oauth2-server │ Validates token against DB
|
|
52
|
+
│ authorise() │
|
|
53
|
+
└────────┬────────────────┘
|
|
54
|
+
│
|
|
55
|
+
├─── Invalid ──▶ OAuth2Error
|
|
56
|
+
│
|
|
57
|
+
▼
|
|
58
|
+
┌─────────────────────────┐
|
|
59
|
+
│ req.oauth = { │
|
|
60
|
+
│ bearerToken: user, │
|
|
61
|
+
│ type: 2 │
|
|
62
|
+
│ } │
|
|
63
|
+
└─────────────────────────┘
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Type 0: JWT
|
|
67
|
+
|
|
68
|
+
Uses jsonwebtoken library for JWT validation.
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
Request with Authorization: Bearer <jwt>
|
|
72
|
+
│
|
|
73
|
+
▼
|
|
74
|
+
┌─────────────────────────┐
|
|
75
|
+
│ Extract Bearer Token │ from Authorization header
|
|
76
|
+
└────────┬────────────────┘
|
|
77
|
+
│
|
|
78
|
+
├─── No match ──▶ Error 143
|
|
79
|
+
│
|
|
80
|
+
▼
|
|
81
|
+
┌─────────────────────────┐
|
|
82
|
+
│ jwt.verify() │ Using secret and algorithms
|
|
83
|
+
└────────┬────────────────┘
|
|
84
|
+
│
|
|
85
|
+
├─── Invalid ──▶ Error 143
|
|
86
|
+
│
|
|
87
|
+
▼
|
|
88
|
+
┌─────────────────────────┐
|
|
89
|
+
│ req.oauth = { │
|
|
90
|
+
│ bearerToken: decoded,│
|
|
91
|
+
│ type: 0 │
|
|
92
|
+
│ } │
|
|
93
|
+
└─────────────────────────┘
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Configuration
|
|
97
|
+
|
|
98
|
+
### OAuth2 Configuration
|
|
99
|
+
|
|
100
|
+
Located in `registry.serviceConfig.oauth`:
|
|
101
|
+
|
|
102
|
+
```javascript
|
|
103
|
+
{
|
|
104
|
+
"oauth": {
|
|
105
|
+
"type": 2,
|
|
106
|
+
"grants": ["password", "refresh_token"],
|
|
107
|
+
"debug": false,
|
|
108
|
+
"accessTokenLifetime": 7200,
|
|
109
|
+
"refreshTokenLifetime": 1209600
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### JWT Configuration
|
|
115
|
+
|
|
116
|
+
```javascript
|
|
117
|
+
{
|
|
118
|
+
"oauth": {
|
|
119
|
+
"type": 0,
|
|
120
|
+
"secret": "jwt_secret_key",
|
|
121
|
+
"algorithms": ["HS256"],
|
|
122
|
+
"audience": "my-app"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Tenant-Specific OAuth
|
|
128
|
+
|
|
129
|
+
Tenants can override OAuth settings via `tenantOauth`:
|
|
130
|
+
|
|
131
|
+
```javascript
|
|
132
|
+
{
|
|
133
|
+
"type": 0, // Override to JWT
|
|
134
|
+
"secret": "tenant_jwt_secret"
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## OAuth Service Integration
|
|
139
|
+
|
|
140
|
+
Default OAuth service configuration:
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
{
|
|
144
|
+
"oauthService": {
|
|
145
|
+
"name": "oauth",
|
|
146
|
+
"tokenApi": "/token",
|
|
147
|
+
"authorizationApi": "/authorization",
|
|
148
|
+
"pinApi": "/pin"
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Error Codes
|
|
154
|
+
|
|
155
|
+
| Code | Description |
|
|
156
|
+
|------|-------------|
|
|
157
|
+
| 143 | Invalid or missing JWT token |
|
|
158
|
+
| OAuth2Error | Various OAuth2 errors (invalid_token, expired, etc.) |
|
|
159
|
+
|
|
160
|
+
## Properties Set
|
|
161
|
+
|
|
162
|
+
| Property | Type | Description |
|
|
163
|
+
|----------|------|-------------|
|
|
164
|
+
| `req.oauth.bearerToken` | object | Decoded token or user object |
|
|
165
|
+
| `req.oauth.type` | number | OAuth type (0=JWT, 2=OAuth2) |
|
|
166
|
+
|
|
167
|
+
## Usage Notes
|
|
168
|
+
|
|
169
|
+
- OAuth type can be set globally or per-tenant
|
|
170
|
+
- JWT requires `Authorization: Bearer <token>` header
|
|
171
|
+
- OAuth2 uses `access_token` query parameter or header
|
|
172
|
+
- Tenant OAuth settings override global settings
|
|
173
|
+
- OAuth model is provided by configuration (provision module)
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Response Middleware
|
|
2
|
+
|
|
3
|
+
**File:** `mw/response/index.js`
|
|
4
|
+
|
|
5
|
+
**Order:** 4
|
|
6
|
+
|
|
7
|
+
The Response middleware adds standardized response builder methods to the request object.
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
|
|
11
|
+
- Adds `req.soajs.buildResponse()` for creating SOAJS-formatted responses
|
|
12
|
+
- Adds `req.soajs.controllerResponse()` for sending gateway responses
|
|
13
|
+
- Provides consistent error/success response structure
|
|
14
|
+
|
|
15
|
+
## Flow
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Incoming Request
|
|
19
|
+
│
|
|
20
|
+
▼
|
|
21
|
+
┌─────────────────────────┐
|
|
22
|
+
│ Attach buildResponse() │ Creates SOAJS response objects
|
|
23
|
+
└────────────┬────────────┘
|
|
24
|
+
│
|
|
25
|
+
▼
|
|
26
|
+
┌─────────────────────────────┐
|
|
27
|
+
│ Attach controllerResponse()│ Sends JSON responses
|
|
28
|
+
└────────────┬────────────────┘
|
|
29
|
+
│
|
|
30
|
+
▼
|
|
31
|
+
next()
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Methods Added
|
|
35
|
+
|
|
36
|
+
### `req.soajs.buildResponse(error, data)`
|
|
37
|
+
|
|
38
|
+
Creates a SOAJS-formatted response object.
|
|
39
|
+
|
|
40
|
+
**Parameters:**
|
|
41
|
+
- `error` - Error object or array of errors (optional)
|
|
42
|
+
- `data` - Response data (optional)
|
|
43
|
+
|
|
44
|
+
**Returns:**
|
|
45
|
+
```javascript
|
|
46
|
+
// Success
|
|
47
|
+
{
|
|
48
|
+
"result": true,
|
|
49
|
+
"data": { ... }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Error
|
|
53
|
+
{
|
|
54
|
+
"result": false,
|
|
55
|
+
"errors": {
|
|
56
|
+
"codes": [100, 101],
|
|
57
|
+
"details": [
|
|
58
|
+
{ "code": 100, "message": "Error message" },
|
|
59
|
+
{ "code": 101, "message": "Another error" }
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### `req.soajs.controllerResponse(jsonObj)`
|
|
66
|
+
|
|
67
|
+
Sends a JSON response and ends the request.
|
|
68
|
+
|
|
69
|
+
**Parameters:**
|
|
70
|
+
- `jsonObj` - Response object with optional `status`, `headObj`, `code`, `msg`
|
|
71
|
+
|
|
72
|
+
**Examples:**
|
|
73
|
+
```javascript
|
|
74
|
+
// Success response
|
|
75
|
+
req.soajs.controllerResponse({ data: { user: "john" } });
|
|
76
|
+
|
|
77
|
+
// Error response
|
|
78
|
+
req.soajs.controllerResponse({
|
|
79
|
+
status: 403,
|
|
80
|
+
code: 150,
|
|
81
|
+
msg: "Access denied"
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// With custom headers
|
|
85
|
+
req.soajs.controllerResponse({
|
|
86
|
+
status: 429,
|
|
87
|
+
msg: "Too many requests",
|
|
88
|
+
headObj: {
|
|
89
|
+
"Retry-After": 60,
|
|
90
|
+
"X-RateLimit-Limit": 100
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Response Format
|
|
96
|
+
|
|
97
|
+
All responses include:
|
|
98
|
+
- `Content-Type: application/json` header
|
|
99
|
+
- HTTP status code (default: 200)
|
|
100
|
+
- JSON body
|
|
101
|
+
|
|
102
|
+
## Usage Notes
|
|
103
|
+
|
|
104
|
+
- `controllerResponse()` only available when `controllerResponse: true` in config
|
|
105
|
+
- Prevents double-ending responses (tracks `ended` state)
|
|
106
|
+
- Automatically formats errors using `buildResponse()` when `code` and `msg` provided
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# SOAJS Middleware
|
|
2
|
+
|
|
3
|
+
**File:** `mw/soajs/index.js`
|
|
4
|
+
|
|
5
|
+
**Order:** 1
|
|
6
|
+
|
|
7
|
+
The SOAJS middleware is the first middleware in the pipeline. It initializes the `req.soajs` context object that all subsequent middleware depend on.
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
|
|
11
|
+
- Creates the `req.soajs` namespace
|
|
12
|
+
- Attaches the logger instance
|
|
13
|
+
- Attaches the current registry reference
|
|
14
|
+
- Attaches core utility modules (meta, validator)
|
|
15
|
+
|
|
16
|
+
## Flow
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Incoming Request
|
|
20
|
+
│
|
|
21
|
+
▼
|
|
22
|
+
┌─────────────────┐
|
|
23
|
+
│ Create │ if (!req.soajs) req.soajs = {}
|
|
24
|
+
│ req.soajs │
|
|
25
|
+
└────────┬────────┘
|
|
26
|
+
│
|
|
27
|
+
▼
|
|
28
|
+
┌─────────────────┐
|
|
29
|
+
│ Attach Logger │ req.soajs.log = log
|
|
30
|
+
└────────┬────────┘
|
|
31
|
+
│
|
|
32
|
+
▼
|
|
33
|
+
┌─────────────────┐
|
|
34
|
+
│ Attach │ req.soajs.registry = registryModule.get()
|
|
35
|
+
│ Registry │
|
|
36
|
+
└────────┬────────┘
|
|
37
|
+
│
|
|
38
|
+
▼
|
|
39
|
+
┌─────────────────┐
|
|
40
|
+
│ Attach Core │ req.soajs.meta = core.meta
|
|
41
|
+
│ Modules │ req.soajs.validator = core.validator
|
|
42
|
+
└────────┬────────┘
|
|
43
|
+
│
|
|
44
|
+
▼
|
|
45
|
+
next()
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Configuration
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
{
|
|
52
|
+
"log": logger, // Logger instance
|
|
53
|
+
"core": coreModule // soajs.core.libs module
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Properties Set
|
|
58
|
+
|
|
59
|
+
| Property | Type | Description |
|
|
60
|
+
|----------|------|-------------|
|
|
61
|
+
| `req.soajs.log` | object | Logger instance for request-scoped logging |
|
|
62
|
+
| `req.soajs.registry` | object | Current environment registry |
|
|
63
|
+
| `req.soajs.meta` | object | Metadata utilities from core |
|
|
64
|
+
| `req.soajs.validator` | object | JSON schema validator |
|
|
65
|
+
|
|
66
|
+
## Usage Notes
|
|
67
|
+
|
|
68
|
+
- This middleware MUST be first in the pipeline
|
|
69
|
+
- All other middleware assume `req.soajs` exists
|
|
70
|
+
- The registry is fetched fresh on each request (reference, not copy)
|
|
71
|
+
- Logger supports: `debug()`, `info()`, `warn()`, `error()`, `fatal()`
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# Traffic Middleware
|
|
2
|
+
|
|
3
|
+
**File:** `mw/traffic/index.js`
|
|
4
|
+
|
|
5
|
+
**Order:** 16
|
|
6
|
+
|
|
7
|
+
The Traffic middleware implements rate limiting (throttling) with configurable strategies and storage backends.
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
|
|
11
|
+
- Enforces rate limiting per tenant/IP
|
|
12
|
+
- Supports configurable throttling strategies
|
|
13
|
+
- Provides memory and MongoDB storage backends
|
|
14
|
+
- Returns rate limit headers in responses
|
|
15
|
+
|
|
16
|
+
## Flow
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Incoming Request
|
|
20
|
+
│
|
|
21
|
+
▼
|
|
22
|
+
┌─────────────────────────┐
|
|
23
|
+
│ Check Prerequisites │ registry, tenant, controller exist?
|
|
24
|
+
└────────┬────────────────┘
|
|
25
|
+
│
|
|
26
|
+
├─── Missing ──▶ next()
|
|
27
|
+
│
|
|
28
|
+
▼
|
|
29
|
+
┌─────────────────────────┐
|
|
30
|
+
│ Determine Strategy │ publicAPIStrategy or privateAPIStrategy
|
|
31
|
+
└────────┬────────────────┘
|
|
32
|
+
│
|
|
33
|
+
├─── No strategy ──▶ next()
|
|
34
|
+
│
|
|
35
|
+
▼
|
|
36
|
+
┌─────────────────────────┐
|
|
37
|
+
│ Get Throttling Config │ Priority:
|
|
38
|
+
│ │ 1. servicesConfig.gateway.throttling
|
|
39
|
+
│ │ 2. custom.gateway.value.traffic.throttling
|
|
40
|
+
│ │ 3. serviceConfig.throttling
|
|
41
|
+
└────────┬────────────────┘
|
|
42
|
+
│
|
|
43
|
+
▼
|
|
44
|
+
┌─────────────────────────┐
|
|
45
|
+
│ Check Throttling │ Get/increment counter
|
|
46
|
+
│ Counter │ Compare to limit
|
|
47
|
+
└────────┬────────────────┘
|
|
48
|
+
│
|
|
49
|
+
├─── Under limit ──▶ next()
|
|
50
|
+
│
|
|
51
|
+
├─── Over limit, retries left ──▶ Delay and retry
|
|
52
|
+
│
|
|
53
|
+
└─── Over limit, no retries ──▶ 429 Too Many Requests
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Configuration
|
|
57
|
+
|
|
58
|
+
### Strategy Definition
|
|
59
|
+
|
|
60
|
+
Located in `registry.serviceConfig.throttling`:
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
{
|
|
64
|
+
"throttling": {
|
|
65
|
+
"publicAPIStrategy": "default", // For unauthenticated requests
|
|
66
|
+
"privateAPIStrategy": "heavy", // For authenticated requests
|
|
67
|
+
"default": {
|
|
68
|
+
"status": 1, // 0=Off, 1=On
|
|
69
|
+
"type": 1, // 0=tenant, 1=tenant+IP
|
|
70
|
+
"window": 60000, // Time window in ms
|
|
71
|
+
"limit": 50, // Max requests per window
|
|
72
|
+
"retries": 2, // Retries when over limit
|
|
73
|
+
"delay": 1000 // Delay between retries (ms)
|
|
74
|
+
},
|
|
75
|
+
"heavy": {
|
|
76
|
+
"status": 1,
|
|
77
|
+
"type": 1,
|
|
78
|
+
"window": 60000,
|
|
79
|
+
"limit": 500,
|
|
80
|
+
"retries": 2,
|
|
81
|
+
"delay": 1000
|
|
82
|
+
},
|
|
83
|
+
"strict": {
|
|
84
|
+
"status": 1,
|
|
85
|
+
"type": 1,
|
|
86
|
+
"window": 60000,
|
|
87
|
+
"limit": 10,
|
|
88
|
+
"retries": 0,
|
|
89
|
+
"delay": 0
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Service-Specific Override
|
|
96
|
+
|
|
97
|
+
Via custom registry or tenant config:
|
|
98
|
+
|
|
99
|
+
```javascript
|
|
100
|
+
{
|
|
101
|
+
"throttling": {
|
|
102
|
+
"oauth": {
|
|
103
|
+
"publicAPIStrategy": "strict",
|
|
104
|
+
"apis": ["/token"] // Only these APIs
|
|
105
|
+
},
|
|
106
|
+
"payment": {
|
|
107
|
+
"publicAPIStrategy": "strict",
|
|
108
|
+
"privateAPIStrategy": "heavy",
|
|
109
|
+
"apis": ["/charge", "/refund"]
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Strategy Selection
|
|
116
|
+
|
|
117
|
+
| Request Type | Strategy Used |
|
|
118
|
+
|--------------|---------------|
|
|
119
|
+
| Public API (no auth) | `publicAPIStrategy` |
|
|
120
|
+
| Private API (authenticated) | `privateAPIStrategy` |
|
|
121
|
+
|
|
122
|
+
Determined by: `req.soajs.controller.serviceParams.isAPIPublic`
|
|
123
|
+
|
|
124
|
+
## Storage Backends
|
|
125
|
+
|
|
126
|
+
### Memory (Default)
|
|
127
|
+
|
|
128
|
+
- Fast, in-process storage
|
|
129
|
+
- Lost on restart
|
|
130
|
+
- Good for single-instance deployments
|
|
131
|
+
|
|
132
|
+
### MongoDB
|
|
133
|
+
|
|
134
|
+
- Persistent storage
|
|
135
|
+
- Shared across instances
|
|
136
|
+
- Required for multi-instance deployments
|
|
137
|
+
|
|
138
|
+
Configure via `registry.custom.gateway.value.traffic.model`:
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
{
|
|
142
|
+
"traffic": {
|
|
143
|
+
"model": "mongo" // or "memory"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Rate Limit Response
|
|
149
|
+
|
|
150
|
+
When rate limit exceeded:
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
HTTP/1.1 429 Too Many Requests
|
|
154
|
+
Content-Type: application/json
|
|
155
|
+
Retry-After: 45
|
|
156
|
+
X-RateLimit-Limit: 50
|
|
157
|
+
X-RateLimit-Remaining: 0
|
|
158
|
+
|
|
159
|
+
{
|
|
160
|
+
"result": false,
|
|
161
|
+
"errors": { ... }
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Response Headers
|
|
166
|
+
|
|
167
|
+
| Header | Description |
|
|
168
|
+
|--------|-------------|
|
|
169
|
+
| `X-RateLimit-Limit` | Maximum requests allowed |
|
|
170
|
+
| `X-RateLimit-Remaining` | Requests remaining in window |
|
|
171
|
+
| `Retry-After` | Seconds until window resets |
|
|
172
|
+
|
|
173
|
+
## Throttle Key Structure
|
|
174
|
+
|
|
175
|
+
```javascript
|
|
176
|
+
{
|
|
177
|
+
"l1": "tenant_id",
|
|
178
|
+
"l2": "client_ip"
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Creates unique tracking per tenant per IP.
|
|
183
|
+
|
|
184
|
+
## Usage Notes
|
|
185
|
+
|
|
186
|
+
- Throttling disabled if no strategy configured
|
|
187
|
+
- Service-specific config overrides global config
|
|
188
|
+
- APIs array allows targeting specific endpoints
|
|
189
|
+
- Retries add delay before rejection
|
|
190
|
+
- MongoDB model required for horizontal scaling
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# URL Middleware
|
|
2
|
+
|
|
3
|
+
**File:** `mw/url/index.js`
|
|
4
|
+
|
|
5
|
+
**Order:** 8
|
|
6
|
+
|
|
7
|
+
The URL middleware parses the request URL and extracts service routing information.
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
|
|
11
|
+
- Parses URL into components (service name, version, path)
|
|
12
|
+
- Validates service exists in registry
|
|
13
|
+
- Normalizes header and query parameters (key, access_token)
|
|
14
|
+
- Creates `req.soajs.controller.serviceParams` structure
|
|
15
|
+
|
|
16
|
+
## Flow
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Incoming Request
|
|
20
|
+
│
|
|
21
|
+
▼
|
|
22
|
+
┌─────────────────┐
|
|
23
|
+
│ Ensure │ if (!req.soajs.controller)
|
|
24
|
+
│ Controller │ req.soajs.controller = {}
|
|
25
|
+
└────────┬────────┘
|
|
26
|
+
│
|
|
27
|
+
▼
|
|
28
|
+
┌─────────────────┐
|
|
29
|
+
│ Parse URL │ url.parse(req.url, true)
|
|
30
|
+
└────────┬────────┘
|
|
31
|
+
│
|
|
32
|
+
▼
|
|
33
|
+
┌─────────────────┐
|
|
34
|
+
│ Extract │ service_n, service_v, service_nv
|
|
35
|
+
│ Service Info │ serviceInfo, path
|
|
36
|
+
└────────┬────────┘
|
|
37
|
+
│
|
|
38
|
+
├─── No service name? ──▶ Error 136
|
|
39
|
+
│
|
|
40
|
+
├─── Service not in registry? ──▶ Error 130
|
|
41
|
+
│
|
|
42
|
+
▼
|
|
43
|
+
┌─────────────────┐
|
|
44
|
+
│ Normalize │ key, access_token from headers/query
|
|
45
|
+
│ Auth Params │
|
|
46
|
+
└────────┬────────┘
|
|
47
|
+
│
|
|
48
|
+
▼
|
|
49
|
+
next()
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## URL Parsing
|
|
53
|
+
|
|
54
|
+
**URL Format:** `/{service}/{version}/{path}`
|
|
55
|
+
|
|
56
|
+
| Component | Example | Description |
|
|
57
|
+
|-----------|---------|-------------|
|
|
58
|
+
| `service_n` | `oauth` | Service name |
|
|
59
|
+
| `service_v` | `1` | Service version (optional) |
|
|
60
|
+
| `service_nv` | `oauth/1` | Service with version |
|
|
61
|
+
| `path` | `/token` | API path |
|
|
62
|
+
|
|
63
|
+
**Examples:**
|
|
64
|
+
```
|
|
65
|
+
/oauth/token → service: oauth, version: null, path: /token
|
|
66
|
+
/oauth/1/token → service: oauth, version: 1, path: /token
|
|
67
|
+
/urac/3/user/login → service: urac, version: 3, path: /user/login
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Properties Set
|
|
71
|
+
|
|
72
|
+
| Property | Description |
|
|
73
|
+
|----------|-------------|
|
|
74
|
+
| `req.soajs.controller.serviceParams.service_n` | Service name |
|
|
75
|
+
| `req.soajs.controller.serviceParams.service_v` | Service version |
|
|
76
|
+
| `req.soajs.controller.serviceParams.service_nv` | Service with version |
|
|
77
|
+
| `req.soajs.controller.serviceParams.serviceInfo` | URL path segments array |
|
|
78
|
+
| `req.soajs.controller.serviceParams.parsedUrl` | Full parsed URL object |
|
|
79
|
+
| `req.query` | Query parameters |
|
|
80
|
+
|
|
81
|
+
## Auth Parameter Normalization
|
|
82
|
+
|
|
83
|
+
The middleware normalizes authentication parameters:
|
|
84
|
+
|
|
85
|
+
1. **Key:** `req.headers.key` ← `req.headers.key || req.query.key`
|
|
86
|
+
2. **Access Token:** `req.query.access_token` ←
|
|
87
|
+
- `req.query.access_token`
|
|
88
|
+
- `req.headers.access_token`
|
|
89
|
+
- Bearer token from `Authorization` header
|
|
90
|
+
|
|
91
|
+
## Error Codes
|
|
92
|
+
|
|
93
|
+
| Code | Description |
|
|
94
|
+
|------|-------------|
|
|
95
|
+
| 130 | Service not found in registry |
|
|
96
|
+
| 136 | Empty or missing service name |
|
|
97
|
+
|
|
98
|
+
## Usage Notes
|
|
99
|
+
|
|
100
|
+
- Throws TypeError if SOAJS middleware hasn't run first
|
|
101
|
+
- Special handling for `soajs` service name (gateway routes)
|
|
102
|
+
- Authorization header is deleted after extracting Bearer token
|