underpost 2.8.884 → 2.8.886

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 +3 -0
  2. package/.github/workflows/ghpkg.ci.yml +1 -1
  3. package/.github/workflows/npmpkg.ci.yml +1 -1
  4. package/.github/workflows/publish.ci.yml +5 -5
  5. package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -1
  6. package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
  7. package/CHANGELOG.md +145 -1
  8. package/Dockerfile +1 -1
  9. package/README.md +5 -121
  10. package/bin/build.js +18 -9
  11. package/bin/deploy.js +102 -197
  12. package/bin/file.js +4 -6
  13. package/cli.md +16 -12
  14. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  15. package/manifests/deployment/dd-test-development/deployment.yaml +54 -54
  16. package/manifests/deployment/dd-test-development/proxy.yaml +4 -4
  17. package/manifests/lxd/underpost-setup.sh +5 -5
  18. package/package.json +3 -3
  19. package/scripts/ssl.sh +164 -0
  20. package/src/cli/baremetal.js +7 -7
  21. package/src/cli/cloud-init.js +1 -1
  22. package/src/cli/cluster.js +31 -3
  23. package/src/cli/cron.js +9 -1
  24. package/src/cli/db.js +64 -2
  25. package/src/cli/deploy.js +189 -4
  26. package/src/cli/env.js +43 -0
  27. package/src/cli/fs.js +96 -2
  28. package/src/cli/image.js +15 -0
  29. package/src/cli/index.js +17 -4
  30. package/src/cli/monitor.js +33 -2
  31. package/src/cli/repository.js +95 -2
  32. package/src/cli/run.js +315 -51
  33. package/src/cli/script.js +32 -0
  34. package/src/cli/secrets.js +34 -0
  35. package/src/cli/test.js +42 -1
  36. package/src/client/components/core/Css.js +16 -8
  37. package/src/client/components/core/Docs.js +5 -13
  38. package/src/client/components/core/Modal.js +48 -29
  39. package/src/client/components/core/Router.js +6 -3
  40. package/src/client/components/core/Worker.js +205 -118
  41. package/src/client/components/core/windowGetDimensions.js +229 -162
  42. package/src/client/components/default/MenuDefault.js +1 -0
  43. package/src/client.dev.js +6 -3
  44. package/src/db/DataBaseProvider.js +65 -12
  45. package/src/db/mariadb/MariaDB.js +39 -6
  46. package/src/db/mongo/MongooseDB.js +51 -133
  47. package/src/index.js +2 -2
  48. package/src/mailer/EmailRender.js +58 -9
  49. package/src/mailer/MailerProvider.js +99 -25
  50. package/src/runtime/express/Express.js +32 -38
  51. package/src/runtime/lampp/Dockerfile +1 -1
  52. package/src/server/auth.js +9 -28
  53. package/src/server/backup.js +20 -0
  54. package/src/server/client-build-live.js +23 -12
  55. package/src/server/client-build.js +136 -91
  56. package/src/server/client-dev-server.js +35 -8
  57. package/src/server/client-icons.js +19 -0
  58. package/src/server/conf.js +543 -80
  59. package/src/server/dns.js +184 -42
  60. package/src/server/downloader.js +65 -24
  61. package/src/server/object-layer.js +260 -162
  62. package/src/server/peer.js +3 -9
  63. package/src/server/proxy.js +93 -76
  64. package/src/server/runtime.js +15 -21
  65. package/src/server/ssr.js +4 -4
  66. package/src/server/start.js +39 -0
  67. package/src/server/tls.js +251 -0
  68. package/src/server/valkey.js +11 -10
  69. package/src/ws/IoInterface.js +133 -39
  70. package/src/ws/IoServer.js +80 -31
  71. package/src/ws/core/core.ws.connection.js +50 -16
  72. package/src/ws/core/core.ws.emit.js +47 -8
  73. package/src/ws/core/core.ws.server.js +62 -10
  74. package/manifests/maas/lxd-preseed.yaml +0 -32
  75. package/src/server/ssl.js +0 -108
  76. /package/{manifests/maas → scripts}/device-scan.sh +0 -0
  77. /package/{manifests/maas → scripts}/gpu-diag.sh +0 -0
  78. /package/{manifests/maas → scripts}/maas-setup.sh +0 -0
  79. /package/{manifests/maas → scripts}/nat-iptables.sh +0 -0
  80. /package/{manifests/maas → scripts}/nvim.sh +0 -0
  81. /package/{manifests/maas → scripts}/snap-clean.sh +0 -0
  82. /package/{manifests/maas → scripts}/ssh-cluster-info.sh +0 -0
@@ -1,152 +1,70 @@
1
1
  import mongoose from 'mongoose';
2
2
  import { loggerFactory } from '../../server/logger.js';
3
3
  import { getCapVariableName } from '../../client/components/core/CommonJs.js';
4
- import { shellCd, shellExec } from '../../server/process.js';
4
+
5
+ /**
6
+ * Module for connecting to and loading models for a MongoDB database using Mongoose.
7
+ * @module src/db/MongooseDB.js
8
+ * @namespace MongooseDBNamespace
9
+ */
5
10
 
6
11
  const logger = loggerFactory(import.meta);
7
12
 
8
- const MongooseDB = {
9
- connect: async (host, name) => {
13
+ /**
14
+ * @class
15
+ * @alias MongooseDBService
16
+ * @memberof MongooseDBNamespace
17
+ * @classdesc Manages the Mongoose connection lifecycle and dynamic loading of database models
18
+ * based on API configuration.
19
+ */
20
+ class MongooseDBService {
21
+ /**
22
+ * Establishes a Mongoose connection to the specified MongoDB instance.
23
+ *
24
+ * @async
25
+ * @param {string} host - The MongoDB host (e.g., 'mongodb://localhost:27017').
26
+ * @param {string} name - The database name.
27
+ * @returns {Promise<mongoose.Connection>} A promise that resolves to the established Mongoose connection object.
28
+ */
29
+ async connect(host, name) {
10
30
  const uri = `${host}/${name}`;
11
- // logger.info('MongooseDB connect', { host, name, uri });
31
+ logger.info('MongooseDB connect', { host, name, uri });
12
32
  return await mongoose
13
33
  .createConnection(uri, {
14
- // useNewUrlParser: true,
15
- // useUnifiedTopology: true,
34
+ // Options like useNewUrlParser and useUnifiedTopology are often set here.
16
35
  })
17
36
  .asPromise();
18
- return new Promise((resolve, reject) =>
19
- mongoose
20
- .connect(
21
- uri,
22
- // ,{
23
- // useNewUrlParser: true,
24
- // useUnifiedTopology: true,
25
- // }
26
- )
27
- .then((db) => {
28
- logger.info(`db connected`, uri);
29
- return resolve(db);
30
- })
31
- .catch((err) => {
32
- logger.error(err, { host, name, error: err.stack });
33
- // return reject(err);
34
- return resolve(undefined);
35
- }),
36
- );
37
- },
38
- loadModels: async function (options = { apis: ['test'], conn: new mongoose.Connection() }) {
37
+ }
38
+
39
+ /**
40
+ * Dynamically loads Mongoose models for a list of APIs and binds them to the given connection.
41
+ *
42
+ * @async
43
+ * @param {object} [options] - Options for model loading.
44
+ * @param {Array<string>} [options.apis=['test']] - List of API names (folders) to load models from.
45
+ * @param {mongoose.Connection} [options.conn=new mongoose.Connection()] - The active Mongoose connection.
46
+ * @returns {Promise<object>} A promise that resolves to an object map of loaded Mongoose models.
47
+ */
48
+ async loadModels(options = { apis: ['test'], conn: new mongoose.Connection() }) {
39
49
  const { conn, apis } = options;
40
50
  const models = {};
41
51
  for (const api of apis) {
52
+ // Dynamic import of the model file
42
53
  const { ProviderSchema } = await import(`../../api/${api}/${api}.model.js`);
43
- const keyModel = getCapVariableName(api);
54
+ const keyModel = getCapVariableName(api); // Assuming this returns a capitalized model name
44
55
  models[keyModel] = conn.model(keyModel, ProviderSchema);
45
56
  }
46
57
 
47
58
  return models;
48
- },
49
- server: async function () {
50
- logger.info('platform', process.platform);
51
- switch (process.platform) {
52
- case 'win32':
53
- {
54
- // https://www.mongodb.com/docs/v7.0/tutorial/install-mongodb-on-windows-unattended/
55
-
56
- // C:\Program Files\MongoDB\Tools\100\bin
57
-
58
- const urlDownload = `https://fastdl.mongodb.org/windows/mongodb-windows-x86_64-7.0.14-signed.msi`;
59
- const folderPath = `./engine-private/setup`;
60
- if (!fs.existsSync(folderPath)) fs.mkdirSync(folderPath, { recursive: true });
61
- const fullPath = `${folderPath}/${urlDownload.split('/').pop()}`;
62
- logger.info('destination', fullPath);
63
- shellCd(folderPath);
64
- }
65
- break;
66
- case 'linux':
67
- {
68
- if (!process.argv.includes('server')) {
69
- logger.info('remove');
70
- shellExec(`sudo apt-get purge mongodb-org*`);
71
- shellExec(`sudo rm -r /var/log/mongodb`);
72
- shellExec(`sudo rm -r /var/lib/mongodb`);
73
- // restore lib
74
- // shellExec(`sudo chown -R mongodb:mongodb /var/lib/mongodb/*`);
75
- // mongod --repair
76
-
77
- if (process.argv.includes('legacy')) {
78
- // TODO:
79
- if (process.argv.includes('rocky')) {
80
- // https://github.com/mongodb/mongodb-selinux
81
- // https://www.mongodb.com/docs/v7.0/tutorial/install-mongodb-enterprise-on-red-hat/
82
- // https://www.mongodb.com/docs/v6.0/tutorial/install-mongodb-on-red-hat/
83
- // https://www.mongodb.com/docs/v4.4/tutorial/install-mongodb-on-red-hat/
84
- // dnf install selinux-policy-devel
85
- // git clone https://github.com/mongodb/mongodb-selinux
86
- // cd mongodb-selinux
87
- // make
88
- // sudo make install
89
- // yum list installed | grep mongo
90
- // sudo yum erase $(rpm -qa | grep mongodb)
91
- // remove service
92
- // sudo systemctl reset-failed
93
- // MongoDB 5.0+ requires a CPU with AVX support
94
- // check: grep avx /proc/cpuinfo
95
- }
96
- logger.info('install legacy 4.4');
97
- shellExec(`wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | sudo apt-key add -`);
98
-
99
- shellExec(
100
- `echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/4.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.4.list`,
101
- );
102
-
103
- shellExec(`sudo apt-get update`);
104
-
105
- shellExec(
106
- `sudo apt-get install mongodb-org=4.4.8 mongodb-org-server=4.4.8 mongodb-org-shell=4.4.8 mongodb-org-mongos=4.4.8 mongodb-org-tools=4.4.8`,
107
- );
108
- } else {
109
- logger.info('install 7.0');
110
- shellExec(`curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc | \
111
- sudo gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg \
112
- --dearmor`);
113
- shellExec(
114
- `echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list`,
115
- );
116
-
117
- shellExec(`sudo apt-get update`);
118
-
119
- shellExec(`sudo apt-get install -y mongodb-org`);
120
- }
121
- }
122
- logger.info('clean server environment');
123
- shellExec(`sudo service mongod stop`);
124
- shellExec(`sudo systemctl unmask mongod`);
125
- shellExec(`sudo pkill -f mongod`);
126
- shellExec(`sudo systemctl enable mongod.service`);
127
-
128
- shellExec(`sudo chown -R mongodb:mongodb /var/lib/mongodb`);
129
- shellExec(`sudo chown mongodb:mongodb /tmp/mongodb-27017.sock`);
130
-
131
- shellExec(`sudo chown -R mongod:mongod /var/lib/mongodb`);
132
- shellExec(`sudo chown mongod:mongod /tmp/mongodb-27017.sock`);
133
-
134
- logger.info('run server');
135
- shellExec(`sudo service mongod restart`);
136
-
137
- const checkStatus = () => {
138
- logger.info('check status');
139
- shellExec(`sudo systemctl status mongod`);
140
- shellExec(`sudo systemctl --type=service | grep mongod`);
141
- };
142
-
143
- checkStatus();
144
- }
145
- break;
146
- default:
147
- break;
148
- }
149
- },
150
- };
151
-
152
- export { MongooseDB };
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Singleton instance of the MongooseDBService class for backward compatibility.
64
+ * @alias MongooseDB
65
+ * @memberof MongooseDBNamespace
66
+ * @type {MongooseDBService}
67
+ */
68
+ const MongooseDB = new MongooseDBService();
69
+
70
+ export { MongooseDB, MongooseDBService as MongooseDBClass };
package/src/index.js CHANGED
@@ -25,7 +25,7 @@ import UnderpostStartUp from './server/start.js';
25
25
 
26
26
  /**
27
27
  * Underpost main module methods
28
- * @class
28
+ * @class Underpost
29
29
  * @memberof Underpost
30
30
  */
31
31
  class Underpost {
@@ -35,7 +35,7 @@ class Underpost {
35
35
  * @type {String}
36
36
  * @memberof Underpost
37
37
  */
38
- static version = 'v2.8.884';
38
+ static version = 'v2.8.886';
39
39
  /**
40
40
  * Repository cli API
41
41
  * @static
@@ -1,7 +1,31 @@
1
1
  import { ssrFactory } from '../server/ssr.js';
2
2
 
3
- const EmailRender = {
4
- style: {
3
+ /**
4
+ * Module for handling the rendering and styling of HTML emails using SSR components.
5
+ * @module src/mailer/EmailRender.js
6
+ * @namespace EmailRenderNamespace
7
+ */
8
+
9
+ /**
10
+ * @class
11
+ * @alias EmailRenderService
12
+ * @memberof EmailRenderNamespace
13
+ * @classdesc Utility class for managing CSS styles and rendering email templates using
14
+ * Server-Side Rendering (SSR) components.
15
+ */
16
+ class EmailRenderService {
17
+ /**
18
+ * Defines the base CSS styles for different elements within the email template.
19
+ * Keys are CSS selectors (or class names), and values are objects of CSS properties.
20
+ * @type {object.<string, object.<string, string>>}
21
+ * @property {object} body - Styles for the main email body wrapper.
22
+ * @property {object} .container - Styles for the main content container.
23
+ * @property {object} h1 - Styles for primary headings.
24
+ * @property {object} p - Styles for standard paragraphs.
25
+ * @property {object} button - Styles for call-to-action buttons.
26
+ * @property {object} .footer - Styles for the email footer.
27
+ */
28
+ style = {
5
29
  body: {
6
30
  'font-family': 'Arial, sans-serif',
7
31
  'background-color': '#f4f4f4',
@@ -46,22 +70,47 @@ const EmailRender = {
46
70
  'font-size': '14px',
47
71
  color: '#999999',
48
72
  },
49
- },
50
- renderStyle: function (classObj) {
73
+ };
74
+
75
+ /**
76
+ * Converts a style object defined in the `this.style` property into a CSS style string.
77
+ *
78
+ * @param {string} classObj - The key corresponding to a style object in `this.style`.
79
+ * @returns {string} A string containing inline CSS properties (e.g., ` property: value;`).
80
+ */
81
+ renderStyle(classObj) {
82
+ if (!this.style[classObj]) return '';
51
83
  return Object.keys(this.style[classObj])
52
84
  .map((classKey) => ` ${classKey}: ${this.style[classObj][classKey]};`)
53
85
  .join(``);
54
- },
86
+ }
55
87
 
56
- getTemplates: async function (options = { templates: {} }) {
88
+ /**
89
+ * Loads and renders email templates using the SSR factory.
90
+ *
91
+ * @async
92
+ * @param {object} [options] - Options containing the template names.
93
+ * @param {object.<string, string>} [options.templates={}] - Map of template keys to their SSR component file names.
94
+ * @returns {Promise<object.<string, string>>} A promise that resolves to an object map of rendered HTML email strings.
95
+ */
96
+ async getTemplates(options = { templates: {} }) {
57
97
  const templates = {};
58
98
  for (const templateKey of Object.keys(options.templates)) {
59
99
  const ssrEmailComponent = options.templates[templateKey];
100
+ // Note: ssrFactory is assumed to load and return a functional component/function
60
101
  const SrrComponent = await ssrFactory(`./src/client/ssr/mailer/${ssrEmailComponent}.js`);
61
102
  templates[templateKey] = SrrComponent(this, options);
62
103
  }
63
104
  return templates;
64
- },
65
- };
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Singleton instance of the EmailRenderService class for backward compatibility.
110
+ * @alias EmailRender
111
+ * @memberof EmailRenderNamespace
112
+ * @type {EmailRenderService}
113
+ */
114
+ const EmailRender = new EmailRenderService();
66
115
 
67
- export { EmailRender };
116
+ export { EmailRender, EmailRenderService as EmailRenderClass };
@@ -2,11 +2,67 @@ import nodemailer from 'nodemailer';
2
2
  import { loggerFactory } from '../server/logger.js';
3
3
  import { EmailRender } from './EmailRender.js';
4
4
 
5
+ /**
6
+ * Module for configuring and sending emails using Nodemailer.
7
+ * @module src/mailer/MailerProvider.js
8
+ * @namespace MailerProviderNamespace
9
+ */
10
+
5
11
  const logger = loggerFactory(import.meta);
6
12
 
7
- const MailerProvider = {
8
- instance: {},
9
- load: async function (
13
+ /**
14
+ * @typedef {object} MailerOptions
15
+ * @property {string} id - Unique identifier for the mailer configuration.
16
+ * @property {string} [meta='mailer'] - Meta identifier for logging/context.
17
+ * @property {object} sender - The default sender details.
18
+ * @property {string} sender.email - The default sender email address.
19
+ * @property {string} sender.name - The default sender name.
20
+ * @property {object} transport - Nodemailer transport configuration.
21
+ * @property {string} transport.host - SMTP host.
22
+ * @property {number} [transport.port=587] - SMTP port.
23
+ * @property {boolean} [transport.secure=false] - Use TLS (true for 465, false for other ports).
24
+ * @property {object} transport.auth - Authentication details.
25
+ * @property {string} transport.auth.user - Username.
26
+ * @property {string} transport.auth.pass - Password.
27
+ * @property {string} [host=''] - Application host for context.
28
+ * @property {string} [path=''] - Application path for context.
29
+ * @property {object.<string, string>} templates - Map of template keys to SSR component file names.
30
+ * @memberof MailerProviderNamespace
31
+ */
32
+
33
+ /**
34
+ * @class
35
+ * @alias MailerProviderService
36
+ * @memberof MailerProviderNamespace
37
+ * @classdesc Manages multiple Nodemailer transporter instances and handles loading of
38
+ * email templates and sending emails.
39
+ */
40
+ class MailerProviderService {
41
+ /**
42
+ * Internal storage for mailer instances (transporters, options, templates), keyed by ID.
43
+ * @type {object.<string, object>}
44
+ * @private
45
+ */
46
+ #instance = {};
47
+
48
+ /**
49
+ * Retrieves the internal instance storage for direct access (used for backward compatibility).
50
+ * @returns {object.<string, object>} The internal mailer instance map.
51
+ */
52
+ get instance() {
53
+ return this.#instance;
54
+ }
55
+
56
+ /**
57
+ * Loads and initializes a new mailer provider instance using Nodemailer.
58
+ * The created instance is stored internally and includes the transporter and rendered templates.
59
+ *
60
+ * @async
61
+ * @param {MailerOptions} [options] - Configuration options for the mailer instance.
62
+ * @returns {Promise<object|undefined>} A promise that resolves to the initialized mailer instance
63
+ * object, or `undefined` on error.
64
+ */
65
+ async load(
10
66
  options = {
11
67
  id: '',
12
68
  meta: 'mailer',
@@ -33,18 +89,13 @@ const MailerProvider = {
33
89
  ) {
34
90
  try {
35
91
  options.transport.tls = {
36
- rejectUnauthorized: false,
92
+ rejectUnauthorized: false, // allows self-signed certs for local/dev
37
93
  };
38
94
  const { id } = options;
39
- // Generate test SMTP service account from ethereal.email
40
- // Only needed if you don't have a real mail account for testing
41
- // let testAccount = await nodemailer.createTestAccount();
42
95
 
43
- // create reusable transporter object using the default SMTP transport
44
96
  const transporter = nodemailer.createTransport(options.transport);
45
97
 
46
- // console.log('load logger', { url: options.meta });
47
- this.instance[id] = {
98
+ this.#instance[id] = {
48
99
  ...options,
49
100
  transporter,
50
101
  templates: await EmailRender.getTemplates(options),
@@ -87,13 +138,28 @@ const MailerProvider = {
87
138
  },
88
139
  };
89
140
 
90
- return this.instance[id];
141
+ return this.#instance[id];
91
142
  } catch (error) {
92
143
  logger.error(error, error.stack);
93
144
  return undefined;
94
145
  }
95
- },
96
- send: async function (
146
+ }
147
+
148
+ /**
149
+ * Sends an email using a previously loaded transporter instance.
150
+ *
151
+ * @async
152
+ * @param {object} [options] - Options for sending the email.
153
+ * @param {string} options.id - The ID of the mailer instance/transporter to use.
154
+ * @param {object} options.sendOptions - Nodemailer mail options.
155
+ * @param {string} [options.sendOptions.from] - Sender address (defaults to loaded instance sender).
156
+ * @param {string} options.sendOptions.to - List of receivers (comma-separated).
157
+ * @param {string} options.sendOptions.subject - Subject line.
158
+ * @param {string} [options.sendOptions.text] - Plain text body.
159
+ * @param {string} [options.sendOptions.html] - HTML body.
160
+ * @returns {Promise<object|undefined>} A promise that resolves to the Nodemailer `info` object, or `undefined` on error.
161
+ */
162
+ async send(
97
163
  options = {
98
164
  id: '',
99
165
  sendOptions: {
@@ -114,26 +180,34 @@ const MailerProvider = {
114
180
  ) {
115
181
  try {
116
182
  const { id, sendOptions } = options;
117
- if (!sendOptions.from) sendOptions.from = `${this.instance[id].sender.name} <${this.instance[id].sender.email}>`;
183
+ const instance = this.#instance[id];
118
184
 
119
- // send mail with defined transport object
120
- const info = await this.instance[id].transporter.sendMail(sendOptions);
185
+ if (!instance) {
186
+ logger.error(`Mailer instance with ID '${id}' not loaded.`);
187
+ return undefined;
188
+ }
121
189
 
122
- // console.log('Message sent: %s', info.messageId);
123
- // logger.info('Message sent', info);
190
+ if (!sendOptions.from) sendOptions.from = `${instance.sender.name} <${instance.sender.email}>`;
124
191
 
125
- // Message sent: <b658f8ca-6296-ccf4-8306-87d57a0b4321@example.com>
192
+ // send mail with defined transport object
193
+ const info = await instance.transporter.sendMail(sendOptions);
126
194
 
127
- // Preview only available when sending through an Ethereal account
128
- // console.log("Preview URL: %s", nodemailer.getTestMessageUrl(info));
129
- // Preview URL: https://ethereal.email/message/WaQKMgKddxQDoou...
195
+ // logger.info('Message sent', info);
130
196
 
131
197
  return info;
132
198
  } catch (error) {
133
199
  logger.error(error, error.stack);
134
200
  return undefined;
135
201
  }
136
- },
137
- };
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Singleton instance of the MailerProviderService class for backward compatibility.
207
+ * @alias MailerProvider
208
+ * @memberof MailerProviderNamespace
209
+ * @type {MailerProviderService}
210
+ */
211
+ const MailerProvider = new MailerProviderService();
138
212
 
139
- export { MailerProvider };
213
+ export { MailerProvider, MailerProviderService as MailerProviderClass };
@@ -21,6 +21,8 @@ import { createPeerServer } from '../../server/peer.js';
21
21
  import { createValkeyConnection } from '../../server/valkey.js';
22
22
  import { applySecurity, authMiddlewareFactory } from '../../server/auth.js';
23
23
  import { ssrMiddlewareFactory } from '../../server/ssr.js';
24
+ import { TLS } from '../../server/tls.js';
25
+ import { shellExec } from '../../server/process.js';
24
26
 
25
27
  const logger = loggerFactory(import.meta);
26
28
 
@@ -43,6 +45,7 @@ class ExpressService {
43
45
  * @param {string[]} [config.apis] - A list of API names to load and attach routers for.
44
46
  * @param {string[]} config.origins - Allowed origins for CORS.
45
47
  * @param {string} [config.directory] - The directory for static files (if overriding default).
48
+ * @param {boolean} [config.useLocalSsl] - Whether to use local SSL for the instance.
46
49
  * @param {string} [config.ws] - The WebSocket server name to use.
47
50
  * @param {object} [config.mailer] - Mailer configuration.
48
51
  * @param {object} [config.db] - Database configuration.
@@ -50,7 +53,6 @@ class ExpressService {
50
53
  * @param {boolean} [config.peer] - Whether to enable the peer server.
51
54
  * @param {object} [config.valkey] - Valkey connection configuration.
52
55
  * @param {string} [config.apiBaseHost] - Base host for the API (if running separate API).
53
- * @param {number} [config.devApiPort] - The dynamically calculated development API port used for CORS in dev mode.
54
56
  * @param {string} config.redirectTarget - The full target URL for redirection (used if `redirect` is true).
55
57
  * @param {string} config.rootHostPath - The root path for public host assets (e.g., `/public/hostname`).
56
58
  * @param {object} config.confSSR - The SSR configuration object, used to look up Mailer templates.
@@ -66,6 +68,7 @@ class ExpressService {
66
68
  apis,
67
69
  origins,
68
70
  directory,
71
+ useLocalSsl,
69
72
  ws,
70
73
  mailer,
71
74
  db,
@@ -73,7 +76,6 @@ class ExpressService {
73
76
  peer,
74
77
  valkey,
75
78
  apiBaseHost,
76
- devApiPort, // New parameter for dev environment CORS
77
79
  redirectTarget,
78
80
  rootHostPath,
79
81
  confSSR,
@@ -113,7 +115,6 @@ class ExpressService {
113
115
 
114
116
  // Logging, Compression, and Body Parsers
115
117
  app.use(loggerMiddleware(import.meta));
116
- // Compression filter logic is correctly inlined here
117
118
  app.use(compression({ filter: (req, res) => !req.headers['x-no-compression'] && compression.filter(req, res) }));
118
119
  app.use(express.json({ limit: '100MB' }));
119
120
  app.use(express.urlencoded({ extended: true, limit: '20MB' }));
@@ -131,35 +132,6 @@ class ExpressService {
131
132
  // Static file serving
132
133
  app.use('/', express.static(directory ? directory : `.${rootHostPath}`));
133
134
 
134
- // Swagger path definition
135
- const swaggerJsonPath = `./public/${host}${path === '/' ? path : `${path}/`}swagger-output.json`;
136
- const swaggerPath = `${path === '/' ? `/api-docs` : `${path}/api-docs`}`;
137
-
138
- // Flag swagger requests before security middleware
139
- if (fs.existsSync(swaggerJsonPath)) {
140
- app.use(swaggerPath, (req, res, next) => {
141
- res.locals.isSwagger = true;
142
- next();
143
- });
144
- }
145
-
146
- // Security and CORS
147
- applySecurity(app, {
148
- origin: (origin, callback) => {
149
- // Use devApiPort if provided to calculate the allowed development CORS origin
150
- const devOrigin =
151
- apis && process.env.NODE_ENV === 'development' && devApiPort ? [`http://localhost:${devApiPort}`] : [];
152
-
153
- const allowedOrigins = origins.concat(devOrigin);
154
-
155
- if (!origin || allowedOrigins.includes(origin)) {
156
- callback(null, true);
157
- } else {
158
- callback(new Error('Not allowed by CORS'));
159
- }
160
- },
161
- });
162
-
163
135
  // Handle redirection-only instances
164
136
  if (redirect) {
165
137
  app.use((req, res, next) => {
@@ -174,9 +146,20 @@ class ExpressService {
174
146
 
175
147
  // Create HTTP server for regular instances (required for WebSockets)
176
148
  const server = createServer({}, app);
177
- if (peer) portsUsed++; // Peer server uses one additional port
178
149
 
179
150
  if (!apiBaseHost) {
151
+ // Swagger path definition
152
+ const swaggerJsonPath = `./public/${host}${path === '/' ? path : `${path}/`}swagger-output.json`;
153
+ const swaggerPath = `${path === '/' ? `/api-docs` : `${path}/api-docs`}`;
154
+
155
+ // Flag swagger requests before security middleware
156
+ if (fs.existsSync(swaggerJsonPath)) {
157
+ app.use(swaggerPath, (req, res, next) => {
158
+ res.locals.isSwagger = true;
159
+ next();
160
+ });
161
+ }
162
+
180
163
  // Swagger UI setup
181
164
  if (fs.existsSync(swaggerJsonPath)) {
182
165
  const swaggerDoc = JSON.parse(fs.readFileSync(swaggerJsonPath, 'utf8'));
@@ -184,6 +167,14 @@ class ExpressService {
184
167
  app.use(swaggerPath, swaggerUi.serve, swaggerUi.setup(swaggerDoc));
185
168
  }
186
169
 
170
+ // Security and CORS
171
+ if (process.env.NODE_ENV === 'development' && useLocalSsl)
172
+ origins = origins.map((origin) => origin.replace('http', 'https'));
173
+
174
+ applySecurity(app, {
175
+ origin: origins,
176
+ });
177
+
187
178
  // Database and Valkey connections
188
179
  if (db && apis) await DataBaseProvider.load({ apis, host, path, db });
189
180
  if (valkey) await createValkeyConnection({ host, path }, valkey);
@@ -216,10 +207,10 @@ class ExpressService {
216
207
  // WebSocket server setup
217
208
  if (ws) {
218
209
  const { createIoServer } = await import(`../../ws/${ws}/${ws}.ws.server.js`);
219
- const { options, meta } = await createIoServer(server, { host, path, db, port, origins });
210
+ const { options, meta, ioServer } = await createIoServer(server, { host, path, db, port, origins });
220
211
 
221
212
  // Listen on the main port for the WS server
222
- await UnderpostStartUp.API.listenPortController(UnderpostStartUp.API.listenServerFactory(), port, {
213
+ await UnderpostStartUp.API.listenPortController(ioServer, port, {
223
214
  runtime: 'nodejs',
224
215
  client: null,
225
216
  host,
@@ -230,12 +221,11 @@ class ExpressService {
230
221
 
231
222
  // Peer server setup
232
223
  if (peer) {
224
+ portsUsed++; // Peer server uses one additional port
233
225
  const peerPort = newInstance(port + portsUsed); // portsUsed is 1 here
234
226
  const { options, meta, peerServer } = await createPeerServer({
235
227
  port: peerPort,
236
- devPort: port,
237
228
  origins,
238
- host,
239
229
  path,
240
230
  });
241
231
  await UnderpostStartUp.API.listenPortController(peerServer, peerPort, {
@@ -253,7 +243,11 @@ class ExpressService {
253
243
  for (const [_, ssrMiddleware] of Object.entries(ssr)) app.use(ssrMiddleware);
254
244
 
255
245
  // Start listening on the main port
256
- await UnderpostStartUp.API.listenPortController(server, port, runningData);
246
+ if (useLocalSsl && process.env.NODE_ENV === 'development') {
247
+ if (!TLS.validateSecureContext()) shellExec(`node bin/deploy tls`);
248
+ const { ServerSSL } = await TLS.createSslServer(app);
249
+ await UnderpostStartUp.API.listenPortController(ServerSSL, port, runningData);
250
+ } else await UnderpostStartUp.API.listenPortController(server, port, runningData);
257
251
 
258
252
  return { portsUsed };
259
253
  }
@@ -32,7 +32,7 @@ RUN mkdir -p /opt/lampp/htdocs && \
32
32
  chmod -R a+rX /opt/lampp/htdocs
33
33
 
34
34
  # Install Node.js
35
- RUN curl -fsSL https://rpm.nodesource.com/setup_23.x | bash -
35
+ RUN curl -fsSL https://rpm.nodesource.com/setup_24.x | bash -
36
36
  RUN dnf install nodejs -y
37
37
  RUN dnf clean all
38
38