underpost 2.8.884 → 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.
Files changed (43) hide show
  1. package/README.md +4 -120
  2. package/bin/deploy.js +9 -10
  3. package/bin/file.js +4 -6
  4. package/cli.md +15 -11
  5. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  6. package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
  7. package/package.json +1 -1
  8. package/src/cli/cluster.js +21 -0
  9. package/src/cli/cron.js +8 -0
  10. package/src/cli/db.js +63 -1
  11. package/src/cli/deploy.js +156 -3
  12. package/src/cli/env.js +43 -0
  13. package/src/cli/fs.js +94 -0
  14. package/src/cli/image.js +8 -0
  15. package/src/cli/index.js +17 -4
  16. package/src/cli/monitor.js +0 -1
  17. package/src/cli/repository.js +95 -2
  18. package/src/client/components/core/Css.js +16 -0
  19. package/src/client/components/core/Docs.js +5 -13
  20. package/src/client/components/core/Modal.js +48 -29
  21. package/src/client/components/core/Router.js +6 -3
  22. package/src/client/components/core/Worker.js +205 -118
  23. package/src/client/components/default/MenuDefault.js +1 -0
  24. package/src/client.dev.js +6 -3
  25. package/src/db/DataBaseProvider.js +65 -12
  26. package/src/db/mariadb/MariaDB.js +39 -6
  27. package/src/db/mongo/MongooseDB.js +51 -133
  28. package/src/index.js +1 -1
  29. package/src/mailer/EmailRender.js +58 -9
  30. package/src/mailer/MailerProvider.js +98 -25
  31. package/src/runtime/express/Express.js +20 -34
  32. package/src/server/auth.js +9 -28
  33. package/src/server/client-build-live.js +14 -5
  34. package/src/server/client-dev-server.js +21 -8
  35. package/src/server/conf.js +78 -25
  36. package/src/server/peer.js +2 -2
  37. package/src/server/runtime.js +0 -5
  38. package/src/server/start.js +39 -0
  39. package/src/ws/IoInterface.js +132 -39
  40. package/src/ws/IoServer.js +79 -31
  41. package/src/ws/core/core.ws.connection.js +50 -16
  42. package/src/ws/core/core.ws.emit.js +47 -8
  43. package/src/ws/core/core.ws.server.js +62 -10
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Repository module for managing Git operations and configurations.
3
+ * @module src/cli/repository.js
4
+ * @namespace UnderpostRepository
5
+ */
6
+
1
7
  import { commitData } from '../client/components/core/CommonJs.js';
2
8
  import dotenv from 'dotenv';
3
9
  import { pbcopy, shellCd, shellExec } from '../server/process.js';
@@ -11,8 +17,23 @@ dotenv.config();
11
17
 
12
18
  const logger = loggerFactory(import.meta);
13
19
 
20
+ /**
21
+ * @class UnderpostRepository
22
+ * @description Manages Git operations and configurations.
23
+ * This class provides a set of static methods to automate various
24
+ * Git operations, including cloning, pulling, and committing changes.
25
+ * @memberof UnderpostRepository
26
+ */
14
27
  class UnderpostRepository {
15
28
  static API = {
29
+ /**
30
+ * Clones a Git repository from GitHub.
31
+ * @param {string} [gitUri=`${process.env.GITHUB_USERNAME}/pwa-microservices-template`] - The URI of the GitHub repository (e.g., "username/repository").
32
+ * @param {object} [options={ bare: false, g8: false }] - Cloning options.
33
+ * @param {boolean} [options.bare=false] - If true, performs a bare clone.
34
+ * @param {boolean} [options.g8=false] - If true, uses the .g8 extension.
35
+ * @memberof UnderpostRepository
36
+ */
16
37
  clone(gitUri = `${process.env.GITHUB_USERNAME}/pwa-microservices-template`, options = { bare: false, g8: false }) {
17
38
  const gExtension = options.g8 === true ? '.g8' : '.git';
18
39
  const repoName = gitUri.split('/').pop();
@@ -26,6 +47,14 @@ class UnderpostRepository {
26
47
  },
27
48
  );
28
49
  },
50
+ /**
51
+ * Pulls updates from a GitHub repository.
52
+ * @param {string} [repoPath='./'] - The local path to the repository.
53
+ * @param {string} [gitUri=`${process.env.GITHUB_USERNAME}/pwa-microservices-template`] - The URI of the GitHub repository.
54
+ * @param {object} [options={ g8: false }] - Pulling options.
55
+ * @param {boolean} [options.g8=false] - If true, uses the .g8 extension.
56
+ * @memberof UnderpostRepository
57
+ */
29
58
  pull(
30
59
  repoPath = './',
31
60
  gitUri = `${process.env.GITHUB_USERNAME}/pwa-microservices-template`,
@@ -41,6 +70,18 @@ class UnderpostRepository {
41
70
  },
42
71
  );
43
72
  },
73
+ /**
74
+ * Creates a Git commit with a conventional commit message.
75
+ * @param {string} [repoPath='./'] - The local path to the repository.
76
+ * @param {string} [commitType='feat'] - The type of commit (e.g., 'feat', 'fix', 'docs', 'reset').
77
+ * @param {string} [subModule=''] - The submodule or scope of the commit.
78
+ * @param {string} [message=''] - The commit message.
79
+ * @param {object} [options={ copy: false, info: false, empty: false }] - Commit options.
80
+ * @param {boolean} [options.copy=false] - If true, copies the commit message to the clipboard.
81
+ * @param {boolean} [options.info=false] - If true, displays information about commit types.
82
+ * @param {boolean} [options.empty=false] - If true, allows an empty commit.
83
+ * @memberof UnderpostRepository
84
+ */
44
85
  commit(
45
86
  repoPath = './',
46
87
  commitType = 'feat',
@@ -65,9 +106,18 @@ class UnderpostRepository {
65
106
  shellExec(`cd ${repoPath} && git commit ${options?.empty ? `--allow-empty ` : ''}-m "${_message}"`);
66
107
  },
67
108
 
109
+ /**
110
+ * Pushes commits to a remote GitHub repository.
111
+ * @param {string} [repoPath='./'] - The local path to the repository.
112
+ * @param {string} [gitUri=`${process.env.GITHUB_USERNAME}/pwa-microservices-template`] - The URI of the GitHub repository.
113
+ * @param {object} [options={ f: false, g8: false }] - Push options.
114
+ * @param {boolean} [options.f=false] - If true, forces the push.
115
+ * @param {boolean} [options.g8=false] - If true, uses the .g8 extension.
116
+ * @memberof UnderpostRepository
117
+ */
68
118
  push(
69
119
  repoPath = './',
70
- gitUri = `${process.env.GITHUB_USERNAME}/pwa-microservices-template}`,
120
+ gitUri = `${process.env.GITHUB_USERNAME}/pwa-microservices-template`,
71
121
  options = { f: false, g8: false },
72
122
  ) {
73
123
  const gExtension = options.g8 === true || options.G8 === true ? '.g8' : '.git';
@@ -90,11 +140,34 @@ class UnderpostRepository {
90
140
  );
91
141
  },
92
142
 
93
- new(repositoryName, options = { dev: false, deployId: false, cluster: false }) {
143
+ /**
144
+ * Creates a new Underpost project, service, or configuration.
145
+ * @param {string} repositoryName - The name of the new project or service, or a deployId.
146
+ * @param {object} [options={ dev: false, deployId: false, cluster: false, subConf: '' }] - Creation options.
147
+ * @param {boolean} [options.dev=false] - If true, sets up a development project.
148
+ * @param {boolean} [options.deployId=false] - If true, creates deploy ID configuration files.
149
+ * @param {boolean} [options.cluster=false] - If true, creates cluster configuration files.
150
+ * @param {string} [options.subConf=''] - If provided, creates a sub-configuration for a deployId.
151
+ * @returns {Promise<void>} A promise that resolves when the operation is complete.
152
+ * @memberof UnderpostRepository
153
+ */
154
+ new(repositoryName, options = { dev: false, deployId: false, cluster: false, subConf: '' }) {
94
155
  return new Promise(async (resolve, reject) => {
95
156
  try {
96
157
  await logger.setUpInfo();
97
158
  actionInitLog();
159
+ if (options.subConf && typeof options.subConf === 'string') {
160
+ const deployId = repositoryName;
161
+ logger.info('Creating sub conf', {
162
+ deployId,
163
+ subConf: options.subConf,
164
+ });
165
+ fs.copySync(
166
+ `./engine-private/conf/${deployId}/conf.server.json`,
167
+ `./engine-private/conf/${deployId}/conf.server.dev.${options.subConf}.json`,
168
+ );
169
+ return resolve();
170
+ }
98
171
  if (repositoryName === 'service')
99
172
  return resolve(
100
173
  await UnderpostStartUp.API.listenPortController(UnderpostStartUp.API.listenServerFactory(), ':'),
@@ -126,12 +199,26 @@ class UnderpostRepository {
126
199
  });
127
200
  },
128
201
 
202
+ /**
203
+ * Gets a list of deleted files from a Git repository.
204
+ * @param {string} [path='.'] - The path to the repository.
205
+ * @returns {string[]} An array of deleted file paths.
206
+ * @memberof UnderpostRepository
207
+ */
129
208
  getDeleteFiles(path = '.') {
130
209
  const commandUntrack = `cd ${path} && git ls-files --deleted`;
131
210
  const diffUntrackOutput = shellExec(commandUntrack, { stdout: true, silent: true });
132
211
  return diffUntrackOutput.toString().split('\n').filter(Boolean);
133
212
  },
134
213
 
214
+ /**
215
+ * Gets a list of changed (modified and untracked) files in a Git repository.
216
+ * @param {string} [path='.'] - The path to the repository.
217
+ * @param {string} [extension=''] - An optional file extension to filter by.
218
+ * @param {boolean} [head=false] - If true, diffs against HEAD^.
219
+ * @returns {string[]} An array of changed file paths.
220
+ * @memberof UnderpostRepository
221
+ */
135
222
  getChangedFiles(path = '.', extension = '', head = false) {
136
223
  const extensionFilter = extension ? `-- '***.${extension}'` : '';
137
224
  const command = `cd ${path} && git diff ${head ? 'HEAD^ HEAD ' : ''}--name-only ${extensionFilter}`;
@@ -146,6 +233,12 @@ class UnderpostRepository {
146
233
  .concat(diffUntrackOutput.toString().split('\n').filter(Boolean))
147
234
  .filter((f) => !deleteFiles.includes(f));
148
235
  },
236
+ /**
237
+ * Updates the private configuration repository for a given deployId.
238
+ * @param {string} deployId - The deployment ID.
239
+ * @returns {{validVersion: boolean, engineVersion: string, deployVersion: string}} An object indicating if the versions are valid.
240
+ * @memberof UnderpostRepository
241
+ */
149
242
  privateConfUpdate(deployId) {
150
243
  shellCd(`/home/dd/engine`);
151
244
  const privateRepoName = `engine-${deployId.split('dd-')[1]}-private`;
@@ -831,6 +831,14 @@ const subThemeManager = {
831
831
  color: ${this.lightColor};
832
832
  background-color: ${lightenHex(this.lightColor, 0.8)};
833
833
  }
834
+ .main-sub-btn-active {
835
+ color: ${this.lightColor};
836
+ background-color: rgba(0, 0, 0, 0.3);
837
+ }
838
+ .main-sub-btn-active:hover {
839
+ color: ${this.lightColor};
840
+ background-color: rgba(0, 0, 0, 0.2);
841
+ }
834
842
  </style>`;
835
843
  };
836
844
  },
@@ -845,6 +853,14 @@ const subThemeManager = {
845
853
  color: ${lightenHex(this.darkColor, 0.8)};
846
854
  background-color: ${darkenHex(this.darkColor, 0.75)};
847
855
  }
856
+ .main-sub-btn-active {
857
+ color: ${lightenHex(this.darkColor, 0.8)};
858
+ background-color: rgba(255, 255, 255, 0.3);
859
+ }
860
+ .main-sub-btn-active:hover {
861
+ color: ${lightenHex(this.darkColor, 0.8)};
862
+ background-color: rgba(255, 255, 255, 0.2);
863
+ }
848
864
  </style>`;
849
865
  };
850
866
  },
@@ -8,7 +8,7 @@ import { htmls, s } from './VanillaJs.js';
8
8
  // https://mintlify.com/docs/quickstart
9
9
 
10
10
  const Docs = {
11
- RenderModal: async function (type, modalOptions) {
11
+ RenderModal: async function (type) {
12
12
  const docData = this.Data.find((d) => d.type === type);
13
13
  const ModalId = `modal-docs-${docData.type}`;
14
14
  const { barConfig } = await Themes[Css.currentTheme]();
@@ -35,7 +35,7 @@ const Docs = {
35
35
  observer: true,
36
36
  barMode: 'top-bottom-bar',
37
37
  query: true,
38
- ...modalOptions,
38
+ RouterInstance: Modal.Data['modal-docs'].options.RouterInstance,
39
39
  });
40
40
  Modal.Data[ModalId].onObserverListener[ModalId] = () => {
41
41
  if (s(`.iframe-${ModalId}`))
@@ -118,25 +118,17 @@ const Docs = {
118
118
  const { idModal } = options;
119
119
  this.Tokens[idModal] = options;
120
120
  setTimeout(() => {
121
- const cleanActive = () => {
122
- s(`.btn-docs-src`).classList.remove('main-btn-menu-active');
123
- s(`.btn-docs-api`).classList.remove('main-btn-menu-active');
124
- s(`.btn-docs-coverage`).classList.remove('main-btn-menu-active');
125
- };
126
121
  s(`.btn-docs-src`).onclick = async () => {
127
122
  setQueryPath({ path: 'docs', queryPath: 'src' });
128
- cleanActive();
129
- await this.RenderModal('src', options.modalOptions);
123
+ await this.RenderModal('src');
130
124
  };
131
125
  s(`.btn-docs-api`).onclick = async () => {
132
126
  setQueryPath({ path: 'docs', queryPath: 'api' });
133
- cleanActive();
134
- await this.RenderModal('api', options.modalOptions);
127
+ await this.RenderModal('api');
135
128
  };
136
129
  s(`.btn-docs-coverage`).onclick = async () => {
137
130
  setQueryPath({ path: 'docs', queryPath: 'coverage' });
138
- cleanActive();
139
- await this.RenderModal('coverage', options.modalOptions);
131
+ await this.RenderModal('coverage');
140
132
  };
141
133
 
142
134
  s(`.btn-docs-coverage-link`).onclick = () => {
@@ -21,6 +21,8 @@ import {
21
21
  getProxyPath,
22
22
  setPath,
23
23
  coreUI,
24
+ sanitizeRoute,
25
+ getQueryParams,
24
26
  } from './Router.js';
25
27
  import { NotificationManager } from './NotificationManager.js';
26
28
  import { EventsUI } from './EventsUI.js';
@@ -90,11 +92,6 @@ const Modal = {
90
92
  query: options.query ? `${window.location.search}` : undefined,
91
93
  getTop: () => {
92
94
  const result = windowGetH() - (options.heightBottomBar ? options.heightBottomBar : heightDefaultBottomBar);
93
- // TODO: mobile padding gap on init size top height, Iphone SE responsive case
94
- // logger.warn('getTop', {
95
- // top: result,
96
- // height: Modal.Data[idModal].getHeight(),
97
- // });
98
95
  return result;
99
96
  },
100
97
  getHeight: () => {
@@ -120,6 +117,10 @@ const Modal = {
120
117
  }px`
121
118
  : `-${ops?.open ? '0px' : originSlideMenuWidth}px`
122
119
  }`,
120
+ center: () => {
121
+ top = `${windowGetH() / 2 - height / 2}px`;
122
+ left = `${windowGetW() / 2 - width / 2}px`;
123
+ },
123
124
  };
124
125
 
125
126
  if (options && 'mode' in options) {
@@ -1169,6 +1170,7 @@ const Modal = {
1169
1170
  });
1170
1171
  EventsUI.onClick(`.action-btn-home`, async () => {
1171
1172
  await Modal.onHomeRouterEvent();
1173
+ subMenuHandler(Object.keys(options.RouterInstance.Routes()));
1172
1174
  Object.keys(this.Data[idModal].onHome).map((keyListener) => this.Data[idModal].onHome[keyListener]());
1173
1175
  });
1174
1176
  EventsUI.onClick(`.action-btn-app-icon`, () => s(`.action-btn-home`).click());
@@ -1349,7 +1351,6 @@ const Modal = {
1349
1351
  removeEvent();
1350
1352
  }
1351
1353
  });
1352
- // TODO: mobile padding gap on init size top height, Iphone SE responsive case
1353
1354
  setTimeout(window.onresize);
1354
1355
  });
1355
1356
  })();
@@ -1387,10 +1388,7 @@ const Modal = {
1387
1388
  return;
1388
1389
  }
1389
1390
 
1390
- if (idModal !== 'main-body' && options.mode !== 'view' && !options.disableCenter) {
1391
- top = `${windowGetH() / 2 - height / 2}px`;
1392
- left = `${windowGetW() / 2 - width / 2}px`;
1393
- }
1391
+ if (idModal !== 'main-body' && options.mode !== 'view' && !options.disableCenter) Modal.Data[idModal].center();
1394
1392
 
1395
1393
  const render = html` <style class="style-${idModal}">
1396
1394
  .${idModal} {
@@ -1860,26 +1858,14 @@ const Modal = {
1860
1858
  s(`.btn-maximize-${idModal}`).style.display = null;
1861
1859
 
1862
1860
  // Restore original dimensions and position
1861
+ this.Data[idModal].center();
1863
1862
  modal.style.transform = '';
1864
- modal.style.height = '';
1865
- left = 0;
1866
- width = 300;
1867
- modal.style.left = `${left}px`;
1863
+ modal.style.height = `${height}px`;
1864
+ modal.style.left = left;
1865
+ modal.style.top = top;
1868
1866
  modal.style.width = `${width}px`;
1869
1867
  modal.style.overflow = '';
1870
1868
 
1871
- // Reset drag position
1872
- dragPosition = { x: 0, y: 0 };
1873
-
1874
- // Set new position
1875
- modal.style.transform = `translate(0, 0)`;
1876
-
1877
- // Adjust top position based on top bar visibility
1878
- const heightDefaultTopBar = 40; // Default top bar height if not specified
1879
- s(`.${idModal}`).style.top = s(`.main-body-btn-ui-close`).classList.contains('hide')
1880
- ? `0px`
1881
- : `${options.heightTopBar ? options.heightTopBar : heightDefaultTopBar}px`;
1882
-
1883
1869
  // Re-enable drag after restore
1884
1870
  if (dragInstance) {
1885
1871
  dragInstance.updateOptions({
@@ -2413,7 +2399,7 @@ const buildBadgeToolTipMenuOption = (id, sideKey = 'left') => {
2413
2399
  };
2414
2400
 
2415
2401
  const isSubMenuOpen = (subMenuId) => {
2416
- return s(`.down-arrow-submenu-${subMenuId}`).style.rotate === '180deg';
2402
+ return s(`.down-arrow-submenu-${subMenuId}`) && s(`.down-arrow-submenu-${subMenuId}`).style.rotate === '180deg';
2417
2403
  };
2418
2404
 
2419
2405
  const subMenuRender = async (subMenuId) => {
@@ -2425,7 +2411,15 @@ const subMenuRender = async (subMenuId) => {
2425
2411
  if (!menuBtn || !menuContainer || !arrow) return;
2426
2412
 
2427
2413
  const top = () => {
2428
- menuContainer.style.top = menuBtn.offsetTop + Modal.Data['modal-menu'].options.heightTopBar + 'px';
2414
+ let value = menuBtn.offsetTop + Modal.Data['modal-menu'].options.heightTopBar;
2415
+
2416
+ const isHidden = s('.main-body-btn-ui-open').classList.contains('hide');
2417
+ const isTopBottom = Modal.Data['modal-menu'].options.barMode === 'top-bottom-bar';
2418
+
2419
+ if (!isTopBottom && isHidden) value -= 51;
2420
+ else if (!isHidden) value += 51;
2421
+
2422
+ menuContainer.style.top = `${value}px`;
2429
2423
  };
2430
2424
 
2431
2425
  Modal.subMenuBtnClass[subMenuId] = {
@@ -2472,4 +2466,29 @@ const subMenuRender = async (subMenuId) => {
2472
2466
  }, 500);
2473
2467
  };
2474
2468
 
2475
- export { Modal, renderMenuLabel, renderViewTitle, buildBadgeToolTipMenuOption, subMenuRender, isSubMenuOpen };
2469
+ const subMenuHandler = (routes, route) => {
2470
+ route = sanitizeRoute(route);
2471
+ for (let _route of routes) {
2472
+ _route = sanitizeRoute(_route);
2473
+ if (_route !== route) {
2474
+ if (isSubMenuOpen(_route)) subMenuRender(_route);
2475
+ }
2476
+ }
2477
+ setTimeout(() => {
2478
+ let cid = getQueryParams().cid;
2479
+ if (s(`.main-sub-btn-active`)) s(`.main-sub-btn-active`).classList.remove('main-sub-btn-active');
2480
+ if (cid && s(`.btn-${route}-${cid}`)) {
2481
+ s(`.btn-${route}-${cid}`).classList.add('main-sub-btn-active');
2482
+ }
2483
+ });
2484
+ };
2485
+
2486
+ export {
2487
+ Modal,
2488
+ renderMenuLabel,
2489
+ renderViewTitle,
2490
+ buildBadgeToolTipMenuOption,
2491
+ subMenuRender,
2492
+ isSubMenuOpen,
2493
+ subMenuHandler,
2494
+ };
@@ -7,7 +7,7 @@
7
7
  import { titleFormatted } from './CommonJs.js';
8
8
  import { loggerFactory } from './Logger.js';
9
9
  import { htmls, s } from './VanillaJs.js';
10
- import { Modal } from './Modal.js';
10
+ import { Modal, subMenuHandler } from './Modal.js';
11
11
  import { Worker } from './Worker.js';
12
12
 
13
13
  const logger = loggerFactory(import.meta, { trace: true });
@@ -162,6 +162,7 @@ const Router = function (options = { Routes: () => {}, e: new PopStateEvent() })
162
162
 
163
163
  if (path === pushPath) {
164
164
  for (const event of Object.keys(RouterEvents)) RouterEvents[event](routerEvent);
165
+ subMenuHandler(Object.keys(Routes()), route);
165
166
  setDocTitle(route);
166
167
  return Routes()[`/${route}`].render();
167
168
  }
@@ -256,14 +257,15 @@ const closeModalRouteChangeEvent = (options = {}) => {
256
257
  * @param {string} options.route - The route associated with the modal view.
257
258
  * @memberof PwaRouter
258
259
  */
259
- const handleModalViewRoute = (options = { route: '' }) => {
260
- const { route } = options;
260
+ const handleModalViewRoute = (options = { RouterInstance: { Routes: () => {} }, route: '' }) => {
261
+ const { route, RouterInstance } = options;
261
262
  if (!route) return;
262
263
 
263
264
  let path = window.location.pathname;
264
265
  if (path !== '/' && path[path.length - 1] === '/') path = path.slice(0, -1);
265
266
  const proxyPath = getProxyPath();
266
267
  const newPath = `${proxyPath}${route}`;
268
+ if (RouterInstance && RouterInstance.Routes) subMenuHandler(Object.keys(RouterInstance.Routes()), route);
267
269
 
268
270
  if (path !== newPath) {
269
271
  setPath(newPath);
@@ -311,4 +313,5 @@ export {
311
313
  getProxyPath,
312
314
  setPath,
313
315
  setQueryParams,
316
+ sanitizeRoute,
314
317
  };