mirror of https://github.com/Budibase/budibase.git
6 changed files with 177 additions and 96 deletions
@ -0,0 +1,105 @@ |
|||
const env = require("../environment") |
|||
const { apiKeyTable } = require("../db/dynamoClient") |
|||
|
|||
const DEFAULT_USAGE = { |
|||
rows: 0, |
|||
storage: 0, |
|||
views: 0, |
|||
automationRuns: 0, |
|||
users: 0, |
|||
} |
|||
|
|||
const DEFAULT_PLAN = { |
|||
rows: 1000, |
|||
// 1 GB
|
|||
storage: 8589934592, |
|||
views: 10, |
|||
automationRuns: 100, |
|||
users: 10000, |
|||
} |
|||
|
|||
function buildUpdateParams(key, property, usage) { |
|||
return { |
|||
primary: key, |
|||
condition: |
|||
"attribute_exists(#quota) AND attribute_exists(#limits) AND #quota.#prop < #limits.#prop AND #quotaReset > :now", |
|||
expression: "ADD #quota.#prop :usage", |
|||
names: { |
|||
"#quota": "usageQuota", |
|||
"#prop": property, |
|||
"#limits": "usageLimits", |
|||
"#quotaReset": "quotaReset", |
|||
}, |
|||
values: { |
|||
":usage": usage, |
|||
":now": Date.now(), |
|||
}, |
|||
} |
|||
} |
|||
|
|||
function getNewQuotaReset() { |
|||
return Date.now() + 2592000000 |
|||
} |
|||
|
|||
exports.Properties = { |
|||
ROW: "rows", |
|||
UPLOAD: "storage", |
|||
VIEW: "views", |
|||
USER: "users", |
|||
AUTOMATION: "automationRuns", |
|||
} |
|||
|
|||
exports.getAPIKey = async appId => { |
|||
if (!env.USE_QUOTAS) { |
|||
return { apiKey: null } |
|||
} |
|||
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) => { |
|||
if (!env.USE_QUOTAS) { |
|||
return |
|||
} |
|||
try { |
|||
await apiKeyTable.update(buildUpdateParams(apiKey, property, usage)) |
|||
} catch (err) { |
|||
// conditional check means the condition failed, need to check why
|
|||
if (err.code === "ConditionalCheckFailedException") { |
|||
// get the API key so we can check it
|
|||
const keyObj = await apiKeyTable.get({ primary: apiKey }) |
|||
// the usage quota or usage limits didn't exist
|
|||
if (keyObj && (keyObj.usageQuota == null || keyObj.usageLimits == null)) { |
|||
keyObj.usageQuota = |
|||
keyObj.usageQuota == null ? DEFAULT_USAGE : keyObj.usageQuota |
|||
keyObj.usageLimits = |
|||
keyObj.usageLimits == null ? DEFAULT_PLAN : keyObj.usageLimits |
|||
keyObj.quotaReset = getNewQuotaReset() |
|||
await apiKeyTable.put({ item: keyObj }) |
|||
return |
|||
} |
|||
// we have in fact breached the reset period
|
|||
else if (keyObj && keyObj.quotaReset <= Date.now()) { |
|||
// update the quota reset period and reset the values for all properties
|
|||
keyObj.quotaReset = getNewQuotaReset() |
|||
for (let prop of Object.keys(keyObj.usageQuota)) { |
|||
if (prop === property) { |
|||
keyObj.usageQuota[prop] = usage > 0 ? usage : 0 |
|||
} else { |
|||
keyObj.usageQuota[prop] = 0 |
|||
} |
|||
} |
|||
await apiKeyTable.put({ item: keyObj }) |
|||
return |
|||
} |
|||
} |
|||
throw err |
|||
} |
|||
} |
|||
Loading…
Reference in new issue