lupine.api 1.1.57 → 1.1.59

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 (137) hide show
  1. package/README.md +3 -3
  2. package/admin/admin-about.tsx +12 -16
  3. package/admin/admin-config.tsx +47 -44
  4. package/admin/admin-css.tsx +3 -3
  5. package/admin/admin-db.tsx +75 -75
  6. package/admin/admin-frame-helper.tsx +364 -364
  7. package/admin/admin-frame.tsx +164 -164
  8. package/admin/admin-index.tsx +65 -65
  9. package/admin/admin-login.tsx +111 -111
  10. package/admin/admin-menu-edit.tsx +637 -637
  11. package/admin/admin-menu-list.tsx +87 -87
  12. package/admin/admin-page-edit.tsx +564 -564
  13. package/admin/admin-page-list.tsx +83 -83
  14. package/admin/admin-performance.tsx +28 -28
  15. package/admin/admin-release.tsx +427 -404
  16. package/admin/admin-resources.tsx +382 -382
  17. package/admin/admin-shell.tsx +89 -89
  18. package/admin/admin-table-data.tsx +146 -146
  19. package/admin/admin-table-list.tsx +230 -230
  20. package/admin/admin-test-animations.tsx +395 -395
  21. package/admin/admin-test-component.tsx +823 -808
  22. package/admin/admin-test-edit.tsx +319 -319
  23. package/admin/admin-test-themes.tsx +56 -56
  24. package/admin/admin-tokens.tsx +338 -338
  25. package/admin/design/admin-design.tsx +174 -174
  26. package/admin/design/block-grid.tsx +36 -36
  27. package/admin/design/block-grid1.tsx +21 -21
  28. package/admin/design/block-paragraph.tsx +19 -19
  29. package/admin/design/block-title.tsx +19 -19
  30. package/admin/design/design-block-box.tsx +140 -140
  31. package/admin/design/drag-data.tsx +24 -24
  32. package/admin/index.ts +9 -9
  33. package/admin/package.json +15 -15
  34. package/admin/tsconfig.json +127 -127
  35. package/dev/copy-folder.js +32 -32
  36. package/dev/cp-index-html.js +69 -69
  37. package/dev/file-utils.js +12 -12
  38. package/dev/index.js +18 -19
  39. package/dev/package.json +12 -12
  40. package/dev/plugin-ifelse.js +168 -168
  41. package/dev/plugin-ifelse.test.js +37 -37
  42. package/dev/run-cmd.js +14 -14
  43. package/dev/send-request.js +12 -12
  44. package/package.json +55 -55
  45. package/src/admin-api/admin-api-helper.ts +210 -205
  46. package/src/admin-api/admin-api.ts +65 -65
  47. package/src/admin-api/admin-auth.ts +152 -146
  48. package/src/admin-api/admin-config.ts +94 -84
  49. package/src/admin-api/admin-csv.ts +94 -94
  50. package/src/admin-api/admin-db.ts +269 -269
  51. package/src/admin-api/admin-menu.ts +135 -135
  52. package/src/admin-api/admin-page.ts +135 -135
  53. package/src/admin-api/admin-performance.ts +128 -128
  54. package/src/admin-api/admin-release.ts +703 -700
  55. package/src/admin-api/admin-resources.ts +318 -318
  56. package/src/admin-api/admin-token-helper.ts +82 -79
  57. package/src/admin-api/admin-tokens.ts +90 -90
  58. package/src/admin-api/index.ts +2 -2
  59. package/src/admin-api/web-config-api.ts +19 -19
  60. package/src/api/api-cache.ts +103 -103
  61. package/src/api/api-helper.ts +44 -44
  62. package/src/api/api-module.ts +67 -60
  63. package/src/api/api-router.ts +177 -177
  64. package/src/api/api-shared-storage.ts +64 -64
  65. package/src/api/async-storage.ts +5 -5
  66. package/src/api/debug-service.ts +56 -56
  67. package/src/api/encode-html.ts +27 -27
  68. package/src/api/handle-status.ts +75 -75
  69. package/src/api/index.ts +15 -16
  70. package/src/api/mini-web-socket.ts +270 -270
  71. package/src/api/server-content-type.ts +82 -82
  72. package/src/api/server-render.ts +235 -215
  73. package/src/api/shell-service.ts +74 -74
  74. package/src/api/simple-storage.ts +80 -80
  75. package/src/api/static-server.ts +128 -125
  76. package/src/api/to-client-delivery.ts +26 -26
  77. package/src/app/app-cache.ts +55 -55
  78. package/src/app/app-helper.ts +62 -62
  79. package/src/app/app-message.ts +109 -109
  80. package/src/app/app-shared-storage.ts +363 -363
  81. package/src/app/app-start.ts +136 -136
  82. package/src/app/cleanup-exit.ts +16 -16
  83. package/src/app/host-to-path.ts +38 -38
  84. package/src/app/index.ts +11 -11
  85. package/src/app/process-dev-requests.ts +130 -130
  86. package/src/app/web-listener.ts +294 -294
  87. package/src/app/web-processor.ts +47 -42
  88. package/src/app/web-server.ts +100 -100
  89. package/src/common-js/web-env.js +104 -104
  90. package/src/index.ts +7 -7
  91. package/src/lang/api-lang-en.ts +26 -26
  92. package/src/lang/api-lang-zh-cn.ts +27 -27
  93. package/src/lang/index.ts +2 -2
  94. package/src/lang/lang-helper.ts +76 -76
  95. package/src/lang/lang-props.ts +6 -6
  96. package/src/lib/db/db-helper.ts +23 -23
  97. package/src/lib/db/db-mysql.ts +249 -250
  98. package/src/lib/db/db-sqlite.ts +101 -101
  99. package/src/lib/db/db.spec.ts +28 -28
  100. package/src/lib/db/db.ts +325 -325
  101. package/src/lib/db/index.ts +5 -5
  102. package/src/lib/index.ts +3 -3
  103. package/src/lib/logger.spec.ts +214 -214
  104. package/src/lib/logger.ts +281 -281
  105. package/src/lib/runtime-require.ts +37 -37
  106. package/src/lib/utils/cookie-util.ts +34 -34
  107. package/src/lib/utils/crypto.ts +58 -58
  108. package/src/lib/utils/date-utils.ts +317 -317
  109. package/src/lib/utils/deep-merge.ts +37 -37
  110. package/src/lib/utils/delay.ts +12 -12
  111. package/src/lib/utils/file-setting.ts +55 -55
  112. package/src/lib/utils/format-bytes.ts +11 -11
  113. package/src/lib/utils/fs-utils.ts +158 -158
  114. package/src/lib/utils/get-env.ts +27 -27
  115. package/src/lib/utils/index.ts +12 -12
  116. package/src/lib/utils/is-type.ts +48 -48
  117. package/src/lib/utils/load-env.ts +14 -14
  118. package/src/lib/utils/pad.ts +6 -6
  119. package/src/models/api-base.ts +5 -5
  120. package/src/models/api-module-props.ts +10 -11
  121. package/src/models/api-router-props.ts +26 -26
  122. package/src/models/app-cache-props.ts +33 -33
  123. package/src/models/app-data-props.ts +10 -10
  124. package/src/models/app-helper-props.ts +6 -6
  125. package/src/models/app-shared-storage-props.ts +38 -38
  126. package/src/models/app-start-props.ts +18 -18
  127. package/src/models/async-storage-props.ts +13 -13
  128. package/src/models/db-config.ts +30 -30
  129. package/src/models/host-to-path-props.ts +12 -12
  130. package/src/models/index.ts +16 -16
  131. package/src/models/json-object.ts +8 -8
  132. package/src/models/locals-props.ts +36 -36
  133. package/src/models/logger-props.ts +84 -84
  134. package/src/models/simple-storage-props.ts +13 -14
  135. package/src/models/to-client-delivery-props.ts +6 -6
  136. package/tsconfig.json +115 -115
  137. package/dev/plugin-gen-versions.js +0 -20
@@ -1,363 +1,363 @@
1
- /**
2
- * A persistent storage to store data in primary process and share to all workers
3
- *
4
- * You should use apiStorage in api module
5
- */
6
- import * as fs from 'fs/promises';
7
- import * as path from 'path';
8
- import cluster from 'cluster';
9
- import { SimpleStorage } from '../api';
10
- import { FsUtils, Logger } from '../lib';
11
- import {
12
- SimpleStorageDataProps,
13
- AppSharedStorageApiPrefix,
14
- AppSharedStorageMessageId,
15
- AppSharedStorageWebPrefix,
16
- IAppSharedStorage,
17
- StorageMessageFromSubProcess,
18
- } from '../models';
19
- import { registerMessageHandlerFromPrimary, registerMessageHandlerFromWorker } from './app-message';
20
-
21
- // in Api scope, use ApiSharedStorage instead of this
22
- // storage cross clusters, loaded when start and saved before exist
23
- export class AppSharedStorage implements IAppSharedStorage {
24
- private static instance: IAppSharedStorage;
25
- configMap: { [key: string]: { fPath: string; storage: SimpleStorage } } = {};
26
- logger = new Logger('server-config');
27
-
28
- private constructor() {}
29
-
30
- public static getInstance(): IAppSharedStorage {
31
- if (!AppSharedStorage.instance) {
32
- AppSharedStorage.instance = new AppSharedStorage();
33
- // register app message handlers
34
- registerMessageHandlerFromPrimary(
35
- AppSharedStorageMessageId,
36
- AppSharedStorage.instance.messageFromPrimaryProcess.bind(AppSharedStorage.instance)
37
- );
38
- registerMessageHandlerFromWorker(
39
- AppSharedStorageMessageId,
40
- AppSharedStorage.instance.messageFromSubProcess.bind(AppSharedStorage.instance)
41
- );
42
- }
43
- return AppSharedStorage.instance;
44
- }
45
-
46
- private getWorker(workerId: number) {
47
- for (let i in cluster.workers) {
48
- if (cluster.workers[i]?.id === workerId) return cluster.workers[i];
49
- }
50
- }
51
-
52
- private getStorageMap(appName: string) {
53
- let storage = this.configMap[appName];
54
- if (!storage) {
55
- this.configMap[appName] = { fPath: '', storage: new SimpleStorage({}) };
56
- storage = this.configMap[appName];
57
- }
58
- return storage;
59
- }
60
- private getStorage(appName: string): SimpleStorage {
61
- return this.getStorageMap(appName).storage;
62
- }
63
-
64
- // this is primary, msg is from a client
65
- messageFromSubProcess(msgObject: StorageMessageFromSubProcess) {
66
- if (!cluster.isPrimary || !msgObject.action || !msgObject.key || !msgObject.appName || !msgObject.workerId) {
67
- console.error('AppStorage got wrong message: ', msgObject);
68
- return;
69
- }
70
-
71
- if (msgObject.action === 'get') {
72
- const storage = this.getStorage(msgObject.appName);
73
- const value = storage.get(msgObject.key, '');
74
- // console.log(`AppStorage get value from ${msgObject.pid} in ${process.pid}, for key: ${msgObject.key}`);
75
- // send message back to the worker from the worker id
76
- const worker = this.getWorker(msgObject.workerId);
77
- if (!worker) {
78
- console.error("AppStorage can' find the worker: ", msgObject);
79
- return;
80
- }
81
- worker.send({
82
- value,
83
- ...msgObject,
84
- });
85
- } else if (msgObject.action === 'getWithPrefix') {
86
- const storage = this.getStorage(msgObject.appName);
87
- const value = storage.getWithPrefix(msgObject.key);
88
- // console.log(`AppStorage getWithPrefix from ${msgObject.pid} in ${process.pid}, for key: ${msgObject.key}`);
89
- // send message back to the worker from the worker id
90
- const worker = this.getWorker(msgObject.workerId);
91
- if (!worker) {
92
- console.error("AppStorage can' find the worker: ", msgObject);
93
- return;
94
- }
95
- worker.send({
96
- value: JSON.stringify(value),
97
- ...msgObject,
98
- });
99
- } else if (msgObject.action === 'set') {
100
- const storage = this.getStorage(msgObject.appName);
101
- storage.set(msgObject.key, msgObject.value);
102
- } else if (msgObject.action === 'load') {
103
- this.load(msgObject.appName, msgObject.rootPath || '');
104
- } else if (msgObject.action === 'save') {
105
- this.save(msgObject.appName);
106
- } else {
107
- this.logger.warn(`Unknown message: ${msgObject.action}`);
108
- }
109
- }
110
-
111
- // this is a worker and msg is from Primary
112
- // when debug is on, it's in primary, but it shouldn't receive those msgs
113
- // mainly for get (a worker requests a get, primaary sends the value back to here)
114
- messageFromPrimaryProcess(msgObject: StorageMessageFromSubProcess) {
115
- AppSharedStorageWorker.messageFromPrimaryProcess(msgObject);
116
- }
117
-
118
- // should be only called from primary when the app is starting
119
- async load(appName: string, rootPath: string) {
120
- if (!cluster.isPrimary) {
121
- // throw new Error('AppStorage.load should be only called from primary');
122
- await AppSharedStorageWorker.load(appName, rootPath);
123
- return;
124
- }
125
-
126
- const map = this.getStorageMap(appName);
127
- map.fPath = path.join(rootPath, 'config.json');
128
-
129
- let tempPath = map.fPath;
130
- try {
131
- if (!(await FsUtils.pathExist(tempPath))) {
132
- tempPath = path.join(rootPath, 'resources', 'config_default.json');
133
- }
134
-
135
- let json;
136
- if ((json = await fs.readFile(tempPath, 'utf-8'))) {
137
- // it will still save to map.fPath
138
- map.storage.setContent(JSON.parse(json));
139
- map.storage.Dirty = false;
140
- this.logger.info(`Loading shared storage for [ ${appName} ] from ${tempPath}`);
141
- }
142
- } catch (e: any) {
143
- this.logger.error('Loading json file failed: ' + tempPath, e.message);
144
- }
145
- }
146
-
147
- // called from primary before exit, or from api to save changes
148
- async save(appName?: string, exit?: boolean) {
149
- if (!cluster.isPrimary) {
150
- await AppSharedStorageWorker.save(appName);
151
- return;
152
- }
153
-
154
- console.log(`${process.pid} - AppStorage save, appName: ${appName}, exit: ${exit}`);
155
- if (appName) {
156
- const map = this.configMap[appName];
157
- if (map && map.fPath && map.storage.size() > 0 && map.storage.Dirty) {
158
- await map.storage.saveContent(map.fPath);
159
- }
160
- } else {
161
- // save all data
162
- for (let appName in this.configMap) {
163
- const map = this.configMap[appName];
164
- if (map && map.fPath && map.storage.size() > 0 && map.storage.Dirty) {
165
- await map.storage.saveContent(map.fPath);
166
- }
167
- }
168
- }
169
- }
170
-
171
- // this can be called in primary or worker
172
- get(appName: string, key: string): Promise<string> {
173
- return new Promise((resolve, reject) => {
174
- // console.log(`${process.pid} - AppStorage get value for key: ${key}`);
175
-
176
- if (!cluster.isPrimary) {
177
- AppSharedStorageWorker.get(appName, key, resolve, reject);
178
- return;
179
- }
180
-
181
- // in primary
182
- resolve(this.getStorage(appName).get(key, ''));
183
- });
184
- }
185
- getWeb(appName: string, key: string): Promise<string> {
186
- return this.get(appName, AppSharedStorageWebPrefix + key);
187
- }
188
- getApi(appName: string, key: string): Promise<string> {
189
- return this.get(appName, AppSharedStorageApiPrefix + key);
190
- }
191
- async getWebAll(appName: string): Promise<SimpleStorageDataProps> {
192
- const webAll = await this.getWithPrefix(appName, AppSharedStorageWebPrefix);
193
-
194
- const webSettingShortKey: SimpleStorageDataProps = {};
195
- for (let item of Object.keys(webAll)) {
196
- const newItem = item.substring(AppSharedStorageWebPrefix.length);
197
- webSettingShortKey[newItem] = webAll[item];
198
- }
199
-
200
- return webSettingShortKey;
201
- }
202
- getWithPrefix(appName: string, prefixKey: string): Promise<SimpleStorageDataProps> {
203
- return new Promise((resolve, reject) => {
204
- // console.log(`${process.pid} - AppStorage getWithPrefix for prefixKey: ${prefixKey}`);
205
-
206
- if (!cluster.isPrimary) {
207
- AppSharedStorageWorker.getWithPrefix(appName, prefixKey, resolve, reject);
208
- return;
209
- }
210
-
211
- // in primary
212
- resolve(this.getStorage(appName).getWithPrefix(prefixKey));
213
- });
214
- }
215
-
216
- // this can be called in primary or worker
217
- set(appName: string, key: string, value: any) {
218
- if (!cluster.isPrimary) {
219
- AppSharedStorageWorker.set(appName, key, value);
220
- return;
221
- }
222
-
223
- // in primary
224
- this.getStorage(appName).set(key, value);
225
- }
226
- setWeb(appName: string, key: string, value: any) {
227
- return this.set(appName, AppSharedStorageWebPrefix + key, value);
228
- }
229
- setApi(appName: string, key: string, value: any) {
230
- return this.set(appName, AppSharedStorageApiPrefix + key, value);
231
- }
232
- }
233
-
234
- type AppSharedStorageWorkerMap = { [key: string]: any };
235
- class AppSharedStorageWorker {
236
- static handleMap: AppSharedStorageWorkerMap = {};
237
- static logger = new Logger('server-config');
238
-
239
- static messageFromPrimaryProcess(msgObject: StorageMessageFromSubProcess) {
240
- if (cluster.isPrimary || !msgObject.action || !msgObject.key || !msgObject.uniqueKey) {
241
- console.error('AppSharedStorageWorker got wrong message: ', msgObject);
242
- return;
243
- }
244
-
245
- if (msgObject.action === 'get') {
246
- // console.log(`${process.pid} - AppStorage get value end for key: ${msgObject.key}`);
247
-
248
- const value = msgObject.value;
249
- // how to pass the value to the caller
250
- const map = this.handleMap[msgObject.uniqueKey];
251
- delete this.handleMap[msgObject.uniqueKey];
252
- if (map) {
253
- map.resolve(value);
254
- } else {
255
- throw new Error(`Unknown uniqueKey: ${msgObject.uniqueKey}`);
256
- }
257
- } else if (msgObject.action === 'getWithPrefix') {
258
- console.log(`${process.pid} - AppStorage get value end for key: ${msgObject.key}`);
259
- const value = JSON.parse(msgObject.value);
260
- // how to pass the value to the caller
261
- const map = this.handleMap[msgObject.uniqueKey];
262
- delete this.handleMap[msgObject.uniqueKey];
263
- if (map) {
264
- map.resolve(value);
265
- } else {
266
- throw new Error(`Unknown uniqueKey: ${msgObject.uniqueKey}`);
267
- }
268
- } else {
269
- this.logger.warn(`Unknown message: ${msgObject.action}`);
270
- }
271
- }
272
-
273
- static async load(appName: string, rootPath: string) {
274
- if (cluster.isPrimary) {
275
- throw new Error('AppSharedStorageWorker should be only called from workers');
276
- }
277
- const obj: StorageMessageFromSubProcess = {
278
- id: AppSharedStorageMessageId,
279
- pid: process.pid,
280
- workerId: cluster.worker?.id || 0,
281
- action: 'load',
282
- appName: appName,
283
- rootPath: rootPath,
284
- key: 'load',
285
- };
286
- process.send!(obj);
287
- }
288
-
289
- static async save(appName?: string) {
290
- if (cluster.isPrimary) {
291
- throw new Error('AppSharedStorageWorker should be only called from workers');
292
- }
293
- const obj: StorageMessageFromSubProcess = {
294
- id: AppSharedStorageMessageId,
295
- pid: process.pid,
296
- workerId: cluster.worker?.id || 0,
297
- action: 'save',
298
- appName: appName || '',
299
- key: 'save',
300
- };
301
- process.send!(obj);
302
- }
303
-
304
- static get(appName: string, key: string, resolve: (value: any) => void, reject: (reason: any) => void) {
305
- if (cluster.isPrimary) {
306
- throw new Error('AppSharedStorageWorker should be only called from workers');
307
- }
308
- const uniqueKey = key + ':' + crypto.randomUUID();
309
- AppSharedStorageWorker.handleMap[uniqueKey] = { resolve, reject };
310
- const obj: StorageMessageFromSubProcess = {
311
- id: AppSharedStorageMessageId,
312
- pid: process.pid,
313
- workerId: cluster.worker?.id || 0,
314
- action: 'get',
315
- appName,
316
- uniqueKey,
317
- key,
318
- };
319
- process.send!(obj);
320
- }
321
-
322
- static getWithPrefix(
323
- appName: string,
324
- prefixKey: string,
325
- resolve: (value: any) => void,
326
- reject: (reason: any) => void
327
- ) {
328
- if (cluster.isPrimary) {
329
- throw new Error('AppSharedStorageWorker should be only called from workers');
330
- }
331
- const uniqueKey = prefixKey + ':' + crypto.randomUUID();
332
- AppSharedStorageWorker.handleMap[uniqueKey] = { resolve, reject };
333
- const obj: StorageMessageFromSubProcess = {
334
- id: AppSharedStorageMessageId,
335
- pid: process.pid,
336
- workerId: cluster.worker?.id || 0,
337
- action: 'getWithPrefix',
338
- appName,
339
- uniqueKey,
340
- key: prefixKey,
341
- };
342
- process.send!(obj);
343
- }
344
-
345
- static set(appName: string, key: string, value: any) {
346
- if (cluster.isPrimary) {
347
- throw new Error('AppSharedStorageWorker should be only called from workers');
348
- }
349
- const obj: StorageMessageFromSubProcess = {
350
- id: AppSharedStorageMessageId,
351
- pid: process.pid,
352
- workerId: cluster.worker?.id || 0,
353
- action: 'set',
354
- appName,
355
- key,
356
- value,
357
- };
358
- process.send!(obj);
359
- }
360
- }
361
-
362
- // this can be used in app, but in api, it should use getAppStorage()
363
- export const appStorage = /* @__PURE__ */ AppSharedStorage.getInstance();
1
+ /**
2
+ * A persistent storage to store data in primary process and share to all workers
3
+ *
4
+ * You should use apiStorage in api module
5
+ */
6
+ import * as fs from 'fs/promises';
7
+ import * as path from 'path';
8
+ import cluster from 'cluster';
9
+ import { SimpleStorage } from '../api';
10
+ import { FsUtils, Logger } from '../lib';
11
+ import {
12
+ SimpleStorageDataProps,
13
+ AppSharedStorageApiPrefix,
14
+ AppSharedStorageMessageId,
15
+ AppSharedStorageWebPrefix,
16
+ IAppSharedStorage,
17
+ StorageMessageFromSubProcess,
18
+ } from '../models';
19
+ import { registerMessageHandlerFromPrimary, registerMessageHandlerFromWorker } from './app-message';
20
+
21
+ // in Api scope, use ApiSharedStorage instead of this
22
+ // storage cross clusters, loaded when start and saved before exist
23
+ export class AppSharedStorage implements IAppSharedStorage {
24
+ private static instance: IAppSharedStorage;
25
+ configMap: { [key: string]: { fPath: string; storage: SimpleStorage } } = {};
26
+ logger = new Logger('server-config');
27
+
28
+ private constructor() {}
29
+
30
+ public static getInstance(): IAppSharedStorage {
31
+ if (!AppSharedStorage.instance) {
32
+ AppSharedStorage.instance = new AppSharedStorage();
33
+ // register app message handlers
34
+ registerMessageHandlerFromPrimary(
35
+ AppSharedStorageMessageId,
36
+ AppSharedStorage.instance.messageFromPrimaryProcess.bind(AppSharedStorage.instance)
37
+ );
38
+ registerMessageHandlerFromWorker(
39
+ AppSharedStorageMessageId,
40
+ AppSharedStorage.instance.messageFromSubProcess.bind(AppSharedStorage.instance)
41
+ );
42
+ }
43
+ return AppSharedStorage.instance;
44
+ }
45
+
46
+ private getWorker(workerId: number) {
47
+ for (let i in cluster.workers) {
48
+ if (cluster.workers[i]?.id === workerId) return cluster.workers[i];
49
+ }
50
+ }
51
+
52
+ private getStorageMap(appName: string) {
53
+ let storage = this.configMap[appName];
54
+ if (!storage) {
55
+ this.configMap[appName] = { fPath: '', storage: new SimpleStorage({}) };
56
+ storage = this.configMap[appName];
57
+ }
58
+ return storage;
59
+ }
60
+ private getStorage(appName: string): SimpleStorage {
61
+ return this.getStorageMap(appName).storage;
62
+ }
63
+
64
+ // this is primary, msg is from a client
65
+ messageFromSubProcess(msgObject: StorageMessageFromSubProcess) {
66
+ if (!cluster.isPrimary || !msgObject.action || !msgObject.key || !msgObject.appName || !msgObject.workerId) {
67
+ console.error('AppStorage got wrong message: ', msgObject);
68
+ return;
69
+ }
70
+
71
+ if (msgObject.action === 'get') {
72
+ const storage = this.getStorage(msgObject.appName);
73
+ const value = storage.get(msgObject.key, '');
74
+ // console.log(`AppStorage get value from ${msgObject.pid} in ${process.pid}, for key: ${msgObject.key}`);
75
+ // send message back to the worker from the worker id
76
+ const worker = this.getWorker(msgObject.workerId);
77
+ if (!worker) {
78
+ console.error("AppStorage can' find the worker: ", msgObject);
79
+ return;
80
+ }
81
+ worker.send({
82
+ value,
83
+ ...msgObject,
84
+ });
85
+ } else if (msgObject.action === 'getWithPrefix') {
86
+ const storage = this.getStorage(msgObject.appName);
87
+ const value = storage.getWithPrefix(msgObject.key);
88
+ // console.log(`AppStorage getWithPrefix from ${msgObject.pid} in ${process.pid}, for key: ${msgObject.key}`);
89
+ // send message back to the worker from the worker id
90
+ const worker = this.getWorker(msgObject.workerId);
91
+ if (!worker) {
92
+ console.error("AppStorage can' find the worker: ", msgObject);
93
+ return;
94
+ }
95
+ worker.send({
96
+ value: JSON.stringify(value),
97
+ ...msgObject,
98
+ });
99
+ } else if (msgObject.action === 'set') {
100
+ const storage = this.getStorage(msgObject.appName);
101
+ storage.set(msgObject.key, msgObject.value);
102
+ } else if (msgObject.action === 'load') {
103
+ this.load(msgObject.appName, msgObject.rootPath || '');
104
+ } else if (msgObject.action === 'save') {
105
+ this.save(msgObject.appName);
106
+ } else {
107
+ this.logger.warn(`Unknown message: ${msgObject.action}`);
108
+ }
109
+ }
110
+
111
+ // this is a worker and msg is from Primary
112
+ // when debug is on, it's in primary, but it shouldn't receive those msgs
113
+ // mainly for get (a worker requests a get, primaary sends the value back to here)
114
+ messageFromPrimaryProcess(msgObject: StorageMessageFromSubProcess) {
115
+ AppSharedStorageWorker.messageFromPrimaryProcess(msgObject);
116
+ }
117
+
118
+ // should be only called from primary when the app is starting
119
+ async load(appName: string, rootPath: string) {
120
+ if (!cluster.isPrimary) {
121
+ // throw new Error('AppStorage.load should be only called from primary');
122
+ await AppSharedStorageWorker.load(appName, rootPath);
123
+ return;
124
+ }
125
+
126
+ const map = this.getStorageMap(appName);
127
+ map.fPath = path.join(rootPath, 'config.json');
128
+
129
+ let tempPath = map.fPath;
130
+ try {
131
+ if (!(await FsUtils.pathExist(tempPath))) {
132
+ tempPath = path.join(rootPath, 'resources', 'config_default.json');
133
+ }
134
+
135
+ let json;
136
+ if ((json = await fs.readFile(tempPath, 'utf-8'))) {
137
+ // it will still save to map.fPath
138
+ map.storage.setContent(JSON.parse(json));
139
+ map.storage.Dirty = false;
140
+ this.logger.info(`Loading shared storage for [ ${appName} ] from ${tempPath}`);
141
+ }
142
+ } catch (e: any) {
143
+ this.logger.error('Loading json file failed: ' + tempPath, e.message);
144
+ }
145
+ }
146
+
147
+ // called from primary before exit, or from api to save changes
148
+ async save(appName?: string, exit?: boolean) {
149
+ if (!cluster.isPrimary) {
150
+ await AppSharedStorageWorker.save(appName);
151
+ return;
152
+ }
153
+
154
+ console.log(`${process.pid} - AppStorage save, appName: ${appName}, exit: ${exit}`);
155
+ if (appName) {
156
+ const map = this.configMap[appName];
157
+ if (map && map.fPath && map.storage.size() > 0 && map.storage.Dirty) {
158
+ await map.storage.saveContent(map.fPath);
159
+ }
160
+ } else {
161
+ // save all data
162
+ for (let appName in this.configMap) {
163
+ const map = this.configMap[appName];
164
+ if (map && map.fPath && map.storage.size() > 0 && map.storage.Dirty) {
165
+ await map.storage.saveContent(map.fPath);
166
+ }
167
+ }
168
+ }
169
+ }
170
+
171
+ // this can be called in primary or worker
172
+ get(appName: string, key: string): Promise<string> {
173
+ return new Promise((resolve, reject) => {
174
+ // console.log(`${process.pid} - AppStorage get value for key: ${key}`);
175
+
176
+ if (!cluster.isPrimary) {
177
+ AppSharedStorageWorker.get(appName, key, resolve, reject);
178
+ return;
179
+ }
180
+
181
+ // in primary
182
+ resolve(this.getStorage(appName).get(key, ''));
183
+ });
184
+ }
185
+ getWeb(appName: string, key: string): Promise<string> {
186
+ return this.get(appName, AppSharedStorageWebPrefix + key);
187
+ }
188
+ getApi(appName: string, key: string): Promise<string> {
189
+ return this.get(appName, AppSharedStorageApiPrefix + key);
190
+ }
191
+ async getWebAll(appName: string): Promise<SimpleStorageDataProps> {
192
+ const webAll = await this.getWithPrefix(appName, AppSharedStorageWebPrefix);
193
+
194
+ const webSettingShortKey: SimpleStorageDataProps = {};
195
+ for (let item of Object.keys(webAll)) {
196
+ const newItem = item.substring(AppSharedStorageWebPrefix.length);
197
+ webSettingShortKey[newItem] = webAll[item];
198
+ }
199
+
200
+ return webSettingShortKey;
201
+ }
202
+ getWithPrefix(appName: string, prefixKey: string): Promise<SimpleStorageDataProps> {
203
+ return new Promise((resolve, reject) => {
204
+ // console.log(`${process.pid} - AppStorage getWithPrefix for prefixKey: ${prefixKey}`);
205
+
206
+ if (!cluster.isPrimary) {
207
+ AppSharedStorageWorker.getWithPrefix(appName, prefixKey, resolve, reject);
208
+ return;
209
+ }
210
+
211
+ // in primary
212
+ resolve(this.getStorage(appName).getWithPrefix(prefixKey));
213
+ });
214
+ }
215
+
216
+ // this can be called in primary or worker
217
+ set(appName: string, key: string, value: any) {
218
+ if (!cluster.isPrimary) {
219
+ AppSharedStorageWorker.set(appName, key, value);
220
+ return;
221
+ }
222
+
223
+ // in primary
224
+ this.getStorage(appName).set(key, value);
225
+ }
226
+ setWeb(appName: string, key: string, value: any) {
227
+ return this.set(appName, AppSharedStorageWebPrefix + key, value);
228
+ }
229
+ setApi(appName: string, key: string, value: any) {
230
+ return this.set(appName, AppSharedStorageApiPrefix + key, value);
231
+ }
232
+ }
233
+
234
+ type AppSharedStorageWorkerMap = { [key: string]: any };
235
+ class AppSharedStorageWorker {
236
+ static handleMap: AppSharedStorageWorkerMap = {};
237
+ static logger = new Logger('server-config');
238
+
239
+ static messageFromPrimaryProcess(msgObject: StorageMessageFromSubProcess) {
240
+ if (cluster.isPrimary || !msgObject.action || !msgObject.key || !msgObject.uniqueKey) {
241
+ console.error('AppSharedStorageWorker got wrong message: ', msgObject);
242
+ return;
243
+ }
244
+
245
+ if (msgObject.action === 'get') {
246
+ // console.log(`${process.pid} - AppStorage get value end for key: ${msgObject.key}`);
247
+
248
+ const value = msgObject.value;
249
+ // how to pass the value to the caller
250
+ const map = this.handleMap[msgObject.uniqueKey];
251
+ delete this.handleMap[msgObject.uniqueKey];
252
+ if (map) {
253
+ map.resolve(value);
254
+ } else {
255
+ throw new Error(`Unknown uniqueKey: ${msgObject.uniqueKey}`);
256
+ }
257
+ } else if (msgObject.action === 'getWithPrefix') {
258
+ console.log(`${process.pid} - AppStorage get value end for key: ${msgObject.key}`);
259
+ const value = JSON.parse(msgObject.value);
260
+ // how to pass the value to the caller
261
+ const map = this.handleMap[msgObject.uniqueKey];
262
+ delete this.handleMap[msgObject.uniqueKey];
263
+ if (map) {
264
+ map.resolve(value);
265
+ } else {
266
+ throw new Error(`Unknown uniqueKey: ${msgObject.uniqueKey}`);
267
+ }
268
+ } else {
269
+ this.logger.warn(`Unknown message: ${msgObject.action}`);
270
+ }
271
+ }
272
+
273
+ static async load(appName: string, rootPath: string) {
274
+ if (cluster.isPrimary) {
275
+ throw new Error('AppSharedStorageWorker should be only called from workers');
276
+ }
277
+ const obj: StorageMessageFromSubProcess = {
278
+ id: AppSharedStorageMessageId,
279
+ pid: process.pid,
280
+ workerId: cluster.worker?.id || 0,
281
+ action: 'load',
282
+ appName: appName,
283
+ rootPath: rootPath,
284
+ key: 'load',
285
+ };
286
+ process.send!(obj);
287
+ }
288
+
289
+ static async save(appName?: string) {
290
+ if (cluster.isPrimary) {
291
+ throw new Error('AppSharedStorageWorker should be only called from workers');
292
+ }
293
+ const obj: StorageMessageFromSubProcess = {
294
+ id: AppSharedStorageMessageId,
295
+ pid: process.pid,
296
+ workerId: cluster.worker?.id || 0,
297
+ action: 'save',
298
+ appName: appName || '',
299
+ key: 'save',
300
+ };
301
+ process.send!(obj);
302
+ }
303
+
304
+ static get(appName: string, key: string, resolve: (value: any) => void, reject: (reason: any) => void) {
305
+ if (cluster.isPrimary) {
306
+ throw new Error('AppSharedStorageWorker should be only called from workers');
307
+ }
308
+ const uniqueKey = key + ':' + crypto.randomUUID();
309
+ AppSharedStorageWorker.handleMap[uniqueKey] = { resolve, reject };
310
+ const obj: StorageMessageFromSubProcess = {
311
+ id: AppSharedStorageMessageId,
312
+ pid: process.pid,
313
+ workerId: cluster.worker?.id || 0,
314
+ action: 'get',
315
+ appName,
316
+ uniqueKey,
317
+ key,
318
+ };
319
+ process.send!(obj);
320
+ }
321
+
322
+ static getWithPrefix(
323
+ appName: string,
324
+ prefixKey: string,
325
+ resolve: (value: any) => void,
326
+ reject: (reason: any) => void
327
+ ) {
328
+ if (cluster.isPrimary) {
329
+ throw new Error('AppSharedStorageWorker should be only called from workers');
330
+ }
331
+ const uniqueKey = prefixKey + ':' + crypto.randomUUID();
332
+ AppSharedStorageWorker.handleMap[uniqueKey] = { resolve, reject };
333
+ const obj: StorageMessageFromSubProcess = {
334
+ id: AppSharedStorageMessageId,
335
+ pid: process.pid,
336
+ workerId: cluster.worker?.id || 0,
337
+ action: 'getWithPrefix',
338
+ appName,
339
+ uniqueKey,
340
+ key: prefixKey,
341
+ };
342
+ process.send!(obj);
343
+ }
344
+
345
+ static set(appName: string, key: string, value: any) {
346
+ if (cluster.isPrimary) {
347
+ throw new Error('AppSharedStorageWorker should be only called from workers');
348
+ }
349
+ const obj: StorageMessageFromSubProcess = {
350
+ id: AppSharedStorageMessageId,
351
+ pid: process.pid,
352
+ workerId: cluster.worker?.id || 0,
353
+ action: 'set',
354
+ appName,
355
+ key,
356
+ value,
357
+ };
358
+ process.send!(obj);
359
+ }
360
+ }
361
+
362
+ // this can be used in app, but in api, it should use getAppStorage()
363
+ export const appStorage = /* @__PURE__ */ AppSharedStorage.getInstance();