diff --git a/Accounts/src/Pages/Login.tsx b/Accounts/src/Pages/Login.tsx index 203464a947..9c2f8aef52 100644 --- a/Accounts/src/Pages/Login.tsx +++ b/Accounts/src/Pages/Login.tsx @@ -7,7 +7,12 @@ import { import Route from "Common/Types/API/Route"; import URL from "Common/Types/API/URL"; import { JSONArray, JSONObject } from "Common/Types/JSON"; -import ModelForm, { FormType, ModelField } from "Common/UI/Components/Forms/ModelForm"; +import ModelForm, { + FormType, + ModelField, +} from "Common/UI/Components/Forms/ModelForm"; +import { CustomElementProps } from "Common/UI/Components/Forms/Types/Field"; +import FormValues from "Common/UI/Components/Forms/Types/FormValues"; import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType"; import Link from "Common/UI/Components/Link/Link"; import Captcha from "Common/UI/Components/Captcha/Captcha"; @@ -85,8 +90,10 @@ const LoginPage: () => JSX.Element = () => { React.useState(false); const [captchaResetSignal, setCaptchaResetSignal] = React.useState(0); - const handleCaptchaReset = React.useCallback(() => { - setCaptchaResetSignal((current: number) => current + 1); + const handleCaptchaReset: () => void = React.useCallback(() => { + setCaptchaResetSignal((current: number) => { + return current + 1; + }); }, []); let loginFields: Array> = [ { @@ -134,17 +141,22 @@ const LoginPage: () => JSX.Element = () => { "Complete the captcha challenge so we know you're not a bot.", required: true, showEvenIfPermissionDoesNotExist: true, - getCustomElement: (_values, customProps) => ( - { - customProps.onChange?.(token); - }} - onBlur={customProps.onBlur} - /> - ), + getCustomElement: ( + _values: FormValues, + customProps: CustomElementProps, + ) => { + return ( + { + customProps.onChange?.(token); + }} + onBlur={customProps.onBlur} + /> + ); + }, }, ]); } diff --git a/Accounts/src/Pages/Register.tsx b/Accounts/src/Pages/Register.tsx index 9894656671..2730bbfdae 100644 --- a/Accounts/src/Pages/Register.tsx +++ b/Accounts/src/Pages/Register.tsx @@ -4,8 +4,13 @@ import URL from "Common/Types/API/URL"; import Dictionary from "Common/Types/Dictionary"; import { JSONObject } from "Common/Types/JSON"; import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage"; -import ModelForm, { FormType, ModelField } from "Common/UI/Components/Forms/ModelForm"; +import ModelForm, { + FormType, + ModelField, +} from "Common/UI/Components/Forms/ModelForm"; +import { CustomElementProps } from "Common/UI/Components/Forms/Types/Field"; import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType"; +import FormValues from "Common/UI/Components/Forms/Types/FormValues"; import Link from "Common/UI/Components/Link/Link"; import PageLoader from "Common/UI/Components/Loader/PageLoader"; import Captcha from "Common/UI/Components/Captcha/Captcha"; @@ -48,8 +53,10 @@ const RegisterPage: () => JSX.Element = () => { React.useState(false); const [captchaResetSignal, setCaptchaResetSignal] = React.useState(0); - const handleCaptchaReset = React.useCallback(() => { - setCaptchaResetSignal((current: number) => current + 1); + const handleCaptchaReset: () => void = React.useCallback(() => { + setCaptchaResetSignal((current: number) => { + return current + 1; + }); }, []); if (UserUtil.isLoggedIn()) { @@ -212,17 +219,22 @@ const RegisterPage: () => JSX.Element = () => { "Complete the captcha challenge so we know you're not a bot.", required: true, showEvenIfPermissionDoesNotExist: true, - getCustomElement: (_values, customProps) => ( - { - customProps.onChange?.(token); - }} - onBlur={customProps.onBlur} - /> - ), + getCustomElement: ( + _values: FormValues, + customProps: CustomElementProps, + ) => { + return ( + { + customProps.onChange?.(token); + }} + onBlur={customProps.onBlur} + /> + ); + }, }, ]); } @@ -266,7 +278,10 @@ const RegisterPage: () => JSX.Element = () => { maxPrimaryButtonWidth={true} fields={formFields} createOrUpdateApiUrl={apiUrl} - onBeforeCreate={(item: User, miscDataProps: JSONObject): Promise => { + onBeforeCreate={( + item: User, + miscDataProps: JSONObject, + ): Promise => { if (isCaptchaEnabled) { const captchaToken: string | undefined = ( miscDataProps["captchaToken"] as string | undefined diff --git a/Common/Server/EnvironmentConfig.ts b/Common/Server/EnvironmentConfig.ts index e5bd8750d2..f8216f2629 100644 --- a/Common/Server/EnvironmentConfig.ts +++ b/Common/Server/EnvironmentConfig.ts @@ -329,11 +329,9 @@ export const ProvisionSsl: boolean = process.env["PROVISION_SSL"] === "true"; export const CaptchaEnabled: boolean = process.env["CAPTCHA_ENABLED"] === "true"; -export const CaptchaSecretKey: string = - process.env["CAPTCHA_SECRET_KEY"] || ""; +export const CaptchaSecretKey: string = process.env["CAPTCHA_SECRET_KEY"] || ""; -export const CaptchaSiteKey: string = - process.env["CAPTCHA_SITE_KEY"] || ""; +export const CaptchaSiteKey: string = process.env["CAPTCHA_SITE_KEY"] || ""; export const WorkflowScriptTimeoutInMS: number = process.env[ "WORKFLOW_SCRIPT_TIMEOUT_IN_MS" diff --git a/Common/Server/Utils/Captcha.ts b/Common/Server/Utils/Captcha.ts index a4e1e3eec3..897d3733a2 100644 --- a/Common/Server/Utils/Captcha.ts +++ b/Common/Server/Utils/Captcha.ts @@ -1,4 +1,4 @@ -import axios, { AxiosError } from "axios"; +import axios, { AxiosError, AxiosResponse } from "axios"; import BadDataException from "../../Types/Exception/BadDataException"; import logger from "./Logger"; import { CaptchaEnabled, CaptchaSecretKey } from "../EnvironmentConfig"; @@ -12,6 +12,11 @@ const REQUEST_TIMEOUT_MS: number = 5000; const GENERIC_ERROR_MESSAGE: string = "Captcha verification failed. Please try again."; +type HCaptchaResponse = { + success?: boolean; + [key: string]: unknown; +}; + class CaptchaUtil { public static isCaptchaEnabled(): boolean { return CaptchaEnabled && Boolean(CaptchaSecretKey); @@ -69,16 +74,17 @@ class CaptchaUtil { params.append("remoteip", remoteIp); } - const response = await axios.post( - "https://hcaptcha.com/siteverify", - params.toString(), - { - headers: { - "content-type": "application/x-www-form-urlencoded", + const response: AxiosResponse = + await axios.post( + "https://hcaptcha.com/siteverify", + params.toString(), + { + headers: { + "content-type": "application/x-www-form-urlencoded", + }, + timeout: REQUEST_TIMEOUT_MS, }, - timeout: REQUEST_TIMEOUT_MS, - }, - ); + ); if (!response.data?.success) { logger.warn( diff --git a/Common/Server/Utils/Greenlock/Greenlock.ts b/Common/Server/Utils/Greenlock/Greenlock.ts index f37a518a15..fdbec685ff 100644 --- a/Common/Server/Utils/Greenlock/Greenlock.ts +++ b/Common/Server/Utils/Greenlock/Greenlock.ts @@ -37,8 +37,8 @@ export default class GreenlockUtil { expiresAt: QueryHelper.lessThanEqualTo( OneUptimeDate.addRemoveDays( OneUptimeDate.getCurrentDate(), - 40 // 40 days before expiry - ) + 40, // 40 days before expiry + ), ), }, limit: LIMIT_MAX, @@ -55,7 +55,7 @@ export default class GreenlockUtil { }); logger.debug( - `Found ${certificates.length} certificates which are expiring soon` + `Found ${certificates.length} certificates which are expiring soon`, ); // order certificate for each domain @@ -70,12 +70,12 @@ export default class GreenlockUtil { try { //validate cname const isValidCname: boolean = await data.validateCname( - certificate.domain + certificate.domain, ); if (!isValidCname) { logger.debug( - `CNAME is not valid for domain: ${certificate.domain}` + `CNAME is not valid for domain: ${certificate.domain}`, ); // if cname is not valid then remove the domain @@ -83,7 +83,7 @@ export default class GreenlockUtil { await data.notifyDomainRemoved(certificate.domain); logger.error( - `Cname is not valid for domain: ${certificate.domain}` + `Cname is not valid for domain: ${certificate.domain}`, ); } else { logger.debug(`CNAME is valid for domain: ${certificate.domain}`); @@ -94,12 +94,12 @@ export default class GreenlockUtil { }); logger.debug( - `Certificate renewed for domain: ${certificate.domain}` + `Certificate renewed for domain: ${certificate.domain}`, ); } } catch (e) { logger.error( - `Error renewing certificate for domain: ${certificate.domain}` + `Error renewing certificate for domain: ${certificate.domain}`, ); logger.error(e); } @@ -139,7 +139,7 @@ export default class GreenlockUtil { }): Promise { try { logger.debug( - `GreenlockUtil - Ordering certificate for domain: ${data.domain}` + `GreenlockUtil - Ordering certificate for domain: ${data.domain}`, ); let { domain } = data; @@ -150,13 +150,13 @@ export default class GreenlockUtil { if (!acmeAccountKeyInBase64) { throw new ServerException( - "No lets encrypt account key found in environment variables. Please add one." + "No lets encrypt account key found in environment variables. Please add one.", ); } let acmeAccountKey: string = Buffer.from( acmeAccountKeyInBase64, - "base64" + "base64", ).toString(); acmeAccountKey = Text.replaceAll(acmeAccountKey, "\\n", "\n"); @@ -197,13 +197,13 @@ export default class GreenlockUtil { challengeCreateFn: async ( authz: acme.Authorization, challenge: Challenge, - keyAuthorization: string + keyAuthorization: string, ) => { // Satisfy challenge here /* http-01 */ if (challenge.type === "http-01") { logger.debug( - `Creating challenge for domain: ${authz.identifier.value}` + `Creating challenge for domain: ${authz.identifier.value}`, ); const acmeChallenge: AcmeChallenge = new AcmeChallenge(); @@ -219,18 +219,18 @@ export default class GreenlockUtil { }); logger.debug( - `Challenge created for domain: ${authz.identifier.value}` + `Challenge created for domain: ${authz.identifier.value}`, ); } }, challengeRemoveFn: async ( authz: acme.Authorization, - challenge: Challenge + challenge: Challenge, ) => { // Clean up challenge here logger.debug( - `Removing challenge for domain: ${authz.identifier.value}` + `Removing challenge for domain: ${authz.identifier.value}`, ); if (challenge.type === "http-01") { @@ -247,7 +247,7 @@ export default class GreenlockUtil { } logger.debug( - `Challenge removed for domain: ${authz.identifier.value}` + `Challenge removed for domain: ${authz.identifier.value}`, ); }, }); @@ -328,11 +328,11 @@ export default class GreenlockUtil { if (IsBillingEnabled) { throw new ServerException( - `Unable to order certificate for ${data.domain}. Please contact support at support@oneuptime.com for more information.` + `Unable to order certificate for ${data.domain}. Please contact support at support@oneuptime.com for more information.`, ); } else { throw new ServerException( - `Unable to order certificate for ${data.domain}. Please make sure that your server can be accessed publicly over port 80 (HTTP) and port 443 (HTTPS). If the problem persists, please refer to server logs for more information. Please also set up LOG_LEVEL=DEBUG to get more detailed server logs.` + `Unable to order certificate for ${data.domain}. Please make sure that your server can be accessed publicly over port 80 (HTTP) and port 443 (HTTPS). If the problem persists, please refer to server logs for more information. Please also set up LOG_LEVEL=DEBUG to get more detailed server logs.`, ); } } diff --git a/Common/UI/Components/Captcha/Captcha.tsx b/Common/UI/Components/Captcha/Captcha.tsx index aea5040f3e..eb13477b69 100644 --- a/Common/UI/Components/Captcha/Captcha.tsx +++ b/Common/UI/Components/Captcha/Captcha.tsx @@ -18,16 +18,22 @@ const Captcha: React.FC = ({ onBlur, className, }: CaptchaProps): JSX.Element => { - const captchaRef = React.useRef(null); - const onTokenChangeRef = React.useRef(onTokenChange); + const captchaRef: React.MutableRefObject = + React.useRef(null); + const onTokenChangeRef: React.MutableRefObject< + CaptchaProps["onTokenChange"] + > = React.useRef(onTokenChange); React.useEffect(() => { onTokenChangeRef.current = onTokenChange; }, [onTokenChange]); - const handleTokenChange = React.useCallback((token: string | null) => { - onTokenChangeRef.current?.(token || ""); - }, []); + const handleTokenChange: (token: string | null) => void = React.useCallback( + (token: string | null) => { + onTokenChangeRef.current?.(token || ""); + }, + [], + ); React.useEffect(() => { captchaRef.current?.resetCaptcha();