underpost 2.8.883 → 2.8.885
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 +4 -116
- package/bin/deploy.js +9 -10
- package/bin/file.js +4 -6
- package/cli.md +15 -11
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
- package/package.json +1 -1
- package/src/api/user/user.service.js +3 -10
- package/src/cli/cluster.js +21 -0
- package/src/cli/cron.js +8 -0
- package/src/cli/db.js +63 -1
- package/src/cli/deploy.js +156 -3
- package/src/cli/env.js +43 -0
- package/src/cli/fs.js +94 -0
- package/src/cli/image.js +8 -0
- package/src/cli/index.js +17 -4
- package/src/cli/monitor.js +0 -1
- package/src/cli/repository.js +95 -2
- package/src/client/components/core/Css.js +16 -0
- package/src/client/components/core/Docs.js +5 -13
- package/src/client/components/core/Modal.js +57 -39
- package/src/client/components/core/Router.js +6 -3
- package/src/client/components/core/Worker.js +205 -118
- package/src/client/components/default/MenuDefault.js +1 -0
- package/src/client.dev.js +6 -3
- package/src/db/DataBaseProvider.js +65 -12
- package/src/db/mariadb/MariaDB.js +39 -6
- package/src/db/mongo/MongooseDB.js +51 -133
- package/src/index.js +1 -1
- package/src/mailer/EmailRender.js +58 -9
- package/src/mailer/MailerProvider.js +98 -25
- package/src/runtime/express/Express.js +248 -0
- package/src/runtime/lampp/Lampp.js +27 -8
- package/src/server/auth.js +82 -43
- package/src/server/client-build-live.js +14 -5
- package/src/server/client-dev-server.js +21 -8
- package/src/server/conf.js +78 -25
- package/src/server/peer.js +2 -2
- package/src/server/runtime.js +49 -208
- package/src/server/start.js +39 -0
- package/src/ws/IoInterface.js +132 -39
- package/src/ws/IoServer.js +79 -31
- package/src/ws/core/core.ws.connection.js +50 -16
- package/src/ws/core/core.ws.emit.js +47 -8
- package/src/ws/core/core.ws.server.js +62 -10
- package/src/runtime/nginx/Nginx.js +0 -3
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility class for managing Progressive Web App (PWA) worker functionalities,
|
|
3
|
+
* including service worker registration, caching, and notification management.
|
|
4
|
+
* This class is designed to be used as a singleton instance (exported as 'Worker').
|
|
5
|
+
* @module src/client/components/core/Worker.js
|
|
6
|
+
* @namespace PwaWorker
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
import { BtnIcon } from './BtnIcon.js';
|
|
2
10
|
import { s4 } from './CommonJs.js';
|
|
3
11
|
import { EventsUI } from './EventsUI.js';
|
|
@@ -9,11 +17,64 @@ import { s } from './VanillaJs.js';
|
|
|
9
17
|
import { getProxyPath } from './Router.js';
|
|
10
18
|
const logger = loggerFactory(import.meta);
|
|
11
19
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Manages the PWA lifecycle, service workers, and related client-side events.
|
|
22
|
+
* @memberof PwaWorker
|
|
23
|
+
*/
|
|
24
|
+
class PwaWorker {
|
|
25
|
+
/**
|
|
26
|
+
* The application title, usually from the <title> tag content.
|
|
27
|
+
* @type {string}
|
|
28
|
+
*/
|
|
29
|
+
title = '';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Tracks if notification permission has been granted and is active.
|
|
33
|
+
* @type {boolean}
|
|
34
|
+
*/
|
|
35
|
+
notificationActive = false;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A function reference to the service worker's update method (registration.update()),
|
|
39
|
+
* dynamically set upon successful registration status check.
|
|
40
|
+
* @type {function(): Promise<void>}
|
|
41
|
+
*/
|
|
42
|
+
updateServiceWorker = async () => {};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Router instance reference, initialized during the `instance` call.
|
|
46
|
+
* @type {object | null}
|
|
47
|
+
*/
|
|
48
|
+
RouterInstance = null;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Creates an instance of PwaWorker and initializes the application title.
|
|
52
|
+
* @memberof PwaWorker
|
|
53
|
+
*/
|
|
54
|
+
constructor() {
|
|
55
|
+
this.title = `${s('title').textContent}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Checks if the application is running in development mode (localhost or 127.0.0.1).
|
|
60
|
+
* @memberof PwaWorker
|
|
61
|
+
* @returns {boolean} True if in development mode.
|
|
62
|
+
*/
|
|
63
|
+
devMode() {
|
|
64
|
+
return location.origin.match('localhost') || location.origin.match('127.0.0.1');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Initializes the PWA worker, registers global/worker event listeners,
|
|
69
|
+
* checks service worker status, and renders the initial content.
|
|
70
|
+
* This is the main entry point for the worker setup.
|
|
71
|
+
* @memberof PwaWorker
|
|
72
|
+
* @param {object} options - Configuration options.
|
|
73
|
+
* @param {function(): object} options.router - Function to get the router instance.
|
|
74
|
+
* @param {function(): Promise<void>} options.render - Function to render the application's UI.
|
|
75
|
+
* @returns {Promise<void>}
|
|
76
|
+
*/
|
|
77
|
+
async instance({ router, render }) {
|
|
17
78
|
window.ononline = async () => {
|
|
18
79
|
logger.warn('ononline');
|
|
19
80
|
};
|
|
@@ -23,13 +84,15 @@ const Worker = {
|
|
|
23
84
|
setTimeout(() => {
|
|
24
85
|
if ('onLine' in navigator && navigator.onLine) window.ononline();
|
|
25
86
|
});
|
|
87
|
+
|
|
26
88
|
if ('serviceWorker' in navigator) {
|
|
27
89
|
navigator.serviceWorker.addEventListener('controllerchange', () => {
|
|
28
90
|
logger.info('The controller of current browsing context has changed.');
|
|
29
91
|
});
|
|
30
92
|
navigator.serviceWorker.ready.then((worker) => {
|
|
31
|
-
logger.info('Ready', worker);
|
|
32
|
-
|
|
93
|
+
logger.info('Service Worker Ready', worker);
|
|
94
|
+
|
|
95
|
+
// event message listener
|
|
33
96
|
navigator.serviceWorker.addEventListener('message', (event) => {
|
|
34
97
|
logger.info('Received event message', event.data);
|
|
35
98
|
const { status } = event.data;
|
|
@@ -68,36 +131,53 @@ const Worker = {
|
|
|
68
131
|
LoadRouter(this.RouterInstance);
|
|
69
132
|
LoadingAnimation.removeSplashScreen();
|
|
70
133
|
if (this.devMode()) {
|
|
71
|
-
const delayLiveReload = 1250;
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
// Worker.reload(delayLiveReload);
|
|
81
|
-
});
|
|
134
|
+
// const delayLiveReload = 1250;
|
|
135
|
+
// window.addEventListener('visibilitychange', () => {
|
|
136
|
+
// if (document.visibilityState === 'visible') {
|
|
137
|
+
// this.reload(delayLiveReload);
|
|
138
|
+
// }
|
|
139
|
+
// });
|
|
140
|
+
// window.addEventListener('focus', () => {
|
|
141
|
+
// this.reload(delayLiveReload);
|
|
142
|
+
// });
|
|
82
143
|
}
|
|
83
144
|
window.serviceWorkerReady = true;
|
|
84
|
-
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Gets the current service worker registration.
|
|
149
|
+
* @memberof PwaWorker
|
|
150
|
+
* @returns {Promise<ServiceWorkerRegistration | undefined>} The service worker registration object, or undefined.
|
|
151
|
+
*/
|
|
152
|
+
async getRegistration() {
|
|
153
|
+
return navigator.serviceWorker.getRegistration();
|
|
154
|
+
}
|
|
85
155
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
156
|
+
/**
|
|
157
|
+
* Forces the current service worker to skip waiting and reloads the page
|
|
158
|
+
* to apply the new service worker immediately.
|
|
159
|
+
* @memberof PwaWorker
|
|
160
|
+
* @param {number} [timeOut=3000] - Delay in milliseconds before reloading the page.
|
|
161
|
+
* @returns {Promise<void>} A promise that resolves after the page is reloaded.
|
|
162
|
+
*/
|
|
163
|
+
async reload(timeOut = 3000) {
|
|
164
|
+
return new Promise((resolve) => {
|
|
165
|
+
if (navigator.serviceWorker && navigator.serviceWorker.controller) {
|
|
93
166
|
navigator.serviceWorker.controller.postMessage({
|
|
94
167
|
status: 'skipWaiting',
|
|
95
168
|
});
|
|
96
|
-
|
|
169
|
+
}
|
|
97
170
|
setTimeout(() => resolve(location.reload()), timeOut);
|
|
98
171
|
});
|
|
99
|
-
}
|
|
100
|
-
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Updates the application by clearing specific caches and running the service worker update logic.
|
|
176
|
+
* Cache names matching 'components/', 'services/', or '.index.js' are deleted.
|
|
177
|
+
* @memberof PwaWorker
|
|
178
|
+
* @returns {Promise<void>}
|
|
179
|
+
*/
|
|
180
|
+
async update() {
|
|
101
181
|
const isInstall = await this.status();
|
|
102
182
|
if (isInstall) {
|
|
103
183
|
const cacheNames = await caches.keys();
|
|
@@ -108,11 +188,17 @@ const Worker = {
|
|
|
108
188
|
}
|
|
109
189
|
await this.updateServiceWorker();
|
|
110
190
|
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Checks the current status of all service worker registrations and sets the
|
|
195
|
+
* `updateServiceWorker` function reference if an active worker is found.
|
|
196
|
+
* @memberof PwaWorker
|
|
197
|
+
* @returns {Promise<boolean>} True if at least one service worker is registered.
|
|
198
|
+
*/
|
|
199
|
+
status() {
|
|
114
200
|
let status = false;
|
|
115
|
-
return new Promise((resolve
|
|
201
|
+
return new Promise((resolve) => {
|
|
116
202
|
if ('serviceWorker' in navigator) {
|
|
117
203
|
navigator.serviceWorker
|
|
118
204
|
.getRegistrations()
|
|
@@ -122,81 +208,87 @@ const Worker = {
|
|
|
122
208
|
else if (registration.waiting) logger.info('waiting', registration);
|
|
123
209
|
else if (registration.active) {
|
|
124
210
|
logger.info('active', registration);
|
|
211
|
+
// Dynamically set the update function
|
|
125
212
|
this.updateServiceWorker = async () => await registration.update();
|
|
126
213
|
}
|
|
127
214
|
}
|
|
128
215
|
if (registrations.length > 0) status = true;
|
|
216
|
+
resolve(status);
|
|
129
217
|
})
|
|
130
218
|
.catch((...args) => {
|
|
131
|
-
logger.error(...args);
|
|
132
|
-
|
|
133
|
-
})
|
|
134
|
-
.finally((...args) => {
|
|
135
|
-
logger.info('Finally status', args);
|
|
136
|
-
return resolve(status);
|
|
219
|
+
logger.error('Error getting service worker registrations:', ...args);
|
|
220
|
+
resolve(false);
|
|
137
221
|
});
|
|
138
222
|
} else {
|
|
139
|
-
logger.warn('Disabled');
|
|
140
|
-
|
|
223
|
+
logger.warn('Service Worker Disabled in browser');
|
|
224
|
+
resolve(false);
|
|
141
225
|
}
|
|
142
226
|
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Registers the service worker (`sw.js`) with the browser.
|
|
231
|
+
* @memberof PwaWorker
|
|
232
|
+
* @returns {Promise<Array<any>>} A promise that resolves with the registration arguments.
|
|
233
|
+
*/
|
|
234
|
+
install() {
|
|
235
|
+
return new Promise((resolve) => {
|
|
146
236
|
if ('serviceWorker' in navigator) {
|
|
147
237
|
navigator.serviceWorker
|
|
148
238
|
.register(`${getProxyPath()}sw.js`, {
|
|
149
|
-
// scope: getProxyPath(),
|
|
150
|
-
// scope: '/',
|
|
151
239
|
type: 'module',
|
|
152
240
|
})
|
|
153
241
|
.then((...args) => {
|
|
154
|
-
logger.warn('
|
|
242
|
+
logger.warn('Service Worker Registered', args);
|
|
243
|
+
resolve(args);
|
|
155
244
|
})
|
|
156
245
|
.catch((...args) => {
|
|
157
|
-
logger.error(...args);
|
|
158
|
-
|
|
159
|
-
})
|
|
160
|
-
.finally((...args) => {
|
|
161
|
-
logger.info('Finally install', args);
|
|
162
|
-
return resolve(args);
|
|
246
|
+
logger.error('Error registering service worker:', ...args);
|
|
247
|
+
resolve(args);
|
|
163
248
|
});
|
|
164
249
|
} else {
|
|
165
|
-
logger.warn('Disabled');
|
|
166
|
-
|
|
250
|
+
logger.warn('Service Worker Disabled in browser');
|
|
251
|
+
resolve([]);
|
|
167
252
|
}
|
|
168
253
|
});
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Unregisters all service workers and deletes all application caches.
|
|
258
|
+
* @memberof PwaWorker
|
|
259
|
+
* @returns {Promise<Array<any>>} A promise that resolves after uninstallation.
|
|
260
|
+
*/
|
|
261
|
+
uninstall() {
|
|
262
|
+
return new Promise(async (resolve) => {
|
|
172
263
|
if ('serviceWorker' in navigator) {
|
|
173
|
-
|
|
174
|
-
.getRegistrations()
|
|
175
|
-
.
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
})
|
|
187
|
-
.finally((...args) => {
|
|
188
|
-
logger.info('Finally uninstall', args);
|
|
189
|
-
return resolve(args);
|
|
190
|
-
});
|
|
264
|
+
try {
|
|
265
|
+
const registrations = await navigator.serviceWorker.getRegistrations();
|
|
266
|
+
const cacheNames = await caches.keys();
|
|
267
|
+
for (const cacheName of cacheNames) await caches.delete(cacheName);
|
|
268
|
+
for (const registration of registrations) {
|
|
269
|
+
logger.info('Removing service worker registration', registration);
|
|
270
|
+
registration.unregister();
|
|
271
|
+
}
|
|
272
|
+
resolve([]);
|
|
273
|
+
} catch (error) {
|
|
274
|
+
logger.error('Error during service worker uninstallation:', error);
|
|
275
|
+
resolve([error]);
|
|
276
|
+
}
|
|
191
277
|
} else {
|
|
192
|
-
logger.warn('Disabled');
|
|
193
|
-
|
|
278
|
+
logger.warn('Service Worker Disabled in browser');
|
|
279
|
+
resolve([]);
|
|
194
280
|
}
|
|
195
281
|
});
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Requests permission from the user to display notifications.
|
|
286
|
+
* Sets the internal `notificationActive` state.
|
|
287
|
+
* @memberof PwaWorker
|
|
288
|
+
* @returns {Promise<boolean>} True if permission is granted, false otherwise.
|
|
289
|
+
*/
|
|
290
|
+
notificationRequestPermission() {
|
|
291
|
+
return new Promise((resolve) =>
|
|
200
292
|
Notification.requestPermission().then((result) => {
|
|
201
293
|
if (result === 'granted') {
|
|
202
294
|
this.notificationActive = true;
|
|
@@ -207,8 +299,14 @@ const Worker = {
|
|
|
207
299
|
}
|
|
208
300
|
}),
|
|
209
301
|
);
|
|
210
|
-
}
|
|
211
|
-
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Shows a sample notification if permission is granted.
|
|
306
|
+
* @memberof PwaWorker
|
|
307
|
+
* @returns {void}
|
|
308
|
+
*/
|
|
309
|
+
notificationShow() {
|
|
212
310
|
Notification.requestPermission().then((result) => {
|
|
213
311
|
if (result === 'granted') {
|
|
214
312
|
navigator.serviceWorker.ready.then((registration) => {
|
|
@@ -217,18 +315,25 @@ const Worker = {
|
|
|
217
315
|
icon: '../images/touch/chrome-touch-icon.png',
|
|
218
316
|
vibrate: [200, 100, 200, 100, 200, 100, 200],
|
|
219
317
|
tag: 'vibration-sample',
|
|
220
|
-
requireInteraction: true,
|
|
318
|
+
requireInteraction: true,
|
|
221
319
|
});
|
|
222
320
|
});
|
|
223
321
|
}
|
|
224
322
|
});
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Renders the UI for PWA settings, including buttons for cleaning cache and worker management.
|
|
327
|
+
* It also attaches the click handler for the 'clean-cache' button.
|
|
328
|
+
* @memberof PwaWorker
|
|
329
|
+
* @returns {Promise<string>} The HTML string for the settings section.
|
|
330
|
+
*/
|
|
331
|
+
async RenderSetting() {
|
|
228
332
|
setTimeout(() => {
|
|
333
|
+
// Event listener for the clean cache button
|
|
229
334
|
EventsUI.onClick(`.btn-clean-cache`, async (e) => {
|
|
230
335
|
e.preventDefault();
|
|
231
|
-
//
|
|
336
|
+
// Clear local storage, uninstall the worker, and reload
|
|
232
337
|
localStorage.clear();
|
|
233
338
|
await this.uninstall();
|
|
234
339
|
await this.reload();
|
|
@@ -252,33 +357,15 @@ const Worker = {
|
|
|
252
357
|
label: html`<i class="fas fa-sync-alt"></i> ${Translate.Render('Reload')}`,
|
|
253
358
|
})}
|
|
254
359
|
</div>`;
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
EventsUI.onClick(`.btn-uninstall-service-controller`, async (e) => {
|
|
264
|
-
e.preventDefault();
|
|
265
|
-
const result = await this.uninstall();
|
|
266
|
-
s(`.btn-uninstall-service-controller`).classList.add('hide');
|
|
267
|
-
s(`.btn-install-service-controller`).classList.remove('hide');
|
|
268
|
-
});
|
|
269
|
-
EventsUI.onClick(`.btn-reload`, async (e) => {
|
|
270
|
-
e.preventDefault();
|
|
271
|
-
location.reload();
|
|
272
|
-
});
|
|
273
|
-
const workerStatus = await this.status();
|
|
274
|
-
if (workerStatus) {
|
|
275
|
-
s(`.btn-install-service-controller`).classList.add('hide');
|
|
276
|
-
s(`.btn-uninstall-service-controller`).classList.remove('hide');
|
|
277
|
-
} else {
|
|
278
|
-
s(`.btn-uninstall-service-controller`).classList.add('hide');
|
|
279
|
-
s(`.btn-install-service-controller`).classList.remove('hide');
|
|
280
|
-
}
|
|
281
|
-
},
|
|
282
|
-
};
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Create the singleton instance
|
|
364
|
+
const PwaWorkerInstance = new PwaWorker();
|
|
365
|
+
|
|
366
|
+
// Export the new class name for modern usage
|
|
367
|
+
export { PwaWorker };
|
|
283
368
|
|
|
284
|
-
|
|
369
|
+
// Export the instance with the old name (`Worker`) for backward compatibility,
|
|
370
|
+
// ensuring existing code consuming the module continues to work.
|
|
371
|
+
export { PwaWorkerInstance as Worker };
|
|
@@ -186,6 +186,7 @@ const MenuDefault = {
|
|
|
186
186
|
})}
|
|
187
187
|
${await BtnIcon.Render({
|
|
188
188
|
class: 'in wfa main-btn-menu main-btn-chat',
|
|
189
|
+
useMenuBtn: true,
|
|
189
190
|
label: html`${renderMenuLabel({
|
|
190
191
|
icon: html`<i class="far fa-comments"></i>`,
|
|
191
192
|
text: html`<span class="menu-label-text">${Translate.Render('chat')}</span>`,
|
package/src/client.dev.js
CHANGED
|
@@ -6,16 +6,19 @@
|
|
|
6
6
|
import dotenv from 'dotenv';
|
|
7
7
|
import { loggerFactory } from './server/logger.js';
|
|
8
8
|
import { ProcessController } from './server/process.js';
|
|
9
|
-
import { Config } from './server/conf.js';
|
|
9
|
+
import { Config, buildClientStaticConf } from './server/conf.js';
|
|
10
10
|
import { createClientDevServer } from './server/client-dev-server.js';
|
|
11
|
-
dotenv.config();
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
dotenv.config();
|
|
14
13
|
|
|
15
14
|
const logger = loggerFactory(import.meta);
|
|
16
15
|
|
|
17
16
|
await logger.setUpInfo();
|
|
18
17
|
|
|
18
|
+
await buildClientStaticConf();
|
|
19
|
+
|
|
20
|
+
await Config.build();
|
|
21
|
+
|
|
19
22
|
await createClientDevServer();
|
|
20
23
|
|
|
21
24
|
ProcessController.init(logger);
|
|
@@ -1,29 +1,73 @@
|
|
|
1
1
|
import { MongooseDB } from './mongo/MongooseDB.js';
|
|
2
2
|
import { loggerFactory } from '../server/logger.js';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Module for managing and loading various database connections (e.g., Mongoose, MariaDB).
|
|
6
|
+
* @module src/db/DataBaseProvider.js
|
|
7
|
+
* @namespace DataBaseProviderNamespace
|
|
8
|
+
*/
|
|
9
|
+
|
|
4
10
|
const logger = loggerFactory(import.meta);
|
|
5
11
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
12
|
+
/**
|
|
13
|
+
* @class
|
|
14
|
+
* @alias DataBaseProviderService
|
|
15
|
+
* @memberof DataBaseProviderNamespace
|
|
16
|
+
* @classdesc Centralized service for loading, managing, and accessing multiple database connections
|
|
17
|
+
* based on application configuration (host, path, provider type).
|
|
18
|
+
*/
|
|
19
|
+
class DataBaseProviderService {
|
|
20
|
+
/**
|
|
21
|
+
* Internal storage for database connection instances, keyed by host+path.
|
|
22
|
+
* @type {object.<string, object>}
|
|
23
|
+
* @private
|
|
24
|
+
*/
|
|
25
|
+
#instance = {};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Retrieves the internal instance storage for direct access (used for backward compatibility).
|
|
29
|
+
* @returns {object.<string, object>} The internal connection instance map.
|
|
30
|
+
*/
|
|
31
|
+
get instance() {
|
|
32
|
+
return this.#instance;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Loads and initializes a database provider based on the configuration.
|
|
37
|
+
* If the connection is already loaded for the given host/path, it returns the existing instance.
|
|
38
|
+
*
|
|
39
|
+
* @async
|
|
40
|
+
* @param {object} [options] - Configuration for the database connection.
|
|
41
|
+
* @param {Array<string>} [options.apis=[]] - List of APIs whose models should be loaded (for Mongoose).
|
|
42
|
+
* @param {string} [options.host=''] - The host part of the application context (e.g., domain).
|
|
43
|
+
* @param {string} [options.path=''] - The path part of the application context.
|
|
44
|
+
* @param {object} [options.db={}] - The specific database configuration object.
|
|
45
|
+
* @param {string} options.db.provider - The name of the database provider ('mongoose', 'mariadb', etc.).
|
|
46
|
+
* @param {string} options.db.host - The database server host.
|
|
47
|
+
* @param {string} options.db.name - The database name.
|
|
48
|
+
* @returns {Promise<object|undefined>} A promise that resolves to the initialized provider object
|
|
49
|
+
* or `undefined` on error or if the provider is already loaded.
|
|
50
|
+
*/
|
|
51
|
+
async load(options = { apis: [], host: '', path: '', db: {} }) {
|
|
9
52
|
try {
|
|
10
53
|
const { apis, host, path, db } = options;
|
|
54
|
+
const key = `${host}${path}`;
|
|
11
55
|
|
|
12
|
-
if (!this
|
|
56
|
+
if (!this.#instance[key]) this.#instance[key] = {};
|
|
13
57
|
|
|
14
|
-
if (!db || this
|
|
58
|
+
if (!db || this.#instance[key][db.provider]) return this.#instance[key][db.provider];
|
|
15
59
|
|
|
16
|
-
// logger.info(`Load ${db.provider} provider`,
|
|
60
|
+
// logger.info(`Load ${db.provider} provider`, key);
|
|
17
61
|
switch (db.provider) {
|
|
18
62
|
case 'mongoose':
|
|
19
63
|
{
|
|
20
64
|
const conn = await MongooseDB.connect(db.host, db.name);
|
|
21
|
-
this
|
|
65
|
+
this.#instance[key][db.provider] = {
|
|
22
66
|
models: await MongooseDB.loadModels({ conn, apis }),
|
|
23
67
|
connection: conn,
|
|
24
68
|
close: async () => {
|
|
25
69
|
return await new Promise((resolve) => {
|
|
26
|
-
|
|
70
|
+
this.#instance[key][db.provider].connection.close().then(() => {
|
|
27
71
|
// logger.info('Mongoose connection is disconnected', db);
|
|
28
72
|
return resolve();
|
|
29
73
|
});
|
|
@@ -35,11 +79,20 @@ const DataBaseProvider = {
|
|
|
35
79
|
default:
|
|
36
80
|
break;
|
|
37
81
|
}
|
|
38
|
-
return this
|
|
82
|
+
return this.#instance[key][db.provider];
|
|
39
83
|
} catch (error) {
|
|
40
84
|
logger.error(error, { error: error.stack, options });
|
|
41
85
|
return undefined;
|
|
42
86
|
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Singleton instance of the DataBaseProviderService class for backward compatibility.
|
|
92
|
+
* @alias DataBaseProvider
|
|
93
|
+
* @memberof DataBaseProviderNamespace
|
|
94
|
+
* @type {DataBaseProviderService}
|
|
95
|
+
*/
|
|
96
|
+
const DataBaseProvider = new DataBaseProviderService();
|
|
97
|
+
|
|
98
|
+
export { DataBaseProvider, DataBaseProviderService as DataBaseProviderClass };
|
|
@@ -2,10 +2,35 @@ import mariadb from 'mariadb';
|
|
|
2
2
|
|
|
3
3
|
import { loggerFactory } from '../../server/logger.js';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Module for interacting with MariaDB/MySQL databases using the mariadb connector.
|
|
7
|
+
* @module src/db/MariaDB.js
|
|
8
|
+
* @namespace MariaDBNamespace
|
|
9
|
+
*/
|
|
10
|
+
|
|
5
11
|
const logger = loggerFactory(import.meta);
|
|
6
12
|
|
|
7
|
-
|
|
8
|
-
|
|
13
|
+
/**
|
|
14
|
+
* @class
|
|
15
|
+
* @alias MariaDBService
|
|
16
|
+
* @memberof MariaDBNamespace
|
|
17
|
+
* @classdesc Provides a simplified interface for executing queries against a MariaDB/MySQL database
|
|
18
|
+
* using a connection pool, ensuring connection management (acquisition and release).
|
|
19
|
+
*/
|
|
20
|
+
class MariaDBService {
|
|
21
|
+
/**
|
|
22
|
+
* Executes a SQL query against the MariaDB database.
|
|
23
|
+
*
|
|
24
|
+
* @async
|
|
25
|
+
* @param {object} options - The database connection and query options.
|
|
26
|
+
* @param {string} [options.host='127.0.0.1'] - The database host.
|
|
27
|
+
* @param {number} [options.port=3306] - The database port.
|
|
28
|
+
* @param {string} [options.user='root'] - The database user.
|
|
29
|
+
* @param {string} [options.password=''] - The database password.
|
|
30
|
+
* @param {string} options.query - The SQL query string to execute.
|
|
31
|
+
* @returns {Promise<any>} The result of the database query.
|
|
32
|
+
*/
|
|
33
|
+
async query(options) {
|
|
9
34
|
const { host, port, user, password, query } = options;
|
|
10
35
|
const pool = mariadb.createPool({
|
|
11
36
|
host: 'host' in options ? host : '127.0.0.1',
|
|
@@ -22,12 +47,20 @@ const MariaDB = {
|
|
|
22
47
|
if (error.stack.startsWith('TypeError: Do not know how to serialize a BigInt')) return;
|
|
23
48
|
logger.error(error, error.stack);
|
|
24
49
|
} finally {
|
|
25
|
-
if (conn) conn.release(); //release to pool
|
|
50
|
+
if (conn) conn.release(); // release to pool
|
|
26
51
|
await pool.end();
|
|
27
52
|
}
|
|
28
53
|
|
|
29
54
|
return result;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Singleton instance of the MariaDBService class for backward compatibility.
|
|
60
|
+
* @alias MariaDB
|
|
61
|
+
* @memberof MariaDBNamespace
|
|
62
|
+
* @type {MariaDBService}
|
|
63
|
+
*/
|
|
64
|
+
const MariaDB = new MariaDBService();
|
|
32
65
|
|
|
33
|
-
export { MariaDB };
|
|
66
|
+
export { MariaDB, MariaDBService as MariaDBClass };
|