underpost 3.2.10 → 3.2.12
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/.vscode/extensions.json +9 -9
- package/.vscode/settings.json +12 -1
- package/CHANGELOG.md +92 -1
- package/CLI-HELP.md +80 -26
- package/README.md +6 -10
- package/bin/build.js +9 -6
- package/bin/build.template.js +187 -0
- package/bin/deploy.js +29 -18
- package/conf.js +1 -4
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
- package/manifests/lxd/lxd-admin-profile.yaml +12 -3
- package/manifests/mongodb-4.4/headless-service.yaml +10 -0
- package/manifests/mongodb-4.4/kustomization.yaml +3 -1
- package/manifests/mongodb-4.4/mongodb-nodeport.yaml +17 -0
- package/manifests/mongodb-4.4/pv-pvc.yaml +10 -14
- package/manifests/mongodb-4.4/statefulset.yaml +79 -0
- package/manifests/mongodb-4.4/storage-class.yaml +9 -0
- package/manifests/valkey/statefulset.yaml +1 -1
- package/manifests/valkey/valkey-nodeport.yaml +17 -0
- package/package.json +3 -3
- package/scripts/ipxe-setup.sh +52 -49
- package/scripts/k3s-node-setup.sh +84 -68
- package/scripts/lxd-vm-setup.sh +193 -8
- package/scripts/maas-nat-firewalld.sh +145 -0
- package/src/cli/baremetal.js +115 -93
- package/src/cli/cluster.js +548 -221
- package/src/cli/deploy.js +131 -166
- package/src/cli/fs.js +11 -3
- package/src/cli/index.js +75 -17
- package/src/cli/lxd.js +1034 -240
- package/src/cli/monitor.js +9 -3
- package/src/cli/release.js +72 -36
- package/src/cli/repository.js +10 -16
- package/src/cli/run.js +72 -55
- package/src/cli/secrets.js +11 -2
- package/src/client/components/core/Auth.js +4 -3
- package/src/client/components/core/ClientEvents.js +76 -0
- package/src/client/components/core/EventBus.js +4 -0
- package/src/client/components/core/Modal.js +82 -41
- package/src/db/DataBaseProvider.js +9 -9
- package/src/db/mariadb/MariaDB.js +2 -1
- package/src/db/mongo/MongoBootstrap.js +592 -522
- package/src/db/mongo/MongooseDB.js +19 -15
- package/src/index.js +1 -1
- package/src/server/conf.js +67 -19
- package/src/server/proxy.js +9 -2
- package/src/server/start.js +8 -4
- package/src/server/valkey.js +2 -0
- package/bin/file.js +0 -220
- package/bin/vs.js +0 -74
- package/bin/zed.js +0 -84
|
@@ -45,6 +45,79 @@ const AppointmentEventType = {
|
|
|
45
45
|
submitted: 'appointment:submitted',
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
+
const ModalEventType = {
|
|
49
|
+
close: 'modal:close',
|
|
50
|
+
menu: 'modal:menu',
|
|
51
|
+
collapseMenu: 'modal:collapse-menu',
|
|
52
|
+
extendMenu: 'modal:extend-menu',
|
|
53
|
+
dragEnd: 'modal:drag-end',
|
|
54
|
+
observer: 'modal:observer',
|
|
55
|
+
click: 'modal:click',
|
|
56
|
+
expandUi: 'modal:expand-ui',
|
|
57
|
+
barUiOpen: 'modal:bar-ui-open',
|
|
58
|
+
barUiClose: 'modal:bar-ui-close',
|
|
59
|
+
reload: 'modal:reload',
|
|
60
|
+
home: 'modal:home',
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const ModalListenerChannels = {
|
|
64
|
+
onCloseListener: ModalEventType.close,
|
|
65
|
+
onMenuListener: ModalEventType.menu,
|
|
66
|
+
onCollapseMenuListener: ModalEventType.collapseMenu,
|
|
67
|
+
onExtendMenuListener: ModalEventType.extendMenu,
|
|
68
|
+
onDragEndListener: ModalEventType.dragEnd,
|
|
69
|
+
onObserverListener: ModalEventType.observer,
|
|
70
|
+
onClickListener: ModalEventType.click,
|
|
71
|
+
onExpandUiListener: ModalEventType.expandUi,
|
|
72
|
+
onBarUiOpen: ModalEventType.barUiOpen,
|
|
73
|
+
onBarUiClose: ModalEventType.barUiClose,
|
|
74
|
+
onReloadModalListener: ModalEventType.reload,
|
|
75
|
+
onHome: ModalEventType.home,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const createModalEventChannel = (bus, type) => {
|
|
79
|
+
const busKey = (key) => `${type}::${key}`;
|
|
80
|
+
return new Proxy(
|
|
81
|
+
{},
|
|
82
|
+
{
|
|
83
|
+
get(_target, prop) {
|
|
84
|
+
if (typeof prop === 'symbol') return undefined;
|
|
85
|
+
const key = busKey(prop);
|
|
86
|
+
if (!bus.has(key)) return undefined;
|
|
87
|
+
return (detail) => bus.emitKey(key, detail);
|
|
88
|
+
},
|
|
89
|
+
set(_target, prop, value) {
|
|
90
|
+
if (typeof prop !== 'symbol' && typeof value === 'function') bus.on(type, value, { key: busKey(prop) });
|
|
91
|
+
return true;
|
|
92
|
+
},
|
|
93
|
+
deleteProperty(_target, prop) {
|
|
94
|
+
if (typeof prop !== 'symbol') bus.off(busKey(prop));
|
|
95
|
+
return true;
|
|
96
|
+
},
|
|
97
|
+
has(_target, prop) {
|
|
98
|
+
return typeof prop !== 'symbol' && bus.has(busKey(prop));
|
|
99
|
+
},
|
|
100
|
+
ownKeys() {
|
|
101
|
+
const prefix = `${type}::`;
|
|
102
|
+
return bus.keysOf(type).map((key) => String(key).slice(prefix.length));
|
|
103
|
+
},
|
|
104
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
105
|
+
if (typeof prop !== 'symbol' && bus.has(busKey(prop)))
|
|
106
|
+
return { enumerable: true, configurable: true, writable: true, value: undefined };
|
|
107
|
+
return undefined;
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// One EventBus per modal id, surfaced through the legacy channel names.
|
|
114
|
+
const createModalEvents = () => {
|
|
115
|
+
const bus = new EventBus();
|
|
116
|
+
const channels = {};
|
|
117
|
+
for (const [name, type] of Object.entries(ModalListenerChannels)) channels[name] = createModalEventChannel(bus, type);
|
|
118
|
+
return { bus, channels };
|
|
119
|
+
};
|
|
120
|
+
|
|
48
121
|
const authLoginEvents = new EventBus();
|
|
49
122
|
const authLogoutEvents = new EventBus();
|
|
50
123
|
const authSignupEvents = new EventBus();
|
|
@@ -70,6 +143,9 @@ export {
|
|
|
70
143
|
KeyboardEventType,
|
|
71
144
|
AccountEventType,
|
|
72
145
|
AppointmentEventType,
|
|
146
|
+
ModalEventType,
|
|
147
|
+
ModalListenerChannels,
|
|
148
|
+
createModalEvents,
|
|
73
149
|
authLoginEvents,
|
|
74
150
|
authLogoutEvents,
|
|
75
151
|
authSignupEvents,
|
|
@@ -36,6 +36,7 @@ import { Worker } from './Worker.js';
|
|
|
36
36
|
import { Scroll } from './Scroll.js';
|
|
37
37
|
import { windowGetH, windowGetW } from './windowGetDimensions.js';
|
|
38
38
|
import { SearchBox } from './SearchBox.js';
|
|
39
|
+
import { createModalEvents } from './ClientEvents.js';
|
|
39
40
|
|
|
40
41
|
const logger = loggerFactory(import.meta, { trace: true });
|
|
41
42
|
|
|
@@ -93,6 +94,11 @@ const logger = loggerFactory(import.meta, { trace: true });
|
|
|
93
94
|
/**
|
|
94
95
|
* @typedef {object} ModalDataEntry
|
|
95
96
|
* @property {ModalRenderOptions} options - Original render options.
|
|
97
|
+
* @property {import('./EventBus.js').EventBus} events - Per-modal event bus backing the listener channels below.
|
|
98
|
+
*
|
|
99
|
+
* The `onX` channels are EventBus-backed proxies that preserve the historical
|
|
100
|
+
* `{ [key]: listener }` map API: assign to register, read+call to invoke a single
|
|
101
|
+
* listener, `delete` to remove, and `Object.keys` to enumerate registered keys.
|
|
96
102
|
* @property {Object.<string, Function>} onCloseListener - Close event listeners keyed by id.
|
|
97
103
|
* @property {Object.<string, Function>} onMenuListener - Menu button event listeners.
|
|
98
104
|
* @property {Object.<string, Function>} onCollapseMenuListener - Collapse menu listeners.
|
|
@@ -123,6 +129,69 @@ class Modal {
|
|
|
123
129
|
/** @type {Object.<string, ModalDataEntry>} */
|
|
124
130
|
static Data = {};
|
|
125
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Bottom offset of the slide-menu area for the given options.
|
|
134
|
+
* @param {ModalRenderOptions} options
|
|
135
|
+
* @param {number} [heightDefaultBottomBar=0]
|
|
136
|
+
* @returns {number}
|
|
137
|
+
*/
|
|
138
|
+
static getModalTop(options, heightDefaultBottomBar = 0) {
|
|
139
|
+
return windowGetH() - (options.heightBottomBar ? options.heightBottomBar : heightDefaultBottomBar);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Available modal height, accounting for the top/bottom bars when the UI is expanded.
|
|
144
|
+
* @param {ModalRenderOptions} options
|
|
145
|
+
* @param {number} [heightDefaultTopBar=50]
|
|
146
|
+
* @param {number} [heightDefaultBottomBar=0]
|
|
147
|
+
* @returns {number}
|
|
148
|
+
*/
|
|
149
|
+
static getModalHeight(options, heightDefaultTopBar = 50, heightDefaultBottomBar = 0) {
|
|
150
|
+
const barsVisible = s(`.main-body-btn-ui-close`) && !s(`.main-body-btn-ui-close`).classList.contains('hide');
|
|
151
|
+
return (
|
|
152
|
+
windowGetH() -
|
|
153
|
+
(barsVisible
|
|
154
|
+
? (options.heightTopBar ? options.heightTopBar : heightDefaultTopBar) +
|
|
155
|
+
(options.heightBottomBar ? options.heightBottomBar : heightDefaultBottomBar)
|
|
156
|
+
: 0)
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Left CSS value for a slide menu in its open/closed state.
|
|
162
|
+
* @param {ModalRenderOptions} options
|
|
163
|
+
* @param {{ originSlideMenuWidth: number, collapseSlideMenuWidth: number }} widths
|
|
164
|
+
* @param {{ open: boolean }} [ops={ open: false }]
|
|
165
|
+
* @returns {string}
|
|
166
|
+
*/
|
|
167
|
+
static getModalMenuLeftStyle(options, { originSlideMenuWidth, collapseSlideMenuWidth }, ops = { open: false }) {
|
|
168
|
+
return `${
|
|
169
|
+
options.mode === 'slide-menu-right'
|
|
170
|
+
? `${
|
|
171
|
+
windowGetW() +
|
|
172
|
+
(ops?.open
|
|
173
|
+
? -1 * originSlideMenuWidth +
|
|
174
|
+
(options.mode === 'slide-menu-right' && s(`.btn-icon-menu-mode-right`).classList.contains('hide')
|
|
175
|
+
? originSlideMenuWidth - collapseSlideMenuWidth
|
|
176
|
+
: 0)
|
|
177
|
+
: originSlideMenuWidth)
|
|
178
|
+
}px`
|
|
179
|
+
: `-${ops?.open ? '0px' : originSlideMenuWidth}px`
|
|
180
|
+
}`;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Viewport-centered top/left CSS values for a modal of the given size.
|
|
185
|
+
* @param {{ width: number, height: number }} param0
|
|
186
|
+
* @returns {{ top: string, left: string }}
|
|
187
|
+
*/
|
|
188
|
+
static getModalCenter({ width, height }) {
|
|
189
|
+
return {
|
|
190
|
+
top: `${windowGetH() / 2 - height / 2}px`,
|
|
191
|
+
left: `${windowGetW() / 2 - width / 2}px`,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
126
195
|
/**
|
|
127
196
|
* Create or reload a modal. When the modal already exists in the DOM the
|
|
128
197
|
* existing instance is reloaded via its onReloadModalListener callbacks.
|
|
@@ -175,52 +244,21 @@ class Modal {
|
|
|
175
244
|
const heightDefaultTopBar = 50;
|
|
176
245
|
const heightDefaultBottomBar = 0;
|
|
177
246
|
const idModal = options.id ? options.id : getId(this.Data, 'modal-');
|
|
247
|
+
const { bus: eventBus, channels: eventChannels } = createModalEvents();
|
|
178
248
|
this.Data[idModal] = {
|
|
179
249
|
options,
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
onCollapseMenuListener: {},
|
|
183
|
-
onExtendMenuListener: {},
|
|
184
|
-
onDragEndListener: {},
|
|
185
|
-
onObserverListener: {},
|
|
186
|
-
onClickListener: {},
|
|
187
|
-
onExpandUiListener: {},
|
|
188
|
-
onBarUiOpen: {},
|
|
189
|
-
onBarUiClose: {},
|
|
190
|
-
onReloadModalListener: {},
|
|
191
|
-
onHome: {},
|
|
250
|
+
events: eventBus,
|
|
251
|
+
...eventChannels,
|
|
192
252
|
homeModals: options.homeModals ? options.homeModals : [],
|
|
193
253
|
query: options.query ? `${window.location.search}` : undefined,
|
|
194
|
-
getTop: () =>
|
|
195
|
-
|
|
196
|
-
return result;
|
|
197
|
-
},
|
|
198
|
-
getHeight: () => {
|
|
199
|
-
return (
|
|
200
|
-
windowGetH() -
|
|
201
|
-
(s(`.main-body-btn-ui-close`) && !s(`.main-body-btn-ui-close`).classList.contains('hide')
|
|
202
|
-
? (options.heightTopBar ? options.heightTopBar : heightDefaultTopBar) +
|
|
203
|
-
(options.heightBottomBar ? options.heightBottomBar : heightDefaultBottomBar)
|
|
204
|
-
: 0)
|
|
205
|
-
);
|
|
206
|
-
},
|
|
254
|
+
getTop: () => Modal.getModalTop(options, heightDefaultBottomBar),
|
|
255
|
+
getHeight: () => Modal.getModalHeight(options, heightDefaultTopBar, heightDefaultBottomBar),
|
|
207
256
|
getMenuLeftStyle: (ops = { open: false }) =>
|
|
208
|
-
|
|
209
|
-
options.mode === 'slide-menu-right'
|
|
210
|
-
? `${
|
|
211
|
-
windowGetW() +
|
|
212
|
-
(ops?.open
|
|
213
|
-
? -1 * originSlideMenuWidth +
|
|
214
|
-
(options.mode === 'slide-menu-right' && s(`.btn-icon-menu-mode-right`).classList.contains('hide')
|
|
215
|
-
? originSlideMenuWidth - collapseSlideMenuWidth
|
|
216
|
-
: 0)
|
|
217
|
-
: originSlideMenuWidth)
|
|
218
|
-
}px`
|
|
219
|
-
: `-${ops?.open ? '0px' : originSlideMenuWidth}px`
|
|
220
|
-
}`,
|
|
257
|
+
Modal.getModalMenuLeftStyle(options, { originSlideMenuWidth, collapseSlideMenuWidth }, ops),
|
|
221
258
|
center: () => {
|
|
222
|
-
top =
|
|
223
|
-
|
|
259
|
+
const { top: centeredTop, left: centeredLeft } = Modal.getModalCenter({ width, height });
|
|
260
|
+
top = centeredTop;
|
|
261
|
+
left = centeredLeft;
|
|
224
262
|
},
|
|
225
263
|
...this.Data[idModal],
|
|
226
264
|
};
|
|
@@ -1029,6 +1067,10 @@ class Modal {
|
|
|
1029
1067
|
hoverFocusCtl.checkDismiss();
|
|
1030
1068
|
};
|
|
1031
1069
|
EventsUI.onClick(`.top-bar-search-box-container`, () => {
|
|
1070
|
+
if (SearchBox.Data.skipOpen) {
|
|
1071
|
+
SearchBox.Data.skipOpen = false;
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1032
1074
|
searchBoxHistoryOpen();
|
|
1033
1075
|
searchBoxCallBack(formDataInfoNode[0]);
|
|
1034
1076
|
const inputEl = s(`.${inputSearchBoxId}`);
|
|
@@ -2138,7 +2180,6 @@ class Modal {
|
|
|
2138
2180
|
dragInstance = setDragInstance();
|
|
2139
2181
|
if (options && options.maximize) s(`.btn-maximize-${idModal}`).click();
|
|
2140
2182
|
if (options.observer) {
|
|
2141
|
-
this.Data[idModal].onObserverListener = {};
|
|
2142
2183
|
this.Data[idModal].observerCallBack = () => {
|
|
2143
2184
|
// logger.info('ResizeObserver', `.${idModal}`, s(`.${idModal}`).offsetWidth, s(`.${idModal}`).offsetHeight);
|
|
2144
2185
|
if (this.Data[idModal] && this.Data[idModal].onObserverListener)
|
|
@@ -2,6 +2,7 @@ import { MongooseDB } from './mongo/MongooseDB.js';
|
|
|
2
2
|
import { loggerFactory } from '../server/logger.js';
|
|
3
3
|
import { getCapVariableName } from '../client/components/core/CommonJs.js';
|
|
4
4
|
import { resolveHostKeyContext } from '../server/conf.js';
|
|
5
|
+
import Underpost from '../index.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Module for managing and loading various database connections (e.g., Mongoose, MariaDB).
|
|
@@ -34,7 +35,6 @@ class DataBaseProviderService {
|
|
|
34
35
|
return this.#instance;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
|
|
38
38
|
/**
|
|
39
39
|
* Retrieves a loaded provider bucket for a context.
|
|
40
40
|
* @param {{host?: string, path?: string}|string} [context={ host: '', path: '' }] - Context object or key.
|
|
@@ -191,23 +191,23 @@ class DataBaseProviderService {
|
|
|
191
191
|
path: options.path,
|
|
192
192
|
db: options.db
|
|
193
193
|
? {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
194
|
+
provider: options.db.provider,
|
|
195
|
+
name: options.db.name ? '***' : undefined,
|
|
196
|
+
host: options.db.host ? '***' : undefined,
|
|
197
|
+
user: options.db.user ? '***' : undefined,
|
|
198
|
+
password: options.db.password ? '***' : undefined,
|
|
199
|
+
}
|
|
200
200
|
: {},
|
|
201
201
|
};
|
|
202
202
|
logger.error(error.message, { safeOptions });
|
|
203
|
+
if (Underpost.env.isInsideContainer()) Underpost.env.set('container-status', 'error');
|
|
203
204
|
return undefined;
|
|
204
205
|
}
|
|
205
206
|
}
|
|
206
207
|
}
|
|
207
208
|
|
|
208
|
-
|
|
209
209
|
export {
|
|
210
210
|
DataBaseProviderService as DataBaseProviderClass,
|
|
211
211
|
DataBaseProviderService as default,
|
|
212
|
-
DataBaseProviderService
|
|
212
|
+
DataBaseProviderService,
|
|
213
213
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createPool } from 'mariadb';
|
|
2
|
-
|
|
2
|
+
import Underpost from '../../index.js';
|
|
3
3
|
import { loggerFactory } from '../../server/logger.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -51,6 +51,7 @@ class MariaDBService {
|
|
|
51
51
|
console.log(result);
|
|
52
52
|
} catch (error) {
|
|
53
53
|
logger.error('MariaDB query failed', { error: error.message });
|
|
54
|
+
if (Underpost.env.isInsideContainer()) Underpost.env.set('container-status', 'error');
|
|
54
55
|
} finally {
|
|
55
56
|
if (conn) conn.release(); // release to pool
|
|
56
57
|
await pool.end();
|