@underpostnet/underpost 3.0.3 → 3.1.0

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 (82) hide show
  1. package/{.env.production → .env.example} +20 -2
  2. package/.github/workflows/ghpkg.ci.yml +1 -1
  3. package/.github/workflows/gitlab.ci.yml +1 -1
  4. package/.github/workflows/npmpkg.ci.yml +22 -7
  5. package/.github/workflows/publish.ci.yml +5 -5
  6. package/.github/workflows/pwa-microservices-template-page.cd.yml +3 -3
  7. package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
  8. package/.github/workflows/release.cd.yml +3 -2
  9. package/.vscode/extensions.json +9 -8
  10. package/.vscode/settings.json +3 -2
  11. package/CHANGELOG.md +146 -1
  12. package/CLI-HELP.md +71 -52
  13. package/README.md +2 -2
  14. package/bin/build.js +4 -1
  15. package/bin/deploy.js +150 -208
  16. package/bin/file.js +2 -1
  17. package/bin/vs.js +3 -3
  18. package/conf.js +30 -13
  19. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
  20. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
  21. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  22. package/manifests/deployment/dd-test-development/deployment.yaml +52 -52
  23. package/manifests/deployment/dd-test-development/proxy.yaml +4 -4
  24. package/manifests/pv-pvc-dd.yaml +1 -1
  25. package/package.json +48 -43
  26. package/scripts/k3s-node-setup.sh +1 -1
  27. package/src/api/document/document.service.js +1 -1
  28. package/src/api/file/file.controller.js +3 -1
  29. package/src/api/file/file.service.js +28 -5
  30. package/src/api/user/user.router.js +10 -5
  31. package/src/api/user/user.service.js +7 -7
  32. package/src/cli/baremetal.js +6 -10
  33. package/src/cli/cloud-init.js +0 -3
  34. package/src/cli/db.js +54 -71
  35. package/src/cli/deploy.js +64 -12
  36. package/src/cli/env.js +4 -4
  37. package/src/cli/fs.js +0 -2
  38. package/src/cli/image.js +0 -3
  39. package/src/cli/index.js +27 -13
  40. package/src/cli/monitor.js +5 -6
  41. package/src/cli/repository.js +322 -35
  42. package/src/cli/run.js +118 -69
  43. package/src/cli/secrets.js +0 -3
  44. package/src/cli/ssh.js +1 -1
  45. package/src/client/components/core/AgGrid.js +20 -5
  46. package/src/client/components/core/Content.js +22 -3
  47. package/src/client/components/core/Docs.js +21 -4
  48. package/src/client/components/core/FileExplorer.js +71 -4
  49. package/src/client/components/core/Input.js +1 -1
  50. package/src/client/components/core/Modal.js +20 -6
  51. package/src/client/public/default/sitemap +3 -3
  52. package/src/client/public/test/sitemap +3 -3
  53. package/src/client.build.js +0 -3
  54. package/src/client.dev.js +0 -3
  55. package/src/db/DataBaseProvider.js +17 -2
  56. package/src/db/mariadb/MariaDB.js +14 -9
  57. package/src/db/mongo/MongooseDB.js +17 -1
  58. package/src/index.js +1 -1
  59. package/src/proxy.js +0 -3
  60. package/src/runtime/express/Express.js +7 -1
  61. package/src/runtime/lampp/Lampp.js +6 -13
  62. package/src/server/auth.js +6 -9
  63. package/src/server/backup.js +2 -3
  64. package/src/server/client-build-docs.js +178 -3
  65. package/src/server/client-build-live.js +9 -18
  66. package/src/server/client-build.js +175 -38
  67. package/src/server/client-dev-server.js +14 -13
  68. package/src/server/conf.js +357 -149
  69. package/src/server/cron.js +2 -1
  70. package/src/server/dns.js +28 -12
  71. package/src/server/downloader.js +0 -2
  72. package/src/server/logger.js +27 -9
  73. package/src/server/peer.js +0 -2
  74. package/src/server/process.js +1 -50
  75. package/src/server/proxy.js +4 -8
  76. package/src/server/runtime.js +5 -8
  77. package/src/server/ssr.js +0 -3
  78. package/src/server/start.js +5 -5
  79. package/src/server/tls.js +0 -2
  80. package/src/server.js +0 -4
  81. package/.env.development +0 -43
  82. package/.env.test +0 -43
@@ -544,10 +544,13 @@ const FileExplorer = {
544
544
  this.eGui = document.createElement('div');
545
545
  const isPublic = params.data.isPublic;
546
546
  const toggleId = `toggle-public-${params.data._id}`;
547
+ const hasGenericFile = !!params.data.hasGenericFile;
548
+ const hasMdFile = !!params.data.hasMdFile;
549
+
547
550
  this.eGui.innerHTML = html`
548
551
  <div class="fl">
549
552
  ${await BtnIcon.Render({
550
- class: `in fll management-table-btn-mini btn-file-download-${params.data._id}`,
553
+ class: `in fll management-table-btn-mini btn-file-download-${params.data._id}${!hasGenericFile ? ' btn-disabled' : ''}`,
551
554
  label: html` <i class="fas fa-download"></i>`,
552
555
  type: 'button',
553
556
  })}
@@ -562,10 +565,15 @@ const FileExplorer = {
562
565
  type: 'button',
563
566
  })}
564
567
  ${await BtnIcon.Render({
565
- class: `in fll management-table-btn-mini btn-file-copy-content-link-${params.data._id}`,
568
+ class: `in fll management-table-btn-mini btn-file-copy-content-link-${params.data._id}${!hasGenericFile ? ' btn-disabled' : ''}`,
566
569
  label: html`<i class="fas fa-copy"></i>`,
567
570
  type: 'button',
568
571
  })}
572
+ ${await BtnIcon.Render({
573
+ class: `in fll management-table-btn-mini btn-file-copy-md-link-${params.data._id}${!hasMdFile ? ' btn-disabled' : ''}`,
574
+ label: html`<i class="fas fa-file-code"></i>`,
575
+ type: 'button',
576
+ })}
569
577
  ${await BtnIcon.Render({
570
578
  class: `in fll management-table-btn-mini btn-file-edit-${params.data._id}`,
571
579
  label: html`<i class="fas fa-edit"></i>`,
@@ -591,11 +599,46 @@ const FileExplorer = {
591
599
  ? getApiBaseUrl({ id: originObj.fileId._id, endpoint: 'file/blob' })
592
600
  : undefined;
593
601
 
602
+ const mdBlobUri =
603
+ originObj && originObj.mdFileId
604
+ ? getApiBaseUrl({ id: originObj.mdFileId._id, endpoint: 'file/blob' })
605
+ : undefined;
606
+
594
607
  if (!originObj) {
595
608
  s(`.btn-file-view-${params.data._id}`).classList.add('hide');
596
609
  s(`.btn-file-copy-content-link-${params.data._id}`).classList.add('hide');
597
610
  }
598
611
 
612
+ // Disable download button if no generic file
613
+ if (!hasGenericFile) {
614
+ const dlBtn = s(`.btn-file-download-${params.data._id}`);
615
+ if (dlBtn) {
616
+ dlBtn.style.opacity = '0.4';
617
+ dlBtn.style.cursor = 'not-allowed';
618
+ dlBtn.style.pointerEvents = 'none';
619
+ }
620
+ }
621
+
622
+ // Disable copy generic file link button if no generic file
623
+ if (!hasGenericFile) {
624
+ const copyBtn = s(`.btn-file-copy-content-link-${params.data._id}`);
625
+ if (copyBtn) {
626
+ copyBtn.style.opacity = '0.4';
627
+ copyBtn.style.cursor = 'not-allowed';
628
+ copyBtn.style.pointerEvents = 'none';
629
+ }
630
+ }
631
+
632
+ // Disable copy md file link button if no md file
633
+ if (!hasMdFile) {
634
+ const mdCopyBtn = s(`.btn-file-copy-md-link-${params.data._id}`);
635
+ if (mdCopyBtn) {
636
+ mdCopyBtn.style.opacity = '0.4';
637
+ mdCopyBtn.style.cursor = 'not-allowed';
638
+ mdCopyBtn.style.pointerEvents = 'none';
639
+ }
640
+ }
641
+
599
642
  EventsUI.onClick(`.btn-file-view-${params.data._id}`, async (e) => {
600
643
  e.preventDefault();
601
644
  if (location.href !== url) {
@@ -606,6 +649,7 @@ const FileExplorer = {
606
649
 
607
650
  EventsUI.onClick(`.btn-file-copy-content-link-${params.data._id}`, async (e) => {
608
651
  e.preventDefault();
652
+ if (!hasGenericFile || !blobUri) return;
609
653
  await copyData(blobUri);
610
654
  NotificationManager.Push({
611
655
  html: Translate.Render('success-copy-data'),
@@ -613,10 +657,21 @@ const FileExplorer = {
613
657
  });
614
658
  });
615
659
 
660
+ EventsUI.onClick(`.btn-file-copy-md-link-${params.data._id}`, async (e) => {
661
+ e.preventDefault();
662
+ if (!hasMdFile || !mdBlobUri) return;
663
+ await copyData(mdBlobUri);
664
+ NotificationManager.Push({
665
+ html: Translate.Render('success-copy-data'),
666
+ status: 'success',
667
+ });
668
+ });
669
+
616
670
  EventsUI.onClick(`.btn-file-download-${params.data._id}`, async (e) => {
617
671
  e.preventDefault();
672
+ if (!hasGenericFile) return;
618
673
  try {
619
- // Use FileService with blob/ prefix for centralized blob fetching
674
+ // Use FileService with blob/ prefix for blob fetching
620
675
  const { data: blobArray, status } = await FileService.get({ id: `blob/${params.data.fileId}` });
621
676
  if (status === 'success' && blobArray && blobArray[0]) {
622
677
  downloadFile(blobArray[0], params.data.name);
@@ -721,6 +776,12 @@ const FileExplorer = {
721
776
  documentInstance[docIndex].isPublic = data.isPublic;
722
777
  }
723
778
 
779
+ // Refresh the isPublic column cell in the grid
780
+ const rowNode = AgGrid.grids[gridFileId].getRowNode(params.node.id);
781
+ if (rowNode) {
782
+ rowNode.setDataValue('isPublic', data.isPublic);
783
+ }
784
+
724
785
  // Update button icon
725
786
  const btnElement = s(`.${toggleId}`);
726
787
  if (btnElement) {
@@ -1347,7 +1408,13 @@ const FileExplorer = {
1347
1408
  { field: 'name', flex: 2, headerName: 'Title', cellRenderer: LoadFileNameRenderer },
1348
1409
  { field: 'mdFileName', flex: 1, headerName: 'MD File Name' },
1349
1410
  { field: 'fileName', flex: 1, headerName: 'Generic File Name' },
1350
- { headerName: '', width: 150, cellRenderer: LoadFileActionsRenderer },
1411
+ {
1412
+ field: 'isPublic',
1413
+ headerName: 'Public',
1414
+ width: 90,
1415
+ cellDataType: 'boolean',
1416
+ },
1417
+ { headerName: '', width: 180, cellRenderer: LoadFileActionsRenderer },
1351
1418
  ],
1352
1419
  },
1353
1420
  })}
@@ -101,7 +101,7 @@ const getFileFromFileData = (fileData) => {
101
101
  /**
102
102
  * Fetch file content from blob endpoint and create File object.
103
103
  * Used for metadata-only format files during edit mode.
104
- * Uses FileService with blob/ prefix for centralized blob fetching.
104
+ * Uses FileService with blob/ prefix for blob fetching.
105
105
  *
106
106
  * @async
107
107
  * @function getFileFromBlobEndpoint
@@ -2424,20 +2424,34 @@ const Modal = {
2424
2424
  },
2425
2425
  };
2426
2426
 
2427
- const renderMenuLabel = ({ img, text, icon }) => {
2428
- if (!img) return html`<span class="inl menu-btn-icon">${icon}</span> ${text}`;
2429
- return html`<img class="abs center img-btn-square-menu" src="${getProxyPath()}assets/ui-icons/${img}" />
2427
+ const renderMenuLabel = ({ img, src, text, icon }) => {
2428
+ if (!img && !src) return html`<span class="inl menu-btn-icon">${icon}</span> ${text}`;
2429
+ const imgSrc = src ? src : `${getProxyPath()}assets/ui-icons/${img}`;
2430
+ return html`<img class="abs center img-btn-square-menu" src="${imgSrc}" />
2430
2431
  <div class="abs center main-btn-menu-text">${text}</div>`;
2431
2432
  };
2432
2433
 
2433
2434
  const renderViewTitle = (
2434
- options = { icon: '', img: '', text: '', assetFolder: '', 'ui-icons': '', dim, top, topText: '' },
2435
+ options = {
2436
+ icon: '',
2437
+ img: '',
2438
+ text: '',
2439
+ assetFolder: '',
2440
+ 'ui-icon': '',
2441
+ imgClass: '',
2442
+ textClass: '',
2443
+ dim,
2444
+ top,
2445
+ topText: '',
2446
+ },
2435
2447
  ) => {
2436
2448
  if (options.dim === undefined) options.dim = 30;
2437
2449
  const { img, text, icon, dim, top } = options;
2438
2450
  if (!img && !options['ui-icon']) return html`<span class="view-title-icon">${icon}</span> ${text}`;
2451
+ const imgClass = options.imgClass || 'abs img-btn-square-view-title';
2452
+ const textClass = options.textClass || 'in text-btn-square-view-title';
2439
2453
  return html`<img
2440
- class="abs img-btn-square-view-title"
2454
+ class="${imgClass}"
2441
2455
  style="${renderCssAttr({
2442
2456
  style: {
2443
2457
  width: `${dim}px`,
@@ -2450,7 +2464,7 @@ const renderViewTitle = (
2450
2464
  : img}"
2451
2465
  />
2452
2466
  <div
2453
- class="in text-btn-square-view-title"
2467
+ class="${textClass}"
2454
2468
  style="${renderCssAttr({
2455
2469
  style: {
2456
2470
  // 'padding-left': `${20 + dim}px`,
@@ -1,5 +1,5 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
- <xsl:stylesheet version="2.0"
2
+ <xsl:stylesheet version="1.0"
3
3
  xmlns:html="http://www.w3.org/TR/REC-html40"
4
4
  xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"
5
5
  xmlns:sitemap="http://www.sitemaps.org/schemas/sitemap/0.9"
@@ -67,7 +67,7 @@
67
67
  <div id="content">
68
68
  <h1>XML Sitemap</h1>
69
69
  <p class="desc"> This is a sitemap generated by <a
70
- href="https://www.nexodev.org">nexodev.org</a>
70
+ href="{{web-url}}">{{web-url}}</a>
71
71
  </p>
72
72
  <xsl:if test="count(sitemap:sitemapindex/sitemap:sitemap) &gt; 0">
73
73
  <table id="sitemap" cellpadding="3">
@@ -145,4 +145,4 @@
145
145
  </html>
146
146
 
147
147
  </xsl:template>
148
- </xsl:stylesheet>
148
+ </xsl:stylesheet>
@@ -1,5 +1,5 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
- <xsl:stylesheet version="2.0"
2
+ <xsl:stylesheet version="1.0"
3
3
  xmlns:html="http://www.w3.org/TR/REC-html40"
4
4
  xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"
5
5
  xmlns:sitemap="http://www.sitemaps.org/schemas/sitemap/0.9"
@@ -67,7 +67,7 @@
67
67
  <div id="content">
68
68
  <h1>XML Sitemap</h1>
69
69
  <p class="desc"> This is a sitemap generated by <a
70
- href="https://www.nexodev.org">nexodev.org</a>
70
+ href="{{web-url}}">{{web-url}}</a>
71
71
  </p>
72
72
  <xsl:if test="count(sitemap:sitemapindex/sitemap:sitemap) &gt; 0">
73
73
  <table id="sitemap" cellpadding="3">
@@ -145,4 +145,4 @@
145
145
  </html>
146
146
 
147
147
  </xsl:template>
148
- </xsl:stylesheet>
148
+ </xsl:stylesheet>
@@ -3,14 +3,11 @@
3
3
  // https://nodejs.org/api
4
4
  // https://expressjs.com/en/4x/api.html
5
5
 
6
- import dotenv from 'dotenv';
7
6
  import { loggerFactory } from './server/logger.js';
8
7
  import { Config } from './server/conf.js';
9
8
  import { ProcessController } from './server/process.js';
10
9
  import { clientLiveBuild } from './server/client-build-live.js';
11
10
 
12
- dotenv.config();
13
-
14
11
  await Config.build();
15
12
 
16
13
  const logger = loggerFactory(import.meta);
package/src/client.dev.js CHANGED
@@ -3,14 +3,11 @@
3
3
  // https://nodejs.org/api
4
4
  // https://expressjs.com/en/4x/api.html
5
5
 
6
- import dotenv from 'dotenv';
7
6
  import { loggerFactory } from './server/logger.js';
8
7
  import { ProcessController } from './server/process.js';
9
8
  import { Config, buildClientStaticConf } from './server/conf.js';
10
9
  import { createClientDevServer } from './server/client-dev-server.js';
11
10
 
12
- dotenv.config();
13
-
14
11
  const logger = loggerFactory(import.meta);
15
12
 
16
13
  await logger.setUpInfo();
@@ -13,7 +13,7 @@ const logger = loggerFactory(import.meta);
13
13
  * @class
14
14
  * @alias DataBaseProviderService
15
15
  * @memberof DataBaseProviderService
16
- * @classdesc Centralized service for loading, managing, and accessing multiple database connections
16
+ * @classdesc Service for loading, managing, and accessing multiple database connections
17
17
  * based on application configuration (host, path, provider type).
18
18
  */
19
19
  class DataBaseProviderService {
@@ -81,7 +81,22 @@ class DataBaseProviderService {
81
81
  }
82
82
  return this.#instance[key][db.provider];
83
83
  } catch (error) {
84
- logger.error(error, { error: error.stack, options });
84
+ // Sanitize options to prevent credential exposure in logs
85
+ const safeOptions = {
86
+ apis: options.apis,
87
+ host: options.host,
88
+ path: options.path,
89
+ db: options.db
90
+ ? {
91
+ provider: options.db.provider,
92
+ name: options.db.name ? '***' : undefined,
93
+ host: options.db.host ? '***' : undefined,
94
+ user: options.db.user ? '***' : undefined,
95
+ password: options.db.password ? '***' : undefined,
96
+ }
97
+ : {},
98
+ };
99
+ logger.error(error.message, { safeOptions });
85
100
  return undefined;
86
101
  }
87
102
  }
@@ -16,6 +16,11 @@ const logger = loggerFactory(import.meta);
16
16
  * @memberof MariaDBService
17
17
  * @classdesc Provides a simplified interface for executing queries against a MariaDB/MySQL database
18
18
  * using a connection pool, ensuring connection management (acquisition and release).
19
+ *
20
+ * Connection credentials are resolved in the following order:
21
+ * 1. Explicit values passed in the `options` parameter.
22
+ * 2. Environment variables (`MARIADB_HOST`, `MARIADB_PORT`, `MARIADB_USER`, `MARIADB_PASSWORD`).
23
+ * 3. Safe built-in defaults (`127.0.0.1`, `3306`, `root`, empty password).
19
24
  */
20
25
  class MariaDBService {
21
26
  /**
@@ -23,20 +28,20 @@ class MariaDBService {
23
28
  *
24
29
  * @async
25
30
  * @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.
31
+ * @param {string} [options.host] - The database host. Falls back to `process.env.MARIADB_HOST` then `'127.0.0.1'`.
32
+ * @param {number} [options.port] - The database port. Falls back to `process.env.MARIADB_PORT` then `3306`.
33
+ * @param {string} [options.user] - The database user. Falls back to `process.env.MARIADB_USER` then `'root'`.
34
+ * @param {string} [options.password] - The database password. Falls back to `process.env.MARIADB_PASSWORD` then `''`.
30
35
  * @param {string} options.query - The SQL query string to execute.
31
36
  * @returns {Promise<any>} The result of the database query.
32
37
  */
33
38
  async query(options) {
34
39
  const { host, port, user, password, query } = options;
35
40
  const pool = createPool({
36
- host: 'host' in options ? host : '127.0.0.1',
37
- port: 'port' in options ? port : 3306,
38
- user: 'user' in options ? user : 'root',
39
- password: 'password' in options ? password : '',
41
+ host: 'host' in options ? host : process.env.MARIADB_HOST || '127.0.0.1',
42
+ port: 'port' in options ? port : parseInt(process.env.MARIADB_PORT, 10) || 3306,
43
+ user: 'user' in options ? user : process.env.MARIADB_USER || 'root',
44
+ password: 'password' in options ? password : process.env.MARIADB_PASSWORD || '',
40
45
  });
41
46
  let conn, result;
42
47
  try {
@@ -45,7 +50,7 @@ class MariaDBService {
45
50
  logger.info('query');
46
51
  console.log(result);
47
52
  } catch (error) {
48
- logger.error(error, error.stack);
53
+ logger.error('MariaDB query failed', { error: error.message });
49
54
  } finally {
50
55
  if (conn) conn.release(); // release to pool
51
56
  await pool.end();
@@ -16,17 +16,33 @@ const logger = loggerFactory(import.meta);
16
16
  * @memberof MongooseDBService
17
17
  * @classdesc Manages the Mongoose connection lifecycle and dynamic loading of database models
18
18
  * based on API configuration.
19
+ *
20
+ * Connection parameters are resolved in the following order:
21
+ * 1. Explicit values passed as arguments to {@link MongooseDBService#connect}.
22
+ * 2. Environment variables (`DB_HOST` for host, `DB_NAME` for database name).
23
+ * 3. No built-in defaults — both `host` and `name` are required from the caller or environment.
19
24
  */
20
25
  class MongooseDBService {
21
26
  /**
22
27
  * Establishes a Mongoose connection to the specified MongoDB instance.
23
28
  *
24
29
  * @async
25
- * @param {string} host - The MongoDB host (e.g., 'mongodb://localhost:27017').
30
+ * @param {string} host - The MongoDB host URI (e.g., `'mongodb://localhost:27017'`).
31
+ * Falls back to `process.env.DB_HOST` when not provided.
26
32
  * @param {string} name - The database name.
33
+ * Falls back to `process.env.DB_NAME` when not provided.
27
34
  * @returns {Promise<mongoose.Connection>} A promise that resolves to the established Mongoose connection object.
35
+ * @throws {Error} If neither the argument nor the corresponding environment variable supplies a value.
28
36
  */
29
37
  async connect(host, name) {
38
+ host = host || process.env.DB_HOST;
39
+ name = name || process.env.DB_NAME;
40
+
41
+ if (!host || !name) {
42
+ const missing = [!host && 'host (DB_HOST)', !name && 'name (DB_NAME)'].filter(Boolean).join(', ');
43
+ throw new Error(`MongooseDBService.connect: missing required parameter(s): ${missing}`);
44
+ }
45
+
30
46
  const uri = `${host}/${name}`;
31
47
  // logger.info('MongooseDB connect', { host, name, uri });
32
48
  return await mongoose
package/src/index.js CHANGED
@@ -42,7 +42,7 @@ class Underpost {
42
42
  * @type {String}
43
43
  * @memberof Underpost
44
44
  */
45
- static version = 'v3.0.3';
45
+ static version = 'v3.1.0';
46
46
 
47
47
  /**
48
48
  * Required Node.js major version
package/src/proxy.js CHANGED
@@ -3,14 +3,11 @@
3
3
  // https://nodejs.org/api
4
4
  // https://expressjs.com/en/4x/api.html
5
5
 
6
- import dotenv from 'dotenv';
7
6
  import { loggerFactory } from './server/logger.js';
8
7
  import { buildProxy } from './server/proxy.js';
9
8
  import { ProcessController } from './server/process.js';
10
9
  import { Config } from './server/conf.js';
11
10
 
12
- dotenv.config();
13
-
14
11
  await Config.build(process.argv[2], process.argv[3], process.argv[4]);
15
12
 
16
13
  const logger = loggerFactory(import.meta);
@@ -249,7 +249,13 @@ class ExpressService {
249
249
  for (const [_, ssrMiddleware] of Object.entries(ssr)) app.use(ssrMiddleware);
250
250
 
251
251
  // Start listening on the main port
252
- if (useLocalSsl && process.env.NODE_ENV === 'development') {
252
+ // When behind a dev proxy (isDevProxyContext), the proxy handles TLS termination,
253
+ // so backend servers should listen on plain HTTP to be reachable via http://localhost:PORT
254
+ if (
255
+ (useLocalSsl || process.argv.find((arg) => arg === 'tls')) &&
256
+ process.env.NODE_ENV === 'development' &&
257
+ !isDevProxyContext()
258
+ ) {
253
259
  if (!Underpost.tls.validateSecureContext()) shellExec(`node bin/deploy tls`);
254
260
  const { ServerSSL } = await Underpost.tls.createSslServer(app);
255
261
  await Underpost.start.listenPortController(ServerSSL, port, runningData);
@@ -21,11 +21,11 @@ const logger = loggerFactory(import.meta);
21
21
  class LamppService {
22
22
  /**
23
23
  * @method
24
- * @type {string | undefined}
24
+ * @type {string}
25
25
  * @description Stores the accumulated Apache virtual host configuration (router definition).
26
26
  * @memberof LamppService
27
27
  */
28
- router;
28
+ router = '';
29
29
 
30
30
  /**
31
31
  * @public
@@ -42,7 +42,7 @@ class LamppService {
42
42
  * @memberof LamppService
43
43
  */
44
44
  constructor() {
45
- this.router = undefined;
45
+ this.router = '';
46
46
  this.ports = [];
47
47
  }
48
48
 
@@ -125,7 +125,6 @@ class LamppService {
125
125
 
126
126
  // 6. Start the service
127
127
  cmd = `sudo /opt/lampp/lampp start`;
128
- if (this.router) fs.writeFileSync(`./tmp/lampp-router.conf`, this.router, 'utf-8');
129
128
  shellExec(cmd);
130
129
  }
131
130
 
@@ -139,13 +138,7 @@ class LamppService {
139
138
  * @memberof LamppService
140
139
  */
141
140
  appendRouter(render) {
142
- if (!this.router) {
143
- if (fs.existsSync(`./tmp/lampp-router.conf`)) {
144
- this.router = fs.readFileSync(`./tmp/lampp-router.conf`, 'utf-8');
145
- return this.router + render;
146
- }
147
- return (this.router = render);
148
- }
141
+ if (!this.router) return (this.router = render);
149
142
  return (this.router += render);
150
143
  }
151
144
 
@@ -154,10 +147,10 @@ class LamppService {
154
147
  *
155
148
  * @memberof LamppService
156
149
  * @returns {void}
150
+ * @method removeRouter
157
151
  */
158
152
  removeRouter() {
159
- this.router = undefined;
160
- if (fs.existsSync(`./tmp/lampp-router.conf`)) fs.rmSync(`./tmp/lampp-router.conf`);
153
+ this.router = '';
161
154
  }
162
155
 
163
156
  /**
@@ -4,7 +4,6 @@
4
4
  * @namespace Auth
5
5
  */
6
6
 
7
- import dotenv from 'dotenv';
8
7
  import jwt from 'jsonwebtoken';
9
8
  import { loggerFactory } from './logger.js';
10
9
  import crypto from 'crypto';
@@ -19,7 +18,6 @@ import cookieParser from 'cookie-parser';
19
18
  import { DataBaseProvider } from '../db/DataBaseProvider.js';
20
19
  import { isDevProxyContext } from './conf.js';
21
20
 
22
- dotenv.config();
23
21
  const logger = loggerFactory(import.meta);
24
22
 
25
23
  // Promisified crypto functions
@@ -349,12 +347,7 @@ const cookieOptionsFactory = (req, host) => {
349
347
  secure,
350
348
  sameSite,
351
349
  path: '/',
352
- domain:
353
- process.env.NODE_ENV === 'production' ||
354
- isDevProxyContext() ||
355
- (req.headers.host && req.headers.host.toLocaleLowerCase().match(host))
356
- ? host
357
- : 'localhost',
350
+ domain: process.env.NODE_ENV === 'production' || isDevProxyContext() ? host : 'localhost',
358
351
  maxAge,
359
352
  };
360
353
 
@@ -389,7 +382,11 @@ async function createSessionAndUserToken(user, User, req, res, options = { host:
389
382
  };
390
383
 
391
384
  // push session
392
- const updatedUser = await User.findByIdAndUpdate(user._id, { $push: { activeSessions: newSession } }, { new: true });
385
+ const updatedUser = await User.findByIdAndUpdate(
386
+ user._id,
387
+ { $push: { activeSessions: newSession } },
388
+ { returnDocument: 'after' },
389
+ );
393
390
  const session = updatedUser.activeSessions[updatedUser.activeSessions.length - 1];
394
391
  const jwtid = session._id.toString();
395
392
 
@@ -7,10 +7,8 @@
7
7
  import fs from 'fs-extra';
8
8
  import { loggerFactory } from './logger.js';
9
9
  import { shellExec } from './process.js';
10
- import dotenv from 'dotenv';
11
10
  import Underpost from '../index.js';
12
-
13
- dotenv.config();
11
+ import { loadCronDeployEnv } from './conf.js';
14
12
 
15
13
  const logger = loggerFactory(import.meta);
16
14
 
@@ -33,6 +31,7 @@ class BackUp {
33
31
  * @memberof UnderpostBakcUp
34
32
  */
35
33
  static callback = async function (deployList, options = { git: false }) {
34
+ loadCronDeployEnv();
36
35
  if ((!deployList || deployList === 'dd') && fs.existsSync(`./engine-private/deploy/dd.router`))
37
36
  deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').trim();
38
37