From 5413fef3e3ace5d378b8b27781dbcbc9b1d5b4d9 Mon Sep 17 00:00:00 2001 From: Xevion Date: Thu, 19 Dec 2024 16:36:19 -0600 Subject: [PATCH] Add 'revalidate' route, add DIRECTUS_REVALIDATE_KEY --- .env.example | 1 + src/env/schema.mjs | 1 + src/pages/api/revalidate.ts | 102 ++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 src/pages/api/revalidate.ts diff --git a/.env.example b/.env.example index 38b70c1..bed3a81 100644 --- a/.env.example +++ b/.env.example @@ -9,3 +9,4 @@ # Example: # SERVERVAR=foo # NEXT_PUBLIC_CLIENTVAR=bar +DIRECTUS_REVALIDATE_KEY= diff --git a/src/env/schema.mjs b/src/env/schema.mjs index c621d13..05c6629 100644 --- a/src/env/schema.mjs +++ b/src/env/schema.mjs @@ -6,6 +6,7 @@ import { z } from "zod"; * This way you can ensure the app isn't built with invalid env vars. */ export const serverSchema = z.object({ + DIRECTUS_REVALIDATE_KEY: z.string(), NODE_ENV: z.enum(["development", "test", "production"]), }); diff --git a/src/pages/api/revalidate.ts b/src/pages/api/revalidate.ts new file mode 100644 index 0000000..6bb0d28 --- /dev/null +++ b/src/pages/api/revalidate.ts @@ -0,0 +1,102 @@ +import { readItem, readItems } from "@directus/sdk"; +import type { NextApiRequest, NextApiResponse } from "next"; +import { z } from "zod"; +import directus from "../../utils/directus"; + +async function getURLs( + type: string, + key: string, + payload: Map, +): Promise { + if (type == "project_link" || type == "project_technology") { + console.error({ + message: `Failed to provide URls for '${type}' type`, + type, + key, + payload, + }); + return []; + } + + if (type === "project") return ["/projects", `/projects/${key}`]; + if (type === "metadata") return ["/"]; + if (type === "technology") { + const urls = ["/technology"]; + + // Get all projects with the technology + const all_projects = await directus.request(readItems("project")); + if (all_projects != null) { + for (const project of all_projects) { + if (project.technologies?.some((t) => t.id === key)) + urls.push(`/projects/${project.id}`); + } + } + + return urls; + } + + if (type === "projects") { + const urls = ["/projects", `/projects/${key}`]; + // TODO: If 'featured', index page should be revalidated + + const project = await directus.request(readItem("project", key)); + if (project != null) return urls; + } + + return null; +} + +const requestSchema = z.object({ + type: z.string(), + keys: z.array(z.string()).min(1), + source: z.map(z.string(), z.any()), + payload: z.map(z.string(), z.any()), +}); + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse, +) { + if (req.method !== "POST") + return res.status(405).json({ message: "Method not allowed" }); + + if ( + req.headers["Authorization"] !== + "Bearer " + process.env.DIRECTUS_REVALIDATE_KEY + ) + return res.status(401).json({ message: "Invalid token" }); + + try { + // Verify JSON body + const { success, data } = requestSchema.safeParse(req.body); + if (!success) return res.status(400).json({ message: "Invalid JSON body" }); + + // Get URLs + const urls = await getURLs(data.type, data.keys[0]!, data.payload); + if (urls === null) + return res + .status(404) + .json({ revalidated: false, message: "Collection not found" }); + + // Revalidate all URLs + try { + await Promise.all(urls.map((url) => res.revalidate(url))); + } catch (error) { + console.error({ message: "Error while revalidating", error }); + return res.status(500).json({ + revalidated: false, + message: "Error while revalidating", + urls, + }); + } + + // Return success + return res.json({ revalidated: true, urls }); + } catch (error) { + console.error({ + message: "Error while preparing to revalidate", + error, + }); + return res.status(500).send("Error revalidating"); + } +}