b2b-platform-utils 1.1.0 → 1.1.2
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/cryptoWrapper.js +10 -12
- package/fileSystem.js +16 -4
- package/localCache.js +163 -20
- package/package.json +2 -2
package/cryptoWrapper.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
// Purpose: PBKDF2-based password hashing/validation with a salt fetched from cache or config file.
|
|
4
|
-
// Note: Public API preserved exactly.
|
|
5
4
|
|
|
6
5
|
const crypto = require('crypto');
|
|
7
6
|
const { getValue } = require('./localCache');
|
|
@@ -12,31 +11,30 @@ let keyLen = 64;
|
|
|
12
11
|
|
|
13
12
|
module.exports = {
|
|
14
13
|
/**
|
|
15
|
-
* Get salt value from in-memory cache or fallback to config file.
|
|
14
|
+
* Get salt value from in-memory cache or fallback to config file ./config/config.json (resolved from process.cwd()).
|
|
16
15
|
*/
|
|
17
16
|
getSalt: () => {
|
|
18
17
|
let salt = getValue('salt');
|
|
19
|
-
// Migrations/seeders flow when keys are not existing in runtime
|
|
20
18
|
if (!salt) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
try {
|
|
20
|
+
let config = readFile('./config/config.json'); // resolved relative to service root
|
|
21
|
+
config = JSON.parse(config);
|
|
22
|
+
salt = config?.salt;
|
|
23
|
+
} catch {
|
|
24
|
+
// Intentionally swallow ENOENT/parse errors; caller will fail deterministically if salt is missing.
|
|
25
|
+
}
|
|
24
26
|
}
|
|
25
27
|
return salt;
|
|
26
28
|
},
|
|
27
29
|
|
|
28
|
-
/**
|
|
29
|
-
* Derive a password hash using PBKDF2-SHA512.
|
|
30
|
-
*/
|
|
30
|
+
/** Derive a password hash using PBKDF2-SHA512. */
|
|
31
31
|
generatePassword: (password) => {
|
|
32
32
|
return crypto
|
|
33
33
|
.pbkdf2Sync(password, module.exports.getSalt(), iterationsCount, keyLen, 'sha512')
|
|
34
34
|
.toString('hex');
|
|
35
35
|
},
|
|
36
36
|
|
|
37
|
-
/**
|
|
38
|
-
* Constant-time style comparison for provided password vs stored hash.
|
|
39
|
-
*/
|
|
37
|
+
/** Constant-time style comparison for provided password vs stored hash. */
|
|
40
38
|
validatePassword: (password, hash) => {
|
|
41
39
|
const derived = crypto
|
|
42
40
|
.pbkdf2Sync(password, module.exports.getSalt(), iterationsCount, keyLen, 'sha512')
|
package/fileSystem.js
CHANGED
|
@@ -1,28 +1,40 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
// Purpose: Simple sync file helpers
|
|
3
|
+
// Purpose: Simple sync file helpers that resolve relative paths from the service root (process.cwd()).
|
|
4
4
|
|
|
5
5
|
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
/** Resolve absolute path; if relative, resolve from process.cwd(). */
|
|
9
|
+
function resolvePath(fileName) {
|
|
10
|
+
return path.isAbsolute(fileName) ? fileName : path.resolve(process.cwd(), fileName);
|
|
11
|
+
}
|
|
6
12
|
|
|
7
13
|
module.exports = {
|
|
8
14
|
/**
|
|
9
15
|
* Save data as JSON into a file (overwrites if exists).
|
|
16
|
+
* Ensures parent directory exists.
|
|
10
17
|
*/
|
|
11
18
|
saveFile: (fileName, data) => {
|
|
12
|
-
|
|
19
|
+
const p = resolvePath(fileName);
|
|
20
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
21
|
+
fs.writeFileSync(p, JSON.stringify(data));
|
|
13
22
|
},
|
|
14
23
|
|
|
15
24
|
/**
|
|
16
25
|
* Read text content from a file using UTF-8.
|
|
26
|
+
* Relative paths are resolved from process.cwd().
|
|
17
27
|
*/
|
|
18
28
|
readFile: (fileName) => {
|
|
19
|
-
|
|
29
|
+
const p = resolvePath(fileName);
|
|
30
|
+
return fs.readFileSync(p, 'utf-8');
|
|
20
31
|
},
|
|
21
32
|
|
|
22
33
|
/**
|
|
23
34
|
* Remove a file from the filesystem.
|
|
24
35
|
*/
|
|
25
36
|
removeFile: (fileName) => {
|
|
26
|
-
|
|
37
|
+
const p = resolvePath(fileName);
|
|
38
|
+
fs.unlinkSync(p);
|
|
27
39
|
}
|
|
28
40
|
};
|
package/localCache.js
CHANGED
|
@@ -1,34 +1,115 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* @module inMemoryConfig
|
|
5
|
+
* @description
|
|
6
|
+
* Process-scoped in-memory key-value store for configuration.
|
|
7
|
+
* - Backward compatible with existing API surface.
|
|
8
|
+
* - Adds currency helpers with explicit opt-in ENV fallback.
|
|
9
|
+
*
|
|
10
|
+
* Design goals:
|
|
11
|
+
* 1) No side effects on import (ENV is read lazily, only if enabled).
|
|
12
|
+
* 2) "Common" bag is the single source of truth for cross-cutting config.
|
|
13
|
+
* 3) Services that do NOT use base currency remain unaffected by ENV.
|
|
14
|
+
*/
|
|
5
15
|
|
|
16
|
+
// ---- Internal state ---------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
/** @type {Record<string, any>} */
|
|
6
19
|
let cacheData = {};
|
|
20
|
+
|
|
21
|
+
/** @type {string} */
|
|
7
22
|
let serviceKey = 'notification';
|
|
23
|
+
|
|
24
|
+
/** @type {number} */
|
|
8
25
|
let bufferChunkSize = 512000;
|
|
26
|
+
|
|
27
|
+
/** @type {string} */
|
|
9
28
|
const GEO_DEFAULT = 'WW';
|
|
10
29
|
|
|
30
|
+
/** @type {boolean} */
|
|
31
|
+
let envBaseCurrencyFallbackEnabled = false; // opt-in per process
|
|
32
|
+
|
|
33
|
+
// ---- Utilities --------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Ensure the "common" bag exists and return it by reference.
|
|
37
|
+
* @returns {Record<string, any>}
|
|
38
|
+
*/
|
|
39
|
+
function ensureCommonBag() {
|
|
40
|
+
if (!cacheData.common || typeof cacheData.common !== 'object') {
|
|
41
|
+
cacheData.common = {};
|
|
42
|
+
}
|
|
43
|
+
return cacheData.common;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Normalize to a 3-letter uppercase currency code.
|
|
48
|
+
* Returns null if invalid.
|
|
49
|
+
* @param {unknown} value
|
|
50
|
+
* @returns {string|null}
|
|
51
|
+
*/
|
|
52
|
+
function normalizeCurrencyCode(value) {
|
|
53
|
+
if (typeof value !== 'string') return null;
|
|
54
|
+
const code = value.trim().toUpperCase();
|
|
55
|
+
return /^[A-Z]{3}$/.test(code) ? code : null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ---- Public API: KV ---------------------------------------------------------
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Set a top-level key in the cache.
|
|
62
|
+
* @param {string} key
|
|
63
|
+
* @param {any} value
|
|
64
|
+
*/
|
|
11
65
|
function setValue(key, value) {
|
|
12
66
|
cacheData[key] = value;
|
|
13
67
|
}
|
|
14
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Get a top-level value from the cache.
|
|
71
|
+
* @param {string} key
|
|
72
|
+
* @returns {any|null}
|
|
73
|
+
*/
|
|
15
74
|
function getValue(key) {
|
|
16
|
-
return cacheData
|
|
75
|
+
return Object.prototype.hasOwnProperty.call(cacheData, key)
|
|
76
|
+
? cacheData[key]
|
|
77
|
+
: null;
|
|
17
78
|
}
|
|
18
79
|
|
|
19
|
-
|
|
20
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Shallow-merge a config object into the cache.
|
|
82
|
+
* @param {Record<string, any>} data
|
|
83
|
+
*/
|
|
84
|
+
function applyConfig(data) {
|
|
85
|
+
for (const k in data) {
|
|
86
|
+
if (Object.prototype.hasOwnProperty.call(data, k)) {
|
|
87
|
+
setValue(k, data[k]);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Return a direct reference to the whole cache (mutable).
|
|
94
|
+
* @returns {Record<string, any>}
|
|
95
|
+
*/
|
|
96
|
+
function getData() {
|
|
97
|
+
return cacheData;
|
|
21
98
|
}
|
|
22
99
|
|
|
100
|
+
// ---- Public API: service/meta ----------------------------------------------
|
|
101
|
+
|
|
23
102
|
/**
|
|
24
103
|
* Return current microservice key.
|
|
104
|
+
* @returns {string}
|
|
25
105
|
*/
|
|
26
106
|
function getService() {
|
|
27
107
|
return serviceKey;
|
|
28
108
|
}
|
|
29
109
|
|
|
30
110
|
/**
|
|
31
|
-
*
|
|
111
|
+
* Override microservice key at runtime.
|
|
112
|
+
* @param {string} key
|
|
32
113
|
*/
|
|
33
114
|
function setService(key) {
|
|
34
115
|
if (typeof key === 'string' && key.length) {
|
|
@@ -36,12 +117,18 @@ function setService(key) {
|
|
|
36
117
|
}
|
|
37
118
|
}
|
|
38
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Get configured buffer chunk size.
|
|
122
|
+
* @returns {number}
|
|
123
|
+
*/
|
|
39
124
|
function getBufferChunkSize() {
|
|
40
125
|
return bufferChunkSize;
|
|
41
126
|
}
|
|
42
127
|
|
|
43
128
|
/**
|
|
44
|
-
* Initialize environment/customer from process params
|
|
129
|
+
* Initialize environment/customer from process params.
|
|
130
|
+
* Kept for compatibility with existing bootstrap flows.
|
|
131
|
+
* @param {string[]} params
|
|
45
132
|
*/
|
|
46
133
|
function parseInitConfig(params) {
|
|
47
134
|
const environment = params[2];
|
|
@@ -51,37 +138,93 @@ function parseInitConfig(params) {
|
|
|
51
138
|
}
|
|
52
139
|
|
|
53
140
|
/**
|
|
54
|
-
*
|
|
141
|
+
* Return the "common" bag snapshot ({} if missing).
|
|
142
|
+
* @returns {Record<string, any>}
|
|
55
143
|
*/
|
|
56
|
-
function
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
144
|
+
function getCommonValue() {
|
|
145
|
+
return cacheData.common && typeof cacheData.common === 'object'
|
|
146
|
+
? cacheData.common
|
|
147
|
+
: {};
|
|
60
148
|
}
|
|
61
149
|
|
|
150
|
+
// ---- Public API: domain helpers --------------------------------------------
|
|
151
|
+
|
|
62
152
|
/**
|
|
63
|
-
*
|
|
153
|
+
* Read GEODefault from "common" or return fallback.
|
|
154
|
+
* @returns {string}
|
|
64
155
|
*/
|
|
65
|
-
function
|
|
66
|
-
return
|
|
156
|
+
function getDefaultGEO() {
|
|
157
|
+
return getValue('common')?.GEODefault || GEO_DEFAULT;
|
|
67
158
|
}
|
|
68
159
|
|
|
69
160
|
/**
|
|
70
|
-
*
|
|
161
|
+
* Enable or disable ENV fallback for BaseCurrency.
|
|
162
|
+
* When enabled and "common.BaseCurrency" is not set,
|
|
163
|
+
* getBaseCurrency() will use process.env.BASE_CURRENCY.
|
|
164
|
+
* @param {boolean} enabled
|
|
71
165
|
*/
|
|
72
|
-
function
|
|
73
|
-
|
|
166
|
+
function enableEnvBaseCurrencyFallback(enabled = true) {
|
|
167
|
+
envBaseCurrencyFallbackEnabled = Boolean(enabled);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Get BaseCurrency from "common", otherwise (optionally) from ENV.
|
|
172
|
+
* Returns null if nothing is set or valid.
|
|
173
|
+
* @returns {string|null}
|
|
174
|
+
*/
|
|
175
|
+
function getBaseCurrency() {
|
|
176
|
+
// 1) Primary source: "common.BaseCurrency"
|
|
177
|
+
const fromCommon = getValue('common')?.BaseCurrency;
|
|
178
|
+
const normalizedCommon = normalizeCurrencyCode(fromCommon);
|
|
179
|
+
if (normalizedCommon) return normalizedCommon;
|
|
180
|
+
|
|
181
|
+
// 2) Optional fallback: process.env.BASE_CURRENCY (lazy read)
|
|
182
|
+
if (envBaseCurrencyFallbackEnabled) {
|
|
183
|
+
const normalizedEnv = normalizeCurrencyCode(process.env.BASE_CURRENCY);
|
|
184
|
+
if (normalizedEnv) return normalizedEnv;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// 3) Nothing found
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Explicitly set BaseCurrency into the "common" bag.
|
|
193
|
+
* @param {string} code - 3-letter code, e.g., "USD", "EUR", "GBP".
|
|
194
|
+
* @throws {Error} If code is invalid.
|
|
195
|
+
*/
|
|
196
|
+
function setBaseCurrency(code) {
|
|
197
|
+
const normalized = normalizeCurrencyCode(code);
|
|
198
|
+
if (!normalized) {
|
|
199
|
+
throw new Error(
|
|
200
|
+
'Invalid currency code. Expected a 3-letter uppercase code like "USD", "EUR", or "GBP".'
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
const common = ensureCommonBag();
|
|
204
|
+
common.BaseCurrency = normalized;
|
|
74
205
|
}
|
|
75
206
|
|
|
207
|
+
// ---- Exports ---------------------------------------------------------------
|
|
208
|
+
|
|
76
209
|
module.exports = {
|
|
210
|
+
// KV
|
|
77
211
|
setValue,
|
|
78
212
|
getValue,
|
|
79
|
-
parseInitConfig,
|
|
80
213
|
applyConfig,
|
|
81
214
|
getData,
|
|
215
|
+
|
|
216
|
+
// Service/meta
|
|
82
217
|
getService,
|
|
83
218
|
setService,
|
|
84
219
|
getBufferChunkSize,
|
|
220
|
+
parseInitConfig,
|
|
221
|
+
getCommonValue,
|
|
222
|
+
|
|
223
|
+
// Domain helpers
|
|
85
224
|
getDefaultGEO,
|
|
86
|
-
|
|
225
|
+
|
|
226
|
+
// Currency helpers
|
|
227
|
+
getBaseCurrency,
|
|
228
|
+
setBaseCurrency,
|
|
229
|
+
enableEnvBaseCurrencyFallback
|
|
87
230
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "b2b-platform-utils",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "Shared utilities for Node.js microservices: errors map, local cache, logger, numbers, dates, filesystem, media optimization, paginator, slugger, crypto wrapper, sanitize HTML, sorting.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"license": "KingSizer",
|
|
@@ -22,4 +22,4 @@
|
|
|
22
22
|
"moment": "^2.30.1",
|
|
23
23
|
"sharp": "^0.33.0"
|
|
24
24
|
}
|
|
25
|
-
}
|
|
25
|
+
}
|