UNPKG

@directus/api

Version:

Directus is a real-time API and App dashboard for managing SQL database content

124 lines (122 loc) 5.32 kB
import async_handler_default from "../utils/async-handler.js"; import { getEntitlementManager } from "../license/entitlements/manager.js"; import { respond } from "../middleware/respond.js"; import { GRACE_PERIOD_MS, getCoreGraceExpiresAt } from "../license/utils/get-core-grace-expires-at.js"; import { ServerService } from "../services/server.js"; import { getLicenseManager } from "../license/manager.js"; import "../license/index.js"; import { isAdmin } from "../utils/is-admin.js"; import is_admin_default from "../middleware/is-admin.js"; import { ForbiddenError, InvalidPayloadError } from "@directus/errors"; import express from "express"; import { fromZodError } from "zod-validation-error"; import { ResolveInput } from "@directus/license"; //#region src/controllers/license.ts const router = express.Router(); router.get("/", is_admin_default, async_handler_default(async (_req, res, next) => { const licenseManager = getLicenseManager(); const entitlementManager = getEntitlementManager(); const [license, status, downgradeReason, seatUsage, collectionUsage, flowUsage] = await Promise.all([ licenseManager.getLicense(), licenseManager.getStatus(), licenseManager.getDowngradeReason(), entitlementManager.getUsage("seats"), entitlementManager.getUsage("collections"), entitlementManager.getUsage("flows") ]); const editable = licenseManager.getEditable(); const source = licenseManager.getSource(); let expiresAt = license.meta.expires_at; let gracePeriod = license.meta.grace_period; if (source === null && status === "grace") { const coreGraceExpiresAt = await getCoreGraceExpiresAt(); if (coreGraceExpiresAt !== null) { expiresAt = coreGraceExpiresAt; gracePeriod = Math.floor(GRACE_PERIOD_MS / 1e3); } } const payload = { name: license.meta.name, editable, status, source, downgrade_reason: downgradeReason, renews_at: license.meta.renews_at, expires_at: expiresAt, entitlements: license.entitlements, grace_period: gracePeriod, offline: license.meta.offline, usage: { seats: seatUsage, collections: collectionUsage, flows: flowUsage } }; res.locals["payload"] = { data: payload }; return next(); }), respond); router.post("/", is_admin_default, async_handler_default(async (req, _res, next) => { if (!req.body.license_key) throw new InvalidPayloadError({ reason: "A \"license_key\" is required" }); await getLicenseManager().activate(req.body.license_key); return next(); }), respond); router.patch("/", is_admin_default, async_handler_default(async (req, _res, next) => { if (!req.body.license_key) throw new InvalidPayloadError({ reason: "A \"license_key\" is required" }); await getLicenseManager().update(req.body.license_key); return next(); }), respond); router.delete("/", is_admin_default, async_handler_default(async (_req, _res, next) => { await getLicenseManager().deactivate(); return next(); }), respond); router.post("/preview", async_handler_default(async (req, res, next) => { if (await new ServerService({ schema: req.schema }).isSetupCompleted() && !isAdmin(req.accountability)) throw new ForbiddenError(); if (!req.body.license_key) throw new InvalidPayloadError({ reason: "A \"license_key\" is required" }); const preview = await getLicenseManager().preview(req.body.license_key); const payload = { plan_name: preview.plan_name, expires_at: preview.expires_at, renews_at: preview.renews_at, production_enabled: preview.entitlements.production_enabled.override ?? preview.entitlements.production_enabled.default }; res.locals["payload"] = { data: payload }; return next(); }), respond); router.get("/portal", is_admin_default, async_handler_default(async (_req, res) => { const portal = await getLicenseManager().billingPortalUrl(); res.redirect(portal); })); router.get("/addons", is_admin_default, async_handler_default(async (_req, res, next) => { const payload = await getLicenseManager().availableAddons(); res.locals["payload"] = { data: payload }; return next(); }), respond); router.patch("/addons/:id", is_admin_default, async_handler_default(async (req, _res, next) => { if (typeof req.body.quantity !== "number") throw new InvalidPayloadError({ reason: "A numbered \"quantity\" is required" }); await getLicenseManager().setAddonQuantity({ addonId: req.params["id"], quantity: req.body.quantity }); return next(); }), respond); router.delete("/addons/:id", is_admin_default, async_handler_default(async (req, _res, next) => { await getLicenseManager().removeAddon(req.params["id"]); return next(); }), respond); router.post("/pending-resolution", is_admin_default, async_handler_default(async (req, res, next) => { const payload = await getLicenseManager().pendingResolution({ adminId: req.accountability.user, licenseKey: req.body.license_key }); res.locals["payload"] = { data: payload }; return next(); }), respond); router.post("/resolve", is_admin_default, async_handler_default(async (req, _res, next) => { const { error, data } = ResolveInput.safeParse(req.body); if (error) throw new InvalidPayloadError({ reason: fromZodError(error).message }); await getLicenseManager().applyResolution(data, { accountability: req.accountability }); return next(); }), respond); var license_default = router; //#endregion export { license_default as default };