From 1eb7154c898d2fea0fdbb48db8d1ffcab5eeff4f Mon Sep 17 00:00:00 2001 From: Ross Date: Thu, 5 May 2022 12:49:02 +0200 Subject: [PATCH] fixup captcha/response handling --- src/controller/index.ts | 4 +-- src/interfaces.ts | 2 +- src/scripts/contact.ts | 64 ++++++++++++++++++++++------------------- 3 files changed, 38 insertions(+), 32 deletions(-) diff --git a/src/controller/index.ts b/src/controller/index.ts index b45de4d8..3b4cca34 100644 --- a/src/controller/index.ts +++ b/src/controller/index.ts @@ -2,7 +2,7 @@ import * as Joi from 'joi'; import { logger } from '../logging'; import { Config } from '../config'; -import { Response, ResponseMessage, ContactFormRequest, ValidatedFormData } from '../interfaces'; +import { ResponseData, ResponseMessage, ContactFormRequest, ValidatedFormData } from '../interfaces'; import { contactMailer, EmailPlaintext } from '../mailer'; import { getAggregatedFeed } from '../blog'; import { getUserReposWithStars } from '../github'; @@ -32,7 +32,7 @@ const contactFormSchema = Joi.object() const captchaStore = new CaptchaStore(); -function getResponseObj(success: boolean, msg: ResponseMessage | ResponseMessage[]): Response { +function getResponseObj(success: boolean, msg: ResponseMessage | ResponseMessage[]): ResponseData { // If we were passed a single Message object, put it in an array for standardisation if (!Array.isArray(msg)) { msg = [msg]; diff --git a/src/interfaces.ts b/src/interfaces.ts index d00480e5..52a97572 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -3,7 +3,7 @@ export interface ResponseMessage { text: string; } -export interface Response { +export interface ResponseData { success: boolean; messages: ResponseMessage[]; } diff --git a/src/scripts/contact.ts b/src/scripts/contact.ts index 5750644f..f7f1330c 100644 --- a/src/scripts/contact.ts +++ b/src/scripts/contact.ts @@ -1,11 +1,14 @@ import axios, { AxiosResponse } from 'axios'; -import { Response, ContactFormRequest } from '../interfaces'; +import { ResponseData, ContactFormRequest } from '../interfaces'; import { ErrorMessages } from '../errors'; import { Config } from '../config'; const CAPTCHA_ID = 'captcha-img'; +// Exclude resetting labels for the following field IDs +const EXCLUDE_LABELS_FOR = ['captcha'] + // A basic contact form class export default class ContactForm { private readonly _form: HTMLFormElement; @@ -18,43 +21,44 @@ export default class ContactForm { // Make formLabels object where key is the label's 'for' element name and value is the HTMLLabelElement this._formLabels = {}; const labels = this._form.getElementsByTagName('label'); - for (let i = 0; i < labels.length; i++) { - const key = labels[i].htmlFor; - this._formLabels[key] = labels[i]; + for ( let i = 0; i < labels.length; i++ ) { + const key = labels[ i ].htmlFor; + if (!EXCLUDE_LABELS_FOR.includes( key ) ) { + this._formLabels[key] = labels[i]; + } } this._formSubmitBtn = document.getElementById(formId + '-submit-btn'); } - displayResponse(response: Response): void { + displayResponse(responseData: ResponseData): void { let target: HTMLElement; - - response.messages.forEach((message) => { + responseData.messages.forEach( ( message ) => { // Check if target has an associated label to use first - if (message.target in this._formLabels) { - target = this._formLabels[message.target]; + if ( message.target in this._formLabels ) { + target = this._formLabels[ message.target ]; } else { // No label, so our target is the actual element - target = this._form.elements[message.target]; + target = this._form.elements[ message.target ]; } target.innerHTML = message.text; // Add a class so we can colourise the target - if (response.success) { - target.classList.add('success'); + if ( responseData.success ) { + target.classList.add( 'success' ); } else { - target.classList.add('error'); + target.classList.add( 'error' ); target.scrollIntoView(); } - }); + } ); } - handleResponse(response: Response): void { - this.displayResponse(response); + handleResponseData ( responseData: ResponseData ): void { + this.displayResponse(responseData); // If it was a success response, reset the form - if (response.success) { + if (responseData.success) { this.resetInput(); this.resetLabels(); } @@ -79,17 +83,17 @@ export default class ContactForm { xsrfCookieName: 'XSRF-TOKEN', }) .then((response: AxiosResponse) => { - const resp: Response = response.data; - const captchaBas64 = resp.messages[0].text; + const data: ResponseData = response.data; + const captchaBas64 = data.messages[0].text; const captchaImg = document.getElementById(CAPTCHA_ID) as HTMLImageElement; captchaImg.src = captchaBas64; this._form.captcha.value = ''; // Remove old input for user convenience }) .catch((error) => { - let response; - if (error.response) { + let data: ResponseData; + if (error.response === undefined) { // We got no response, so construct a response message client-side - response = { + data = { messages: [ { target: 'submit', @@ -97,9 +101,11 @@ export default class ContactForm { }, ], }; + } else { + data = error.response.data; } - this.handleResponse(response); + this.handleResponseData(data); }); } resetLabels(): void { @@ -191,13 +197,13 @@ export default class ContactForm { timeout: Config.formSubmitTimeoutMs, }) .then((response: AxiosResponse) => { - this.handleResponse(response.data); + this.handleResponseData(response.data); }) .catch((error) => { - let response; - if (!error.response) { + let data: ResponseData; + if (error.response === undefined) { // We got no response, so construct a response message client-side - response = { + data = { messages: [ { target: 'submit', @@ -206,10 +212,10 @@ export default class ContactForm { ], }; } else { - response = error.response.data; + data = error.response.data; } - this.handleResponse(response); + this.handleResponseData(data); }); this.refreshCaptcha(csrfToken);