pink-bears
Version:
Intelligent rate limiting middleware with MongoDB integration and caching for Node.js applications
402 lines (374 loc) • 11.1 kB
JavaScript
const {
Storage,
Fetch,
File,
Next,
Batch,
Status,
Email,
Schedule,
Iparams,
Webhook,
Redis,
} = require("../sparrowapps-message-dispatcher/index.js");
const $Status = new Status();
const axios = require('axios');
const { setContext} = require('../sparrowapps-message-dispatcher/context.js');
class App {
constructor(
sparrowAppsDomain,
authToken,
iframeUrl,
productId,
traceId,
appVersion,
userInstalledId,
accountId,
userId,
appId
) {
this.sparrowAppsDomain = sparrowAppsDomain;
this.authToken = authToken;
this.iframeUrl = iframeUrl;
this.productId = productId;
this.traceId = traceId;
this.appVersion = appVersion;
this.appId = appId;
this.userInstalledId = userInstalledId;
this.accountId = accountId;
this.userId = userId;
}
initiateInstanceObject(InstanceClass, constructorKeys) {
const client = {};
const instanceObject = new InstanceClass();
Object.getOwnPropertyNames(instanceObject).forEach((key) => {
if (typeof instanceObject[key] === "function" && key !== "constructor") {
client[key] = (...args) => {
const constructorArgs = constructorKeys.map(
(cKey) => this[cKey] || cKey
);
return new InstanceClass(...constructorArgs)[key](...args);
};
}
});
Object.getOwnPropertyNames(InstanceClass.prototype).forEach((key) => {
if (
typeof InstanceClass.prototype[key] === "function" &&
key !== "constructor"
) {
client[key] = (...args) => {
const constructorArgs = constructorKeys.map(
(cKey) => this[cKey] || cKey
);
return new InstanceClass(...constructorArgs)[key](...args);
};
}
});
return client;
}
getFetch() {
return this.initiateInstanceObject(Fetch, [
"sparrowAppsDomain",
"authToken",
"iframeUrl",
"productId",
"userId",
]);
}
getFile() {
return this.initiateInstanceObject(File, [process.env.bucketName]);
}
getEmail() {
return this.initiateInstanceObject(Email, [
"sparrowAppsDomain",
"authToken",
"userId",
]);
}
getSchedule() {
return this.initiateInstanceObject(Schedule, [
"sparrowAppsDomain",
"authToken",
"iframeUrl",
"productId",
"traceId",
"appVersion",
"userInstalledId",
"accountId",
"userId",
]);
}
getNext() {
return this.initiateInstanceObject(Next, [process.env.rabbitMQurl]);
}
getStorage() {
return this.initiateInstanceObject(Storage, [
"accountId",
"productId",
"appId",
"sparrowAppsDomain",
"userId",
]);
}
getIparams() {
return this.initiateInstanceObject(Iparams, [
"accountId",
"productId",
"appId",
"sparrowAppsDomain",
"userId",
]);
}
getWebhook() {
return this.initiateInstanceObject(Webhook, [
"sparrowAppsDomain",
"authToken",
"productId",
"traceId",
"userInstalledId",
"accountId",
"userId",
]);
}
getRedis() {
console.log(`Inside getRedis() index`);
return this.initiateInstanceObject(Redis, [
"accountId",
"productId",
"appId",
"sparrowAppsDomain",
"traceId",
"userInstalledId",
"authToken",
"userId",
]);
}
getBatch() {
return new Batch();
}
}
const app = new App();
exports.initHandlers = () => {
return {
$Fetch: app.getFetch(),
$File: app.getFile(),
$Email: app.getEmail(),
$Schedule: app.getSchedule(),
$Next: app.getNext(),
$Storage: app.getStorage(),
$Batch: app.getBatch(),
$Iparams: app.getIparams(),
$Status,
$Webhook: app.getWebhook(),
$Redis: app.getRedis()
};
};
const decodeJwt = (token) => {
try {
const [header, payload, signature] = token.split('.');
const decodedHeader = Buffer.from(header, 'base64').toString('utf-8');
const decodedPayload = Buffer.from(payload, 'base64').toString('utf-8');
return {
header: JSON.parse(decodedHeader),
payload: JSON.parse(decodedPayload),
signature,
};
} catch (error) {
console.error('Error decoding the JWT:', error.message);
return null;
}
};
const isTokenExpired = (decodedToken) => {
if (!decodedToken || !decodedToken.payload || !decodedToken.payload.exp) {
return false; // Token is considered not expired if there's no expiration claim
}
const expirationTime = decodedToken.payload.exp * 1000; // Convert seconds to milliseconds
return expirationTime < Date.now();
};
exports.handler = async (event, context) => {
setContext(context);
if (
event?.path?.split("/")?.[event?.path.split("/").length - 1] === "testing"
) {
return {
statusCode: 200,
body: JSON.stringify({
message: "success",
}),
};
}
let parentLayer;
if (!event.body) {
parentLayer = event;
} else {
parentLayer = event.body && JSON.parse(event.body);
}
try {
if (event) {
const eventPayloadConfig =
parentLayer?.appConfig?.extraPayload?.parentLayerConfigObject;
const { appId, userInstalledId } = eventPayloadConfig;
const consoleMethods = ["log", "info", "error", "warn"];
consoleMethods.forEach((method) => {
if (!console[`old${method}`]) {
console[`old${method}`] = console[method];
}
console[method] = function (...args) {
const forbiddenWords = args.filter((str) => {
if (typeof str == "string") {
return str.includes(process.env.rabbitMQurl);
}
return;
});
let message =
"SparrowApps---AppId---" +
appId +
"---UserInstalledId---" +
userInstalledId +
"---TraceId---" +
parentLayer.traceId +
" ";
if (forbiddenWords.length) {
message += `print statement forbidden`;
} else {
message += `${args.join(" ")}`;
}
console[`old${method}`](message);
};
});
app.accountId = eventPayloadConfig.accountId;
app.appVersion = eventPayloadConfig.appVersion;
app.authToken = eventPayloadConfig.authToken;
app.iframeUrl = eventPayloadConfig.iframeUrl;
app.productId = eventPayloadConfig.productId;
app.sparrowAppsDomain = eventPayloadConfig.sparrowAppsDomain;
app.traceId = parentLayer.traceId;
app.userInstalledId = eventPayloadConfig.userInstalledId;
app.userId = eventPayloadConfig.userId;
app.appId = eventPayloadConfig.appId;
const expired = isTokenExpired(decodeJwt(eventPayloadConfig.authToken));
if (expired) {
try {
const response = await axios.post(
`${eventPayloadConfig.sparrowAppsDomain}/api/authenticate`,
{
productKey: eventPayloadConfig.userId,
productId: eventPayloadConfig.productId,
},
{
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.productToken}`
},
}
);
app.authToken = response.data.Access_Token;
} catch (error) {
console.error('Error:', error.message);
throw error;
}
}
const appName = eventPayloadConfig.appName?.toLowerCase();
const fileName = `./${appName}/server.js`;
const appServer = require(fileName);
let eventPayload;
const isBackendEvent = [
"onSubmissionComplete",
"onContactCreate",
"onContactUpdate",
"onContactDelete",
"onTicketCommentCreate",
"onTicketCreate",
"onTicketUpdate",
"onTicketDelete",
"onWidgetCreate",
"onWidgetUpdate",
"onWidgetDelete",
"onDashboardCreate",
"onDashboardUpdate",
"onDashboardDelete",
"onReputationReviewCreate",
"onReputationAppPlatformCreate",
"onEmployeeCreate",
"onContactListCreate",
"onContactListDelete",
"onContactListUpdate",
"onQuestionCreate",
"onQuestionUpdate",
"onQuestionDelete",
"onQuestionTypeChanged",
"onSectionCreate",
"onSectionUpdate",
"onSectionDelete",
"onChannelCreate",
"onChannelUpdate",
"onChannelDelete",
"onCXChannelCreate",
"onCXChannelUpdate",
"onCXChannelDelete",
"onSurveyCreate",
"onSurveyEdit",
"onSurveyDelete",
"onSurveyMove",
"onSurveyClose",
"onSurveyRestore",
"onSurveyDuplicate",
"onSubmissionCreate",
"onSubmissionUpdate",
"onSubmissionDelete",
"onAppInstall",
"onAppUninstall",
"onExternalEvent",
"onIndividualExternalEvent"
].includes(eventPayloadConfig.event)
if (isBackendEvent) {
eventPayload = parentLayer.payload;
} else {
eventPayload = {
data: parentLayer.payload,
iparams: eventPayloadConfig.iparams,
};
}
const message = {
200: "Success",
401: "UnAuthorized",
500: "Something went wrong",
};
const data1 = await appServer[eventPayloadConfig.event](
eventPayload,
parentLayer
);
if (isBackendEvent) {
return {
statusCode: 200,
body: JSON.stringify({
data: data1 || {},
}),
};
}
const data = {
statusCode: data1?.status || 200,
body: JSON.stringify({
data: data1?.data,
message: data1?.status ? message[data1.status] : "",
}),
};
return data;
}
return {
statusCode: 500,
body: JSON.stringify({
data: { message: "something went wrong" },
}),
};
} catch (error) {
const data = {
statusCode: 500,
body: JSON.stringify({
error,
}),
};
await $Status.changeStatus(JSON.stringify(error), parentLayer.traceId);
return data;
}
};