mirror of
https://github.com/movie-web/movie-web.git
synced 2024-12-26 09:31:50 +01:00
Fixed hardcoded sitekey, fixed hasCaptcha being ignored, fixed setState while unmounted
This commit is contained in:
parent
a5512b95e5
commit
328414ab06
@ -5,6 +5,7 @@ export interface MetaResponse {
|
|||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
hasCaptcha: boolean;
|
hasCaptcha: boolean;
|
||||||
|
captchaClientKey?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getBackendMeta(url: string): Promise<MetaResponse> {
|
export async function getBackendMeta(url: string): Promise<MetaResponse> {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import { useCopyToClipboard } from "react-use";
|
import { useCopyToClipboard, useMountedState } from "react-use";
|
||||||
|
|
||||||
import { Icon, Icons } from "./Icon";
|
import { Icon, Icons } from "./Icon";
|
||||||
|
|
||||||
@ -9,6 +9,7 @@ export function PassphaseDisplay(props: { mnemonic: string }) {
|
|||||||
const [, copy] = useCopyToClipboard();
|
const [, copy] = useCopyToClipboard();
|
||||||
|
|
||||||
const [hasCopied, setHasCopied] = useState(false);
|
const [hasCopied, setHasCopied] = useState(false);
|
||||||
|
const isMounted = useMountedState();
|
||||||
|
|
||||||
const timeout = useRef<ReturnType<typeof setTimeout>>();
|
const timeout = useRef<ReturnType<typeof setTimeout>>();
|
||||||
|
|
||||||
@ -16,7 +17,7 @@ export function PassphaseDisplay(props: { mnemonic: string }) {
|
|||||||
copy(props.mnemonic);
|
copy(props.mnemonic);
|
||||||
setHasCopied(true);
|
setHasCopied(true);
|
||||||
if (timeout.current) clearTimeout(timeout.current);
|
if (timeout.current) clearTimeout(timeout.current);
|
||||||
timeout.current = setTimeout(() => setHasCopied(false), 500);
|
timeout.current = setTimeout(() => isMounted() && setHasCopied(false), 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -19,7 +19,7 @@ import { useBackendUrl } from "@/hooks/auth/useBackendUrl";
|
|||||||
import { useAuthStore } from "@/stores/auth";
|
import { useAuthStore } from "@/stores/auth";
|
||||||
|
|
||||||
export interface RegistrationData {
|
export interface RegistrationData {
|
||||||
recaptchaToken: string;
|
recaptchaToken?: string;
|
||||||
mnemonic: string;
|
mnemonic: string;
|
||||||
userData: {
|
userData: {
|
||||||
device: string;
|
device: string;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { GoogleReCaptchaProvider } from "react-google-recaptcha-v3";
|
import { GoogleReCaptchaProvider } from "react-google-recaptcha-v3";
|
||||||
|
|
||||||
|
import { MetaResponse } from "@/backend/accounts/meta";
|
||||||
import { SubPageLayout } from "@/pages/layouts/SubPageLayout";
|
import { SubPageLayout } from "@/pages/layouts/SubPageLayout";
|
||||||
import {
|
import {
|
||||||
AccountCreatePart,
|
AccountCreatePart,
|
||||||
@ -9,20 +10,38 @@ import {
|
|||||||
import { PassphraseGeneratePart } from "@/pages/parts/auth/PassphraseGeneratePart";
|
import { PassphraseGeneratePart } from "@/pages/parts/auth/PassphraseGeneratePart";
|
||||||
import { TrustBackendPart } from "@/pages/parts/auth/TrustBackendPart";
|
import { TrustBackendPart } from "@/pages/parts/auth/TrustBackendPart";
|
||||||
import { VerifyPassphrase } from "@/pages/parts/auth/VerifyPassphrasePart";
|
import { VerifyPassphrase } from "@/pages/parts/auth/VerifyPassphrasePart";
|
||||||
import { conf } from "@/setup/config";
|
|
||||||
|
function CaptchaProvider(props: {
|
||||||
|
siteKey: string | null;
|
||||||
|
children: JSX.Element;
|
||||||
|
}) {
|
||||||
|
if (!props.siteKey) return props.children;
|
||||||
|
return (
|
||||||
|
<GoogleReCaptchaProvider reCaptchaKey={props.siteKey}>
|
||||||
|
{props.children}
|
||||||
|
</GoogleReCaptchaProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function RegisterPage() {
|
export function RegisterPage() {
|
||||||
const [step, setStep] = useState(0);
|
const [step, setStep] = useState(0);
|
||||||
const [mnemonic, setMnemonic] = useState<null | string>(null);
|
const [mnemonic, setMnemonic] = useState<null | string>(null);
|
||||||
const [account, setAccount] = useState<null | AccountProfile>(null);
|
const [account, setAccount] = useState<null | AccountProfile>(null);
|
||||||
const reCaptchaKey = conf().RECAPTCHA_SITE_KEY;
|
const [siteKey, setSiteKey] = useState<string | null>(null);
|
||||||
|
|
||||||
|
// TODO because of user data loading (in useAuthRestore()), the register page gets unmounted before finishing the register flow
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GoogleReCaptchaProvider reCaptchaKey={reCaptchaKey}>
|
<CaptchaProvider siteKey={siteKey}>
|
||||||
<SubPageLayout>
|
<SubPageLayout>
|
||||||
{step === 0 ? (
|
{step === 0 ? (
|
||||||
<TrustBackendPart
|
<TrustBackendPart
|
||||||
onNext={() => {
|
onNext={(meta: MetaResponse) => {
|
||||||
|
setSiteKey(
|
||||||
|
meta.hasCaptcha && meta.captchaClientKey
|
||||||
|
? meta.captchaClientKey
|
||||||
|
: null
|
||||||
|
);
|
||||||
setStep(1);
|
setStep(1);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -45,6 +64,7 @@ export function RegisterPage() {
|
|||||||
) : null}
|
) : null}
|
||||||
{step === 3 ? (
|
{step === 3 ? (
|
||||||
<VerifyPassphrase
|
<VerifyPassphrase
|
||||||
|
hasCaptcha={!!siteKey}
|
||||||
mnemonic={mnemonic}
|
mnemonic={mnemonic}
|
||||||
userData={account}
|
userData={account}
|
||||||
onNext={() => {
|
onNext={() => {
|
||||||
@ -54,6 +74,6 @@ export function RegisterPage() {
|
|||||||
) : null}
|
) : null}
|
||||||
{step === 4 ? <p>Success, account now exists</p> : null}
|
{step === 4 ? <p>Success, account now exists</p> : null}
|
||||||
</SubPageLayout>
|
</SubPageLayout>
|
||||||
</GoogleReCaptchaProvider>
|
</CaptchaProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import { AccountProfile } from "@/pages/parts/auth/AccountCreatePart";
|
|||||||
|
|
||||||
interface VerifyPassphraseProps {
|
interface VerifyPassphraseProps {
|
||||||
mnemonic: string | null;
|
mnemonic: string | null;
|
||||||
|
hasCaptcha?: boolean;
|
||||||
userData: AccountProfile | null;
|
userData: AccountProfile | null;
|
||||||
onNext?: () => void;
|
onNext?: () => void;
|
||||||
}
|
}
|
||||||
@ -27,18 +28,20 @@ export function VerifyPassphrase(props: VerifyPassphraseProps) {
|
|||||||
|
|
||||||
const [result, execute] = useAsyncFn(
|
const [result, execute] = useAsyncFn(
|
||||||
async (inputMnemonic: string) => {
|
async (inputMnemonic: string) => {
|
||||||
const recaptchaToken = executeRecaptcha
|
|
||||||
? await executeRecaptcha()
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
if (!props.mnemonic || !props.userData)
|
if (!props.mnemonic || !props.userData)
|
||||||
throw new Error("Data is not valid");
|
throw new Error("Data is not valid");
|
||||||
if (!recaptchaToken) throw new Error("ReCaptcha validation failed");
|
|
||||||
|
let recaptchaToken: string | undefined;
|
||||||
|
if (props.hasCaptcha) {
|
||||||
|
recaptchaToken = executeRecaptcha
|
||||||
|
? await executeRecaptcha()
|
||||||
|
: undefined;
|
||||||
|
if (!recaptchaToken) throw new Error("ReCaptcha validation failed");
|
||||||
|
}
|
||||||
|
|
||||||
if (inputMnemonic !== props.mnemonic)
|
if (inputMnemonic !== props.mnemonic)
|
||||||
throw new Error("Passphrase doesn't match");
|
throw new Error("Passphrase doesn't match");
|
||||||
|
|
||||||
// TODO captcha?
|
|
||||||
|
|
||||||
await register({
|
await register({
|
||||||
mnemonic: inputMnemonic,
|
mnemonic: inputMnemonic,
|
||||||
userData: props.userData,
|
userData: props.userData,
|
||||||
|
@ -8,7 +8,6 @@ interface Config {
|
|||||||
CORS_PROXY_URL: string;
|
CORS_PROXY_URL: string;
|
||||||
NORMAL_ROUTER: boolean;
|
NORMAL_ROUTER: boolean;
|
||||||
BACKEND_URL: string;
|
BACKEND_URL: string;
|
||||||
RECAPTCHA_SITE_KEY: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RuntimeConfig {
|
export interface RuntimeConfig {
|
||||||
@ -19,7 +18,6 @@ export interface RuntimeConfig {
|
|||||||
NORMAL_ROUTER: boolean;
|
NORMAL_ROUTER: boolean;
|
||||||
PROXY_URLS: string[];
|
PROXY_URLS: string[];
|
||||||
BACKEND_URL: string;
|
BACKEND_URL: string;
|
||||||
RECAPTCHA_SITE_KEY: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const env: Record<keyof Config, undefined | string> = {
|
const env: Record<keyof Config, undefined | string> = {
|
||||||
@ -30,7 +28,6 @@ const env: Record<keyof Config, undefined | string> = {
|
|||||||
CORS_PROXY_URL: import.meta.env.VITE_CORS_PROXY_URL,
|
CORS_PROXY_URL: import.meta.env.VITE_CORS_PROXY_URL,
|
||||||
NORMAL_ROUTER: import.meta.env.VITE_NORMAL_ROUTER,
|
NORMAL_ROUTER: import.meta.env.VITE_NORMAL_ROUTER,
|
||||||
BACKEND_URL: import.meta.env.VITE_BACKEND_URL,
|
BACKEND_URL: import.meta.env.VITE_BACKEND_URL,
|
||||||
RECAPTCHA_SITE_KEY: import.meta.env.VITE_RECAPTCHA_SITE_KEY,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// loads from different locations, in order: environment (VITE_{KEY}), window (public/config.js)
|
// loads from different locations, in order: environment (VITE_{KEY}), window (public/config.js)
|
||||||
@ -56,6 +53,5 @@ export function conf(): RuntimeConfig {
|
|||||||
.split(",")
|
.split(",")
|
||||||
.map((v) => v.trim()),
|
.map((v) => v.trim()),
|
||||||
NORMAL_ROUTER: getKey("NORMAL_ROUTER", "false") === "true",
|
NORMAL_ROUTER: getKey("NORMAL_ROUTER", "false") === "true",
|
||||||
RECAPTCHA_SITE_KEY: getKey("RECAPTCHA_SITE_KEY"),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user