fuse-core-express 1.0.2 → 1.0.3
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 +226 -480
- package/decryptor.js +106 -0
- package/{index.mjs → index.js} +167 -22
- package/keys/README.md +47 -0
- package/keys/build-version.json +5 -0
- package/keys/recipient-keys-1.0.3.json +7 -0
- package/keys/signer-keys-1.0.3.json +5 -0
- package/package.json +18 -68
- package/secure-package.vxz +0 -0
- package/CHANGELOG.md +0 -128
- package/LICENSE +0 -21
- package/decrypt-loader.mjs +0 -301
- package/private-1.0.2.pem +0 -28
package/README.md
CHANGED
|
@@ -1,588 +1,334 @@
|
|
|
1
|
-
# FuseCore
|
|
1
|
+
# FuseCore Express Encrypted Package
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
>
|
|
5
|
-
> **Version:** 1.0.2
|
|
6
|
-
> **Protection:** JavaScript Obfuscation + AES-256-CBC + RSA-2048
|
|
7
|
-
> **MIT Compliant:** Private key included in package for open source compliance
|
|
3
|
+
FuseCore Express version - Provides the exact same API as the standard version, but with AES-256-GCM + RSA encryption for code protection.
|
|
8
4
|
|
|
9
|
-
##
|
|
5
|
+
## 🚀 Quick Start
|
|
10
6
|
|
|
11
|
-
|
|
7
|
+
### Installation
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- **RSA-2048 Key Protection** - AES keys encrypted with RSA
|
|
16
|
-
- **Digital Signature** - Code integrity verification
|
|
17
|
-
|
|
18
|
-
## Quick Start
|
|
19
|
-
|
|
20
|
-
```javascript
|
|
21
|
-
import FuseCore from 'fuse-core-express';
|
|
22
|
-
|
|
23
|
-
// Initialize decryption with included private key (MIT compliant)
|
|
24
|
-
FuseCore.initDecryption('./private-1.0.2.pem');
|
|
25
|
-
|
|
26
|
-
// Then use normally
|
|
27
|
-
await FuseCore.init();
|
|
9
|
+
```bash
|
|
10
|
+
npm install fuse-core-express
|
|
28
11
|
```
|
|
29
12
|
|
|
30
|
-
|
|
31
|
-
|
|
13
|
+
### Basic Usage
|
|
32
14
|
|
|
33
|
-
|
|
15
|
+
The FuseCore uses a **two-stage initialization** pattern:
|
|
34
16
|
|
|
35
|
-
|
|
17
|
+
```javascript
|
|
18
|
+
import FuseCore from 'fuse-core-express';
|
|
19
|
+
import path from 'path';
|
|
20
|
+
import { fileURLToPath } from 'url';
|
|
36
21
|
|
|
37
|
-
|
|
22
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
38
23
|
|
|
39
|
-
|
|
24
|
+
// Stage 1: Initialize decryption
|
|
25
|
+
const privateKeyPath = path.join(__dirname, 'keys/recipient-keys-1.0.3.json');
|
|
26
|
+
await FuseCore.initDecryption(privateKeyPath);
|
|
40
27
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
expressApp: null,
|
|
44
|
-
listenHostname: '',
|
|
45
|
-
listenPort: 3000,
|
|
46
|
-
},
|
|
47
|
-
cache: {
|
|
48
|
-
impl: 'memory', // 'memory' or 'redis'
|
|
49
|
-
redis: {
|
|
50
|
-
url: 'redis://localhost:6379',
|
|
51
|
-
connectTimeout: 10000,
|
|
52
|
-
commandTimeout: 5000,
|
|
53
|
-
enableTls: false,
|
|
54
|
-
clientOptions: {}
|
|
55
|
-
}
|
|
56
|
-
},
|
|
28
|
+
// Stage 2: Initialize FuseCore (same as standard version)
|
|
29
|
+
await FuseCore.init({
|
|
57
30
|
log: {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
extraZipStreams: [],
|
|
61
|
-
takeOverConsole: false,
|
|
62
|
-
extraOutputStreams: {
|
|
63
|
-
info: process.stdout,
|
|
64
|
-
error: process.stderr,
|
|
65
|
-
},
|
|
31
|
+
level: 'info',
|
|
32
|
+
format: 'json'
|
|
66
33
|
},
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
defaultHeaders: {
|
|
71
|
-
"X-Requested-With": "FuseCore Web component V1.0"
|
|
72
|
-
},
|
|
73
|
-
agentOptions:{}
|
|
74
|
-
},
|
|
75
|
-
manifest: {
|
|
76
|
-
path: '',
|
|
77
|
-
name: '',
|
|
34
|
+
cache: {
|
|
35
|
+
type: 'memory',
|
|
36
|
+
maxSize: '100MB'
|
|
78
37
|
},
|
|
79
|
-
|
|
80
38
|
monitor: {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
isPrimaryProcess: cluster.isPrimary || cluster.isMaster
|
|
89
|
-
}
|
|
39
|
+
enabled: true,
|
|
40
|
+
interval: 5000
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Use FuseCore API normally
|
|
45
|
+
// ... Your business logic ...
|
|
90
46
|
|
|
47
|
+
// Shutdown FuseCore
|
|
48
|
+
await FuseCore.shutdown();
|
|
91
49
|
```
|
|
92
50
|
|
|
93
|
-
|
|
94
|
-
* common.listenHostname (string) optional, hostname or ip http server listens on, used for config server callback.
|
|
95
|
-
* common.listenPort (number) optional,port number http server listens on, used for config server callback.
|
|
96
|
-
* cache.impl (string) optional, cache implementation, 'memory' or 'redis', default is memory.
|
|
97
|
-
* cache.redis.url (string) optional, Redis connection URL, default is 'redis://localhost:6379'.
|
|
51
|
+
## 📋 Detailed API Documentation
|
|
98
52
|
|
|
99
|
-
|
|
100
|
-
* cache.redis.clientOptions (object) optional, additional Redis client options.
|
|
101
|
-
* log.path (string) optional, directory to save log files.
|
|
102
|
-
* log.level (string) optional, minimal level written to log file,default is info.
|
|
103
|
-
* log.extraZipStreams (array) optional, streams for access log.
|
|
104
|
-
* log.takeOverConsole (bool) optional, default false, indicate log module take over system console, thus all console message will be pipe to log file.
|
|
105
|
-
* log.extraOutputStreams (Object) optional, default null, extra log output stream, should be {info:[...Writable],error:[...Writable]}.
|
|
106
|
-
* ajax.timeout (number) optional, default timeout in millisecond, default is 10000.
|
|
107
|
-
* ajax.slowThreshold (number) optional, default slow log time limit in millisecond, all backend response time greater than limit will be logged to slow log file, default value is 50.
|
|
108
|
-
* ajax.defaultHeaders (object) optional, default headers send to backend, default value is {'x-requested-with':'FuseCore Web Server 1.0'}.
|
|
109
|
-
* ajax.agentOptions (object) optional, extra agent options will pass to http/https agent, also will applied to globalAgent.
|
|
110
|
-
* manifest.path (string) optional, directory contain manifest file.
|
|
111
|
-
* manifest.name (string) optional, manifest name to load.
|
|
53
|
+
### `FuseCore.initDecryption(privateKeyPath)`
|
|
112
54
|
|
|
113
|
-
|
|
114
|
-
* monitor.suffix (string) optional, suffix of all monitor keys.
|
|
115
|
-
* monitor.mem (bool) optional, auto monitor memory usage, default is false.
|
|
116
|
-
* monitor.cpu (bool) optional, auto monitor cpu usage, default is false.
|
|
117
|
-
* monitor.req404 (bool) optional, monitor 404 requests, default is false.
|
|
118
|
-
* monitor.req5xx (bool) optional, monitor 5xx requests, default is false.
|
|
119
|
-
* isPrimaryProcess (bool) optional, indicate current process is primary in cluster mode, monitor module will act differently when run as a worker process.
|
|
55
|
+
Initialize the decryption process, this is the necessary first step for using FuseCore.
|
|
120
56
|
|
|
57
|
+
#### Parameters
|
|
121
58
|
|
|
59
|
+
- **`privateKeyPath`** (string): Path to the recipient private key file
|
|
60
|
+
- Must be a JSON file containing the `privateKey` field
|
|
61
|
+
- Typically named `recipient-keys-{version}.json`
|
|
62
|
+
- Example path: `./keys/recipient-keys-1.0.3.json`
|
|
122
63
|
|
|
123
|
-
|
|
64
|
+
#### Return Value
|
|
124
65
|
|
|
125
|
-
|
|
66
|
+
- Returns FuseCore instance (for method chaining)
|
|
126
67
|
|
|
127
|
-
####
|
|
128
|
-
@deprecated
|
|
68
|
+
#### Examples
|
|
129
69
|
|
|
130
|
-
|
|
131
|
-
//
|
|
132
|
-
|
|
70
|
+
```javascript
|
|
71
|
+
// Use bundled key files (recommended for development/testing)
|
|
72
|
+
const privateKeyPath = path.join(__dirname, 'keys/recipient-keys-1.0.3.json');
|
|
73
|
+
await FuseCore.initDecryption(privateKeyPath);
|
|
133
74
|
|
|
134
|
-
|
|
75
|
+
// Use custom path key files
|
|
76
|
+
await FuseCore.initDecryption('/path/to/your/private-key.json');
|
|
77
|
+
```
|
|
135
78
|
|
|
79
|
+
#### Key File Format
|
|
136
80
|
|
|
137
|
-
|
|
138
|
-
Monitor.init(configObj);
|
|
81
|
+
The private key file should be in JSON format, containing the following fields:
|
|
139
82
|
|
|
140
|
-
|
|
83
|
+
```json
|
|
141
84
|
{
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
cpu: monitor cpu usage (boolean,optional)
|
|
147
|
-
mon404: monitor 404 response (boolean,optional)
|
|
148
|
-
mon5xx: monitor 5xx response (boolean,optional)
|
|
149
|
-
monitorPath: the path for express which remove graph drawer used to get monitor indicators and values
|
|
85
|
+
"privateKey": "-----BEGIN PRIVATE KEY-----\nMIIE...\n-----END PRIVATE KEY-----\n",
|
|
86
|
+
"publicKey": "-----BEGIN PUBLIC KEY-----\nMIIB...\n-----END PUBLIC KEY-----\n",
|
|
87
|
+
"owner": "recipient",
|
|
88
|
+
"timestamp": "2025-08-01T15:58:34.531Z"
|
|
150
89
|
}
|
|
90
|
+
```
|
|
151
91
|
|
|
92
|
+
### `FuseCore.init(options)`
|
|
152
93
|
|
|
153
|
-
|
|
154
|
-
1. Used to adept nestia ip rule,when using request.ip property.
|
|
155
|
-
1. add req.realUrl property presents the url send to nginx.
|
|
94
|
+
Initialize FuseCore instance, exactly the same API as the standard version.
|
|
156
95
|
|
|
96
|
+
#### Prerequisites
|
|
157
97
|
|
|
158
|
-
|
|
159
|
-
Provides function to resolve request headers,such as accept-language or user-agent.
|
|
98
|
+
- ⚠️ **Must call `initDecryption()` first**
|
|
160
99
|
|
|
161
|
-
|
|
100
|
+
#### Parameters
|
|
162
101
|
|
|
163
|
-
|
|
102
|
+
- **`options`** (object): FuseCore configuration options
|
|
103
|
+
- `log`: Logging configuration
|
|
104
|
+
- `cache`: Cache configuration
|
|
105
|
+
- `monitor`: Monitoring configuration
|
|
106
|
+
- etc... (same as standard version)
|
|
164
107
|
|
|
165
|
-
|
|
108
|
+
#### Examples
|
|
166
109
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
{
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
110
|
+
```javascript
|
|
111
|
+
await FuseCore.init({
|
|
112
|
+
log: {
|
|
113
|
+
level: 'debug',
|
|
114
|
+
format: 'pretty',
|
|
115
|
+
output: './logs'
|
|
116
|
+
},
|
|
117
|
+
cache: {
|
|
118
|
+
type: 'redis',
|
|
119
|
+
host: 'localhost',
|
|
120
|
+
port: 6379
|
|
121
|
+
},
|
|
122
|
+
monitor: {
|
|
123
|
+
enabled: true,
|
|
124
|
+
interval: 3000,
|
|
125
|
+
metrics: ['cpu', 'memory', 'requests']
|
|
126
|
+
}
|
|
127
|
+
});
|
|
185
128
|
```
|
|
186
129
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
param:
|
|
190
|
-
|
|
191
|
-
* req: express request object
|
|
192
|
-
* is4PC: true means client is requesting a web page for PC not mobile device.
|
|
130
|
+
### `FuseCore.shutdown()`
|
|
193
131
|
|
|
194
|
-
|
|
132
|
+
Shutdown FuseCore instance and clean up resources.
|
|
195
133
|
|
|
134
|
+
```javascript
|
|
135
|
+
await FuseCore.shutdown();
|
|
196
136
|
```
|
|
197
|
-
{
|
|
198
|
-
languages: {
|
|
199
|
-
"zh-CN": "0,8",
|
|
200
|
-
"en": "0,6"
|
|
201
|
-
},
|
|
202
|
-
primaryLanguage: ["zh-CN"],
|
|
203
|
-
isEnglish: false,
|
|
204
|
-
lang: "zh-cn" | "en"
|
|
205
|
-
}
|
|
206
|
-
```
|
|
207
|
-
|
|
208
137
|
|
|
209
|
-
|
|
138
|
+
## 🔐 Key Management
|
|
210
139
|
|
|
211
|
-
|
|
212
|
-
1. automatically zip logs
|
|
213
|
-
1. automatically removes oldlogs
|
|
214
|
-
1. Requires init
|
|
215
|
-
1. FuseCore.logger's method can be called at any time, but it won't output any data until init finished.
|
|
140
|
+
### Method 1: Use Bundled Keys (Development/Testing)
|
|
216
141
|
|
|
217
|
-
|
|
142
|
+
The package includes ready-to-use key files:
|
|
218
143
|
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
streams:FileStreamRotator.getStream({...}) | [FileStreamRotator.getStream({...})]
|
|
223
|
-
}
|
|
144
|
+
```javascript
|
|
145
|
+
const privateKeyPath = path.join(__dirname, 'keys/recipient-keys-1.0.3.json');
|
|
146
|
+
await FuseCore.initDecryption(privateKeyPath);
|
|
224
147
|
```
|
|
225
148
|
|
|
226
|
-
|
|
149
|
+
### Method 2: Environment Variables (Production Recommended)
|
|
227
150
|
|
|
151
|
+
```bash
|
|
152
|
+
export FUSECORE_RECIPIENT_PRIVATE_KEY="$(cat /secure/path/recipient-private-key.pem)"
|
|
153
|
+
export FUSECORE_SIGNER_PUBLIC_KEY="$(cat /secure/path/signer-public-key.pem)"
|
|
228
154
|
```
|
|
229
|
-
FuseCore.logger.info('some text',someObject);
|
|
230
155
|
|
|
156
|
+
```javascript
|
|
157
|
+
// When environment variables exist, no parameters need to be passed
|
|
158
|
+
await FuseCore.initDecryption();
|
|
231
159
|
```
|
|
232
160
|
|
|
233
|
-
|
|
234
|
-
```
|
|
235
|
-
let FuseCore=require('fuse-core-express');
|
|
236
|
-
let manifest=FuseCore.manifest;
|
|
161
|
+
### Method 3: Custom Key Files
|
|
237
162
|
|
|
238
|
-
|
|
239
|
-
|
|
163
|
+
```javascript
|
|
164
|
+
await FuseCore.initDecryption('/custom/path/to/private-key.json');
|
|
240
165
|
```
|
|
241
166
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
Ajax API
|
|
167
|
+
## 📁 Package File Structure
|
|
245
168
|
|
|
246
|
-
###### Request Options
|
|
247
|
-
|
|
248
|
-
demo:
|
|
249
169
|
```
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
reqContentType:'form',
|
|
262
|
-
resContentType:'json',
|
|
263
|
-
headers:{
|
|
264
|
-
SomeUserDefinedHeader:'this will pass to server'
|
|
265
|
-
},
|
|
266
|
-
isWeb:true,
|
|
267
|
-
anonymous:true,
|
|
268
|
-
cname:'toto_broadcast',
|
|
269
|
-
passClientIP:false,
|
|
270
|
-
req:req,
|
|
271
|
-
res:res
|
|
272
|
-
}
|
|
273
|
-
|
|
170
|
+
node_modules/fuse-core-express/
|
|
171
|
+
├── index.js # Main entry file
|
|
172
|
+
├── secure-package.vxz # Encrypted code package
|
|
173
|
+
├── decryptor.js # Standalone decryptor
|
|
174
|
+
├── keys/ # Key files directory
|
|
175
|
+
│ ├── build-version.json
|
|
176
|
+
│ ├── recipient-keys-1.0.3.json # Recipient keys (contains private key)
|
|
177
|
+
│ ├── signer-keys-1.0.3.json # Signer public key
|
|
178
|
+
│ └── README.md
|
|
179
|
+
├── example-usage.js # Usage examples
|
|
180
|
+
└── package.json
|
|
274
181
|
```
|
|
275
182
|
|
|
276
|
-
|
|
277
|
-
* version: (string) (optional) Version to replace '${version}' part in url.
|
|
278
|
-
* path: (string) (required) Api path apart from url defined in manifest.
|
|
279
|
-
* data: (object) (optional) Data will send to server.It should be a simple object.
|
|
280
|
-
* method: (string) (optional) Http method,default is 'GET'.
|
|
281
|
-
* timeout: (number) (optional) Timeout (microseconds) before server complete response,default is defined in config or init option DEFAULT_TIMEOUT.
|
|
282
|
-
* reqContentType (deprecated alias dataType): (string) (optional) request data format,only support 'json' and 'form'(default).
|
|
283
|
-
* resContentType (deprecated alias contentType): (string) (optional) Content format.If contentType set 'json' or server response with header 'content-type:application/json', response body will be decode automatically.
|
|
284
|
-
* headers: (object) (optional) Headers passed to server.Note:if req object is set,most of req.headers' property will passed to backend,no need to redefine headers in this option.
|
|
285
|
-
* isWeb: (bool) (optional) If true, and exists cookie named 'N1',then cookie N1's value will replace default Accept-Language value.Default is false.
|
|
286
|
-
* anonymous: (bool) (optional) If false, and exists cookie named 'token',then a header named 'Authorization' will be set with value of cookie 'token'.
|
|
287
|
-
* noCache: (bool) (optional) If true, headers named 'If-Modified-Since','If-None-Match' will be removed from header ,and 'Cache-Control' will be set 'no-cache'. Default is true.
|
|
288
|
-
* passClientIP: (bool) (optional) If true, headers ('X-Forwarded-For','X-Real-IP') will be passed to server. Default is true.
|
|
289
|
-
* req: (obj) (optional) (*required by proxy) By default,most of headers in req will pass to backend request.In proxy method,request's body stream will piped to backend request.
|
|
290
|
-
* res: (obj) (unnecessary)(*required by proxy) Proxy will handle response,when reject happened.Only if ret.status === 0 ,proxy don't handle response,you should end response, such as res.status(500).end() .
|
|
291
|
-
|
|
292
|
-
###### Response Data (also as reject error)
|
|
183
|
+
## 🔄 Complete Usage Workflow
|
|
293
184
|
|
|
294
|
-
|
|
185
|
+
### 1. CommonJS Environment
|
|
295
186
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
187
|
+
```javascript
|
|
188
|
+
const FuseCore = require('fuse-core-express');
|
|
189
|
+
const path = require('path');
|
|
190
|
+
|
|
191
|
+
async function initializeFuseCore() {
|
|
192
|
+
try {
|
|
193
|
+
// Decryption initialization
|
|
194
|
+
const privateKeyPath = path.join(__dirname, 'keys/recipient-keys-1.0.3.json');
|
|
195
|
+
await FuseCore.initDecryption(privateKeyPath);
|
|
196
|
+
|
|
197
|
+
// FuseCore initialization
|
|
198
|
+
await FuseCore.init({
|
|
199
|
+
log: { level: 'info' },
|
|
200
|
+
cache: { type: 'memory' }
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Use FuseCore...
|
|
204
|
+
console.log('FuseCore initialized successfully');
|
|
205
|
+
|
|
206
|
+
} catch (error) {
|
|
207
|
+
console.error('Failed to initialize FuseCore:', error.message);
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
306
210
|
}
|
|
307
211
|
|
|
212
|
+
initializeFuseCore();
|
|
308
213
|
```
|
|
309
214
|
|
|
310
|
-
|
|
311
|
-
* status: (number) http response code from backend server.
|
|
312
|
-
* message: (string) error message when exception or error happened.
|
|
313
|
-
* error: (Error) Error object,if exists.
|
|
314
|
-
* data: (*) parsed server response content.Object if content-type option set "json",string if content-type set something else.
|
|
315
|
-
* raw: (string) original server response text,null when calling proxy method.
|
|
316
|
-
* headers: (object) response headers.
|
|
317
|
-
* duration (array) \[seconds,nanoseconds\], time used from request start to finish.
|
|
318
|
-
* totalCount: (number) same value of response's headers\["X-Total-Count"\],null if header not exists.
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
##### Ajax.request
|
|
322
|
-
|
|
323
|
-
```
|
|
324
|
-
FuseCore.ajax.request({
|
|
325
|
-
server: 'property',
|
|
326
|
-
version: 'v4.6',
|
|
327
|
-
timeout: 3000,
|
|
328
|
-
path: path,
|
|
329
|
-
method: 'POST',
|
|
330
|
-
isWeb:true,
|
|
331
|
-
req: req,
|
|
332
|
-
performance: false,
|
|
333
|
-
proxy: 'http://someuser:password@127.0.0.1:7666',
|
|
334
|
-
headers: {
|
|
335
|
-
'origin': 'https://property-staging.nestia.com'
|
|
336
|
-
}
|
|
337
|
-
}).then((data) => {
|
|
338
|
-
res.render('somepage',data.data);
|
|
339
|
-
}, (err) => {
|
|
340
|
-
req.app.locals.logger.error('error request backend API:' + err.message, err);
|
|
341
|
-
if (err.status) {
|
|
342
|
-
res.status(err.status).end();
|
|
343
|
-
}else{
|
|
344
|
-
res.status(500).end();
|
|
345
|
-
}
|
|
346
|
-
});
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
##### Ajax.proxy
|
|
350
|
-
|
|
351
|
-
```
|
|
352
|
-
FuseCore.ajax.proxy({
|
|
353
|
-
server: 'property',
|
|
354
|
-
path: path,
|
|
355
|
-
method: 'POST',
|
|
356
|
-
isWeb:true,
|
|
357
|
-
req: req,
|
|
358
|
-
res: res,
|
|
359
|
-
headers: {
|
|
360
|
-
'origin': 'https://property-staging.nestia.com'
|
|
361
|
-
}
|
|
362
|
-
}).then((data) => {
|
|
363
|
-
}, (err) => {
|
|
364
|
-
FuseCore.logger.error('error upload property image:' + err.message, err);
|
|
365
|
-
if (!err.status) {
|
|
366
|
-
res.status(500).end();
|
|
367
|
-
}
|
|
368
|
-
});
|
|
369
|
-
```
|
|
215
|
+
### 2. ES Module Environment
|
|
370
216
|
|
|
371
|
-
|
|
217
|
+
```javascript
|
|
218
|
+
import FuseCore from 'fuse-core-express';
|
|
219
|
+
import path from 'path';
|
|
220
|
+
import { fileURLToPath } from 'url';
|
|
372
221
|
|
|
373
|
-
|
|
374
|
-
* When one of requests fails,you will always get a resolve callback, which is different from Promise.all.
|
|
375
|
-
* You can use data\[n\].ok to check whether request fails, and also you can get status, and raw data if exists.
|
|
222
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
376
223
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
timeout: 3000,
|
|
383
|
-
path: '/nearby'
|
|
384
|
-
},
|
|
385
|
-
{
|
|
386
|
-
server: 'news',
|
|
387
|
-
version: 'v4.8',
|
|
388
|
-
path: '/news'
|
|
389
|
-
}
|
|
390
|
-
]).then((datas) => {
|
|
391
|
-
let propertyData=datas[0];
|
|
392
|
-
let newsData=datas[1];
|
|
224
|
+
async function initializeFuseCore() {
|
|
225
|
+
try {
|
|
226
|
+
// Decryption initialization
|
|
227
|
+
const privateKeyPath = path.join(__dirname, 'keys/recipient-keys-1.0.3.json');
|
|
228
|
+
await FuseCore.initDecryption(privateKeyPath);
|
|
393
229
|
|
|
394
|
-
//
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
FuseCore supports two cache implementations: memory cache (memory) and Redis cache (redis).
|
|
407
|
-
|
|
408
|
-
##### Memory Cache (Default)
|
|
409
|
-
|
|
410
|
-
```javascript
|
|
411
|
-
let FuseCore = require('fuse-core-express');
|
|
412
|
-
|
|
413
|
-
// Initialize with memory cache
|
|
414
|
-
await FuseCore.init({
|
|
415
|
-
cache: {
|
|
416
|
-
impl: 'memory'
|
|
230
|
+
// FuseCore initialization
|
|
231
|
+
await FuseCore.init({
|
|
232
|
+
log: { level: 'info' },
|
|
233
|
+
cache: { type: 'memory' }
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// Use FuseCore...
|
|
237
|
+
console.log('FuseCore initialized successfully');
|
|
238
|
+
|
|
239
|
+
} catch (error) {
|
|
240
|
+
console.error('Failed to initialize FuseCore:', error.message);
|
|
241
|
+
process.exit(1);
|
|
417
242
|
}
|
|
418
|
-
}
|
|
243
|
+
}
|
|
419
244
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
// timeout is numeric represents seconds, default value is 300 seconds.
|
|
423
|
-
// when timeout is negative number, the cache record will never expire.
|
|
424
|
-
await cache.set('propName', 'propValue', 30);
|
|
245
|
+
initializeFuseCore();
|
|
246
|
+
```
|
|
425
247
|
|
|
426
|
-
|
|
427
|
-
let val = await cache.get('propName');
|
|
428
|
-
console.log(val); // 'propValue'
|
|
248
|
+
## ⚠️ Security Considerations
|
|
429
249
|
|
|
430
|
-
|
|
431
|
-
setTimeout(async function(){
|
|
432
|
-
let val = await cache.get('propName');
|
|
433
|
-
console.log(val); // should be null
|
|
434
|
-
}, 30 * 1000 + 60000);
|
|
250
|
+
### Development Environment
|
|
435
251
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
await cache.set('anotherPropName', null, 30); // This deletes the key
|
|
252
|
+
- ✅ Can use bundled key files for rapid development
|
|
253
|
+
- ✅ Key files are already included in the npm package, ready to use
|
|
439
254
|
|
|
440
|
-
|
|
441
|
-
console.log(val); // null
|
|
442
|
-
```
|
|
255
|
+
### Production Environment
|
|
443
256
|
|
|
444
|
-
|
|
257
|
+
- 🔐 **Do not expose private keys on the client side**
|
|
258
|
+
- 🔐 **Use environment variables or key management services**
|
|
259
|
+
- 🔐 **Rotate keys regularly**
|
|
260
|
+
- 🔐 **Monitor key usage**
|
|
261
|
+
- 🔐 **Restrict access permissions to key files**
|
|
445
262
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
**Configure Redis Cache:**
|
|
263
|
+
### Recommended Production Configuration
|
|
449
264
|
|
|
450
265
|
```javascript
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
//
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
impl: 'redis',
|
|
457
|
-
redis: {
|
|
458
|
-
url: 'redis://localhost:6379', // Redis connection URL
|
|
459
|
-
enableTls: false, // Enable TLS/SSL connection
|
|
460
|
-
clientOptions: { // Additional Redis client options
|
|
461
|
-
// Optional configuration, such as password, database, etc.
|
|
462
|
-
// password: 'your-password',
|
|
463
|
-
// database: 0
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
// Example with TLS enabled
|
|
470
|
-
await FuseCore.init({
|
|
471
|
-
cache: {
|
|
472
|
-
impl: 'redis',
|
|
473
|
-
redis: {
|
|
474
|
-
url: 'redis://redis-server:6380', // Redis server URL
|
|
475
|
-
enableTls: true, // Enable TLS/SSL connection
|
|
476
|
-
clientOptions: {
|
|
477
|
-
// Additional TLS options if needed
|
|
478
|
-
// password: 'your-password',
|
|
479
|
-
// database: 0
|
|
480
|
-
}
|
|
481
|
-
}
|
|
266
|
+
// Production environment recommended to use environment variables
|
|
267
|
+
if (process.env.NODE_ENV === 'production') {
|
|
268
|
+
// Ensure environment variables are set
|
|
269
|
+
if (!process.env.FUSECORE_RECIPIENT_PRIVATE_KEY) {
|
|
270
|
+
throw new Error('FUSECORE_RECIPIENT_PRIVATE_KEY environment variable is required in production');
|
|
482
271
|
}
|
|
483
|
-
|
|
272
|
+
await FuseCore.initDecryption(); // Automatically use environment variables
|
|
273
|
+
} else {
|
|
274
|
+
// Development environment use bundled keys
|
|
275
|
+
const privateKeyPath = path.join(__dirname, 'keys/recipient-keys-1.0.3.json');
|
|
276
|
+
await FuseCore.initDecryption(privateKeyPath);
|
|
277
|
+
}
|
|
484
278
|
```
|
|
485
279
|
|
|
486
|
-
|
|
280
|
+
## 🛠️ Troubleshooting
|
|
487
281
|
|
|
488
|
-
|
|
489
|
-
redis://[:password@]host[:port][/database]
|
|
490
|
-
redis://localhost:6379 // Local Redis, default port
|
|
491
|
-
redis://:mypassword@localhost:6379/1 // With password, using database 1
|
|
492
|
-
redis://redis-server:6379 // Remote Redis server
|
|
493
|
-
rediss://ssl-redis-server:6380 // SSL encrypted connection
|
|
494
|
-
redis://tls-redis-server:6380 // TLS enabled connection (with enableTls: true)
|
|
495
|
-
```
|
|
282
|
+
### Common Errors
|
|
496
283
|
|
|
497
|
-
|
|
284
|
+
#### 1. "FuseCore not decrypted. Call initDecryption(privateKeyPath) first."
|
|
498
285
|
|
|
499
|
-
|
|
500
|
-
let cache = FuseCore.cache;
|
|
286
|
+
**Cause**: Used FuseCore methods without calling `initDecryption()` first
|
|
501
287
|
|
|
502
|
-
|
|
503
|
-
await cache.set('user:123', { name: 'John', age: 30 }, 3600); // Expires in 1 hour
|
|
504
|
-
await cache.set('session:abc', 'session-data', 0); // Never expires
|
|
288
|
+
**Solution**: Ensure to call `await FuseCore.initDecryption(privateKeyPath)` first
|
|
505
289
|
|
|
506
|
-
|
|
507
|
-
let user = await cache.get('user:123');
|
|
508
|
-
console.log(user); // { name: 'John', age: 30 }
|
|
290
|
+
#### 2. "Private key not found: /path/to/key.json"
|
|
509
291
|
|
|
510
|
-
|
|
511
|
-
await cache.set('json-string', '{"name":"John"}'); // Store JSON string
|
|
512
|
-
let jsonStr = await cache.get('json-string'); // Retrieved is still a string
|
|
513
|
-
console.log(typeof jsonStr); // "string"
|
|
292
|
+
**Cause**: Private key file path is incorrect or file doesn't exist
|
|
514
293
|
|
|
515
|
-
|
|
516
|
-
|
|
294
|
+
**Solution**:
|
|
295
|
+
- Check if the file path is correct
|
|
296
|
+
- Ensure the file exists and is readable
|
|
297
|
+
- Use absolute path or correct relative path
|
|
517
298
|
|
|
518
|
-
|
|
519
|
-
let exists = await cache.exists('user:123');
|
|
520
|
-
console.log(exists); // false
|
|
299
|
+
#### 3. "Signature verification failed"
|
|
521
300
|
|
|
522
|
-
|
|
523
|
-
await cache.set('temp:data', 'some-value');
|
|
524
|
-
await cache.expire('temp:data', 600); // Expires in 10 minutes
|
|
301
|
+
**Cause**: Private key doesn't match the encrypted package, or package is corrupted
|
|
525
302
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
303
|
+
**Solution**:
|
|
304
|
+
- Ensure using the correct version of key files
|
|
305
|
+
- Re-download the package or regenerate keys
|
|
529
306
|
|
|
530
|
-
|
|
531
|
-
let keys = await cache.keys('user:*');
|
|
532
|
-
console.log(keys); // ['user:123', 'user:456', ...]
|
|
307
|
+
#### 4. "Key files not found in either ./keys/ or ../keys/ directories"
|
|
533
308
|
|
|
534
|
-
|
|
535
|
-
await cache.flushAll();
|
|
536
|
-
```
|
|
309
|
+
**Cause**: Cannot find the corresponding signer public key file
|
|
537
310
|
|
|
538
|
-
**
|
|
311
|
+
**Solution**: Ensure the `signer-keys-{version}.json` file exists in the keys directory
|
|
539
312
|
|
|
540
|
-
|
|
541
|
-
try {
|
|
542
|
-
await cache.set('mykey', 'myvalue');
|
|
543
|
-
let value = await cache.get('mykey');
|
|
544
|
-
} catch (error) {
|
|
545
|
-
console.error('Cache operation failed:', error);
|
|
546
|
-
}
|
|
547
|
-
```
|
|
548
|
-
|
|
549
|
-
**Close Connection:**
|
|
550
|
-
|
|
551
|
-
```javascript
|
|
552
|
-
// FuseCore will automatically close Redis connection when the application shuts down
|
|
553
|
-
await FuseCore.shutdown();
|
|
554
|
-
```
|
|
313
|
+
## 📚 More Examples
|
|
555
314
|
|
|
556
|
-
|
|
315
|
+
Check the `example-usage.js` file for complete usage examples.
|
|
557
316
|
|
|
558
|
-
##
|
|
559
|
-
|
|
560
|
-
**FuseCore** follows the [MIT License](LICENSE) open source license.
|
|
561
|
-
|
|
562
|
-
### MIT License
|
|
563
|
-
|
|
564
|
-
Copyright (c) 2024 Ds.3783
|
|
565
|
-
|
|
566
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
567
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
568
|
-
in the Software without restriction, including without limitation the rights
|
|
569
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
570
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
571
|
-
furnished to do so, subject to the following conditions:
|
|
572
|
-
|
|
573
|
-
The above copyright notice and this permission notice shall be included in all
|
|
574
|
-
copies or substantial portions of the Software.
|
|
575
|
-
|
|
576
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
577
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
578
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
579
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
580
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
581
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
582
|
-
SOFTWARE.
|
|
317
|
+
## 🔗 Related Links
|
|
583
318
|
|
|
319
|
+
- [FuseCore Standard Version Documentation](https://github.com/ds3783/FuseCore)
|
|
320
|
+
- [Key Management Best Practices](./keys/README.md)
|
|
584
321
|
|
|
322
|
+
## 📞 Support
|
|
585
323
|
|
|
324
|
+
If you encounter issues during use, please:
|
|
586
325
|
|
|
326
|
+
1. Check the troubleshooting section of this documentation
|
|
327
|
+
2. Review the `example-usage.js` examples
|
|
328
|
+
3. Submit an Issue to the project repository
|
|
587
329
|
|
|
330
|
+
---
|
|
588
331
|
|
|
332
|
+
**Version**: 1.0.3
|
|
333
|
+
**Last Updated**: 2025-08-02
|
|
334
|
+
**Encryption Algorithm**: AES-256-GCM + RSA-OAEP + RSA-PSS-SHA256
|