node-server-engine 1.2.0 → 1.2.1
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.example +17 -3
- package/README.md +64 -31
- package/dist/entities/Server/Server.js +0 -3
- package/dist/entities/Server/Server.js.map +1 -1
- package/dist/utils/sendEmail/sendEmail.d.ts +5 -9
- package/dist/utils/sendEmail/sendEmail.js +58 -23
- package/dist/utils/sendEmail/sendEmail.js.map +1 -1
- package/dist/utils/sendEmail/sendEmail.types.d.ts +50 -6
- package/dist/utils/sendEmail/sendEmail.types.js.map +1 -1
- package/package.json +3 -1
- package/src/entities/Server/Server.ts +0 -4
- package/src/utils/sendEmail/sendEmail.ts +58 -24
- package/src/utils/sendEmail/sendEmail.types.ts +60 -6
package/.env.example
CHANGED
|
@@ -118,9 +118,6 @@ PUBSUB_UPDATE_SUBSCRIPTION=
|
|
|
118
118
|
HASH_SECRET=
|
|
119
119
|
HASH_SALT=
|
|
120
120
|
|
|
121
|
-
#Base URL of the email service.
|
|
122
|
-
EMAIL_SERVICE_URL=
|
|
123
|
-
|
|
124
121
|
|
|
125
122
|
##########
|
|
126
123
|
# Elastic Search #
|
|
@@ -154,3 +151,20 @@ DB_MIGRATIONS_DIR=
|
|
|
154
151
|
|
|
155
152
|
# Default message Audience
|
|
156
153
|
DEFAULT_MESSAGE_AUDIENCE=
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
###########
|
|
157
|
+
# SMTP Configuration #
|
|
158
|
+
###########
|
|
159
|
+
|
|
160
|
+
# The SMTP server host (e.g., smtp.gmail.com, smtp.mailtrap.io)
|
|
161
|
+
SMTP_HOST=smtp.example.com
|
|
162
|
+
|
|
163
|
+
# The SMTP port (e.g., 587 for TLS, 465 for SSL, 25 for insecure)
|
|
164
|
+
SMTP_PORT=587
|
|
165
|
+
|
|
166
|
+
# SMTP authentication username (your email address or username)
|
|
167
|
+
SMTP_USER=your-email@example.com
|
|
168
|
+
|
|
169
|
+
# SMTP authentication password (your email password or app password)
|
|
170
|
+
SMTP_PASS=your-email-password
|
package/README.md
CHANGED
|
@@ -191,13 +191,13 @@ The server engine exposes an enumeration for auth types.
|
|
|
191
191
|
|
|
192
192
|
<!-- markdownlint-disable MD033 -->
|
|
193
193
|
|
|
194
|
-
| AuthType | Description
|
|
195
|
-
| --------------- |
|
|
196
|
-
| AuthType.NONE | No authentication. All requests are handled
|
|
197
|
-
| AuthType.JWT | A valid JSON Web Token is required as Bearer token. To be valid it must be properly signed by auth0, and its payload must match with what is set as environment variables.<br>The user's ID is added to `req.user`.
|
|
198
|
-
| AuthType.TLS | Authenticate through mutual TLS. CA and an optional list of white listed host names should be set in the environment variables.
|
|
199
|
-
| AuthType.HMAC | Authenticate with a signature in the payload.<br>**This authentication is deprecated and should be avoided**
|
|
200
|
-
| AuthType.STATIC | A valid shared Bearer token is required. Shared token is stored in env variable (STATIC_TOKEN)
|
|
194
|
+
| AuthType | Description | AuthParams |
|
|
195
|
+
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
196
|
+
| AuthType.NONE | No authentication. All requests are handled | |
|
|
197
|
+
| AuthType.JWT | A valid JSON Web Token is required as Bearer token. To be valid it must be properly signed by auth0, and its payload must match with what is set as environment variables.<br>The user's ID is added to `req.user`. | |
|
|
198
|
+
| AuthType.TLS | Authenticate through mutual TLS. CA and an optional list of white listed host names should be set in the environment variables. | **whitelist** [Array<String>]: List of certificate Common Name or Alt Name that are permitted to make requests to this endpoint. |
|
|
199
|
+
| AuthType.HMAC | Authenticate with a signature in the payload.<br>**This authentication is deprecated and should be avoided** | **secret** [String]: Overrides the secret used for signatures set in environment variables<br>**isGithub** [Boolean]: The request is a Github webhook and therefore uses their signature system and not the default one. |
|
|
200
|
+
| AuthType.STATIC | A valid shared Bearer token is required. Shared token is stored in env variable (STATIC_TOKEN) |
|
|
201
201
|
|
|
202
202
|
<!-- markdownlint-enable MD033 -->
|
|
203
203
|
|
|
@@ -519,12 +519,12 @@ All our custom error classes take a `data` parameter. This will be logged on the
|
|
|
519
519
|
|
|
520
520
|
These options are common to each of the error classes described below.
|
|
521
521
|
|
|
522
|
-
| option | definition | example
|
|
523
|
-
| -------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
524
|
-
| message | A message logged on the backend only | "Could not find user in the DB"
|
|
525
|
-
| severity | The level at which this error should be logged | [Severity](#severity).WARNING
|
|
526
|
-
| data | Some data related to the error that will be logged on the backend. This should help to undetrstand the runtime context. | {userId: 'xf563ugh0'}
|
|
527
|
-
| error | An error object, when this is a wrapper around an existing error object |
|
|
522
|
+
| option | definition | example | default |
|
|
523
|
+
| -------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ------------------------------ |
|
|
524
|
+
| message | A message logged on the backend only | "Could not find user in the DB" | **required** |
|
|
525
|
+
| severity | The level at which this error should be logged | [Severity](#severity).WARNING | [Severity](#severity).CRITICAL |
|
|
526
|
+
| data | Some data related to the error that will be logged on the backend. This should help to undetrstand the runtime context. | {userId: 'xf563ugh0'} | |
|
|
527
|
+
| error | An error object, when this is a wrapper around an existing error object | |
|
|
528
528
|
|
|
529
529
|
#### Severity
|
|
530
530
|
|
|
@@ -674,6 +674,7 @@ new Endpoint({
|
|
|
674
674
|
}
|
|
675
675
|
});
|
|
676
676
|
```
|
|
677
|
+
|
|
677
678
|
---
|
|
678
679
|
|
|
679
680
|
#### Gemini File Upload
|
|
@@ -684,8 +685,7 @@ The request must be made as `multipart/form-data`.
|
|
|
684
685
|
|
|
685
686
|
File should be uploaded under the key file.
|
|
686
687
|
|
|
687
|
-
The file's data will be available at
|
|
688
|
-
|
|
688
|
+
The file's data will be available at `req.body.fileUri req.body.mimeType req.body.originalname`
|
|
689
689
|
|
|
690
690
|
#### Check Permission Middleware
|
|
691
691
|
|
|
@@ -716,14 +716,18 @@ export const checkPermission = (requiredActions: string | string[]) => {
|
|
|
716
716
|
const user = req.user;
|
|
717
717
|
|
|
718
718
|
if (!user || !user.permissions) {
|
|
719
|
-
return res
|
|
719
|
+
return res
|
|
720
|
+
.status(403)
|
|
721
|
+
.json({ message: 'User does not have permissions' });
|
|
720
722
|
}
|
|
721
723
|
|
|
722
724
|
const requiredActionsArray = Array.isArray(requiredActions)
|
|
723
725
|
? requiredActions
|
|
724
726
|
: [requiredActions];
|
|
725
727
|
|
|
726
|
-
const requiredActionsLower = requiredActionsArray.map(action =>
|
|
728
|
+
const requiredActionsLower = requiredActionsArray.map((action) =>
|
|
729
|
+
action.toLowerCase()
|
|
730
|
+
);
|
|
727
731
|
|
|
728
732
|
const hasPermission = user.permissions.some((permission: string) =>
|
|
729
733
|
requiredActionsLower.includes(permission.toLowerCase())
|
|
@@ -813,27 +817,56 @@ await sendPush(userId, notification);
|
|
|
813
817
|
|
|
814
818
|
---
|
|
815
819
|
|
|
816
|
-
---
|
|
817
|
-
|
|
818
820
|
### Send Email
|
|
819
821
|
|
|
820
|
-
Send an email
|
|
822
|
+
Send an email using the SMTP configuration.
|
|
821
823
|
|
|
822
824
|
```javascript
|
|
823
825
|
import { sendEmail } from 'node-server-engine';
|
|
824
|
-
|
|
826
|
+
|
|
827
|
+
const emailOptions = {
|
|
828
|
+
to: 'recipient@example.com',
|
|
829
|
+
subject: 'Welcome to our service!',
|
|
830
|
+
text: 'Hello, welcome to our platform!',
|
|
831
|
+
html: '<h1>Welcome</h1><p>Glad to have you onboard!</p>',
|
|
832
|
+
attachments: [
|
|
833
|
+
{
|
|
834
|
+
filename: 'welcome.txt',
|
|
835
|
+
content: 'Welcome to our service!'
|
|
836
|
+
}
|
|
837
|
+
]
|
|
838
|
+
};
|
|
839
|
+
|
|
840
|
+
const result = await sendEmail(emailOptions);
|
|
841
|
+
console.log(result.status); // 'sent', 'delivered', 'queued', or 'failed'
|
|
825
842
|
```
|
|
826
843
|
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
|
830
|
-
|
|
|
831
|
-
|
|
|
832
|
-
|
|
|
833
|
-
|
|
|
834
|
-
|
|
|
835
|
-
|
|
|
836
|
-
|
|
|
844
|
+
### Parameters
|
|
845
|
+
|
|
846
|
+
| Parameter | Description |
|
|
847
|
+
| ------------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
848
|
+
| `from` | (Optional) Sender's email address. Defaults to the authenticated email. |
|
|
849
|
+
| `to` | Email recipient(s) as a string or array of strings. |
|
|
850
|
+
| `cc` | (Optional) Carbon Copy recipients (string or array). |
|
|
851
|
+
| `bcc` | (Optional) Blind Carbon Copy recipients (string or array). |
|
|
852
|
+
| `subject` | Subject of the email. |
|
|
853
|
+
| `text` | (Optional) Plain text version of the email body. |
|
|
854
|
+
| `html` | (Optional) HTML version of the email body. |
|
|
855
|
+
| `attachments` | (Optional) Array of attachments. Each attachment can include `filename`, `content`, `path`, and other properties. |
|
|
856
|
+
| `replyTo` | (Optional) Email address for replies. |
|
|
857
|
+
| `headers` | (Optional) Custom email headers. |
|
|
858
|
+
| `priority` | (Optional) Email priority (`high`, `normal`, or `low`). |
|
|
859
|
+
|
|
860
|
+
### Return Status
|
|
861
|
+
|
|
862
|
+
The function returns an object with the following status options:
|
|
863
|
+
|
|
864
|
+
| Status | Description |
|
|
865
|
+
| ----------- | ----------------------------------------------------------------------- |
|
|
866
|
+
| `sent` | Email was successfully sent but delivery confirmation is not available. |
|
|
867
|
+
| `delivered` | Email was successfully delivered. |
|
|
868
|
+
| `queued` | Email is queued for delivery but not yet sent. |
|
|
869
|
+
| `failed` | Email could not be sent due to an error. |
|
|
837
870
|
|
|
838
871
|
---
|
|
839
872
|
|
|
@@ -101,9 +101,6 @@ let Server = exports.Server = /*#__PURE__*/function () {
|
|
|
101
101
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
|
102
102
|
allowedHeaders: ['Content-Type', 'Authorization']
|
|
103
103
|
}));
|
|
104
|
-
this.app.options('*', (req, res) => {
|
|
105
|
-
res.sendStatus(204);
|
|
106
|
-
});
|
|
107
104
|
this.registerMiddleware(this.app);
|
|
108
105
|
this.httpServer = (0, _utils.createHttpServer)(this.app);
|
|
109
106
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Server.js","names":["_fs","_interopRequireDefault","require","_path","_promises","_express","_cors","_ws","_SocketClient","_Server2","_Endpoint","_EngineError","_LifecycleController","_PubSub","_utils","_jwt","_report","_tlsConfig","namespace","Server","exports","config","_classCallCheck2","default","_defineProperty2","AbortController","validate","reportDebug","message","endpoints","globalMiddleware","errorMiddleware","initCallbacks","shutdownCallbacks","syncCallbacks","cron","auth","noFetch","checkEnvironment","process","env","RUN_MIGRATION","UNDO_MIGRATION","SQL_HOST","envAssert","isHost","SQL_USER","isString","SQL_PASSWORD","SQL_DB","NODE_ENV","PORT","isPort","SECONDARY_PORT","app","express","use","cors","origin","CORS_ORIGIN","methods","allowedHeaders","options","req","res","sendStatus","registerMiddleware","httpServer","createHttpServer","secondaryApp","secondaryMiddleware","defaultErrorMiddleware","forEach","middleware","secondaryHttpServer","disable","server","headersTimeout","keepAliveTimeout","webSocket","attachWebSocket","_createClass2","key","value","init","initKeySets","callback","runCallback","Promise","all","map","PubSub","job","runCronJob","resolve","listen","reportInfo","data","port","secondaryPort","nodeEnv","handleSignal","watchTlsCaFileChange","shutdown","listening","address","on","error","reportError","exit","close","wss","clients","socket","socketKillTimeout","WEBSOCKET_CLOSE_TIMEOUT","parseInt","setTimeout","readyState","CLOSING","OPEN","terminate","LifecycleController","shutdownRunningInstances","abortController","abort","shutdownKeySets","shutdownSignals","signal","removeAllListeners","getApp","getSecondaryApp","defaultMiddleware","path","endpoint","register","WebSocketServer","request","SocketClient","client","catch","file","fs","watch","event","loadTlsConfig","setSecureContext","tlsConfig","handler","name","interval","setTimeoutPromise","undefined","Error","function","apply","parameters","some","Endpoint","EngineError","Object","values","validator","Array"],"sources":["../../../src/entities/Server/Server.ts"],"sourcesContent":["import fs from 'fs';\nimport http from 'http';\nimport https from 'https';\nimport { AddressInfo } from 'net';\nimport path from 'path';\nimport { setTimeout as setTimeoutPromise } from 'timers/promises';\nimport { ValidatorSpec } from 'envalid';\nimport express, { Application } from 'express';\nimport cors from 'cors';\nimport { Server as WebSocketServer } from 'ws';\nimport { SocketClient } from '../SocketClient';\nimport {\n defaultMiddleware,\n defaultErrorMiddleware,\n secondaryMiddleware,\n shutdownSignals\n} from './Server.const';\nimport {\n ServerOptions,\n ServerCronJon,\n ServerAuthConfig,\n ServerWebSocketConfig,\n ServerGlobalMiddleware,\n ServerErrorMiddleWare,\n ServerCallback\n} from './Server.types';\nimport { Endpoint } from 'entities/Endpoint';\nimport { EngineError } from 'entities/EngineError';\nimport { LifecycleController } from 'entities/LifecycleController';\nimport { PubSub } from 'entities/PubSub';\nimport { checkEnvironment, envAssert, createHttpServer } from 'utils';\nimport { initKeySets, shutdownKeySets } from 'utils/jwt';\nimport { reportInfo, reportError, reportDebug } from 'utils/report';\nimport { tlsConfig, loadTlsConfig, TlsConfig } from 'utils/tlsConfig';\n\nconst namespace = 'engine:Server';\n\n/** Main server class */\nexport class Server {\n /** The express application powering the main endpoints */\n private app: Application;\n /** The node HTTP(S) server, serving the main express application */\n private httpServer: http.Server | https.Server;\n /** The express application powering the support endpoints */\n private secondaryApp: Application;\n /** The node HTTP(S) server, serving the secondary express application */\n private secondaryHttpServer: http.Server | https.Server;\n /** List of endpoint instances that the server should handle */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private endpoints: Array<Endpoint<any>>;\n /** Mapping of env variable name with functions that should run to check their validity */\n private checkEnvironment: Record<string, ValidatorSpec<unknown>>;\n /** Middleware that should run on every request before the endpoint specific logic */\n private globalMiddleware: Array<ServerGlobalMiddleware>;\n /** Error middleware that should run on every request after the endpoint specific logic, but before the generic global middleware */\n private errorMiddleware: Array<\n ServerGlobalMiddleware | ServerErrorMiddleWare\n >;\n /** Functions that should be called on server startup */\n private initCallbacks: Array<ServerCallback>;\n /** Functions that should be called on server shutdown */\n private shutdownCallbacks: Array<ServerCallback>;\n /** When set to true, this indicates that init callbacks should run synchronously in the order they are declared. By default they all run in parallel */\n private syncCallbacks: boolean;\n /** Config to set regularly running jobs on the server */\n private cron?: Array<ServerCronJon>;\n /** Global auth related configuration */\n private auth: ServerAuthConfig;\n /** WebSocket server instance, if configured */\n private wss?: WebSocketServer;\n /** Abort controller that handles signals for the server */\n private abortController = new AbortController();\n\n /** Create a server */\n public constructor(config: ServerOptions) {\n this.validate(config);\n\n reportDebug({\n namespace,\n message: `Creating server`\n });\n\n this.endpoints = config.endpoints;\n this.globalMiddleware = config.globalMiddleware ?? [];\n this.errorMiddleware = config.errorMiddleware ?? [];\n this.initCallbacks = config.initCallbacks ?? [];\n this.shutdownCallbacks = config.shutdownCallbacks ?? [];\n this.syncCallbacks = config.syncCallbacks ?? false;\n this.cron = config.cron;\n this.auth = { noFetch: false, ...config.auth };\n\n // The minimal environment check is different between the migration modes of execution and the regular server execution mode\n // When running in migration mode, we only check that we have SQL related environment variables\n this.checkEnvironment =\n process.env.RUN_MIGRATION === 'true' ||\n process.env.UNDO_MIGRATION === 'true'\n ? {\n SQL_HOST: envAssert.isHost(),\n SQL_USER: envAssert.isString(),\n SQL_PASSWORD: envAssert.isString(),\n SQL_DB: envAssert.isString()\n }\n : {\n NODE_ENV: envAssert.isString(),\n PORT: envAssert.isPort(),\n SECONDARY_PORT: envAssert.isPort(),\n ...config.checkEnvironment\n };\n\n checkEnvironment(this.checkEnvironment);\n\n // Create the primary express core that serves the business logic\n this.app = express();\n\n this.app.use(\n cors({\n origin: process.env.CORS_ORIGIN ?? '*',\n methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],\n allowedHeaders: ['Content-Type', 'Authorization']\n })\n );\n\n this.app.options('*', (req, res) => {\n res.sendStatus(204);\n });\n\n this.registerMiddleware(this.app);\n this.httpServer = createHttpServer(this.app);\n\n // Create a secondary express core that servers metrics and analytics\n // This will be served on a different port, allowing to specify different firewall rules for these routes\n this.secondaryApp = express();\n [...secondaryMiddleware, ...defaultErrorMiddleware].forEach(\n (middleware) => {\n if (middleware) this.secondaryApp.use(middleware);\n }\n );\n this.secondaryHttpServer = createHttpServer(this.secondaryApp);\n\n // Remove the express headers\n this.app.disable('x-powered-by');\n this.secondaryApp.disable('x-powered-by');\n\n // We remove all node timeout on connections and requests\n // This responsibility is delegated to the front-facing load balancer\n [this.httpServer, this.secondaryHttpServer].forEach((server) => {\n server.headersTimeout = 0;\n server.keepAliveTimeout = 0;\n });\n\n if (config.webSocket) {\n this.attachWebSocket(config.webSocket);\n }\n }\n\n /** Start the server */\n public async init(): Promise<void> {\n reportDebug({ namespace, message: `Starting Server` });\n // Fetch the keys for JWT authentication\n // Ignore in test environment as we do symmetrical token signing\n if (process.env.NODE_ENV !== 'test' && !this.auth.noFetch)\n await initKeySets();\n // Run all the init callbacks\n // We can either run them all 1/1 synchronously or run them all in parallel (default)\n // Callback can either be direct function or an object with function and parameters attributes\n\n if (this.syncCallbacks) {\n for (const callback of this.initCallbacks) {\n await this.runCallback(callback);\n }\n } else {\n await Promise.all(\n this.initCallbacks.map((callback) => this.runCallback(callback))\n );\n }\n\n // Initialize the Pub/Sub module\n await PubSub.init();\n // Run all the cronjob\n if (this.cron) {\n this.cron.forEach((job) => {\n this.runCronJob(job);\n });\n }\n\n // Start listening to requests\n await Promise.all([\n new Promise<void>((resolve) =>\n this.httpServer.listen(process.env.PORT, () => {\n resolve();\n })\n ),\n new Promise<void>((resolve) =>\n this.secondaryHttpServer.listen(process.env.SECONDARY_PORT, () => {\n resolve();\n })\n )\n ]);\n reportInfo({\n message: 'SERVER_RUNNING',\n data: {\n port: process.env.PORT,\n secondaryPort: process.env.SECONDARY_PORT,\n nodeEnv: process.env.NODE_ENV\n }\n });\n\n this.handleSignal();\n\n // Watch TSL config files changing\n this.watchTlsCaFileChange();\n }\n\n /** Graceful shutdown */\n public async shutdown(): Promise<void> {\n reportDebug({ namespace, message: `Shutting down Server` });\n\n // Stop listening to HTTP requests\n await Promise.all(\n [this.httpServer, this.secondaryHttpServer].map((server) => {\n // Ignore if the server was already stopped\n // We have observed cases where SIGTERM was thrown multiple times\n if (!server.listening) return;\n const { port } = server.address() as AddressInfo;\n server.on('close', (error: unknown) => {\n if (error) {\n reportError(error);\n process.exit(1);\n } else {\n reportInfo({\n message: `Stopped listening to requests on port ${port}`\n });\n }\n });\n server.close();\n })\n );\n\n // Close all socket connections\n if (this.wss) {\n // First ask then to close gracefully\n this.wss.clients.forEach((socket) => {\n socket.close();\n });\n // Hard terminate all connections still open after a defined time\n const socketKillTimeout = process.env.WEBSOCKET_CLOSE_TIMEOUT\n ? parseInt(process.env.WEBSOCKET_CLOSE_TIMEOUT)\n : 5000;\n await new Promise<void>((resolve) => {\n setTimeout(() => {\n (this.wss as WebSocketServer).clients.forEach((socket) => {\n if (\n socket.readyState === socket.CLOSING ||\n socket.readyState === socket.OPEN\n ) {\n socket.terminate();\n }\n });\n resolve();\n }, socketKillTimeout);\n });\n }\n\n // Run all the shutdown callback\n if (this.syncCallbacks) {\n for (const callback of this.shutdownCallbacks) {\n await this.runCallback(callback);\n }\n } else {\n await Promise.all(\n this.shutdownCallbacks.map((callback) => this.runCallback(callback))\n );\n }\n\n // Shutdown all the running entities\n await LifecycleController.shutdownRunningInstances();\n\n // Abort all the cronjob\n if (this.abortController) this.abortController.abort();\n\n // Stop the keyset timers\n shutdownKeySets();\n\n shutdownSignals.forEach((signal) => process.removeAllListeners(signal));\n }\n\n /** Get the express app instance of the server */\n public getApp(): Application {\n return this.app;\n }\n\n /** Get the express app instance of the server */\n public getSecondaryApp(): Application {\n return this.secondaryApp;\n }\n\n /** Registers all the middleware with the express app */\n private registerMiddleware(app: Application): void {\n // Global middleware can specify a path to which they apply\n [...defaultMiddleware, ...this.globalMiddleware].forEach((middleware) => {\n if (typeof middleware === 'function') {\n app.use(middleware);\n } else {\n app.use(middleware.path, middleware.middleware);\n }\n });\n // Call the register method of each endpoint\n this.endpoints.forEach((endpoint) => {\n endpoint.register(app);\n });\n // Add error middleware\n [...this.errorMiddleware, ...defaultErrorMiddleware].forEach(\n (middleware) => {\n if (typeof middleware === 'function') {\n app.use(middleware);\n } else {\n app.use(middleware.path, middleware.middleware);\n }\n }\n );\n }\n\n /** Add a handler for WebSocket connections */\n private attachWebSocket(options: ServerWebSocketConfig): void {\n this.wss = new WebSocketServer({\n ...options.server,\n server: this.httpServer\n })\n .on('connection', (webSocket, request) => {\n new SocketClient(webSocket, request, options.client);\n })\n .on('error', (error) => {\n reportError(error);\n });\n }\n\n /** Call shutdown if signal gets sent to the process */\n private handleSignal(): void {\n shutdownSignals.forEach((signal) =>\n process.on(signal, () => {\n (async (): Promise<void> => {\n reportInfo(`Received shutdown signal [${signal}]`);\n await this.shutdown();\n })().catch((error) => {\n reportError(error);\n });\n })\n );\n }\n\n /** Watch the TLS certificate change event */\n private watchTlsCaFileChange(): void {\n [\n 'TLS_REQUEST_KEY',\n 'TLS_REQUEST_KEY_PASSPHRASE',\n 'TLS_REQUEST_CERT',\n 'TLS_REQUEST_CA',\n 'TLS_SERVER_KEY',\n 'TLS_SERVER_KEY_PASSPHRASE',\n 'TLS_SERVER_CERT',\n 'TLS_CA'\n ].forEach((key) => {\n const file = process.env[key];\n if (file)\n fs.watch(path.resolve(file), (event) => {\n if (event !== 'change') return;\n reportInfo(`Reloading TLS config [${file} changed]`);\n loadTlsConfig();\n (this.httpServer as https.Server).setSecureContext(\n tlsConfig as TlsConfig\n );\n });\n });\n }\n\n /** Executes a recurring job */\n private runCronJob(job: ServerCronJon): void {\n (async (): Promise<void> => {\n reportInfo({ message: `Starting scheduled job \"${job.handler.name}\"` });\n try {\n await job.handler();\n reportInfo({\n message: `Job \"${job.handler.name}\" finished, starting again in ${job.interval} seconds`\n });\n await setTimeoutPromise(job.interval * 1000, undefined, {\n signal: this.abortController.signal\n });\n this.runCronJob(job);\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n reportDebug({\n namespace,\n message: `Scheduled job \"${job.handler?.name}\" has been aborted`\n });\n } else {\n throw error;\n }\n }\n })().catch((error) => {\n reportError(error);\n });\n }\n\n /** Run a shutdown/init callback */\n private runCallback(callback: ServerCallback): unknown {\n return typeof callback === 'function'\n ? callback()\n : callback.function.apply(null, callback.parameters ?? []);\n }\n\n /**\n * Validate the Server constructor configuration\n */\n private validate(config: ServerOptions): void {\n if (\n !config.endpoints ||\n config.endpoints.some((endpoint) => !(endpoint instanceof Endpoint))\n )\n throw new EngineError(\n 'Server endpoints should be declared and all be Endpoint instances'\n );\n\n if (\n config.checkEnvironment &&\n Object.values(config.checkEnvironment).some(\n (validator) => typeof validator !== 'function'\n )\n )\n throw new EngineError(\n 'Some of the environment checks are not valid functions'\n );\n\n if (\n config.initCallbacks &&\n (!(config.initCallbacks instanceof Array) ||\n config.initCallbacks.some(\n (callback) =>\n !(\n typeof callback === 'function' ||\n typeof callback?.function === 'function'\n )\n ))\n )\n throw new EngineError('Init callbacks is not a list of functions');\n\n if (\n config.shutdownCallbacks &&\n (!(config.shutdownCallbacks instanceof Array) ||\n config.shutdownCallbacks.some(\n (callback) =>\n !(\n typeof callback === 'function' ||\n typeof callback?.function === 'function'\n )\n ))\n )\n throw new EngineError('Shutdown callbacks is not a list of functions');\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"Server.js","names":["_fs","_interopRequireDefault","require","_path","_promises","_express","_cors","_ws","_SocketClient","_Server2","_Endpoint","_EngineError","_LifecycleController","_PubSub","_utils","_jwt","_report","_tlsConfig","namespace","Server","exports","config","_classCallCheck2","default","_defineProperty2","AbortController","validate","reportDebug","message","endpoints","globalMiddleware","errorMiddleware","initCallbacks","shutdownCallbacks","syncCallbacks","cron","auth","noFetch","checkEnvironment","process","env","RUN_MIGRATION","UNDO_MIGRATION","SQL_HOST","envAssert","isHost","SQL_USER","isString","SQL_PASSWORD","SQL_DB","NODE_ENV","PORT","isPort","SECONDARY_PORT","app","express","use","cors","origin","CORS_ORIGIN","methods","allowedHeaders","registerMiddleware","httpServer","createHttpServer","secondaryApp","secondaryMiddleware","defaultErrorMiddleware","forEach","middleware","secondaryHttpServer","disable","server","headersTimeout","keepAliveTimeout","webSocket","attachWebSocket","_createClass2","key","value","init","initKeySets","callback","runCallback","Promise","all","map","PubSub","job","runCronJob","resolve","listen","reportInfo","data","port","secondaryPort","nodeEnv","handleSignal","watchTlsCaFileChange","shutdown","listening","address","on","error","reportError","exit","close","wss","clients","socket","socketKillTimeout","WEBSOCKET_CLOSE_TIMEOUT","parseInt","setTimeout","readyState","CLOSING","OPEN","terminate","LifecycleController","shutdownRunningInstances","abortController","abort","shutdownKeySets","shutdownSignals","signal","removeAllListeners","getApp","getSecondaryApp","defaultMiddleware","path","endpoint","register","options","WebSocketServer","request","SocketClient","client","catch","file","fs","watch","event","loadTlsConfig","setSecureContext","tlsConfig","handler","name","interval","setTimeoutPromise","undefined","Error","function","apply","parameters","some","Endpoint","EngineError","Object","values","validator","Array"],"sources":["../../../src/entities/Server/Server.ts"],"sourcesContent":["import fs from 'fs';\nimport http from 'http';\nimport https from 'https';\nimport { AddressInfo } from 'net';\nimport path from 'path';\nimport { setTimeout as setTimeoutPromise } from 'timers/promises';\nimport { ValidatorSpec } from 'envalid';\nimport express, { Application } from 'express';\nimport cors from 'cors';\nimport { Server as WebSocketServer } from 'ws';\nimport { SocketClient } from '../SocketClient';\nimport {\n defaultMiddleware,\n defaultErrorMiddleware,\n secondaryMiddleware,\n shutdownSignals\n} from './Server.const';\nimport {\n ServerOptions,\n ServerCronJon,\n ServerAuthConfig,\n ServerWebSocketConfig,\n ServerGlobalMiddleware,\n ServerErrorMiddleWare,\n ServerCallback\n} from './Server.types';\nimport { Endpoint } from 'entities/Endpoint';\nimport { EngineError } from 'entities/EngineError';\nimport { LifecycleController } from 'entities/LifecycleController';\nimport { PubSub } from 'entities/PubSub';\nimport { checkEnvironment, envAssert, createHttpServer } from 'utils';\nimport { initKeySets, shutdownKeySets } from 'utils/jwt';\nimport { reportInfo, reportError, reportDebug } from 'utils/report';\nimport { tlsConfig, loadTlsConfig, TlsConfig } from 'utils/tlsConfig';\n\nconst namespace = 'engine:Server';\n\n/** Main server class */\nexport class Server {\n /** The express application powering the main endpoints */\n private app: Application;\n /** The node HTTP(S) server, serving the main express application */\n private httpServer: http.Server | https.Server;\n /** The express application powering the support endpoints */\n private secondaryApp: Application;\n /** The node HTTP(S) server, serving the secondary express application */\n private secondaryHttpServer: http.Server | https.Server;\n /** List of endpoint instances that the server should handle */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private endpoints: Array<Endpoint<any>>;\n /** Mapping of env variable name with functions that should run to check their validity */\n private checkEnvironment: Record<string, ValidatorSpec<unknown>>;\n /** Middleware that should run on every request before the endpoint specific logic */\n private globalMiddleware: Array<ServerGlobalMiddleware>;\n /** Error middleware that should run on every request after the endpoint specific logic, but before the generic global middleware */\n private errorMiddleware: Array<\n ServerGlobalMiddleware | ServerErrorMiddleWare\n >;\n /** Functions that should be called on server startup */\n private initCallbacks: Array<ServerCallback>;\n /** Functions that should be called on server shutdown */\n private shutdownCallbacks: Array<ServerCallback>;\n /** When set to true, this indicates that init callbacks should run synchronously in the order they are declared. By default they all run in parallel */\n private syncCallbacks: boolean;\n /** Config to set regularly running jobs on the server */\n private cron?: Array<ServerCronJon>;\n /** Global auth related configuration */\n private auth: ServerAuthConfig;\n /** WebSocket server instance, if configured */\n private wss?: WebSocketServer;\n /** Abort controller that handles signals for the server */\n private abortController = new AbortController();\n\n /** Create a server */\n public constructor(config: ServerOptions) {\n this.validate(config);\n\n reportDebug({\n namespace,\n message: `Creating server`\n });\n\n this.endpoints = config.endpoints;\n this.globalMiddleware = config.globalMiddleware ?? [];\n this.errorMiddleware = config.errorMiddleware ?? [];\n this.initCallbacks = config.initCallbacks ?? [];\n this.shutdownCallbacks = config.shutdownCallbacks ?? [];\n this.syncCallbacks = config.syncCallbacks ?? false;\n this.cron = config.cron;\n this.auth = { noFetch: false, ...config.auth };\n\n // The minimal environment check is different between the migration modes of execution and the regular server execution mode\n // When running in migration mode, we only check that we have SQL related environment variables\n this.checkEnvironment =\n process.env.RUN_MIGRATION === 'true' ||\n process.env.UNDO_MIGRATION === 'true'\n ? {\n SQL_HOST: envAssert.isHost(),\n SQL_USER: envAssert.isString(),\n SQL_PASSWORD: envAssert.isString(),\n SQL_DB: envAssert.isString()\n }\n : {\n NODE_ENV: envAssert.isString(),\n PORT: envAssert.isPort(),\n SECONDARY_PORT: envAssert.isPort(),\n ...config.checkEnvironment\n };\n\n checkEnvironment(this.checkEnvironment);\n\n // Create the primary express core that serves the business logic\n this.app = express();\n\n this.app.use(\n cors({\n origin: process.env.CORS_ORIGIN ?? '*',\n methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],\n allowedHeaders: ['Content-Type', 'Authorization']\n })\n );\n\n this.registerMiddleware(this.app);\n this.httpServer = createHttpServer(this.app);\n\n // Create a secondary express core that servers metrics and analytics\n // This will be served on a different port, allowing to specify different firewall rules for these routes\n this.secondaryApp = express();\n [...secondaryMiddleware, ...defaultErrorMiddleware].forEach(\n (middleware) => {\n if (middleware) this.secondaryApp.use(middleware);\n }\n );\n this.secondaryHttpServer = createHttpServer(this.secondaryApp);\n\n // Remove the express headers\n this.app.disable('x-powered-by');\n this.secondaryApp.disable('x-powered-by');\n\n // We remove all node timeout on connections and requests\n // This responsibility is delegated to the front-facing load balancer\n [this.httpServer, this.secondaryHttpServer].forEach((server) => {\n server.headersTimeout = 0;\n server.keepAliveTimeout = 0;\n });\n\n if (config.webSocket) {\n this.attachWebSocket(config.webSocket);\n }\n }\n\n /** Start the server */\n public async init(): Promise<void> {\n reportDebug({ namespace, message: `Starting Server` });\n // Fetch the keys for JWT authentication\n // Ignore in test environment as we do symmetrical token signing\n if (process.env.NODE_ENV !== 'test' && !this.auth.noFetch)\n await initKeySets();\n // Run all the init callbacks\n // We can either run them all 1/1 synchronously or run them all in parallel (default)\n // Callback can either be direct function or an object with function and parameters attributes\n\n if (this.syncCallbacks) {\n for (const callback of this.initCallbacks) {\n await this.runCallback(callback);\n }\n } else {\n await Promise.all(\n this.initCallbacks.map((callback) => this.runCallback(callback))\n );\n }\n\n // Initialize the Pub/Sub module\n await PubSub.init();\n // Run all the cronjob\n if (this.cron) {\n this.cron.forEach((job) => {\n this.runCronJob(job);\n });\n }\n\n // Start listening to requests\n await Promise.all([\n new Promise<void>((resolve) =>\n this.httpServer.listen(process.env.PORT, () => {\n resolve();\n })\n ),\n new Promise<void>((resolve) =>\n this.secondaryHttpServer.listen(process.env.SECONDARY_PORT, () => {\n resolve();\n })\n )\n ]);\n reportInfo({\n message: 'SERVER_RUNNING',\n data: {\n port: process.env.PORT,\n secondaryPort: process.env.SECONDARY_PORT,\n nodeEnv: process.env.NODE_ENV\n }\n });\n\n this.handleSignal();\n\n // Watch TSL config files changing\n this.watchTlsCaFileChange();\n }\n\n /** Graceful shutdown */\n public async shutdown(): Promise<void> {\n reportDebug({ namespace, message: `Shutting down Server` });\n\n // Stop listening to HTTP requests\n await Promise.all(\n [this.httpServer, this.secondaryHttpServer].map((server) => {\n // Ignore if the server was already stopped\n // We have observed cases where SIGTERM was thrown multiple times\n if (!server.listening) return;\n const { port } = server.address() as AddressInfo;\n server.on('close', (error: unknown) => {\n if (error) {\n reportError(error);\n process.exit(1);\n } else {\n reportInfo({\n message: `Stopped listening to requests on port ${port}`\n });\n }\n });\n server.close();\n })\n );\n\n // Close all socket connections\n if (this.wss) {\n // First ask then to close gracefully\n this.wss.clients.forEach((socket) => {\n socket.close();\n });\n // Hard terminate all connections still open after a defined time\n const socketKillTimeout = process.env.WEBSOCKET_CLOSE_TIMEOUT\n ? parseInt(process.env.WEBSOCKET_CLOSE_TIMEOUT)\n : 5000;\n await new Promise<void>((resolve) => {\n setTimeout(() => {\n (this.wss as WebSocketServer).clients.forEach((socket) => {\n if (\n socket.readyState === socket.CLOSING ||\n socket.readyState === socket.OPEN\n ) {\n socket.terminate();\n }\n });\n resolve();\n }, socketKillTimeout);\n });\n }\n\n // Run all the shutdown callback\n if (this.syncCallbacks) {\n for (const callback of this.shutdownCallbacks) {\n await this.runCallback(callback);\n }\n } else {\n await Promise.all(\n this.shutdownCallbacks.map((callback) => this.runCallback(callback))\n );\n }\n\n // Shutdown all the running entities\n await LifecycleController.shutdownRunningInstances();\n\n // Abort all the cronjob\n if (this.abortController) this.abortController.abort();\n\n // Stop the keyset timers\n shutdownKeySets();\n\n shutdownSignals.forEach((signal) => process.removeAllListeners(signal));\n }\n\n /** Get the express app instance of the server */\n public getApp(): Application {\n return this.app;\n }\n\n /** Get the express app instance of the server */\n public getSecondaryApp(): Application {\n return this.secondaryApp;\n }\n\n /** Registers all the middleware with the express app */\n private registerMiddleware(app: Application): void {\n // Global middleware can specify a path to which they apply\n [...defaultMiddleware, ...this.globalMiddleware].forEach((middleware) => {\n if (typeof middleware === 'function') {\n app.use(middleware);\n } else {\n app.use(middleware.path, middleware.middleware);\n }\n });\n // Call the register method of each endpoint\n this.endpoints.forEach((endpoint) => {\n endpoint.register(app);\n });\n // Add error middleware\n [...this.errorMiddleware, ...defaultErrorMiddleware].forEach(\n (middleware) => {\n if (typeof middleware === 'function') {\n app.use(middleware);\n } else {\n app.use(middleware.path, middleware.middleware);\n }\n }\n );\n }\n\n /** Add a handler for WebSocket connections */\n private attachWebSocket(options: ServerWebSocketConfig): void {\n this.wss = new WebSocketServer({\n ...options.server,\n server: this.httpServer\n })\n .on('connection', (webSocket, request) => {\n new SocketClient(webSocket, request, options.client);\n })\n .on('error', (error) => {\n reportError(error);\n });\n }\n\n /** Call shutdown if signal gets sent to the process */\n private handleSignal(): void {\n shutdownSignals.forEach((signal) =>\n process.on(signal, () => {\n (async (): Promise<void> => {\n reportInfo(`Received shutdown signal [${signal}]`);\n await this.shutdown();\n })().catch((error) => {\n reportError(error);\n });\n })\n );\n }\n\n /** Watch the TLS certificate change event */\n private watchTlsCaFileChange(): void {\n [\n 'TLS_REQUEST_KEY',\n 'TLS_REQUEST_KEY_PASSPHRASE',\n 'TLS_REQUEST_CERT',\n 'TLS_REQUEST_CA',\n 'TLS_SERVER_KEY',\n 'TLS_SERVER_KEY_PASSPHRASE',\n 'TLS_SERVER_CERT',\n 'TLS_CA'\n ].forEach((key) => {\n const file = process.env[key];\n if (file)\n fs.watch(path.resolve(file), (event) => {\n if (event !== 'change') return;\n reportInfo(`Reloading TLS config [${file} changed]`);\n loadTlsConfig();\n (this.httpServer as https.Server).setSecureContext(\n tlsConfig as TlsConfig\n );\n });\n });\n }\n\n /** Executes a recurring job */\n private runCronJob(job: ServerCronJon): void {\n (async (): Promise<void> => {\n reportInfo({ message: `Starting scheduled job \"${job.handler.name}\"` });\n try {\n await job.handler();\n reportInfo({\n message: `Job \"${job.handler.name}\" finished, starting again in ${job.interval} seconds`\n });\n await setTimeoutPromise(job.interval * 1000, undefined, {\n signal: this.abortController.signal\n });\n this.runCronJob(job);\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n reportDebug({\n namespace,\n message: `Scheduled job \"${job.handler?.name}\" has been aborted`\n });\n } else {\n throw error;\n }\n }\n })().catch((error) => {\n reportError(error);\n });\n }\n\n /** Run a shutdown/init callback */\n private runCallback(callback: ServerCallback): unknown {\n return typeof callback === 'function'\n ? callback()\n : callback.function.apply(null, callback.parameters ?? []);\n }\n\n /**\n * Validate the Server constructor configuration\n */\n private validate(config: ServerOptions): void {\n if (\n !config.endpoints ||\n config.endpoints.some((endpoint) => !(endpoint instanceof Endpoint))\n )\n throw new EngineError(\n 'Server endpoints should be declared and all be Endpoint instances'\n );\n\n if (\n config.checkEnvironment &&\n Object.values(config.checkEnvironment).some(\n (validator) => typeof validator !== 'function'\n )\n )\n throw new EngineError(\n 'Some of the environment checks are not valid functions'\n );\n\n if (\n config.initCallbacks &&\n (!(config.initCallbacks instanceof Array) ||\n config.initCallbacks.some(\n (callback) =>\n !(\n typeof callback === 'function' ||\n typeof callback?.function === 'function'\n )\n ))\n )\n throw new EngineError('Init callbacks is not a list of functions');\n\n if (\n config.shutdownCallbacks &&\n (!(config.shutdownCallbacks instanceof Array) ||\n config.shutdownCallbacks.some(\n (callback) =>\n !(\n typeof callback === 'function' ||\n typeof callback?.function === 'function'\n )\n ))\n )\n throw new EngineError('Shutdown callbacks is not a list of functions');\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EmailOptions, EmailResult } from './sendEmail.types';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* @param
|
|
5
|
-
* @
|
|
6
|
-
* @param translationKey - Key used to get localized strings
|
|
7
|
-
* @param parameters - Variables that can be inserted in the email template and localized strings
|
|
8
|
-
* @param tags - Data injected in the localized strings as HTML tags
|
|
9
|
-
* @param to - Users to which the email should be sent
|
|
3
|
+
* Sends an email using Nodemailer.
|
|
4
|
+
* @param emailOptions - Configuration options for sending the email.
|
|
5
|
+
* @returns A Promise that resolves with an EmailResult indicating success or failure.
|
|
10
6
|
*/
|
|
11
|
-
export declare
|
|
7
|
+
export declare const sendEmail: (emailOptions: EmailOptions) => Promise<EmailResult>;
|
|
@@ -1,32 +1,67 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
3
4
|
Object.defineProperty(exports, "__esModule", {
|
|
4
5
|
value: true
|
|
5
6
|
});
|
|
6
|
-
exports.sendEmail =
|
|
7
|
-
var
|
|
7
|
+
exports.sendEmail = void 0;
|
|
8
|
+
var _nodemailer = _interopRequireDefault(require("nodemailer"));
|
|
8
9
|
/**
|
|
9
|
-
*
|
|
10
|
-
* @param
|
|
11
|
-
* @
|
|
12
|
-
* @param translationKey - Key used to get localized strings
|
|
13
|
-
* @param parameters - Variables that can be inserted in the email template and localized strings
|
|
14
|
-
* @param tags - Data injected in the localized strings as HTML tags
|
|
15
|
-
* @param to - Users to which the email should be sent
|
|
10
|
+
* Sends an email using Nodemailer.
|
|
11
|
+
* @param emailOptions - Configuration options for sending the email.
|
|
12
|
+
* @returns A Promise that resolves with an EmailResult indicating success or failure.
|
|
16
13
|
*/
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
14
|
+
const sendEmail = async emailOptions => {
|
|
15
|
+
try {
|
|
16
|
+
// Create a transport with SMTP configuration
|
|
17
|
+
const transporter = _nodemailer.default.createTransport({
|
|
18
|
+
host: process.env.SMTP_HOST,
|
|
19
|
+
port: parseInt(process.env.SMTP_PORT || '587', 10),
|
|
20
|
+
secure: process.env.SMTP_PORT === '465' ? true : false,
|
|
21
|
+
auth: {
|
|
22
|
+
user: process.env.SMTP_USER,
|
|
23
|
+
pass: process.env.SMTP_PASS
|
|
24
|
+
},
|
|
25
|
+
tls: {
|
|
26
|
+
rejectUnauthorized: process.env.SMTP_PORT === '465' ? true : false
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Construct email options
|
|
31
|
+
const mailOptions = {
|
|
32
|
+
from: emailOptions.from || 'your-email@example.com',
|
|
33
|
+
to: emailOptions.to,
|
|
34
|
+
cc: emailOptions.cc,
|
|
35
|
+
bcc: emailOptions.bcc,
|
|
36
|
+
subject: emailOptions.subject,
|
|
37
|
+
text: emailOptions.text,
|
|
38
|
+
html: emailOptions.html,
|
|
39
|
+
attachments: emailOptions.attachments,
|
|
40
|
+
replyTo: emailOptions.replyTo,
|
|
41
|
+
headers: emailOptions.headers,
|
|
42
|
+
priority: emailOptions.priority
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Send email
|
|
46
|
+
const info = await transporter.sendMail(mailOptions);
|
|
47
|
+
|
|
48
|
+
// Determine email status
|
|
49
|
+
let status = 'sent';
|
|
50
|
+
if (info.response.includes('queued')) {
|
|
51
|
+
status = 'queued';
|
|
52
|
+
} else if (info.response.includes('delivered')) {
|
|
53
|
+
status = 'delivered';
|
|
29
54
|
}
|
|
30
|
-
|
|
31
|
-
|
|
55
|
+
return {
|
|
56
|
+
status,
|
|
57
|
+
messageId: info.messageId
|
|
58
|
+
};
|
|
59
|
+
} catch (error) {
|
|
60
|
+
return {
|
|
61
|
+
status: 'failed',
|
|
62
|
+
error: error.message
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
exports.sendEmail = sendEmail;
|
|
32
67
|
//# sourceMappingURL=sendEmail.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sendEmail.js","names":["
|
|
1
|
+
{"version":3,"file":"sendEmail.js","names":["_nodemailer","_interopRequireDefault","require","sendEmail","emailOptions","transporter","nodemailer","createTransport","host","process","env","SMTP_HOST","port","parseInt","SMTP_PORT","secure","auth","user","SMTP_USER","pass","SMTP_PASS","tls","rejectUnauthorized","mailOptions","from","to","cc","bcc","subject","text","html","attachments","replyTo","headers","priority","info","sendMail","status","response","includes","messageId","error","message","exports"],"sources":["../../../src/utils/sendEmail/sendEmail.ts"],"sourcesContent":["import { EmailOptions, EmailResult, EmailStatus } from './sendEmail.types';\nimport nodemailer, {\n Transporter,\n SendMailOptions,\n SentMessageInfo\n} from 'nodemailer';\n\n/**\n * Sends an email using Nodemailer.\n * @param emailOptions - Configuration options for sending the email.\n * @returns A Promise that resolves with an EmailResult indicating success or failure.\n */\nexport const sendEmail = async (\n emailOptions: EmailOptions\n): Promise<EmailResult> => {\n try {\n // Create a transport with SMTP configuration\n const transporter: Transporter = nodemailer.createTransport({\n host: process.env.SMTP_HOST,\n port: parseInt(process.env.SMTP_PORT || '587', 10),\n secure: process.env.SMTP_PORT === '465' ? true : false,\n auth: {\n user: process.env.SMTP_USER,\n pass: process.env.SMTP_PASS\n },\n tls: {\n rejectUnauthorized: process.env.SMTP_PORT === '465' ? true : false\n }\n });\n\n // Construct email options\n const mailOptions: SendMailOptions = {\n from: emailOptions.from || 'your-email@example.com',\n to: emailOptions.to,\n cc: emailOptions.cc,\n bcc: emailOptions.bcc,\n subject: emailOptions.subject,\n text: emailOptions.text,\n html: emailOptions.html,\n attachments: emailOptions.attachments,\n replyTo: emailOptions.replyTo,\n headers: emailOptions.headers,\n priority: emailOptions.priority\n };\n\n // Send email\n const info: SentMessageInfo = await transporter.sendMail(mailOptions);\n\n // Determine email status\n let status: EmailStatus = 'sent';\n if (info.response.includes('queued')) {\n status = 'queued';\n } else if (info.response.includes('delivered')) {\n status = 'delivered';\n }\n\n return { status, messageId: info.messageId };\n } catch (error) {\n return { status: 'failed', error: (error as Error).message };\n }\n};\n"],"mappings":";;;;;;;AACA,IAAAA,WAAA,GAAAC,sBAAA,CAAAC,OAAA;AAMA;AACA;AACA;AACA;AACA;AACO,MAAMC,SAAS,GAAG,MACvBC,YAA0B,IACD;EACzB,IAAI;IACF;IACA,MAAMC,WAAwB,GAAGC,mBAAU,CAACC,eAAe,CAAC;MAC1DC,IAAI,EAAEC,OAAO,CAACC,GAAG,CAACC,SAAS;MAC3BC,IAAI,EAAEC,QAAQ,CAACJ,OAAO,CAACC,GAAG,CAACI,SAAS,IAAI,KAAK,EAAE,EAAE,CAAC;MAClDC,MAAM,EAAEN,OAAO,CAACC,GAAG,CAACI,SAAS,KAAK,KAAK,GAAG,IAAI,GAAG,KAAK;MACtDE,IAAI,EAAE;QACJC,IAAI,EAAER,OAAO,CAACC,GAAG,CAACQ,SAAS;QAC3BC,IAAI,EAAEV,OAAO,CAACC,GAAG,CAACU;MACpB,CAAC;MACDC,GAAG,EAAE;QACHC,kBAAkB,EAAEb,OAAO,CAACC,GAAG,CAACI,SAAS,KAAK,KAAK,GAAG,IAAI,GAAG;MAC/D;IACF,CAAC,CAAC;;IAEF;IACA,MAAMS,WAA4B,GAAG;MACnCC,IAAI,EAAEpB,YAAY,CAACoB,IAAI,IAAI,wBAAwB;MACnDC,EAAE,EAAErB,YAAY,CAACqB,EAAE;MACnBC,EAAE,EAAEtB,YAAY,CAACsB,EAAE;MACnBC,GAAG,EAAEvB,YAAY,CAACuB,GAAG;MACrBC,OAAO,EAAExB,YAAY,CAACwB,OAAO;MAC7BC,IAAI,EAAEzB,YAAY,CAACyB,IAAI;MACvBC,IAAI,EAAE1B,YAAY,CAAC0B,IAAI;MACvBC,WAAW,EAAE3B,YAAY,CAAC2B,WAAW;MACrCC,OAAO,EAAE5B,YAAY,CAAC4B,OAAO;MAC7BC,OAAO,EAAE7B,YAAY,CAAC6B,OAAO;MAC7BC,QAAQ,EAAE9B,YAAY,CAAC8B;IACzB,CAAC;;IAED;IACA,MAAMC,IAAqB,GAAG,MAAM9B,WAAW,CAAC+B,QAAQ,CAACb,WAAW,CAAC;;IAErE;IACA,IAAIc,MAAmB,GAAG,MAAM;IAChC,IAAIF,IAAI,CAACG,QAAQ,CAACC,QAAQ,CAAC,QAAQ,CAAC,EAAE;MACpCF,MAAM,GAAG,QAAQ;IACnB,CAAC,MAAM,IAAIF,IAAI,CAACG,QAAQ,CAACC,QAAQ,CAAC,WAAW,CAAC,EAAE;MAC9CF,MAAM,GAAG,WAAW;IACtB;IAEA,OAAO;MAAEA,MAAM;MAAEG,SAAS,EAAEL,IAAI,CAACK;IAAU,CAAC;EAC9C,CAAC,CAAC,OAAOC,KAAK,EAAE;IACd,OAAO;MAAEJ,MAAM,EAAE,QAAQ;MAAEI,KAAK,EAAGA,KAAK,CAAWC;IAAQ,CAAC;EAC9D;AACF,CAAC;AAACC,OAAA,CAAAxC,SAAA,GAAAA,SAAA","ignoreList":[]}
|
|
@@ -1,7 +1,51 @@
|
|
|
1
|
-
/** Structure representing an email
|
|
2
|
-
export interface
|
|
3
|
-
/**
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
|
|
1
|
+
/** Structure representing email options for sending an email */
|
|
2
|
+
export interface EmailOptions {
|
|
3
|
+
/** Sender's email address (optional, defaults to the authenticated user) */
|
|
4
|
+
from?: string;
|
|
5
|
+
/** Recipient(s) of the email - can be a string or an array of strings */
|
|
6
|
+
to: string | string[];
|
|
7
|
+
/** Carbon Copy (CC) recipients (optional) */
|
|
8
|
+
cc?: string | string[];
|
|
9
|
+
/** Blind Carbon Copy (BCC) recipients (optional) */
|
|
10
|
+
bcc?: string | string[];
|
|
11
|
+
/** Subject of the email */
|
|
12
|
+
subject: string;
|
|
13
|
+
/** Plain text version of the email body (optional) */
|
|
14
|
+
text?: string;
|
|
15
|
+
/** HTML version of the email body (optional) */
|
|
16
|
+
html?: string;
|
|
17
|
+
/** Attachments to include in the email (optional) */
|
|
18
|
+
attachments?: {
|
|
19
|
+
/** Name of the attached file */
|
|
20
|
+
filename: string;
|
|
21
|
+
/** Content of the attachment (Buffer or string) */
|
|
22
|
+
content?: string | Buffer;
|
|
23
|
+
/** File path of the attachment (alternative to `content`) */
|
|
24
|
+
path?: string;
|
|
25
|
+
/** MIME type of the attachment */
|
|
26
|
+
contentType?: string;
|
|
27
|
+
/** Encoding of the attachment (e.g., base64, utf-8) */
|
|
28
|
+
encoding?: string;
|
|
29
|
+
/** Content ID for inline images (optional) */
|
|
30
|
+
cid?: string;
|
|
31
|
+
}[];
|
|
32
|
+
/** Email address for replies (optional) */
|
|
33
|
+
replyTo?: string;
|
|
34
|
+
/** Custom headers for the email (optional) */
|
|
35
|
+
headers?: {
|
|
36
|
+
[key: string]: string;
|
|
37
|
+
};
|
|
38
|
+
/** Email priority level (optional) */
|
|
39
|
+
priority?: 'high' | 'normal' | 'low';
|
|
40
|
+
}
|
|
41
|
+
/** Possible statuses for email delivery */
|
|
42
|
+
export type EmailStatus = 'sent' | 'delivered' | 'queued' | 'failed';
|
|
43
|
+
/** Structure representing the result of an email sending operation */
|
|
44
|
+
export interface EmailResult {
|
|
45
|
+
/** Status of the email operation */
|
|
46
|
+
status: EmailStatus;
|
|
47
|
+
/** Message ID returned by the mail server (if available) */
|
|
48
|
+
messageId?: string;
|
|
49
|
+
/** Error message in case of failure (if any) */
|
|
50
|
+
error?: string;
|
|
7
51
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sendEmail.types.js","names":[],"sources":["../../../src/utils/sendEmail/sendEmail.types.ts"],"sourcesContent":["/** Structure representing an email
|
|
1
|
+
{"version":3,"file":"sendEmail.types.js","names":[],"sources":["../../../src/utils/sendEmail/sendEmail.types.ts"],"sourcesContent":["/** Structure representing email options for sending an email */\nexport interface EmailOptions {\n /** Sender's email address (optional, defaults to the authenticated user) */\n from?: string;\n\n /** Recipient(s) of the email - can be a string or an array of strings */\n to: string | string[];\n\n /** Carbon Copy (CC) recipients (optional) */\n cc?: string | string[];\n\n /** Blind Carbon Copy (BCC) recipients (optional) */\n bcc?: string | string[];\n\n /** Subject of the email */\n subject: string;\n\n /** Plain text version of the email body (optional) */\n text?: string;\n\n /** HTML version of the email body (optional) */\n html?: string;\n\n /** Attachments to include in the email (optional) */\n attachments?: {\n /** Name of the attached file */\n filename: string;\n /** Content of the attachment (Buffer or string) */\n content?: string | Buffer;\n /** File path of the attachment (alternative to `content`) */\n path?: string;\n /** MIME type of the attachment */\n contentType?: string;\n /** Encoding of the attachment (e.g., base64, utf-8) */\n encoding?: string;\n /** Content ID for inline images (optional) */\n cid?: string;\n }[];\n\n /** Email address for replies (optional) */\n replyTo?: string;\n\n /** Custom headers for the email (optional) */\n headers?: { [key: string]: string };\n\n /** Email priority level (optional) */\n priority?: 'high' | 'normal' | 'low';\n}\n\n/** Possible statuses for email delivery */\nexport type EmailStatus = 'sent' | 'delivered' | 'queued' | 'failed';\n\n/** Structure representing the result of an email sending operation */\nexport interface EmailResult {\n /** Status of the email operation */\n status: EmailStatus;\n /** Message ID returned by the mail server (if available) */\n messageId?: string;\n /** Error message in case of failure (if any) */\n error?: string;\n}\n"],"mappings":"","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-server-engine",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Framework used to develop backend services. This package ships with a lot of features to standardize the creation of services, letting you focus on the business logic.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
"multer": "^1.4.5-lts.1",
|
|
72
72
|
"mysql2": "^3.11.5",
|
|
73
73
|
"nock": "^13.5.6",
|
|
74
|
+
"nodemailer": "^6.10.0",
|
|
74
75
|
"nodemon": "^3.1.9",
|
|
75
76
|
"pg": "^8.13.1",
|
|
76
77
|
"prom-client": "^15.1.3",
|
|
@@ -119,6 +120,7 @@
|
|
|
119
120
|
"@types/morgan": "^1.9.9",
|
|
120
121
|
"@types/multer": "^1.4.12",
|
|
121
122
|
"@types/node": "^22.10.2",
|
|
123
|
+
"@types/nodemailer": "^6.4.17",
|
|
122
124
|
"@types/response-time": "^2.3.8",
|
|
123
125
|
"@types/sequelize": "^4.28.20",
|
|
124
126
|
"@types/sinon-chai": "^4.0.0",
|
|
@@ -1,27 +1,61 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { EmailOptions, EmailResult, EmailStatus } from './sendEmail.types';
|
|
2
|
+
import nodemailer, {
|
|
3
|
+
Transporter,
|
|
4
|
+
SendMailOptions,
|
|
5
|
+
SentMessageInfo
|
|
6
|
+
} from 'nodemailer';
|
|
3
7
|
|
|
4
8
|
/**
|
|
5
|
-
*
|
|
6
|
-
* @param
|
|
7
|
-
* @
|
|
8
|
-
* @param translationKey - Key used to get localized strings
|
|
9
|
-
* @param parameters - Variables that can be inserted in the email template and localized strings
|
|
10
|
-
* @param tags - Data injected in the localized strings as HTML tags
|
|
11
|
-
* @param to - Users to which the email should be sent
|
|
9
|
+
* Sends an email using Nodemailer.
|
|
10
|
+
* @param emailOptions - Configuration options for sending the email.
|
|
11
|
+
* @returns A Promise that resolves with an EmailResult indicating success or failure.
|
|
12
12
|
*/
|
|
13
|
-
export async
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
13
|
+
export const sendEmail = async (
|
|
14
|
+
emailOptions: EmailOptions
|
|
15
|
+
): Promise<EmailResult> => {
|
|
16
|
+
try {
|
|
17
|
+
// Create a transport with SMTP configuration
|
|
18
|
+
const transporter: Transporter = nodemailer.createTransport({
|
|
19
|
+
host: process.env.SMTP_HOST,
|
|
20
|
+
port: parseInt(process.env.SMTP_PORT || '587', 10),
|
|
21
|
+
secure: process.env.SMTP_PORT === '465' ? true : false,
|
|
22
|
+
auth: {
|
|
23
|
+
user: process.env.SMTP_USER,
|
|
24
|
+
pass: process.env.SMTP_PASS
|
|
25
|
+
},
|
|
26
|
+
tls: {
|
|
27
|
+
rejectUnauthorized: process.env.SMTP_PORT === '465' ? true : false
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Construct email options
|
|
32
|
+
const mailOptions: SendMailOptions = {
|
|
33
|
+
from: emailOptions.from || 'your-email@example.com',
|
|
34
|
+
to: emailOptions.to,
|
|
35
|
+
cc: emailOptions.cc,
|
|
36
|
+
bcc: emailOptions.bcc,
|
|
37
|
+
subject: emailOptions.subject,
|
|
38
|
+
text: emailOptions.text,
|
|
39
|
+
html: emailOptions.html,
|
|
40
|
+
attachments: emailOptions.attachments,
|
|
41
|
+
replyTo: emailOptions.replyTo,
|
|
42
|
+
headers: emailOptions.headers,
|
|
43
|
+
priority: emailOptions.priority
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Send email
|
|
47
|
+
const info: SentMessageInfo = await transporter.sendMail(mailOptions);
|
|
48
|
+
|
|
49
|
+
// Determine email status
|
|
50
|
+
let status: EmailStatus = 'sent';
|
|
51
|
+
if (info.response.includes('queued')) {
|
|
52
|
+
status = 'queued';
|
|
53
|
+
} else if (info.response.includes('delivered')) {
|
|
54
|
+
status = 'delivered';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return { status, messageId: info.messageId };
|
|
58
|
+
} catch (error) {
|
|
59
|
+
return { status: 'failed', error: (error as Error).message };
|
|
60
|
+
}
|
|
61
|
+
};
|
|
@@ -1,7 +1,61 @@
|
|
|
1
|
-
/** Structure representing an email
|
|
2
|
-
export interface
|
|
3
|
-
/**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
/** Structure representing email options for sending an email */
|
|
2
|
+
export interface EmailOptions {
|
|
3
|
+
/** Sender's email address (optional, defaults to the authenticated user) */
|
|
4
|
+
from?: string;
|
|
5
|
+
|
|
6
|
+
/** Recipient(s) of the email - can be a string or an array of strings */
|
|
7
|
+
to: string | string[];
|
|
8
|
+
|
|
9
|
+
/** Carbon Copy (CC) recipients (optional) */
|
|
10
|
+
cc?: string | string[];
|
|
11
|
+
|
|
12
|
+
/** Blind Carbon Copy (BCC) recipients (optional) */
|
|
13
|
+
bcc?: string | string[];
|
|
14
|
+
|
|
15
|
+
/** Subject of the email */
|
|
16
|
+
subject: string;
|
|
17
|
+
|
|
18
|
+
/** Plain text version of the email body (optional) */
|
|
19
|
+
text?: string;
|
|
20
|
+
|
|
21
|
+
/** HTML version of the email body (optional) */
|
|
22
|
+
html?: string;
|
|
23
|
+
|
|
24
|
+
/** Attachments to include in the email (optional) */
|
|
25
|
+
attachments?: {
|
|
26
|
+
/** Name of the attached file */
|
|
27
|
+
filename: string;
|
|
28
|
+
/** Content of the attachment (Buffer or string) */
|
|
29
|
+
content?: string | Buffer;
|
|
30
|
+
/** File path of the attachment (alternative to `content`) */
|
|
31
|
+
path?: string;
|
|
32
|
+
/** MIME type of the attachment */
|
|
33
|
+
contentType?: string;
|
|
34
|
+
/** Encoding of the attachment (e.g., base64, utf-8) */
|
|
35
|
+
encoding?: string;
|
|
36
|
+
/** Content ID for inline images (optional) */
|
|
37
|
+
cid?: string;
|
|
38
|
+
}[];
|
|
39
|
+
|
|
40
|
+
/** Email address for replies (optional) */
|
|
41
|
+
replyTo?: string;
|
|
42
|
+
|
|
43
|
+
/** Custom headers for the email (optional) */
|
|
44
|
+
headers?: { [key: string]: string };
|
|
45
|
+
|
|
46
|
+
/** Email priority level (optional) */
|
|
47
|
+
priority?: 'high' | 'normal' | 'low';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Possible statuses for email delivery */
|
|
51
|
+
export type EmailStatus = 'sent' | 'delivered' | 'queued' | 'failed';
|
|
52
|
+
|
|
53
|
+
/** Structure representing the result of an email sending operation */
|
|
54
|
+
export interface EmailResult {
|
|
55
|
+
/** Status of the email operation */
|
|
56
|
+
status: EmailStatus;
|
|
57
|
+
/** Message ID returned by the mail server (if available) */
|
|
58
|
+
messageId?: string;
|
|
59
|
+
/** Error message in case of failure (if any) */
|
|
60
|
+
error?: string;
|
|
7
61
|
}
|