feature/IO-2972-Final-Redis-Sockets-Add Redis Cluster aware logic

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-10-07 16:18:11 -04:00
parent c78db7eb08
commit 4be71726d4
5 changed files with 756 additions and 191 deletions

869
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -19,6 +19,7 @@
"makeitpretty": "prettier --write \"**/*.{css,js,json,jsx,scss}\"" "makeitpretty": "prettier --write \"**/*.{css,js,json,jsx,scss}\""
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-elasticache": "^3.665.0",
"@aws-sdk/client-secrets-manager": "^3.654.0", "@aws-sdk/client-secrets-manager": "^3.654.0",
"@aws-sdk/client-ses": "^3.654.0", "@aws-sdk/client-ses": "^3.654.0",
"@aws-sdk/credential-provider-node": "^3.654.0", "@aws-sdk/credential-provider-node": "^3.654.0",

View File

@@ -15,6 +15,8 @@ const logger = require("./server/utils/logger");
const { applyRedisHelpers } = require("./server/utils/redisHelpers"); const { applyRedisHelpers } = require("./server/utils/redisHelpers");
const { applyIOHelpers } = require("./server/utils/ioHelpers"); const { applyIOHelpers } = require("./server/utils/ioHelpers");
const { redisSocketEvents } = require("./server/web-sockets/redisSocketEvents"); const { redisSocketEvents } = require("./server/web-sockets/redisSocketEvents");
const { ElastiCacheClient, DescribeCacheClustersCommand } = require("@aws-sdk/client-elasticache");
const { default: InstanceManager } = require("./server/utils/instanceMgr");
// Load environment variables // Load environment variables
require("dotenv").config({ require("dotenv").config({
@@ -106,28 +108,65 @@ const applyRoutes = ({ app }) => {
}); });
}; };
/**
* Fetch Redis nodes from AWS ElastiCache
* @returns {Promise<string[]>}
*/
const getRedisNodesFromAWS = async () => {
const client = new ElastiCacheClient({
region: InstanceManager({
imex: "ca-central-1",
rome: "us-east-2"
})
});
const params = {
ReplicationGroupId: process.env.REDIS_CLUSTER_ID,
ShowCacheNodeInfo: true
};
try {
// Fetch the cache clusters associated with the replication group
const command = new DescribeCacheClustersCommand(params);
const response = await client.send(command);
const cacheClusters = response.CacheClusters;
return cacheClusters.flatMap((cluster) =>
cluster.CacheNodes.map((node) => `${node.Endpoint.Address}:${node.Endpoint.Port}`)
);
} catch (err) {
logger.log(`Error fetching Redis nodes from AWS: ${err.message}`, "ERROR", "redis", "api");
throw err;
}
};
/** /**
* Connect to Redis Cluster * Connect to Redis Cluster
* @returns {Promise<unknown>} * @returns {Promise<unknown>}
*/ */
const connectToRedisCluster = () => { const connectToRedisCluster = async () => {
if (isEmpty(process.env?.REDIS_URL) || !isString(process.env?.REDIS_URL)) {
logger.log(`[${process.env.NODE_ENV}] No or Malformed REDIS_URL present.`, "ERROR", "redis", "api");
process.exit(1);
}
let redisServers; let redisServers;
try { if (isString(process.env?.REDIS_CLUSTER_ID) && !isEmpty(process.env?.REDIS_CLUSTER_ID)) {
redisServers = JSON.parse(process.env.REDIS_URL); // Fetch Redis nodes from AWS if AWS environment variables are present
} catch (error) { redisServers = await getRedisNodesFromAWS();
logger.log( } else {
`[${process.env.NODE_ENV}] Failed to parse REDIS_URL: ${error.message}. Exiting...`, // Use the Dockerized Redis cluster in development
"ERROR", if (isEmpty(process.env?.REDIS_URL) || !isString(process.env?.REDIS_URL)) {
"redis", logger.log(`[${process.env.NODE_ENV}] No or Malformed REDIS_URL present.`, "ERROR", "redis", "api");
"api" process.exit(1);
); }
process.exit(1); try {
redisServers = JSON.parse(process.env.REDIS_URL);
} catch (error) {
logger.log(
`[${process.env.NODE_ENV}] Failed to parse REDIS_URL: ${error.message}. Exiting...`,
"ERROR",
"redis",
"api"
);
process.exit(1);
}
} }
const clusterRetryStrategy = (times) => { const clusterRetryStrategy = (times) => {

View File

@@ -18,7 +18,7 @@ const ses = new aws.SES({
// The key apiVersion is no longer supported in v3, and can be removed. // The key apiVersion is no longer supported in v3, and can be removed.
// @deprecated The client uses the "latest" apiVersion. // @deprecated The client uses the "latest" apiVersion.
apiVersion: "latest", apiVersion: "latest",
defaultProvider, credentials: defaultProvider(),
region: InstanceManager({ region: InstanceManager({
imex: "ca-central-1", imex: "ca-central-1",
rome: "us-east-2" rome: "us-east-2"
@@ -96,7 +96,7 @@ const sendServerEmail = async ({ subject, text }) => {
} }
}; };
const sendProManagerWelcomeEmail = async ({to, subject, html}) => { const sendProManagerWelcomeEmail = async ({ to, subject, html }) => {
try { try {
await transporter.sendMail({ await transporter.sendMail({
from: `ProManager <noreply@promanager.web-est.com>`, from: `ProManager <noreply@promanager.web-est.com>`,

View File

@@ -15,7 +15,7 @@ const { taskEmailQueue } = require("./tasksEmailsQueue");
const ses = new aws.SES({ const ses = new aws.SES({
apiVersion: "latest", apiVersion: "latest",
defaultProvider, credentials: defaultProvider(),
region: InstanceManager({ region: InstanceManager({
imex: "ca-central-1", imex: "ca-central-1",
rome: "us-east-2" rome: "us-east-2"