diff --git a/apps/web/app/api/billing/webhook/route.ts b/apps/web/app/api/billing/webhook/route.ts index 1657b7bc3..aef993b06 100644 --- a/apps/web/app/api/billing/webhook/route.ts +++ b/apps/web/app/api/billing/webhook/route.ts @@ -1,4 +1,5 @@ import { getBillingEventHandlerService } from '@kit/billing-gateway'; +import { enhanceRouteHandler } from '@kit/next/routes'; import { getLogger } from '@kit/shared/logger'; import { getSupabaseRouteHandlerClient } from '@kit/supabase/route-handler-client'; @@ -7,43 +8,42 @@ import billingConfig from '~/config/billing.config'; /** * @description Handle the webhooks from Stripe related to checkouts */ -export async function POST(request: Request) { - const provider = billingConfig.provider; - const logger = await getLogger(); +export const POST = enhanceRouteHandler( + async ({ request }) => { + const provider = billingConfig.provider; + const logger = await getLogger(); - const ctx = { - name: 'billing.webhook', - provider, - }; + const ctx = { + name: 'billing.webhook', + provider, + }; - logger.info(ctx, `Received billing webhook. Processing...`); + logger.info(ctx, `Received billing webhook. Processing...`); - const supabaseClientProvider = () => - getSupabaseRouteHandlerClient({ admin: true }); + const supabaseClientProvider = () => + getSupabaseRouteHandlerClient({ admin: true }); - const service = await getBillingEventHandlerService( - supabaseClientProvider, - provider, - billingConfig, - ); - - try { - await service.handleWebhookEvent(request); - - logger.info(ctx, `Successfully processed billing webhook`); - - return new Response('OK', { status: 200 }); - } catch (error) { - console.error(error); - - logger.error( - { - ...ctx, - error: JSON.stringify(error), - }, - `Failed to process billing webhook`, + const service = await getBillingEventHandlerService( + supabaseClientProvider, + provider, + billingConfig, ); - return new Response('Error', { status: 500 }); - } -} + try { + await service.handleWebhookEvent(request); + + logger.info(ctx, `Successfully processed billing webhook`); + + return new Response('OK', { status: 200 }); + } catch (error) { + logger.error(ctx, `Failed to process billing webhook`, error); + + return new Response('Failed to process billing webhook', { + status: 500, + }); + } + }, + { + auth: false, + }, +); diff --git a/apps/web/app/api/db/webhook/route.ts b/apps/web/app/api/db/webhook/route.ts index b38294823..7b10316d3 100644 --- a/apps/web/app/api/db/webhook/route.ts +++ b/apps/web/app/api/db/webhook/route.ts @@ -1,18 +1,21 @@ import { getDatabaseWebhookHandlerService } from '@kit/database-webhooks'; +import { enhanceRouteHandler } from '@kit/next/routes'; /** * @name POST * @description POST handler for the webhook route that handles the webhook event */ -export async function POST(request: Request) { +export const POST = enhanceRouteHandler(async ({ request }) => { const service = getDatabaseWebhookHandlerService(); try { // handle the webhook event await service.handleWebhook(request); + // return a successful response return new Response(null, { status: 200 }); - } catch { + } catch (error) { + // return an error response return new Response(null, { status: 500 }); } -} +}); diff --git a/packages/billing/stripe/src/services/stripe-webhook-handler.service.ts b/packages/billing/stripe/src/services/stripe-webhook-handler.service.ts index 9665ba704..816740e7f 100644 --- a/packages/billing/stripe/src/services/stripe-webhook-handler.service.ts +++ b/packages/billing/stripe/src/services/stripe-webhook-handler.service.ts @@ -45,7 +45,7 @@ export class StripeWebhookHandlerService const stripe = await this.loadStripe(); - const event = stripe.webhooks.constructEvent( + const event = await stripe.webhooks.constructEventAsync( body, signature, webhooksSecret, diff --git a/packages/cms/core/src/create-cms-client.ts b/packages/cms/core/src/create-cms-client.ts index c0cd6be6d..77b307224 100644 --- a/packages/cms/core/src/create-cms-client.ts +++ b/packages/cms/core/src/create-cms-client.ts @@ -28,9 +28,11 @@ async function cmsClientFactory(type: CmsType) { } async function getWordpressClient() { - const { WordpressClient } = await import('../../wordpress/src/wp-client'); + const { createWordpressClient } = await import( + '../../wordpress/src/wp-client' + ); - return new WordpressClient(); + return createWordpressClient(); } async function getKeystaticClient() { @@ -38,9 +40,11 @@ async function getKeystaticClient() { process.env.NEXT_RUNTIME === 'nodejs' || process.env.KEYSTATIC_STORAGE_KIND !== 'local' ) { - const { KeystaticClient } = await import('../../keystatic/src/client'); + const { createKeystaticClient } = await import( + '../../keystatic/src/keystatic-client' + ); - return new KeystaticClient(); + return createKeystaticClient(); } console.error( diff --git a/packages/cms/keystatic/src/index.ts b/packages/cms/keystatic/src/index.ts index 73fee8ee4..a88c01a47 100644 --- a/packages/cms/keystatic/src/index.ts +++ b/packages/cms/keystatic/src/index.ts @@ -1,2 +1,2 @@ -export * from './client'; +export * from './keystatic-client'; export * from './content-renderer'; diff --git a/packages/cms/keystatic/src/client.ts b/packages/cms/keystatic/src/keystatic-client.ts similarity index 97% rename from packages/cms/keystatic/src/client.ts rename to packages/cms/keystatic/src/keystatic-client.ts index c8555cb6b..8c1bf416e 100644 --- a/packages/cms/keystatic/src/client.ts +++ b/packages/cms/keystatic/src/keystatic-client.ts @@ -3,7 +3,11 @@ import { Cms, CmsClient } from '@kit/cms'; import { createKeystaticReader } from './create-reader'; import { PostEntryProps } from './keystatic.config'; -export class KeystaticClient implements CmsClient { +export function createKeystaticClient() { + return new KeystaticClient(); +} + +class KeystaticClient implements CmsClient { async getContentItems(options: Cms.GetContentItemsOptions) { const reader = await createKeystaticReader(); diff --git a/packages/cms/wordpress/src/wp-client.ts b/packages/cms/wordpress/src/wp-client.ts index b9b0d3a46..bf6d81755 100644 --- a/packages/cms/wordpress/src/wp-client.ts +++ b/packages/cms/wordpress/src/wp-client.ts @@ -8,17 +8,19 @@ import { Cms, CmsClient } from '@kit/cms'; import GetTagsOptions = Cms.GetTagsOptions; +export function createWordpressClient( + apiUrl = process.env.WORDPRESS_API_URL as string, +) { + return new WordpressClient(apiUrl); +} + /** * @name WordpressClient * @description Represents a client for interacting with a Wordpress CMS. * Implements the CmsClient interface. */ -export class WordpressClient implements CmsClient { - private readonly apiUrl: string; - - constructor(apiUrl = process.env.WORDPRESS_API_URL as string) { - this.apiUrl = apiUrl; - } +class WordpressClient implements CmsClient { + constructor(private readonly apiUrl: string) {} /** * Retrieves content items from a CMS based on the provided options. diff --git a/packages/next/src/routes/index.ts b/packages/next/src/routes/index.ts index e8a394ba5..f8edcc5dd 100644 --- a/packages/next/src/routes/index.ts +++ b/packages/next/src/routes/index.ts @@ -14,9 +14,19 @@ import { getSupabaseRouteHandlerClient } from '@kit/supabase/route-handler-clien import { captureException, zodParseFactory } from '../utils'; -interface HandlerParams { +interface Config { + auth?: boolean; + captcha?: boolean; + captureException?: boolean; + schema?: Schema; +} + +interface HandlerParams< + Body extends object, + RequireAuth extends boolean | undefined, +> { request: NextRequest; - user: User; + user: RequireAuth extends false ? undefined : User; body: Body; } @@ -41,22 +51,21 @@ interface HandlerParams { * */ export const enhanceRouteHandler = < - Body, + Body extends object, Schema extends z.ZodType, + Params extends Config = Config, >( // Route handler function handler: - | ((params: HandlerParams>) => NextResponse | Response) | (( - params: HandlerParams>, + params: HandlerParams, Params['auth']>, + ) => NextResponse | Response) + | (( + params: HandlerParams, Params['auth']>, ) => Promise), // Parameters object - params?: { - captcha?: boolean; - captureException?: boolean; - schema?: Schema; - }, + params?: Params, ) => { /** * Route handler function. @@ -64,6 +73,10 @@ export const enhanceRouteHandler = < * This function takes a request object as an argument and returns a response object. */ return async function routeHandler(request: NextRequest) { + type UserParam = Params['auth'] extends false ? undefined : User; + + let user: UserParam = undefined as UserParam; + // Check if the captcha token should be verified const shouldVerifyCaptcha = params?.captcha ?? false; @@ -80,15 +93,22 @@ export const enhanceRouteHandler = < } const client = getSupabaseRouteHandlerClient(); - const auth = await requireUser(client); - // If the user is not authenticated, redirect to the specified URL. - if (auth.error) { - return redirect(auth.redirectTo); + const shouldVerifyAuth = params?.auth ?? true; + + // Check if the user should be authenticated + if (shouldVerifyAuth) { + // Get the authenticated user + const auth = await requireUser(client); + + // If the user is not authenticated, redirect to the specified URL. + if (auth.error) { + return redirect(auth.redirectTo); + } + + user = auth.data as UserParam; } - const user = auth.data; - // clone the request to read the body // so that we can pass it to the handler safely let body = await request.clone().json();