2020-10-08 05:56:47 +13:00
|
|
|
const environment = require("../environment")
|
|
|
|
const { apiKeyTable } = require("../db/dynamoClient")
|
|
|
|
|
2020-10-09 23:32:25 +13:00
|
|
|
const DEFAULT_USAGE = {
|
|
|
|
records: 0,
|
|
|
|
storage: 0,
|
|
|
|
views: 0,
|
|
|
|
automationRuns: 0,
|
|
|
|
users: 0,
|
|
|
|
}
|
|
|
|
|
|
|
|
const DEFAULT_PLAN = {
|
|
|
|
records: 1000,
|
|
|
|
// 1 GB
|
|
|
|
storage: 8589934592,
|
|
|
|
views: 10,
|
|
|
|
automationRuns: 100,
|
|
|
|
users: 10000,
|
|
|
|
}
|
|
|
|
|
2020-10-08 05:56:47 +13:00
|
|
|
function buildUpdateParams(key, property, usage) {
|
|
|
|
return {
|
|
|
|
primary: key,
|
2020-10-09 23:32:25 +13:00
|
|
|
condition:
|
|
|
|
"#quota.#prop < #limits.#prop AND #quotaReset > :now AND attribute_exists(#quota) AND attribute_exists(#limits)",
|
2020-10-08 05:56:47 +13:00
|
|
|
expression: "ADD #quota.#prop :usage",
|
|
|
|
names: {
|
|
|
|
"#quota": "usageQuota",
|
|
|
|
"#prop": property,
|
2020-10-09 05:34:41 +13:00
|
|
|
"#limits": "usageLimits",
|
2020-10-08 05:56:47 +13:00
|
|
|
"#quotaReset": "quotaReset",
|
|
|
|
},
|
|
|
|
values: {
|
|
|
|
":usage": usage,
|
|
|
|
":now": Date.now(),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-09 23:32:25 +13:00
|
|
|
function getNewQuotaReset() {
|
|
|
|
return Date.now() + 2592000000
|
|
|
|
}
|
2020-10-08 05:56:47 +13:00
|
|
|
|
|
|
|
exports.Properties = {
|
|
|
|
RECORD: "records",
|
|
|
|
UPLOAD: "storage",
|
|
|
|
VIEW: "views",
|
|
|
|
USER: "users",
|
|
|
|
AUTOMATION: "automationRuns",
|
|
|
|
}
|
|
|
|
|
|
|
|
exports.getAPIKey = async appId => {
|
|
|
|
return apiKeyTable.get({ primary: appId })
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given a specified API key this will add to the usage object for the specified property.
|
|
|
|
* @param {string} apiKey The API key which is to be updated.
|
|
|
|
* @param {string} property The property which is to be added to (within the nested usageQuota object).
|
|
|
|
* @param {number} usage The amount (this can be negative) to adjust the number by.
|
|
|
|
* @returns {Promise<void>} When this completes the API key will now be up to date - the quota period may have
|
|
|
|
* also been reset after this call.
|
|
|
|
*/
|
|
|
|
exports.update = async (apiKey, property, usage) => {
|
|
|
|
// don't try validate in builder
|
|
|
|
if (!environment.CLOUD) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
await apiKeyTable.update(buildUpdateParams(apiKey, property, usage))
|
|
|
|
} catch (err) {
|
2020-10-09 05:34:41 +13:00
|
|
|
if (err.code === "ConditionalCheckFailedException") {
|
2020-10-08 05:56:47 +13:00
|
|
|
// get the API key so we can check it
|
2020-10-09 05:34:41 +13:00
|
|
|
const keyObj = await apiKeyTable.get({ primary: apiKey })
|
2020-10-09 23:32:25 +13:00
|
|
|
// the usage quota or usage limits didn't exist
|
|
|
|
if (keyObj && (keyObj.usageQuota == null || keyObj.usageLimits == null)) {
|
|
|
|
keyObj.usageQuota = DEFAULT_USAGE
|
|
|
|
keyObj.usageLimits = DEFAULT_PLAN
|
|
|
|
keyObj.quotaReset = getNewQuotaReset()
|
|
|
|
await apiKeyTable.put({ item: keyObj })
|
|
|
|
return
|
|
|
|
}
|
2020-10-08 05:56:47 +13:00
|
|
|
// we have infact breached the reset period
|
2020-10-09 23:32:25 +13:00
|
|
|
else if (keyObj && keyObj.quotaReset <= Date.now()) {
|
2020-10-08 05:56:47 +13:00
|
|
|
// update the quota reset period and reset the values for all properties
|
2020-10-09 23:32:25 +13:00
|
|
|
keyObj.quotaReset = getNewQuotaReset()
|
2020-10-09 05:34:41 +13:00
|
|
|
for (let prop of Object.keys(keyObj.usageQuota)) {
|
2020-10-08 05:56:47 +13:00
|
|
|
if (prop === property) {
|
2020-10-09 05:34:41 +13:00
|
|
|
keyObj.usageQuota[prop] = usage > 0 ? usage : 0
|
2020-10-08 05:56:47 +13:00
|
|
|
} else {
|
2020-10-09 05:34:41 +13:00
|
|
|
keyObj.usageQuota[prop] = 0
|
2020-10-08 05:56:47 +13:00
|
|
|
}
|
|
|
|
}
|
2020-10-09 05:34:41 +13:00
|
|
|
await apiKeyTable.put({ item: keyObj })
|
|
|
|
return
|
2020-10-08 05:56:47 +13:00
|
|
|
}
|
|
|
|
throw "Resource limits have been reached"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|