underpost 3.2.10 → 3.2.11

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 (54) hide show
  1. package/.vscode/extensions.json +9 -9
  2. package/.vscode/settings.json +12 -1
  3. package/CHANGELOG.md +74 -1
  4. package/CLI-HELP.md +80 -26
  5. package/README.md +3 -3
  6. package/bin/build.js +9 -6
  7. package/bin/build.template.js +187 -0
  8. package/bin/deploy.js +29 -18
  9. package/conf.js +1 -4
  10. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
  11. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
  12. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  13. package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
  14. package/manifests/lxd/lxd-admin-profile.yaml +12 -3
  15. package/manifests/mongodb-4.4/headless-service.yaml +10 -0
  16. package/manifests/mongodb-4.4/kustomization.yaml +3 -1
  17. package/manifests/mongodb-4.4/mongodb-nodeport.yaml +17 -0
  18. package/manifests/mongodb-4.4/pv-pvc.yaml +10 -14
  19. package/manifests/mongodb-4.4/statefulset.yaml +79 -0
  20. package/manifests/mongodb-4.4/storage-class.yaml +9 -0
  21. package/manifests/valkey/statefulset.yaml +1 -1
  22. package/manifests/valkey/valkey-nodeport.yaml +17 -0
  23. package/package.json +3 -3
  24. package/scripts/ipxe-setup.sh +52 -49
  25. package/scripts/k3s-node-setup.sh +84 -68
  26. package/scripts/lxd-vm-setup.sh +193 -8
  27. package/scripts/maas-nat-firewalld.sh +145 -0
  28. package/src/cli/baremetal.js +115 -93
  29. package/src/cli/cluster.js +548 -221
  30. package/src/cli/deploy.js +131 -166
  31. package/src/cli/fs.js +11 -3
  32. package/src/cli/index.js +75 -17
  33. package/src/cli/lxd.js +1034 -240
  34. package/src/cli/monitor.js +9 -3
  35. package/src/cli/release.js +72 -36
  36. package/src/cli/repository.js +10 -16
  37. package/src/cli/run.js +70 -53
  38. package/src/cli/secrets.js +11 -2
  39. package/src/client/components/core/Auth.js +4 -3
  40. package/src/client/components/core/ClientEvents.js +76 -0
  41. package/src/client/components/core/EventBus.js +4 -0
  42. package/src/client/components/core/Modal.js +82 -41
  43. package/src/db/DataBaseProvider.js +9 -9
  44. package/src/db/mariadb/MariaDB.js +2 -1
  45. package/src/db/mongo/MongoBootstrap.js +592 -522
  46. package/src/db/mongo/MongooseDB.js +19 -15
  47. package/src/index.js +1 -1
  48. package/src/server/conf.js +62 -15
  49. package/src/server/proxy.js +9 -2
  50. package/src/server/start.js +7 -3
  51. package/src/server/valkey.js +2 -0
  52. package/bin/file.js +0 -220
  53. package/bin/vs.js +0 -74
  54. package/bin/zed.js +0 -84
@@ -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
- onCloseListener: {},
181
- onMenuListener: {},
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
- const result = windowGetH() - (options.heightBottomBar ? options.heightBottomBar : heightDefaultBottomBar);
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 = `${windowGetH() / 2 - height / 2}px`;
223
- left = `${windowGetW() / 2 - width / 2}px`;
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
- 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
- }
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();