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.
- package/.env.production +3 -0
- package/.github/workflows/ghpkg.ci.yml +1 -1
- package/.github/workflows/npmpkg.ci.yml +1 -1
- package/.github/workflows/publish.ci.yml +5 -5
- package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -1
- package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
- package/CHANGELOG.md +145 -1
- package/Dockerfile +1 -1
- package/README.md +5 -121
- package/bin/build.js +18 -9
- package/bin/deploy.js +102 -197
- package/bin/file.js +4 -6
- package/cli.md +16 -12
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +54 -54
- package/manifests/deployment/dd-test-development/proxy.yaml +4 -4
- package/manifests/lxd/underpost-setup.sh +5 -5
- package/package.json +3 -3
- package/scripts/ssl.sh +164 -0
- package/src/cli/baremetal.js +7 -7
- package/src/cli/cloud-init.js +1 -1
- package/src/cli/cluster.js +31 -3
- package/src/cli/cron.js +9 -1
- package/src/cli/db.js +64 -2
- package/src/cli/deploy.js +189 -4
- package/src/cli/env.js +43 -0
- package/src/cli/fs.js +96 -2
- package/src/cli/image.js +15 -0
- package/src/cli/index.js +17 -4
- package/src/cli/monitor.js +33 -2
- package/src/cli/repository.js +95 -2
- package/src/cli/run.js +315 -51
- package/src/cli/script.js +32 -0
- package/src/cli/secrets.js +34 -0
- package/src/cli/test.js +42 -1
- package/src/client/components/core/Css.js +16 -8
- package/src/client/components/core/Docs.js +5 -13
- package/src/client/components/core/Modal.js +48 -29
- package/src/client/components/core/Router.js +6 -3
- package/src/client/components/core/Worker.js +205 -118
- package/src/client/components/core/windowGetDimensions.js +229 -162
- package/src/client/components/default/MenuDefault.js +1 -0
- package/src/client.dev.js +6 -3
- package/src/db/DataBaseProvider.js +65 -12
- package/src/db/mariadb/MariaDB.js +39 -6
- package/src/db/mongo/MongooseDB.js +51 -133
- package/src/index.js +2 -2
- package/src/mailer/EmailRender.js +58 -9
- package/src/mailer/MailerProvider.js +99 -25
- package/src/runtime/express/Express.js +32 -38
- package/src/runtime/lampp/Dockerfile +1 -1
- package/src/server/auth.js +9 -28
- package/src/server/backup.js +20 -0
- package/src/server/client-build-live.js +23 -12
- package/src/server/client-build.js +136 -91
- package/src/server/client-dev-server.js +35 -8
- package/src/server/client-icons.js +19 -0
- package/src/server/conf.js +543 -80
- package/src/server/dns.js +184 -42
- package/src/server/downloader.js +65 -24
- package/src/server/object-layer.js +260 -162
- package/src/server/peer.js +3 -9
- package/src/server/proxy.js +93 -76
- package/src/server/runtime.js +15 -21
- package/src/server/ssr.js +4 -4
- package/src/server/start.js +39 -0
- package/src/server/tls.js +251 -0
- package/src/server/valkey.js +11 -10
- package/src/ws/IoInterface.js +133 -39
- package/src/ws/IoServer.js +80 -31
- package/src/ws/core/core.ws.connection.js +50 -16
- package/src/ws/core/core.ws.emit.js +47 -8
- package/src/ws/core/core.ws.server.js +62 -10
- package/manifests/maas/lxd-preseed.yaml +0 -32
- package/src/server/ssl.js +0 -108
- /package/{manifests/maas → scripts}/device-scan.sh +0 -0
- /package/{manifests/maas → scripts}/gpu-diag.sh +0 -0
- /package/{manifests/maas → scripts}/maas-setup.sh +0 -0
- /package/{manifests/maas → scripts}/nat-iptables.sh +0 -0
- /package/{manifests/maas → scripts}/nvim.sh +0 -0
- /package/{manifests/maas → scripts}/snap-clean.sh +0 -0
- /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
|
-
|
|
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
|
-
|
|
9
|
-
|
|
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
|
-
|
|
31
|
+
logger.info('MongooseDB connect', { host, name, uri });
|
|
12
32
|
return await mongoose
|
|
13
33
|
.createConnection(uri, {
|
|
14
|
-
// useNewUrlParser
|
|
15
|
-
// useUnifiedTopology: true,
|
|
34
|
+
// Options like useNewUrlParser and useUnifiedTopology are often set here.
|
|
16
35
|
})
|
|
17
36
|
.asPromise();
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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.
|
|
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
|
-
|
|
4
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
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
|
|
141
|
+
return this.#instance[id];
|
|
91
142
|
} catch (error) {
|
|
92
143
|
logger.error(error, error.stack);
|
|
93
144
|
return undefined;
|
|
94
145
|
}
|
|
95
|
-
}
|
|
96
|
-
|
|
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
|
-
|
|
183
|
+
const instance = this.#instance[id];
|
|
118
184
|
|
|
119
|
-
|
|
120
|
-
|
|
185
|
+
if (!instance) {
|
|
186
|
+
logger.error(`Mailer instance with ID '${id}' not loaded.`);
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
121
189
|
|
|
122
|
-
|
|
123
|
-
// logger.info('Message sent', info);
|
|
190
|
+
if (!sendOptions.from) sendOptions.from = `${instance.sender.name} <${instance.sender.email}>`;
|
|
124
191
|
|
|
125
|
-
//
|
|
192
|
+
// send mail with defined transport object
|
|
193
|
+
const info = await instance.transporter.sendMail(sendOptions);
|
|
126
194
|
|
|
127
|
-
//
|
|
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(
|
|
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
|
-
|
|
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/
|
|
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
|
|