UNPKG

@lucidcms/core

Version:

The core of the Lucid CMS. It's responsible for spinning up the API and serving the CMS.

1 lines 8.74 kB
{"version":3,"file":"storage-check.mjs","names":["constants"],"sources":["../../../../src/services/media/alerts/storage-check.ts"],"sourcesContent":["import constants from \"../../../constants/constants.js\";\nimport type { AlertExecutionPayload } from \"../../../libs/alerts/types.js\";\nimport {\n\tAlertsRepository,\n\tOptionsRepository,\n} from \"../../../libs/repositories/index.js\";\nimport {\n\tformatBytes,\n\tmultiTenancyEnabled,\n} from \"../../../utils/helpers/index.js\";\nimport type { ServiceFn } from \"../../../utils/services/types.js\";\nimport sendEmail from \"../../email/send-email.js\";\nimport getStorageUsage from \"../get-storage-usage.js\";\nimport { getMediaStorageOptionName } from \"../helpers/storage-usage-options.js\";\n\nconst storageCheckAlert: ServiceFn<[AlertExecutionPayload], undefined> = async (\n\tcontext,\n\tdata,\n) => {\n\tconst storageLimit = context.config.media.limits.storageBytes;\n\tif (storageLimit === false) {\n\t\treturn {\n\t\t\terror: undefined,\n\t\t\tdata: undefined,\n\t\t};\n\t}\n\n\tconst source = data?.source ?? \"cron\";\n\tconst trigger = data?.trigger ?? \"scheduled\";\n\tconst triggerMetadata = data?.metadata ?? {};\n\tconst Alerts = new AlertsRepository(context.db.client, context.config.db);\n\tconst Options = new OptionsRepository(context.db.client, context.config.db);\n\n\tconst [storageUsageRes, alertEmailRes] = await Promise.all([\n\t\tgetStorageUsage(\n\t\t\tcontext,\n\t\t\tmultiTenancyEnabled(context.config)\n\t\t\t\t? { grouped: true }\n\t\t\t\t: { includeAllBuckets: true },\n\t\t),\n\t\tOptions.selectSingle({\n\t\t\tselect: [\"value_text\"],\n\t\t\twhere: [\n\t\t\t\t{\n\t\t\t\t\tkey: \"name\",\n\t\t\t\t\toperator: \"=\",\n\t\t\t\t\tvalue: \"system_alert_email\",\n\t\t\t\t},\n\t\t\t],\n\t\t}),\n\t]);\n\tif (storageUsageRes.error) return storageUsageRes;\n\tif (alertEmailRes.error) return alertEmailRes;\n\n\tconst alertEmail = alertEmailRes.data?.value_text ?? null;\n\tconst buckets = storageUsageRes.data.buckets ?? [\n\t\t{\n\t\t\ttenantKey: null,\n\t\t\ttotal: storageUsageRes.data.total,\n\t\t},\n\t];\n\n\tfor (const bucket of buckets) {\n\t\tconst tenantConfig = bucket.tenantKey\n\t\t\t? context.config.tenants.find((tenant) => tenant.key === bucket.tenantKey)\n\t\t\t: undefined;\n\t\tconst tenantScope =\n\t\t\tbucket.tenantKey === null\n\t\t\t\t? undefined\n\t\t\t\t: (tenantConfig?.name ?? bucket.tenantKey);\n\t\tconst storageUsed = bucket.total;\n\t\tconst storageRemaining = Math.max(0, storageLimit - storageUsed);\n\t\tconst rawPercentUsed =\n\t\t\tstorageLimit <= 0 ? 100 : (storageUsed / storageLimit) * 100;\n\t\tconst percentUsed = Math.max(0, Math.floor(rawPercentUsed));\n\t\tconst threshold = [...constants.alerts.storage.thresholds]\n\t\t\t.sort((a, b) => a.percent - b.percent)\n\t\t\t.filter((threshold) => rawPercentUsed >= threshold.percent)\n\t\t\t.at(-1);\n\t\tif (!threshold) continue;\n\n\t\tconst dedupeKey = `storage:${getMediaStorageOptionName(bucket.tenantKey)}:${threshold.percent}`;\n\t\tconst cooldownDate = new Date(\n\t\t\tDate.now() - threshold.cooldownDays * 24 * 60 * 60 * 1000,\n\t\t).toISOString();\n\t\tconst recentAlertsRes = await Alerts.selectMultiple({\n\t\t\tselect: [\"id\"],\n\t\t\twhere: [\n\t\t\t\t{\n\t\t\t\t\tkey: \"type\",\n\t\t\t\t\toperator: \"=\",\n\t\t\t\t\tvalue: constants.alerts.storage.type,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tkey: \"dedupe_key\",\n\t\t\t\t\toperator: \"=\",\n\t\t\t\t\tvalue: dedupeKey,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tkey: \"created_at\",\n\t\t\t\t\toperator: \">=\",\n\t\t\t\t\tvalue: cooldownDate,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t\tif (recentAlertsRes.error) return recentAlertsRes;\n\n\t\tif ((recentAlertsRes.data?.length ?? 0) > 0) continue;\n\n\t\tconst metadata = {\n\t\t\tsource,\n\t\t\ttrigger,\n\t\t\ttriggerMetadata,\n\t\t\ttenantKey: bucket.tenantKey,\n\t\t\ttenantName: tenantConfig?.name ?? null,\n\t\t\tthresholdPercent: threshold.percent,\n\t\t\tcooldownDays: threshold.cooldownDays,\n\t\t\tpercentUsed,\n\t\t\tstorageUsed,\n\t\t\tstorageLimit,\n\t\t\tstorageRemaining,\n\t\t};\n\t\tconst title = context.translate(\n\t\t\t\"server:core.alerts.storage.triggered.title\",\n\t\t\t{\n\t\t\t\tdata: {\n\t\t\t\t\tpercent: threshold.percent,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t\tlet emailId: number | null = null;\n\t\tif (alertEmail) {\n\t\t\tconst sendEmailRes = await sendEmail(context, {\n\t\t\t\ttype: \"internal\",\n\t\t\t\tto: alertEmail,\n\t\t\t\tsubject: context.translate(\"server:core.alerts.storage.email.subject\", {\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tpercent: threshold.percent,\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t\ttemplate: constants.email.templates.storageAlert.key,\n\t\t\t\tpriority: \"high\",\n\t\t\t\tisSystem: true,\n\t\t\t\ttenantKey: tenantConfig && bucket.tenantKey ? bucket.tenantKey : null,\n\t\t\t\tdata: {\n\t\t\t\t\ttriggerSource: source,\n\t\t\t\t\tthresholdPercent: threshold.percent,\n\t\t\t\t\tpercentUsed: metadata.percentUsed,\n\t\t\t\t\tstorageUsed: formatBytes(metadata.storageUsed),\n\t\t\t\t\tstorageLimit: formatBytes(metadata.storageLimit),\n\t\t\t\t\tstorageRemaining: formatBytes(metadata.storageRemaining),\n\t\t\t\t\ttenantScope,\n\t\t\t\t\trepeat: false,\n\t\t\t\t\ttitle,\n\t\t\t\t},\n\t\t\t\tstorage: constants.email.templates.storageAlert.storage ?? undefined,\n\t\t\t});\n\t\t\tif (sendEmailRes.error) return sendEmailRes;\n\n\t\t\temailId = sendEmailRes.data.email.id;\n\t\t}\n\n\t\tconst alertRes = await Alerts.createSingle({\n\t\t\tdata: {\n\t\t\t\ttype: constants.alerts.storage.type,\n\t\t\t\tlevel: threshold.level,\n\t\t\t\tdedupe_key: dedupeKey,\n\t\t\t\ttitle,\n\t\t\t\tmessage: context.translate(\n\t\t\t\t\t\"server:core.alerts.storage.triggered.message\",\n\t\t\t\t\t{\n\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\tpercent: threshold.percent,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t\tmetadata,\n\t\t\t\temail_id: emailId,\n\t\t\t},\n\t\t\treturning: [\"id\"],\n\t\t\tvalidation: {\n\t\t\t\tenabled: true,\n\t\t\t},\n\t\t});\n\t\tif (alertRes.error) return alertRes;\n\t}\n\n\treturn {\n\t\terror: undefined,\n\t\tdata: undefined,\n\t};\n};\n\nexport default storageCheckAlert;\n"],"mappings":"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"}