From 856c37410912826ef34d0c6cee4d5384540a2712 Mon Sep 17 00:00:00 2001 From: Pierniki Date: Tue, 5 Sep 2023 15:40:46 +0200 Subject: [PATCH] refactor: make context generic --- app/api/algolia-webhook/publish/route.ts | 41 +++++++++++++++++++ app/api/algolia-webhook/unpublish/route.ts | 41 +++++++++++++++++++ app/api/algolia-webhook/withBodySchema.ts | 13 +++--- app/api/algolia-webhook/withValidSignature.ts | 4 +- 4 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 app/api/algolia-webhook/publish/route.ts create mode 100644 app/api/algolia-webhook/unpublish/route.ts diff --git a/app/api/algolia-webhook/publish/route.ts b/app/api/algolia-webhook/publish/route.ts new file mode 100644 index 00000000..5d4d2355 --- /dev/null +++ b/app/api/algolia-webhook/publish/route.ts @@ -0,0 +1,41 @@ +import algolia from "algoliasearch" +import { env } from "env.mjs" +import { NextResponse } from "next/server" +import { slateToText } from "utils/slateToText" +import { z } from "zod" +import { withValidSignature } from "../withValidSignature" +import { NextRequestWithValidBody, withBodySchema } from "../withBodySchema" + +const client = algolia(env.ALGOLIA_API_ID, env.ALGOLIA_API_KEY) + +async function handleAlgoliaWebhook(req: NextRequestWithValidBody>) { + try { + const article = req.validBody.data + + const indexingResults = await Promise.allSettled( + article.localizations.map(async ({ locale, title, content }) => { + const index = client.initIndex(`articles-${locale}`) + await index.saveObject({ + objectID: article.id, + title, + content: slateToText(content), + }) + + return { title, locale } + }) + ) + + return NextResponse.json({ result: indexingResults }, { status: 201 }) + } catch (err) { + return NextResponse.json({ message: "Unexpected Error" }, { status: 500 }) + } +} + +const bodySchema = z.object({ + data: z.object({ + localizations: z.array(z.object({ content: z.any(), title: z.string(), locale: z.string() })), + id: z.string(), + }), +}) + +export const POST = withValidSignature(withBodySchema(handleAlgoliaWebhook, bodySchema)) diff --git a/app/api/algolia-webhook/unpublish/route.ts b/app/api/algolia-webhook/unpublish/route.ts new file mode 100644 index 00000000..5d4d2355 --- /dev/null +++ b/app/api/algolia-webhook/unpublish/route.ts @@ -0,0 +1,41 @@ +import algolia from "algoliasearch" +import { env } from "env.mjs" +import { NextResponse } from "next/server" +import { slateToText } from "utils/slateToText" +import { z } from "zod" +import { withValidSignature } from "../withValidSignature" +import { NextRequestWithValidBody, withBodySchema } from "../withBodySchema" + +const client = algolia(env.ALGOLIA_API_ID, env.ALGOLIA_API_KEY) + +async function handleAlgoliaWebhook(req: NextRequestWithValidBody>) { + try { + const article = req.validBody.data + + const indexingResults = await Promise.allSettled( + article.localizations.map(async ({ locale, title, content }) => { + const index = client.initIndex(`articles-${locale}`) + await index.saveObject({ + objectID: article.id, + title, + content: slateToText(content), + }) + + return { title, locale } + }) + ) + + return NextResponse.json({ result: indexingResults }, { status: 201 }) + } catch (err) { + return NextResponse.json({ message: "Unexpected Error" }, { status: 500 }) + } +} + +const bodySchema = z.object({ + data: z.object({ + localizations: z.array(z.object({ content: z.any(), title: z.string(), locale: z.string() })), + id: z.string(), + }), +}) + +export const POST = withValidSignature(withBodySchema(handleAlgoliaWebhook, bodySchema)) diff --git a/app/api/algolia-webhook/withBodySchema.ts b/app/api/algolia-webhook/withBodySchema.ts index 87ea7159..80b6d462 100644 --- a/app/api/algolia-webhook/withBodySchema.ts +++ b/app/api/algolia-webhook/withBodySchema.ts @@ -1,15 +1,18 @@ -import { NextResponse, NextRequest } from "next/server" -import { NextRequestWithBody, hasParsedBody } from "./withValidSignature" +import { NextRequest, NextResponse } from "next/server" +import { hasParsedBody, NextRequestWithBody } from "./withValidSignature" export const withBodySchema = - (fun: (req: NextRequestWithValidBody, context?: any) => Promise, schema: Zod.Schema) => - async (req: NextRequest | NextRequestWithBody, context?: any) => { + ( + fun: (req: NextRequestWithValidBody, context?: TContext) => Promise, + schema: Zod.Schema + ) => + async (req: NextRequest | NextRequestWithBody, context?: TContext) => { try { const hasBody = hasParsedBody(req) const parseResult = schema.safeParse(hasBody ? req.body : await req.json()) if (!parseResult.success) return NextResponse.json({ message: "Bad Request" }, { status: 400 }) - const reqWithBody: NextRequestWithValidBody = Object.assign(req, { validBody: parseResult.data }) + const reqWithBody: NextRequestWithValidBody = Object.assign(req, { validBody: parseResult.data }) return fun(reqWithBody, context) } catch { return NextResponse.json({ message: "Bad Request" }, { status: 400 }) diff --git a/app/api/algolia-webhook/withValidSignature.ts b/app/api/algolia-webhook/withValidSignature.ts index 5c730659..762f4d44 100644 --- a/app/api/algolia-webhook/withValidSignature.ts +++ b/app/api/algolia-webhook/withValidSignature.ts @@ -3,8 +3,8 @@ import { env } from "env.mjs" import { NextRequest, NextResponse } from "next/server" export const withValidSignature = - (fun: (req: NextRequestWithBody, context?: any) => Promise) => - async (req: NextRequest, context?: any) => { + (fun: (req: NextRequestWithBody, context?: TContext) => Promise) => + async (req: NextRequest, context?: TContext) => { const authHeader = req.headers.get("gcms-signature") if (!authHeader) return NextResponse.json({ message: "Unauthorized" }, { status: 401 }) const parsedBody = await req.json()