import 'server-only';
import { redirect } from 'next/navigation';
import { NextRequest, NextResponse } from 'next/server';
import { User } from '@supabase/supabase-js';
import { z } from 'zod';
import { verifyCaptchaToken } from '@kit/auth/captcha/server';
import { requireUser } from '@kit/supabase/require-user';
import { getSupabaseRouteHandlerClient } from '@kit/supabase/route-handler-client';
import { captureException, zodParseFactory } from '../utils';
interface HandlerParams
{
request: NextRequest;
user: User;
body: Body;
}
/**
* Enhanced route handler function.
*
* This function takes a request and parameters object as arguments and returns a route handler function.
* The route handler function can be used to handle HTTP requests and apply additional enhancements
* based on the provided parameters.
*
* Usage:
* export const POST = enhanceRouteHandler(
* ({ request, body, user }) => {
* return new Response(`Hello, ${body.name}!`);
* },
* {
* schema: z.object({
* name: z.string(),
* }),
* },
* );
*
*/
export const enhanceRouteHandler = <
Body,
Schema extends z.ZodType,
>(
// Route handler function
handler:
| ((params: HandlerParams>) => NextResponse | Response)
| ((
params: HandlerParams>,
) => Promise),
// Parameters object
params?: {
captcha?: boolean;
captureException?: boolean;
schema?: Schema;
},
) => {
/**
* Route handler function.
*
* This function takes a request object as an argument and returns a response object.
*/
return async function routeHandler(request: NextRequest) {
// Check if the captcha token should be verified
const shouldVerifyCaptcha = params?.captcha ?? false;
// Verify the captcha token if required and setup
if (shouldVerifyCaptcha) {
const token = captchaTokenGetter(request);
// If the captcha token is not provided, return a 400 response.
if (token) {
await verifyCaptchaToken(token);
} else {
return new Response('Captcha token is required', { status: 400 });
}
}
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 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();
if (params?.schema) {
body = zodParseFactory(params.schema)(body);
}
const shouldCaptureException = params?.captureException ?? true;
if (shouldCaptureException) {
try {
return await handler({ request, body, user });
} catch (error) {
// capture the exception
await captureException(error);
throw error;
}
} else {
// all good, call the handler with the request, body and user
return handler({ request, body, user });
}
};
};
/**
* Get the captcha token from the request headers.
* @param request
*/
function captchaTokenGetter(request: NextRequest) {
return request.headers.get('x-captcha-token');
}