speedclimbing/website

View on GitHub
src/routes/about-us/contact/+server.ts

Summary

Maintainability
A
0 mins
Test Coverage
import type { RequestHandler } from './$types';

const respond = (result: string, headers: Headers) => {
    if (headers.has('Accept') && headers.get('Accept') == 'application/json') {
        return new Response(JSON.stringify({ result: result }), {
            status: 200,
            headers: {
                'Content-Type': 'application/json'
            }
        });
    }

    return new Response('Redirect', {
        status: 303,
        headers: { Location: `/about-us?contact-result=${result}#contact` }
    });
};

interface TokenValidateResponse {
    'error-codes': string[];
    success: boolean;
    action: string;
    cdata: string;
}

async function verifyCfTurnstileResponse(token: string, secret: string) {
    const response = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
        method: 'POST',
        headers: {
            'content-type': 'application/json'
        },
        body: JSON.stringify({
            response: token,
            secret: secret
        })
    });

    const data: TokenValidateResponse = await response.json();

    return data.success;
}

const EMAIL_REGEX =
    /^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;

interface ContactFormSubmission {
    senderEmail: string;
    senderName: string;
    senderMessage: string;
}

async function sendEmail(formrecevrUrl: string, form: ContactFormSubmission) {
    let formData = new FormData();
    formData.set('email', form.senderEmail);
    formData.set('name', form.senderName);
    formData.set('message', form.senderMessage);

    let response = await fetch(formrecevrUrl, {
        method: 'POST',
        body: formData,
        headers: { Accept: 'application/json' }
    });

    return response.ok;
}

const validateFormData = async (
    formData: FormData,
    turnstileSecretKey?: string
): Promise<ContactFormSubmission | false> => {
    const turnstileResponse = formData.get('cf-turnstile-response')?.toString() ?? '';
    if (
        turnstileSecretKey &&
        !(await verifyCfTurnstileResponse(turnstileResponse, turnstileSecretKey))
    ) {
        return false;
    }

    const senderEmail = formData.get('email')?.toString();
    const senderName = formData
        .get('name')
        ?.toString()
        .replaceAll(/[^A-Za-z- ]/g, '');
    const senderMessage = formData.get('message')?.toString();

    if (!senderName || !senderMessage || !senderEmail || !senderEmail.match(EMAIL_REGEX)) {
        return false;
    }

    return {
        senderEmail,
        senderName,
        senderMessage
    };
};

const handleFormSubmission = async (
    formData: FormData,
    env?: { TURNSTILE_SECRET_KEY: string; FORMRECEVR_URL: string }
) => {
    const contactFormSubmission = await validateFormData(formData, env?.TURNSTILE_SECRET_KEY);
    if (!contactFormSubmission) {
        return 'invalid-form';
    }

    if (!env?.FORMRECEVR_URL) {
        return 'error';
    }

    const { senderEmail, senderName, senderMessage } = contactFormSubmission;

    const result = await sendEmail(env?.FORMRECEVR_URL, {
        senderEmail,
        senderName,
        senderMessage
    });

    return result ? 'success' : 'error';
};

export const POST: RequestHandler = async (event) => {
    const formData = await event.request.formData();

    const result = await handleFormSubmission(formData, event.platform?.env);

    return respond(result, event.request.headers);
};