@plusscommunities/pluss-core-aws 2.0.25-auth.0 → 2.0.25-beta.0
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/aws/getDefaultEmailAddress.js +21 -21
- package/aws/getEmailService.js +16 -16
- package/aws/getEmailServiceInfo.js +26 -26
- package/aws/sendEmail.js +31 -31
- package/config.js +1 -1
- package/db/activity/publishActivity.js +22 -22
- package/db/analytics/checkActivityExists.js +15 -15
- package/db/analytics/logAnalyticsActivity.js +69 -37
- package/db/analytics/scheduleOldAggregation.js +14 -14
- package/db/auth/getSiteSetting.js +12 -12
- package/db/auth/getSiteUserTypes.js +16 -16
- package/db/auth/getUserAuth.js +13 -13
- package/db/automatedactions/getActionBySiteTrigger.js +9 -9
- package/db/common/deleteRef.js +21 -21
- package/db/common/editRef.js +36 -36
- package/db/common/getRef.js +23 -23
- package/db/common/getTableCount.js +18 -18
- package/db/common/indexQuery.js +17 -17
- package/db/common/indexQueryRecursive.js +20 -20
- package/db/common/scanRef.js +18 -18
- package/db/common/scanRefRecursive.js +20 -20
- package/db/common/updateAttribute.js +27 -27
- package/db/common/updateRef.js +20 -20
- package/db/linkedUsers/getLinkedBy.js +21 -21
- package/db/linkedUsers/getLinkedTo.js +21 -21
- package/db/notifications/deleteNotificationsByEntity.js +21 -21
- package/db/notifications/getNotificationSetting.js +14 -14
- package/db/notifications/publishNotifications.js +39 -39
- package/db/scheduledActions/deleteActionQueue.js +1 -1
- package/db/scheduledActions/getActionQueueByEntityId.js +10 -10
- package/db/scheduledActions/getActionQueueByEntityKey.js +9 -9
- package/db/scheduledActions/getActionQueueById.js +9 -9
- package/db/scheduledActions/getActionQueueByTriggerAt.js +14 -14
- package/db/scheduledActions/updateActionQueue.js +29 -29
- package/db/strings/getString.js +20 -20
- package/db/strings/logUpdate.js +18 -18
- package/db/templates/getTemplateById.js +1 -1
- package/db/templates/getTemplatesList.js +10 -10
- package/db/templates/updateTemplate.js +9 -9
- package/db/users/getRole.js +1 -1
- package/db/users/getUser.js +9 -9
- package/db/users/getUserByEmail.js +17 -17
- package/helper/audience/filterByAudienceType.js +27 -27
- package/helper/audience/filterOnAudienceType.js +26 -26
- package/helper/audience/getAudience.js +187 -187
- package/helper/audience/getMatchingAudienceTypes.js +21 -21
- package/helper/audience/getMatchingAudienceTypesFromPreview.js +60 -60
- package/helper/audience/getMatchingTags.js +15 -15
- package/helper/audience/isValidAudience.js +20 -20
- package/helper/auth/checkTokenBlacklist.js +17 -17
- package/helper/auth/getApiKeyFromReq.js +2 -2
- package/helper/auth/getSessionUser.js +70 -85
- package/helper/auth/getSessionUserFromReq.js +2 -2
- package/helper/auth/getSessionUserFromReqAuthKey.js +11 -11
- package/helper/auth/validateApiKey.js +32 -32
- package/helper/auth/validateMasterAuth.js +174 -174
- package/helper/auth/validateSiteAccess.js +12 -12
- package/helper/auth/validateSiteSetting.js +7 -7
- package/helper/auth/validateUserLoggedIn.js +19 -19
- package/helper/createGuid.js +5 -5
- package/helper/generateJsonResponse.js +27 -27
- package/helper/getUserPreview.js +57 -57
- package/helper/getUserPreviewFromHeader.js +17 -17
- package/helper/getUserPreviewFromReq.js +17 -17
- package/helper/hqPublishing.js +45 -0
- package/helper/index.js +28 -28
- package/helper/notifySiteConfigs.js +132 -0
- package/helper/opengraph/getOpenGraph.js +12 -12
- package/helper/rates/checkRateLimit.js +38 -38
- package/helper/requestToSource.js +10 -10
- package/helper/sendEmail.js +120 -120
- package/helper/templates/replacePlaceHolders.js +29 -29
- package/helper/time/getLocalTimestamp.js +18 -18
- package/helper/time/getSiteTimezone.js +11 -11
- package/helper/triggerAutomatedAction.js +25 -25
- package/helper/userToUserPreview.js +23 -23
- package/helper/users/getUserTypesByPermission.js +24 -24
- package/helper/users/getUsersByPermission.js +20 -20
- package/notification/prepNotification.js +144 -144
- package/notification/sendNotifications.js +166 -166
- package/package.json +35 -40
- package/templates/supportTicketEmails.js +8 -8
- package/helper/auth/context/AuthenticationContext.js +0 -50
- package/helper/auth/context/AuthenticationStrategy.js +0 -20
- package/helper/auth/context/auth0/Strategy.js +0 -12
- package/helper/auth/context/auth0/functions/decodeAccessToken.js +0 -102
- package/helper/auth/context/auth0/functions/getSessionUser.js +0 -21
- package/helper/auth/context/boltonclarke/Strategy.js +0 -10
- package/helper/auth/context/cognito/Strategy.js +0 -12
- package/helper/auth/context/cognito/functions/getSessionUser.js +0 -76
package/helper/getUserPreview.js
CHANGED
|
@@ -3,66 +3,66 @@ const getUser = require("../db/users/getUser");
|
|
|
3
3
|
const { getConfig } = require("../config");
|
|
4
4
|
|
|
5
5
|
module.exports = async (id, includeType, includeSite, options) => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
const logId = log("getUserPreview", "Input", {
|
|
8
|
+
id,
|
|
9
|
+
includeType,
|
|
10
|
+
includeSite,
|
|
11
|
+
options,
|
|
12
|
+
});
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
if (!id) {
|
|
15
|
+
log("getUserPreview", "NoId", "id is missing", logId);
|
|
16
|
+
return resolve({
|
|
17
|
+
id: null,
|
|
18
|
+
userId: null,
|
|
19
|
+
displayName: "Pluss",
|
|
20
|
+
profilePic: null,
|
|
21
|
+
type: null,
|
|
22
|
+
site: getConfig().anySignUpSite,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
getUser(id)
|
|
27
|
+
.then((user) => {
|
|
28
|
+
const returnObj = {
|
|
29
|
+
id,
|
|
30
|
+
userId: id,
|
|
31
|
+
displayName: user.displayName,
|
|
32
|
+
profilePic: user.profilePic || null,
|
|
33
|
+
};
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
35
|
+
if (includeType || includeSite) {
|
|
36
|
+
returnObj.Roles = user.Roles;
|
|
37
|
+
if (!user.Roles) {
|
|
38
|
+
returnObj.Roles = [
|
|
39
|
+
{
|
|
40
|
+
type: user.type,
|
|
41
|
+
site: user.site,
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
47
|
+
if (includeType) {
|
|
48
|
+
returnObj.type = user.type || null;
|
|
49
|
+
}
|
|
50
|
+
if (includeSite) {
|
|
51
|
+
returnObj.site = user.site || null;
|
|
52
|
+
}
|
|
53
|
+
if (options) {
|
|
54
|
+
if (options.includeEmail) {
|
|
55
|
+
returnObj.email = user.email !== "empty" ? user.email : null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
log("getUserPreview", "Result", returnObj, logId);
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
60
|
+
resolve(returnObj);
|
|
61
|
+
})
|
|
62
|
+
.catch((error) => {
|
|
63
|
+
log("getUserPreview", "Error", error, logId);
|
|
64
|
+
console.log("failed to get user preview", id);
|
|
65
|
+
reject(error);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
68
|
};
|
|
@@ -3,21 +3,21 @@ const getSessionUser = require("./auth/getSessionUser");
|
|
|
3
3
|
const getUserPreview = require("./getUserPreview");
|
|
4
4
|
|
|
5
5
|
module.exports = async (authkey, includeType, inlcudeSite, options) => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
getSessionUser(authkey)
|
|
8
|
+
.then((uid) => {
|
|
9
|
+
getUserPreview(uid, includeType, inlcudeSite, options)
|
|
10
|
+
.then((user) => {
|
|
11
|
+
resolve(user);
|
|
12
|
+
})
|
|
13
|
+
.catch((error) => {
|
|
14
|
+
log("getUserPreviewFromHeader", "Error:getUserPreview", error);
|
|
15
|
+
reject(error);
|
|
16
|
+
});
|
|
17
|
+
})
|
|
18
|
+
.catch((error) => {
|
|
19
|
+
log("getUserPreviewFromHeader", "Error:getSessionUser", error);
|
|
20
|
+
reject(error);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
23
|
};
|
|
@@ -2,21 +2,21 @@ const getSessionUserFromReqAuthKey = require("./auth/getSessionUserFromReqAuthKe
|
|
|
2
2
|
const getUserPreview = require("./getUserPreview");
|
|
3
3
|
|
|
4
4
|
module.exports = async (req, includeType, inlcudeSite, options) => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
getSessionUserFromReqAuthKey(req)
|
|
7
|
+
.then((uid) => {
|
|
8
|
+
getUserPreview(uid, includeType, inlcudeSite, options)
|
|
9
|
+
.then((user) => {
|
|
10
|
+
resolve(user);
|
|
11
|
+
})
|
|
12
|
+
.catch((error) => {
|
|
13
|
+
console.log("failed to get user preview", uid);
|
|
14
|
+
reject(error);
|
|
15
|
+
});
|
|
16
|
+
})
|
|
17
|
+
.catch((error) => {
|
|
18
|
+
console.log("failed to get session user", req);
|
|
19
|
+
reject(error);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
22
|
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PC-1441 Epic 1 — Cross-site Publishing Framework primitives (§13).
|
|
3
|
+
*
|
|
4
|
+
* Entity-agnostic helpers for HQ-published content lifecycle. Newsletter is the
|
|
5
|
+
* first entity to adopt this in July 2026 (Stories A–F under PC-1441); Q4+
|
|
6
|
+
* entities (Surveys, Information, Facilities, Services) plug in by depending on
|
|
7
|
+
* the same primitives — `HqSourceId` / `PublishedToSites` / `TemplateId` are
|
|
8
|
+
* universal field conventions, the helpers below don't reference any newsletter-
|
|
9
|
+
* specific table or shape.
|
|
10
|
+
*
|
|
11
|
+
* Story coverage:
|
|
12
|
+
* - **Story A (PC-1443)** — `isHqLocked` (lock guard for community-copy mutations).
|
|
13
|
+
* - **Story B (PC-1444)** — adds `findCopiesByHqSourceId`, `fanOutHqSource`,
|
|
14
|
+
* `propagateHqEdit`, `cascadeHqRetract`, and `incrementTemplateUseCount`
|
|
15
|
+
* (these will appear as additional exports here).
|
|
16
|
+
*
|
|
17
|
+
* If this file grows beyond ~200 lines or develops sub-themes, refactor to a
|
|
18
|
+
* `hqPublishing/` subdirectory with `index.js` re-exports.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Returns `true` when the entry is a community copy of an HQ-published post —
|
|
23
|
+
* i.e. its `HqSourceId` is **truthy**.
|
|
24
|
+
*
|
|
25
|
+
* **Falsy test (`Boolean(HqSourceId)`) — NOT `!== null`.** Legacy entries and
|
|
26
|
+
* locally-created entries have `HqSourceId` `undefined`; `undefined !== null`
|
|
27
|
+
* evaluates to `true`, which would wrongly lock every pre-feature post. The
|
|
28
|
+
* falsy check accepts `undefined`, `null`, and `""` as "not locked." See PC-1441
|
|
29
|
+
* scoping doc §3.2 AC2.3 + §6.1 + commit `1123c79` on `docs/enterprise-roadmap-scoping`.
|
|
30
|
+
*
|
|
31
|
+
* Universal — no role-based exception, including masters. HQ users edit the
|
|
32
|
+
* HQ-source row (where `!HqSourceId`, so the lock does not apply); the stream
|
|
33
|
+
* cascades to community copies. This is the API-side gate that pairs with the
|
|
34
|
+
* admin UI lock indicator (PC-1448) — defense-in-depth.
|
|
35
|
+
*
|
|
36
|
+
* @param {object|null|undefined} entry - Any object with an `HqSourceId` field
|
|
37
|
+
* (typically a row fetched from the entity's DB table).
|
|
38
|
+
* @returns {boolean} `true` if the entry is a locked community copy; `false`
|
|
39
|
+
* for legacy / locally-authored / HQ-source rows.
|
|
40
|
+
*/
|
|
41
|
+
const isHqLocked = (entry) => Boolean(entry?.HqSourceId);
|
|
42
|
+
|
|
43
|
+
module.exports = {
|
|
44
|
+
isHqLocked,
|
|
45
|
+
};
|
package/helper/index.js
CHANGED
|
@@ -3,28 +3,28 @@ const uuid = require("uuid");
|
|
|
3
3
|
const { encode, decode } = require("html-entities");
|
|
4
4
|
|
|
5
5
|
exports.thisOrDefault = (val, def) => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
if (val) {
|
|
7
|
+
return val;
|
|
8
|
+
}
|
|
9
|
+
return def;
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
exports.getRowId = (site, id) => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
if (_.isEmpty(site)) {
|
|
14
|
+
return id;
|
|
15
|
+
}
|
|
16
|
+
return `${site}_${id}`;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
exports.getMultiRowId = (cols) => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
let result = "";
|
|
21
|
+
cols.forEach((col) => {
|
|
22
|
+
if (!_.isEmpty(result)) {
|
|
23
|
+
result += "_";
|
|
24
|
+
}
|
|
25
|
+
result += col;
|
|
26
|
+
});
|
|
27
|
+
return result;
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
// exports.getExtension = (filename) => {
|
|
@@ -36,7 +36,7 @@ exports.getMultiRowId = (cols) => {
|
|
|
36
36
|
// };
|
|
37
37
|
|
|
38
38
|
exports.getBody = (event) => {
|
|
39
|
-
|
|
39
|
+
return typeof event.body === "string" ? JSON.parse(event.body) : event.body;
|
|
40
40
|
};
|
|
41
41
|
|
|
42
42
|
// exports.generatePassword = () => {
|
|
@@ -47,20 +47,20 @@ exports.getBody = (event) => {
|
|
|
47
47
|
// };
|
|
48
48
|
|
|
49
49
|
exports.log = (action, logKey, data, logId) => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
50
|
+
if (!logId) {
|
|
51
|
+
logId = this.generateLogId();
|
|
52
|
+
}
|
|
53
|
+
console.log(
|
|
54
|
+
`[${action}]:[${logId}]:[${logKey}]`,
|
|
55
|
+
typeof data === "string" || typeof data === "number"
|
|
56
|
+
? data
|
|
57
|
+
: JSON.stringify(data),
|
|
58
|
+
);
|
|
59
|
+
return logId;
|
|
60
60
|
};
|
|
61
61
|
|
|
62
62
|
exports.generateLogId = () => {
|
|
63
|
-
|
|
63
|
+
return uuid.v4().substring(30);
|
|
64
64
|
};
|
|
65
65
|
|
|
66
66
|
exports.encodeHtml = (html) => encode(html);
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notify SiteConfigs Service of Operational Data Changes
|
|
3
|
+
*
|
|
4
|
+
* This helper provides a pre-filtering mechanism for auto-propagation.
|
|
5
|
+
* When operational data changes (sites, usertypes, interfaces, strings, jobTypes),
|
|
6
|
+
* this function:
|
|
7
|
+
*
|
|
8
|
+
* 1. Queries SourceSiteIdIndex to check if any templates use this site as source
|
|
9
|
+
* 2. If none found → early return (no Lambda invoke, 99% of cases)
|
|
10
|
+
* 3. If templates found → invokes siteConfigs Lambda to handle republishing
|
|
11
|
+
*
|
|
12
|
+
* This hybrid approach ensures:
|
|
13
|
+
* - No Lambda latency for most sites (pre-filter catches non-source sites)
|
|
14
|
+
* - Publishing logic stays encapsulated in siteConfigs service
|
|
15
|
+
* - Minimal cross-service coupling
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* const notifySiteConfigs = require("@plusscommunities/pluss-core-aws/helper/notifySiteConfigs");
|
|
19
|
+
* await notifySiteConfigs(siteId, logId);
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const AWS = require("aws-sdk");
|
|
23
|
+
const indexQuery = require("../db/common/indexQuery");
|
|
24
|
+
const { log } = require("./index");
|
|
25
|
+
|
|
26
|
+
const lambda = new AWS.Lambda();
|
|
27
|
+
|
|
28
|
+
const TABLE_NAME = "siteconfigs";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Check if a site has any templates using it as a source
|
|
32
|
+
* @param {string} siteId - The site ID to check
|
|
33
|
+
* @returns {Promise<Array>} Array of template IDs (empty if none)
|
|
34
|
+
*/
|
|
35
|
+
const getTemplatesForSourceSite = async (siteId) => {
|
|
36
|
+
try {
|
|
37
|
+
const result = await indexQuery(TABLE_NAME, {
|
|
38
|
+
IndexName: "SourceSiteIdIndex",
|
|
39
|
+
KeyConditionExpression: "SourceSiteId = :sourceSiteId",
|
|
40
|
+
ExpressionAttributeValues: {
|
|
41
|
+
":sourceSiteId": siteId,
|
|
42
|
+
},
|
|
43
|
+
ProjectionExpression: "Id", // Only need IDs for pre-filter
|
|
44
|
+
});
|
|
45
|
+
return (result.Items || []).map((item) => item.Id);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
// Table or index might not exist in all deployments
|
|
48
|
+
log("notifySiteConfigs", "QueryError", { siteId, error: err.message });
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Invoke the siteConfigs operationalDataChanged Lambda
|
|
55
|
+
* @param {string} siteId - The source site that changed
|
|
56
|
+
* @param {Array<string>} templateIds - List of template IDs to republish
|
|
57
|
+
* @param {string} logId - Log ID for tracing
|
|
58
|
+
*/
|
|
59
|
+
const invokeSiteConfigsLambda = async (siteId, templateIds, logId) => {
|
|
60
|
+
// Build function name from environment
|
|
61
|
+
// Format: {client}-siteConfigs-{stage}-operationalDataChanged
|
|
62
|
+
const client = process.env.client || "dev";
|
|
63
|
+
const stage = process.env.stage || "dev";
|
|
64
|
+
const functionName = `${client}-siteConfigs-${stage}-operationalDataChanged`;
|
|
65
|
+
|
|
66
|
+
const payload = {
|
|
67
|
+
siteId,
|
|
68
|
+
templateIds,
|
|
69
|
+
logId,
|
|
70
|
+
source: "notifySiteConfigs",
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
log(
|
|
74
|
+
"notifySiteConfigs",
|
|
75
|
+
"InvokingLambda",
|
|
76
|
+
{ functionName, siteId, templateCount: templateIds.length },
|
|
77
|
+
logId,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
await lambda
|
|
82
|
+
.invoke({
|
|
83
|
+
FunctionName: functionName,
|
|
84
|
+
InvocationType: "Event", // Async invocation
|
|
85
|
+
Payload: JSON.stringify(payload),
|
|
86
|
+
})
|
|
87
|
+
.promise();
|
|
88
|
+
|
|
89
|
+
log("notifySiteConfigs", "LambdaInvoked", { functionName, siteId }, logId);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
log(
|
|
92
|
+
"notifySiteConfigs",
|
|
93
|
+
"LambdaError",
|
|
94
|
+
{ functionName, siteId, error: err.message },
|
|
95
|
+
logId,
|
|
96
|
+
);
|
|
97
|
+
// Don't throw - this is a non-critical operation
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Notify siteConfigs service that operational data has changed for a site.
|
|
103
|
+
* Performs pre-filtering to avoid unnecessary Lambda invocations.
|
|
104
|
+
*
|
|
105
|
+
* @param {string} siteId - The site ID where operational data changed
|
|
106
|
+
* @param {string} logId - Optional log ID for tracing
|
|
107
|
+
*/
|
|
108
|
+
const notifySiteConfigs = async (siteId, logId) => {
|
|
109
|
+
if (!siteId) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Pre-filter: Check if this site is a source for any templates
|
|
114
|
+
const templateIds = await getTemplatesForSourceSite(siteId);
|
|
115
|
+
|
|
116
|
+
if (templateIds.length === 0) {
|
|
117
|
+
// No Lambda invocation needed
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
log(
|
|
122
|
+
"notifySiteConfigs",
|
|
123
|
+
"TemplatesFound",
|
|
124
|
+
{ siteId, count: templateIds.length },
|
|
125
|
+
logId,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Invoke siteConfigs Lambda to handle republishing
|
|
129
|
+
await invokeSiteConfigsLambda(siteId, templateIds, logId);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
module.exports = notifySiteConfigs;
|
|
@@ -2,17 +2,17 @@ const axios = require("axios");
|
|
|
2
2
|
const { getConfig } = require("../../config");
|
|
3
3
|
|
|
4
4
|
module.exports = async (url) => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
return new Promise(async (resolve, reject) => {
|
|
6
|
+
const request = {
|
|
7
|
+
method: "GET",
|
|
8
|
+
url: `https://opengraph.io/api/1.1/site/${encodeURIComponent(url)}?app_id=${getConfig().thirdPartyAPIKeys.openGraph}`,
|
|
9
|
+
};
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
try {
|
|
12
|
+
const response = await axios(request);
|
|
13
|
+
return resolve(response.data);
|
|
14
|
+
} catch (e) {
|
|
15
|
+
return reject(e);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
18
|
};
|
|
@@ -12,45 +12,45 @@ const updateRef = require("../../db/common/updateRef");
|
|
|
12
12
|
* @returns {Boolean} true if this call is fine. false if the call should be restricted due to rate limits.
|
|
13
13
|
*/
|
|
14
14
|
module.exports = async (rateLimitId, timeframe, limit, noSave) => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
const logId = log("checkRateLimit", "Input", {
|
|
16
|
+
rateLimitId,
|
|
17
|
+
timeframe,
|
|
18
|
+
limit,
|
|
19
|
+
});
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
// get rate limit item
|
|
22
|
+
const item = await getRef("ratelimit", "Id", rateLimitId);
|
|
23
|
+
const now = moment().valueOf();
|
|
24
|
+
const minTime = now - timeframe;
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
const history = item && item.History ? item.History : [];
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
28
|
+
if (history) {
|
|
29
|
+
// check history for entries to determine whether limit is surpassed
|
|
30
|
+
const entriesInTimeLimit = history.filter((e) => e > minTime);
|
|
31
|
+
log(
|
|
32
|
+
"checkRateLimit",
|
|
33
|
+
"entriesInTimeLimit",
|
|
34
|
+
entriesInTimeLimit.length,
|
|
35
|
+
logId,
|
|
36
|
+
);
|
|
37
|
+
if (entriesInTimeLimit.length >= limit) {
|
|
38
|
+
// Limit surpassed
|
|
39
|
+
log("checkRateLimit", "Return", false, logId);
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
};
|
|
44
|
+
// check whether to save the current entry - used for when checking multiple timeframes such as X calls in a minute and X calls in a day
|
|
45
|
+
if (!noSave) {
|
|
46
|
+
history.push(now);
|
|
47
|
+
updateRef("ratelimit", {
|
|
48
|
+
Id: rateLimitId,
|
|
49
|
+
LastSent: now,
|
|
50
|
+
History: history,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
// Limit is fine
|
|
54
|
+
log("checkRateLimit", "Return", true, logId);
|
|
55
|
+
return true;
|
|
56
|
+
};
|
|
@@ -3,14 +3,14 @@ const _ = require("lodash");
|
|
|
3
3
|
|
|
4
4
|
// sends a request to the Pluss Source API
|
|
5
5
|
module.exports = (method, service, endpoint, query, data) => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
6
|
+
return axios({
|
|
7
|
+
method,
|
|
8
|
+
url: `https://pluss60.pluss60-api.com/${service}-demo/${endpoint}${
|
|
9
|
+
!_.isEmpty(query) ? query : ""
|
|
10
|
+
}`,
|
|
11
|
+
data,
|
|
12
|
+
headers: {
|
|
13
|
+
Authorization: "SmartCommunities",
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
16
|
};
|