UNPKG

@directus/api

Version:

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

134 lines (132 loc) 6.29 kB
import async_handler_default from "../utils/async-handler.js"; import { useLogger } from "../logger/index.js"; import { clearSystemCache } from "../cache.js"; import { getDatabase } from "../database/index.js"; import { RevisionsService } from "../services/revisions.js"; import { ExportService } from "../services/export.js"; import { respond } from "../middleware/respond.js"; import { resolveLoginRedirect } from "../auth/utils/resolve-login-redirect.js"; import { sanitizeQuery } from "../utils/sanitize-query.js"; import { UtilsService } from "../services/utils.js"; import { ImportService } from "../services/import/import.js"; import collection_exists_default from "../middleware/collection-exists.js"; import { readMultipartFile } from "../utils/read-multipart-file.js"; import readFileUploadBody from "../middleware/read-file-upload-body.js"; import { queryFlag } from "../utils/query-flag.js"; import { generateTranslations } from "../utils/generate-translations.js"; import { useEnv } from "@directus/env"; import { ForbiddenError, InvalidPayloadError, InvalidQueryError } from "@directus/errors"; import { Router } from "express"; import { fromZodError } from "zod-validation-error"; import { z as z$1 } from "zod"; import bytes from "bytes"; //#region src/controllers/utils.ts const router = Router(); const env = useEnv(); const IMPORT_MAX_FILE_SIZE = bytes.parse(env["IMPORT_MAX_FILE_SIZE"]) ?? void 0; const randomStringSchema = z$1.object({ length: z$1.coerce.number().int().min(1).max(500).default(32) }); router.get("/random/string", async_handler_default(async (req, res) => { const { nanoid } = await import("nanoid"); const { data: value, error } = randomStringSchema.safeParse(req.query); if (error) throw new InvalidQueryError({ reason: fromZodError(error).message }); return res.json({ data: nanoid(value.length) }); })); const SortSchema = z$1.object({ item: z$1.union([z$1.string(), z$1.number()]), to: z$1.union([z$1.string(), z$1.number()]) }).strict(); router.post("/sort/:collection", collection_exists_default, async_handler_default(async (req, res) => { const { error } = SortSchema.safeParse(req.body); if (error) throw new InvalidPayloadError({ reason: fromZodError(error).message }); await new UtilsService({ accountability: req.accountability, schema: req.schema }).sort(req.collection, req.body); return res.status(200).end(); })); router.post("/revert/:revision", async_handler_default(async (req, _res, next) => { await new RevisionsService({ accountability: req.accountability, schema: req.schema }).revert(req.params["revision"]); next(); }), respond); router.post("/import/:collection", collection_exists_default, async_handler_default(async (req, res) => { const service = new ImportService({ accountability: req.accountability, schema: req.schema }); const { mimetype, stream } = await readMultipartFile(req, { maxFileSize: IMPORT_MAX_FILE_SIZE }); await service.import(req.params["collection"], mimetype, stream, { background: queryFlag(req.query["background"]) }); return res.status(200).end(); })); const ImportBatchSchema = z$1.array(z$1.object({ collection: z$1.string(), items: z$1.array(z$1.record(z$1.string(), z$1.unknown())) })).min(1); router.post("/import", readFileUploadBody({ maxFileSize: IMPORT_MAX_FILE_SIZE }), async_handler_default(async (req, res, next) => { const { data: value, error } = ImportBatchSchema.safeParse(req.body); if (error) throw new InvalidPayloadError({ reason: fromZodError(error).message }); if (req.query["mode"] !== void 0 && ["add", "merge"].includes(String(req.query["mode"])) === false) throw new InvalidQueryError({ reason: `"mode" must be one of ["add", "merge"]` }); const mode = req.query["mode"] === "merge" ? "merge" : "add"; const dryRun = queryFlag(req.query["dryRun"]); const dangerouslyAllowDelete = queryFlag(req.query["dangerouslyAllowDelete"]); const result = await new ImportService({ accountability: req.accountability, schema: req.schema }).importBatch(value, { mode, dryRun, dangerouslyAllowDelete }); res.locals["payload"] = { data: result }; return next(); }), respond); router.post("/export/:collection", collection_exists_default, async_handler_default(async (req, _res, next) => { if (!req.body.query) throw new InvalidPayloadError({ reason: `"query" is required` }); if (!req.body.format) throw new InvalidPayloadError({ reason: `"format" is required` }); const service = new ExportService({ accountability: req.accountability, schema: req.schema }); const sanitizedQuery = await sanitizeQuery(req.body.query, req.schema, req.accountability ?? null); service.exportToFile(req.params["collection"], sanitizedQuery, req.body.format, { file: req.body.file }); return next(); }), respond); router.post("/translations/generate", async_handler_default(async (req, res, next) => { if (!req.accountability?.admin) throw new ForbiddenError(); const result = await generateTranslations(req.body, { knex: getDatabase(), schema: req.schema, accountability: req.accountability }); try { await clearSystemCache({ forced: true }); } catch (error) { useLogger().error(error, "Failed to clear system cache after translation changes"); } res.locals["payload"] = { data: result }; return next(); }), respond); router.post("/cache/clear", async_handler_default(async (req, res) => { const service = new UtilsService({ accountability: req.accountability, schema: req.schema }); const clearSystemCache$1 = "system" in req.query && (req.query["system"] === "" || Boolean(req.query["system"])); await service.clearCache({ system: clearSystemCache$1 }); res.status(200).end(); })); router.post("/resolve-redirect", async_handler_default(async (req, res) => { if (!req.body?.redirect) throw new InvalidPayloadError({ reason: `"redirect" is required` }); if (req.body?.provider && typeof req.body.provider !== "string") throw new InvalidPayloadError({ reason: `"provider" must be a string` }); try { const resolved = resolveLoginRedirect(req.body.redirect, { provider: req.body.provider }); return res.json({ data: resolved }); } catch { throw new InvalidPayloadError({ reason: `Invalid "redirect" provided` }); } })); var utils_default = router; //#endregion export { utils_default as default };