UNPKG

@directus/api

Version:

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

173 lines (171 loc) 7.17 kB
import async_handler_default from "../utils/async-handler.js"; import { getMilliseconds } from "../utils/get-milliseconds.js"; import { UUID_REGEX } from "../constants.js"; import { getExtensionManager } from "../extensions/index.js"; import { ExtensionReadError, ExtensionsService } from "../services/extensions.js"; import { getCacheControlHeader } from "../utils/get-cache-headers.js"; import { respond } from "../middleware/respond.js"; import use_collection_default from "../middleware/use-collection.js"; import { destroyOnDisconnect } from "../utils/destroy-on-disconnect.js"; import { handleRegistryError } from "./utils/handle-registry-error.js"; import { useEnv } from "@directus/env"; import { ErrorCode, ForbiddenError, RouteNotFoundError, isDirectusError } from "@directus/errors"; import { isIn } from "@directus/utils"; import express from "express"; import { isNil } from "lodash-es"; import { EXTENSION_TYPES } from "@directus/constants"; import { account, describe, list } from "@directus/extensions-registry"; //#region src/controllers/extensions.ts const router = express.Router(); const env = useEnv(); router.use(use_collection_default("directus_extensions")); router.get("/", async_handler_default(async (req, res, next) => { const extensions = await new ExtensionsService({ accountability: req.accountability, schema: req.schema }).readAll(); res.locals["payload"] = { data: extensions || null }; return next(); }), respond); router.get("/registry", async_handler_default(async (req, res, next) => { if (req.accountability && req.accountability.admin !== true) throw new ForbiddenError(); const { search, limit, offset, sort, filter } = req.sanitizedQuery; const query = {}; if (!isNil(search)) query.search = search; if (!isNil(limit)) query.limit = limit; if (!isNil(offset)) query.offset = offset; if (filter) { const getFilterValue = (key) => { const field = filter[key]; if (!field || !("_eq" in field) || typeof field._eq !== "string") return; return field._eq; }; const by = getFilterValue("by"); const type = getFilterValue("type"); if (by) query.by = by; if (type) { if (isIn(type, EXTENSION_TYPES) === false) throw new ForbiddenError(); query.type = type; } } if (!isNil(sort) && sort[0] && isIn(sort[0], [ "popular", "recent", "downloads" ])) query.sort = sort[0]; if (env["MARKETPLACE_TRUST"] === "sandbox") query.sandbox = true; const options = {}; if (env["MARKETPLACE_REGISTRY"] && typeof env["MARKETPLACE_REGISTRY"] === "string") options.registry = env["MARKETPLACE_REGISTRY"]; let payload; try { payload = await list(query, options); } catch (error) { handleRegistryError(error); } res.locals["payload"] = payload; return next(); }), respond); router.get(`/registry/account/:pk(${UUID_REGEX})`, async_handler_default(async (req, res, next) => { if (req.accountability && req.accountability.admin !== true) throw new ForbiddenError(); if (typeof req.params["pk"] !== "string") throw new ForbiddenError(); const options = {}; if (env["MARKETPLACE_REGISTRY"] && typeof env["MARKETPLACE_REGISTRY"] === "string") options.registry = env["MARKETPLACE_REGISTRY"]; let payload; try { payload = await account(req.params["pk"], options); } catch (error) { handleRegistryError(error); } res.locals["payload"] = payload; return next(); }), respond); router.get(`/registry/extension/:pk(${UUID_REGEX})`, async_handler_default(async (req, res, next) => { if (req.accountability && req.accountability.admin !== true) throw new ForbiddenError(); if (typeof req.params["pk"] !== "string") throw new ForbiddenError(); const options = {}; if (env["MARKETPLACE_REGISTRY"] && typeof env["MARKETPLACE_REGISTRY"] === "string") options.registry = env["MARKETPLACE_REGISTRY"]; let payload; try { payload = await describe(req.params["pk"], options); } catch (error) { handleRegistryError(error); } res.locals["payload"] = payload; return next(); }), respond); router.post("/registry/install", async_handler_default(async (req, _res, next) => { if (req.accountability && req.accountability.admin !== true) throw new ForbiddenError(); const { version, extension } = req.body; if (!version || !extension) throw new ForbiddenError(); await new ExtensionsService({ accountability: req.accountability, schema: req.schema }).install(extension, version); return next(); }), respond); router.post("/registry/reinstall", async_handler_default(async (req, _res, next) => { if (req.accountability && req.accountability.admin !== true) throw new ForbiddenError(); const { extension } = req.body; if (!extension) throw new ForbiddenError(); await new ExtensionsService({ accountability: req.accountability, schema: req.schema }).reinstall(extension); return next(); }), respond); router.delete(`/registry/uninstall/:pk(${UUID_REGEX})`, async_handler_default(async (req, _res, next) => { if (req.accountability && req.accountability.admin !== true) throw new ForbiddenError(); const pk = req.params["pk"]; if (typeof pk !== "string") throw new ForbiddenError(); await new ExtensionsService({ accountability: req.accountability, schema: req.schema }).uninstall(pk); return next(); }), respond); router.patch(`/:pk(${UUID_REGEX})`, async_handler_default(async (req, res, next) => { if (req.accountability && req.accountability.admin !== true) throw new ForbiddenError(); if (typeof req.params["pk"] !== "string") throw new ForbiddenError(); const service = new ExtensionsService({ accountability: req.accountability, schema: req.schema }); try { const result = await service.updateOne(req.params["pk"], req.body); res.locals["payload"] = { data: result || null }; } catch (error) { let finalError = error; if (error instanceof ExtensionReadError) { finalError = error.originalError; if (isDirectusError(finalError, ErrorCode.Forbidden)) return next(); } throw finalError; } return next(); }), respond); router.delete(`/:pk(${UUID_REGEX})`, async_handler_default(async (req, _res, next) => { if (req.accountability && req.accountability.admin !== true) throw new ForbiddenError(); const service = new ExtensionsService({ accountability: req.accountability, schema: req.schema }); const pk = req.params["pk"]; if (typeof pk !== "string") throw new ForbiddenError(); await service.deleteOne(pk); return next(); }), respond); router.get("/sources/:chunk", async_handler_default(async (req, res) => { const chunk$1 = req.params["chunk"]; const extensionManager = getExtensionManager(); const chunkName = chunk$1 === "index.js" ? void 0 : chunk$1; const source = await extensionManager.getAppExtensionChunk(chunkName); if (source === null) throw new RouteNotFoundError({ path: req.path }); res.setHeader("Content-Type", "application/javascript; charset=UTF-8"); res.setHeader("Cache-Control", getCacheControlHeader(req, getMilliseconds(env["EXTENSIONS_CACHE_TTL"]), false, false)); res.setHeader("Vary", "Origin, Cache-Control"); if (destroyOnDisconnect(res, () => source.destroy())) return; source.pipe(res); })); var extensions_default = router; //#endregion export { extensions_default as default };