soajs.controller 4.2.42 → 4.2.43

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.
Files changed (37) hide show
  1. package/.github/workflows/ci.yml +43 -0
  2. package/.github/workflows/release.yml +92 -0
  3. package/ARCHITECTURE.md +193 -0
  4. package/Gruntfile.js +1 -1
  5. package/docs/configuration-custom-registry.md +421 -0
  6. package/docs/configuration-services-config.md +275 -0
  7. package/docs/data-flow.md +138 -0
  8. package/docs/deployment.md +205 -0
  9. package/docs/external-integrations.md +159 -0
  10. package/docs/gateway-routes.md +259 -0
  11. package/docs/maintenance-routes.md +308 -0
  12. package/docs/middleware/awareness.md +120 -0
  13. package/docs/middleware/cors.md +88 -0
  14. package/docs/middleware/enhancer.md +75 -0
  15. package/docs/middleware/favicon.md +36 -0
  16. package/docs/middleware/gotoService.md +146 -0
  17. package/docs/middleware/ip2ban.md +70 -0
  18. package/docs/middleware/key.md +196 -0
  19. package/docs/middleware/keyACL.md +139 -0
  20. package/docs/middleware/lastSeen.md +111 -0
  21. package/docs/middleware/maintenanceMode.md +81 -0
  22. package/docs/middleware/mt.md +188 -0
  23. package/docs/middleware/oauth.md +173 -0
  24. package/docs/middleware/response.md +106 -0
  25. package/docs/middleware/soajs.md +71 -0
  26. package/docs/middleware/traffic.md +190 -0
  27. package/docs/middleware/url.md +102 -0
  28. package/docs/multi-tenant-security.md +183 -0
  29. package/docs/request-flow.md +230 -0
  30. package/docs/service-discovery.md +167 -0
  31. package/mw/key/index.js +16 -1
  32. package/mw/mt/urac.js +21 -1
  33. package/mw/mt/utils.js +2 -0
  34. package/package.json +7 -7
  35. package/test/unit/mw/key/index.js +150 -0
  36. package/test/unit/mw/mt/urac.js +157 -4
  37. package/.travis.yml +0 -72
@@ -0,0 +1,259 @@
1
+ # Built-in Gateway Routes
2
+
3
+ The SOAJS Controller provides special built-in routes that are handled directly by the gateway rather than being proxied to backend services.
4
+
5
+ ## Route Overview
6
+
7
+ ```
8
+ ┌─────────────────────────────────────────────────────────────────────────────────┐
9
+ │ BUILT-IN GATEWAY ROUTES │
10
+ └─────────────────────────────────────────────────────────────────────────────────┘
11
+
12
+ ┌─────────────────────────────────────────────────────────────────────────────┐
13
+ │ │
14
+ │ /soajs/acl │
15
+ │ ├── Method: GET │
16
+ │ ├── Purpose: Retrieve ACL/permissions for authenticated user │
17
+ │ ├── Requires: External key (header), OAuth token (query) │
18
+ │ └── Returns: ACL, packages, user info, tenant info │
19
+ │ │
20
+ │ /soajs/proxy │
21
+ │ ├── Method: Any │
22
+ │ ├── Purpose: Proxy requests to external URLs or cross-environment │
23
+ │ ├── Requires: proxyRoute query parameter │
24
+ │ └── Optional: __env, tCode, extKey parameters │
25
+ │ │
26
+ │ /proxy/redirect (DEPRECATED → use /soajs/proxy) │
27
+ │ │
28
+ └─────────────────────────────────────────────────────────────────────────────┘
29
+ ```
30
+
31
+ ---
32
+
33
+ ## 1. ACL Route (`/soajs/acl`)
34
+
35
+ **Endpoint:** `GET /soajs/acl`
36
+
37
+ **Purpose:** Returns the Access Control List (ACL) and permissions for the currently authenticated user. This route is useful for frontend applications to determine what services and APIs the user has access to.
38
+
39
+ ### Required Headers
40
+
41
+ | Header | Description |
42
+ |--------|-------------|
43
+ | `key` | External tenant key (required) |
44
+
45
+ ### Query Parameters
46
+
47
+ | Parameter | Description |
48
+ |-----------|-------------|
49
+ | `access_token` | OAuth2 access token for user-specific ACL |
50
+
51
+ ### Response Structure
52
+
53
+ ```javascript
54
+ {
55
+ "acl": { // Raw ACL from tenant application or package
56
+ "serviceName": {
57
+ "version": {
58
+ "access": true,
59
+ "apisPermission": "restricted",
60
+ "get": { "apis": { "/api/path": {} } }
61
+ }
62
+ }
63
+ },
64
+ "finalACL": { // Computed ACL with latest versions
65
+ "serviceName": {
66
+ "version": "1",
67
+ "get": ["/api/path"] // Allowed APIs per method (if restricted)
68
+ }
69
+ },
70
+ "packages": ["PACKAGE_CODE"], // Allowed product packages
71
+ "urac": { // User info (if authenticated)
72
+ "_id": "user_id"
73
+ },
74
+ "tenant": { // Tenant info
75
+ "id": "tenant_id",
76
+ "name": "Tenant Name",
77
+ "code": "TCODE",
78
+ "type": "product"
79
+ }
80
+ }
81
+ ```
82
+
83
+ ### Example
84
+
85
+ ```bash
86
+ # Get ACL for authenticated user
87
+ curl -X GET "http://api.example.com/soajs/acl?access_token=YOUR_TOKEN" \
88
+ -H "key: YOUR_EXTERNAL_KEY"
89
+ ```
90
+
91
+ ### Error Codes
92
+
93
+ | Code | Description |
94
+ |------|-------------|
95
+ | 132 | Missing or invalid external key |
96
+ | 400 | OAuth token required but not provided |
97
+
98
+ ---
99
+
100
+ ## 2. Proxy Route (`/soajs/proxy`)
101
+
102
+ **Endpoint:** `ANY /soajs/proxy`
103
+
104
+ **Purpose:** Proxies requests to external URLs or to services in other SOAJS environments. Supports cross-environment communication with automatic key resolution.
105
+
106
+ ### Required Headers
107
+
108
+ | Header | Description |
109
+ |--------|-------------|
110
+ | `key` | External tenant key |
111
+
112
+ ### Query Parameters
113
+
114
+ | Parameter | Required | Description |
115
+ |-----------|----------|-------------|
116
+ | `proxyRoute` | Yes | URL-encoded target route (full URL or service path) |
117
+ | `__env` | No | Target environment code (e.g., "STG", "PROD") for cross-environment |
118
+ | `tCode` | No | Tenant code for automatic key lookup in target environment |
119
+ | `extKey` | No | Explicit external key to use for target environment |
120
+ | `access_token` | No | OAuth token to forward |
121
+
122
+ ### Usage Modes
123
+
124
+ ```
125
+ ┌─────────────────────────────────────────────────────────────────────────────────┐
126
+ │ PROXY REQUEST MODES │
127
+ └─────────────────────────────────────────────────────────────────────────────────┘
128
+
129
+ Mode 1: Direct URL Proxy
130
+ ─────────────────────────
131
+ proxyRoute=http://service:port/path
132
+
133
+ Request flows directly to the specified URL.
134
+ No environment lookup required.
135
+
136
+ Mode 2: Cross-Environment Proxy
137
+ ────────────────────────────────
138
+ proxyRoute=/service/api&__env=PROD&tCode=TENANT
139
+
140
+ 1. Loads target environment registry
141
+ 2. Looks up tenant's external key for that environment
142
+ 3. Constructs URL from registry (protocol://domain:port)
143
+ 4. Forwards request with remote ext key
144
+
145
+ Mode 3: Cross-Environment with Explicit Key
146
+ ───────────────────────────────────────────
147
+ proxyRoute=/service/api&__env=PROD&extKey=xxx
148
+
149
+ Uses the provided extKey instead of looking up tenant key.
150
+
151
+ └─────────────────────────────────────────────────────────────────────────────────┘
152
+ ```
153
+
154
+ ### Examples
155
+
156
+ ```bash
157
+ # Direct URL proxy
158
+ curl -X GET "http://api.example.com/soajs/proxy?proxyRoute=http%3A%2F%2F127.0.0.1%3A4002%2FCheckPhoneNumber&access_token=TOKEN" \
159
+ -H "key: YOUR_EXTERNAL_KEY"
160
+
161
+ # Cross-environment proxy with automatic key resolution
162
+ curl -X GET "http://api.example.com/soajs/proxy?proxyRoute=%2Furac%2FgetUser&__env=STG&access_token=TOKEN" \
163
+ -H "key: YOUR_EXTERNAL_KEY"
164
+
165
+ # Cross-environment proxy with explicit key
166
+ curl -X POST "http://api.example.com/soajs/proxy?proxyRoute=%2Fservice%2Fapi&__env=PROD&extKey=REMOTE_KEY" \
167
+ -H "key: YOUR_EXTERNAL_KEY" \
168
+ -H "Content-Type: application/json" \
169
+ -d '{"data": "value"}'
170
+ ```
171
+
172
+ ### Error Codes
173
+
174
+ | Code | Description |
175
+ |------|-------------|
176
+ | 135 | Invalid proxy URL or connection failed |
177
+ | 137 | No external key found for tenant in target environment |
178
+ | 139 | Missing proxyRoute parameter |
179
+ | 207 | Failed to load target environment registry |
180
+ | 208 | Target environment registry missing protocol/domain/port |
181
+
182
+ ### Request Flow
183
+
184
+ ```
185
+ ┌──────────────────────────────────────────────────────────────────────────────────┐
186
+ │ PROXY REQUEST FLOW │
187
+ └──────────────────────────────────────────────────────────────────────────────────┘
188
+
189
+ Client Request
190
+ │
191
+ ▼
192
+ ┌─────────────────┐
193
+ │ Parse Query │
194
+ │ Parameters │
195
+ └────────┬────────┘
196
+ │
197
+ ├─── proxyRoute (required)
198
+ ├─── __env (optional)
199
+ ├─── tCode (optional)
200
+ └─── extKey (optional)
201
+ │
202
+ ▼
203
+ ┌─────────────────┐ ┌─────────────────┐
204
+ │ __env │ NO │ Direct Proxy │
205
+ │ provided? │───▶│ to proxyRoute │
206
+ └────────┬────────┘ └─────────────────┘
207
+ │ YES
208
+ ▼
209
+ ┌─────────────────┐
210
+ │ Load Remote │
211
+ │ Environment │
212
+ │ Registry │
213
+ └────────┬────────┘
214
+ │
215
+ ▼
216
+ ┌─────────────────┐ ┌─────────────────┐
217
+ │ extKey │YES │ Use Provided │
218
+ │ provided? │───▶│ extKey │
219
+ └────────┬────────┘ └────────┬────────┘
220
+ │ NO │
221
+ ▼ │
222
+ ┌─────────────────┐ │
223
+ │ Lookup Tenant │ │
224
+ │ Key for Env │ │
225
+ └────────┬────────┘ │
226
+ │ │
227
+ └──────────┬───────────┘
228
+ │
229
+ ▼
230
+ ┌─────────────────────────────────────────┐
231
+ │ Construct Full URL │
232
+ │ protocol://domain:port/proxyRoute │
233
+ └────────────────────┬────────────────────┘
234
+ │
235
+ ▼
236
+ ┌─────────────────────────────────────────┐
237
+ │ Forward Request with Headers │
238
+ │ • soajs_roaming: source environment │
239
+ │ • key: remote ext key (if applicable) │
240
+ │ • Original headers (except host) │
241
+ └────────────────────┬────────────────────┘
242
+ │
243
+ ▼
244
+ Target Service
245
+ ```
246
+
247
+ ---
248
+
249
+ ## 3. Deprecated: `/proxy/redirect`
250
+
251
+ **Status:** DEPRECATED
252
+
253
+ **Replacement:** Use `/soajs/proxy` instead
254
+
255
+ This route has identical functionality to `/soajs/proxy` but is deprecated. The gateway will log a warning when this route is used:
256
+
257
+ ```
258
+ Route: [/proxy/redirect] is deprecated. You should use [/soajs/proxy].
259
+ ```
@@ -0,0 +1,308 @@
1
+ # Maintenance Routes
2
+
3
+ The SOAJS Controller provides maintenance endpoints on a separate port (default: 5000) for health checks, configuration management, and system administration.
4
+
5
+ ## Server Overview
6
+
7
+ **File:** `server/maintenance.js`
8
+
9
+ **Default Port:** 5000
10
+
11
+ The maintenance server runs on a separate port from the main gateway to allow internal access for health checks and administration without exposing these endpoints publicly.
12
+
13
+ ## Available Endpoints
14
+
15
+ | Endpoint | Method | Purpose |
16
+ |----------|--------|---------|
17
+ | `/heartbeat` | GET | Basic health check |
18
+ | `/loadProvision` | GET | Reload provision/tenant data |
19
+ | `/reloadRegistry` | GET | Reload environment registry |
20
+ | `/awarenessStat` | GET | Get service awareness statistics |
21
+
22
+ ---
23
+
24
+ ## 1. Heartbeat (`/heartbeat`)
25
+
26
+ **Endpoint:** `GET /heartbeat`
27
+
28
+ **Purpose:** Basic health check to verify the gateway is running.
29
+
30
+ ### Response
31
+
32
+ ```javascript
33
+ {
34
+ "result": true,
35
+ "ts": 1699999999999, // Timestamp
36
+ "service": {
37
+ "service": "controller",
38
+ "type": "rest",
39
+ "route": "/heartbeat"
40
+ }
41
+ }
42
+ ```
43
+
44
+ ### Usage
45
+
46
+ ```bash
47
+ curl http://localhost:5000/heartbeat
48
+ ```
49
+
50
+ ### Kubernetes/Docker Health Probes
51
+
52
+ ```yaml
53
+ # Kubernetes
54
+ livenessProbe:
55
+ httpGet:
56
+ path: /heartbeat
57
+ port: 5000
58
+ initialDelaySeconds: 30
59
+ periodSeconds: 10
60
+
61
+ readinessProbe:
62
+ httpGet:
63
+ path: /heartbeat
64
+ port: 5000
65
+ initialDelaySeconds: 5
66
+ periodSeconds: 5
67
+ ```
68
+
69
+ ---
70
+
71
+ ## 2. Load Provision (`/loadProvision`)
72
+
73
+ **Endpoint:** `GET /loadProvision`
74
+
75
+ **Purpose:** Reloads tenant, key, and package data from the Core DB.
76
+
77
+ ### Response
78
+
79
+ ```javascript
80
+ {
81
+ "result": true,
82
+ "ts": 1699999999999,
83
+ "service": {
84
+ "service": "controller",
85
+ "type": "rest",
86
+ "route": "/loadProvision"
87
+ }
88
+ }
89
+ ```
90
+
91
+ ### Usage
92
+
93
+ ```bash
94
+ curl http://localhost:5000/loadProvision
95
+ ```
96
+
97
+ ### When to Use
98
+
99
+ - After updating tenant configurations
100
+ - After modifying application keys
101
+ - After changing package ACLs
102
+ - To force refresh of cached provision data
103
+
104
+ ---
105
+
106
+ ## 3. Reload Registry (`/reloadRegistry`)
107
+
108
+ **Endpoint:** `GET /reloadRegistry`
109
+
110
+ **Purpose:** Reloads the environment registry from the Core DB.
111
+
112
+ ### Response
113
+
114
+ ```javascript
115
+ {
116
+ "result": true,
117
+ "ts": 1699999999999,
118
+ "service": {
119
+ "service": "controller",
120
+ "type": "rest",
121
+ "route": "/reloadRegistry"
122
+ }
123
+ }
124
+ ```
125
+
126
+ ### Usage
127
+
128
+ ```bash
129
+ curl http://localhost:5000/reloadRegistry
130
+ ```
131
+
132
+ ### What Gets Reloaded
133
+
134
+ - Service definitions and hosts
135
+ - Database configurations
136
+ - Service config (throttling, CORS, OAuth)
137
+ - Custom gateway settings
138
+ - All environment-specific settings
139
+
140
+ ### When to Use
141
+
142
+ - After modifying environment configuration
143
+ - After adding new services
144
+ - After changing service hosts
145
+ - After modifying custom registry settings
146
+
147
+ ---
148
+
149
+ ## 4. Awareness Statistics (`/awarenessStat`)
150
+
151
+ **Endpoint:** `GET /awarenessStat`
152
+
153
+ **Purpose:** Returns the current state of service awareness/discovery.
154
+
155
+ ### Response (Custom Mode)
156
+
157
+ ```javascript
158
+ {
159
+ "result": true,
160
+ "ts": 1699999999999,
161
+ "service": {
162
+ "service": "controller",
163
+ "type": "rest",
164
+ "route": "/awarenessStat"
165
+ },
166
+ "data": {
167
+ "services": {
168
+ "oauth": {
169
+ "1": {
170
+ "hosts": ["host1.local:4002", "host2.local:4002"],
171
+ "healthy": 2
172
+ },
173
+ "2": {
174
+ "hosts": ["host3.local:4002"],
175
+ "healthy": 1
176
+ }
177
+ },
178
+ "urac": {
179
+ "3": {
180
+ "hosts": ["host1.local:4001"],
181
+ "healthy": 1
182
+ }
183
+ }
184
+ }
185
+ }
186
+ }
187
+ ```
188
+
189
+ ### Response (HA Mode)
190
+
191
+ ```javascript
192
+ {
193
+ "result": true,
194
+ "ts": 1699999999999,
195
+ "service": {
196
+ "service": "controller",
197
+ "type": "rest",
198
+ "route": "/awarenessStat"
199
+ },
200
+ "data": {
201
+ "mode": "kubernetes",
202
+ "services": {
203
+ "oauth": {
204
+ "1": {
205
+ "pods": ["oauth-v1-abc123", "oauth-v1-def456"],
206
+ "healthy": 2
207
+ }
208
+ }
209
+ }
210
+ }
211
+ }
212
+ ```
213
+
214
+ ### Usage
215
+
216
+ ```bash
217
+ curl http://localhost:5000/awarenessStat
218
+ ```
219
+
220
+ ### When to Use
221
+
222
+ - To verify service discovery is working
223
+ - To check service health status
224
+ - To debug routing issues
225
+ - To monitor cluster state
226
+
227
+ ---
228
+
229
+ ## Security Considerations
230
+
231
+ ### Network Access
232
+
233
+ The maintenance port should only be accessible from:
234
+ - Internal networks
235
+ - Kubernetes cluster (for probes)
236
+ - Monitoring systems
237
+
238
+ ### Firewall Rules
239
+
240
+ ```bash
241
+ # Example: Only allow internal access to maintenance port
242
+ iptables -A INPUT -p tcp --dport 5000 -s 10.0.0.0/8 -j ACCEPT
243
+ iptables -A INPUT -p tcp --dport 5000 -j DROP
244
+ ```
245
+
246
+ ### Kubernetes NetworkPolicy
247
+
248
+ ```yaml
249
+ apiVersion: networking.k8s.io/v1
250
+ kind: NetworkPolicy
251
+ metadata:
252
+ name: controller-maintenance
253
+ spec:
254
+ podSelector:
255
+ matchLabels:
256
+ soajs.service.name: controller
257
+ ingress:
258
+ - from:
259
+ - namespaceSelector:
260
+ matchLabels:
261
+ name: monitoring
262
+ ports:
263
+ - port: 5000
264
+ ```
265
+
266
+ ---
267
+
268
+ ## Integration with Monitoring
269
+
270
+ ### Prometheus Metrics
271
+
272
+ The heartbeat endpoint can be used with Prometheus blackbox exporter:
273
+
274
+ ```yaml
275
+ # prometheus.yml
276
+ scrape_configs:
277
+ - job_name: 'soajs-controller'
278
+ metrics_path: /probe
279
+ params:
280
+ module: [http_2xx]
281
+ static_configs:
282
+ - targets:
283
+ - http://controller:5000/heartbeat
284
+ relabel_configs:
285
+ - source_labels: [__address__]
286
+ target_label: __param_target
287
+ - source_labels: [__param_target]
288
+ target_label: instance
289
+ - target_label: __address__
290
+ replacement: blackbox-exporter:9115
291
+ ```
292
+
293
+ ### Health Check Scripts
294
+
295
+ ```bash
296
+ #!/bin/bash
297
+ # health-check.sh
298
+
299
+ RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:5000/heartbeat)
300
+
301
+ if [ "$RESPONSE" == "200" ]; then
302
+ echo "Gateway is healthy"
303
+ exit 0
304
+ else
305
+ echo "Gateway health check failed: HTTP $RESPONSE"
306
+ exit 1
307
+ fi
308
+ ```
@@ -0,0 +1,120 @@
1
+ # Awareness Middleware
2
+
3
+ **File:** `mw/awareness/index.js`
4
+
5
+ **Order:** 9
6
+
7
+ The Awareness middleware provides service discovery functions for resolving service hosts.
8
+
9
+ ## Purpose
10
+
11
+ - Attaches service discovery functions to `req.soajs.awareness`
12
+ - Supports both Custom mode (static registry) and HA mode (Kubernetes/Swarm)
13
+ - Provides host resolution and version lookup
14
+
15
+ ## Flow
16
+
17
+ ```
18
+ Incoming Request
19
+ │
20
+ ▼
21
+ ┌─────────────────────┐
22
+ │ Determine Mode │ process.env.SOAJS_DEPLOY_HA?
23
+ │ Custom or HA │
24
+ └────────┬────────────┘
25
+ │
26
+ ├─── No HA ──▶ Use custom.js driver
27
+ │
28
+ ├─── HA ──▶ Use ha.js driver
29
+ │
30
+ ▼
31
+ ┌─────────────────────┐
32
+ │ Attach Awareness │ req.soajs.awareness = { ... }
33
+ │ Functions │
34
+ └────────┬────────────┘
35
+ │
36
+ ▼
37
+ next()
38
+ ```
39
+
40
+ ## Drivers
41
+
42
+ ### Custom Mode (Non-HA)
43
+
44
+ **File:** `mw/awareness/custom.js`
45
+
46
+ - Uses registry's static host lists
47
+ - In-memory health tracking
48
+ - Round-robin load balancing
49
+
50
+ ### HA Mode (Kubernetes/Swarm)
51
+
52
+ **File:** `mw/awareness/ha.js`
53
+
54
+ - Uses infrastructure module for discovery
55
+ - Dynamic service resolution via cluster API
56
+ - Automatic health monitoring
57
+
58
+ ## Methods Added
59
+
60
+ ### `req.soajs.awareness.getHost(serviceName, [version], callback)`
61
+
62
+ Resolves a host for the given service.
63
+
64
+ **Parameters:**
65
+ - `serviceName` - Name of the service
66
+ - `version` - Service version (optional)
67
+ - `callback` - Function receiving `(host)` where host is `"hostname:port"` or `null`
68
+
69
+ **Example:**
70
+ ```javascript
71
+ req.soajs.awareness.getHost("oauth", "1", (host) => {
72
+ if (host) {
73
+ // host = "oauth-service:4002"
74
+ }
75
+ });
76
+ ```
77
+
78
+ ### `req.soajs.awareness.getLatestVersion(serviceName, callback)`
79
+
80
+ Gets the latest available version of a service.
81
+
82
+ **Parameters:**
83
+ - `serviceName` - Name of the service
84
+ - `callback` - Function receiving `(version)` as string or `null`
85
+
86
+ **Example:**
87
+ ```javascript
88
+ req.soajs.awareness.getLatestVersion("oauth", (version) => {
89
+ // version = "2"
90
+ });
91
+ ```
92
+
93
+ ### `req.soajs.awareness.getLatestVersionFromCluster(serviceName, callback)`
94
+
95
+ (HA mode only) Gets the latest version directly from the cluster.
96
+
97
+ **Example:**
98
+ ```javascript
99
+ if (process.env.SOAJS_DEPLOY_HA) {
100
+ req.soajs.awareness.getLatestVersionFromCluster("oauth", (version) => {
101
+ // Fresh lookup from K8s/Swarm API
102
+ });
103
+ }
104
+ ```
105
+
106
+ ## Configuration
107
+
108
+ ```javascript
109
+ {
110
+ "awareness": true, // Enable awareness
111
+ "doNotRebuildCache": false // Skip initial cache build (HA mode)
112
+ }
113
+ ```
114
+
115
+ ## Usage Notes
116
+
117
+ - Custom mode caches hosts in memory, updated via heartbeat
118
+ - HA mode queries cluster API for each lookup (with caching)
119
+ - Version can be omitted to get any available host
120
+ - Returns `null` if service/version not found