cvsa/packages/next/app/[locale]/signup/request.tsx

163 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Dispatch, JSX, SetStateAction } from "react";
import { ApiRequestError, fetcher } from "@/lib/net";
import type { CaptchaVerificationRawResponse, ErrorResponse, SignUpResponse } from "@cvsa/backend";
import { Link } from "@/i18n/navigation";
import { LocalizedMessage } from "./SignUpForm";
import { ErrorDialog } from "@/components/utils/ErrorDialog";
import { string, object, ValidationError, setLocale } from "yup";
setLocale({
mixed: {
default: "yup_errors.field_invalid",
required: () => ({ key: "yup_errors.field_required" })
},
string: {
min: ({ min }) => ({ key: "yup_errors.field_too_short", values: { min } }),
max: ({ max }) => ({ key: "yup_errors.field_too_big", values: { max } })
}
});
interface SignUpFormData {
username: string;
password: string;
nickname?: string;
}
const FormSchema = object().shape({
username: string().required().max(50),
password: string().required().min(4).max(120),
nickname: string().optional().max(30)
});
const validateForm = async (
data: SignUpFormData,
setShowDialog: Dispatch<SetStateAction<boolean>>,
setDialogContent: Dispatch<SetStateAction<JSX.Element>>,
translateErrorMessage: (item: LocalizedMessage | string, path?: string) => string
): Promise<SignUpFormData | null> => {
const { username: usernameInput, password: passwordInput, nickname: nicknameInput } = data;
try {
const formData = await FormSchema.validate(
{
username: usernameInput,
password: passwordInput,
nickname: nicknameInput
},
{ abortEarly: false }
);
return {
username: formData.username,
password: formData.password,
nickname: formData.nickname
};
} catch (e) {
if (!(e instanceof ValidationError)) {
return null;
}
setShowDialog(true);
setDialogContent(
<ErrorDialog closeDialog={() => setShowDialog(false)}>
<p></p>
<span>: </span>
<br />
<ol className="list-decimal list-inside">
{e.errors.map((item, i) => {
return <li key={i}>{translateErrorMessage(item, e.inner[i].path)}</li>;
})}
</ol>
</ErrorDialog>
);
return null;
}
};
interface RequestSignUpArgs {
data: SignUpFormData;
setShowDialog: Dispatch<SetStateAction<boolean>>;
setDialogContent: Dispatch<SetStateAction<JSX.Element>>;
translateErrorMessage: (item: LocalizedMessage | string, path?: string) => string;
setCaptchaUsedState: Dispatch<SetStateAction<boolean>>;
captchaResult: CaptchaVerificationRawResponse | undefined;
t: any;
}
export const requestSignUp = async (url: string, { arg }: { arg: RequestSignUpArgs }) => {
const { data, setShowDialog, setDialogContent, translateErrorMessage, setCaptchaUsedState, captchaResult, t } = arg;
const res = await validateForm(data, setShowDialog, setDialogContent, translateErrorMessage);
if (!res) {
return;
}
const { username, nickname, password } = res;
try {
if (!captchaResult) {
const err = new ApiRequestError("Cannot get captcha result");
err.response = {
code: "UNKNOWN_ERROR",
message: "Cannot get captch verifiction result",
i18n: {
key: "captcha_failed_to_get"
}
} as ErrorResponse;
throw err;
}
setCaptchaUsedState(true);
const registrationResponse = await fetcher<SignUpResponse>(url, {
method: "POST",
withCredentials: true,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${captchaResult!.token}`
},
data: {
username: username,
password: password,
nickname: nickname
}
});
return registrationResponse;
} catch (error) {
if (error instanceof ApiRequestError && error.response) {
const res = error.response as ErrorResponse;
setShowDialog(true);
setDialogContent(
<ErrorDialog closeDialog={() => setShowDialog(false)} errorCode={res.code}>
<p></p>
<p>
: <br />
{res.i18n
? t.rich(res.i18n.key, {
...res.i18n.values,
support: (chunks: string) => <Link href="/support">{chunks}</Link>
})
: res.message}
</p>
</ErrorDialog>
);
} else if (error instanceof Error) {
setShowDialog(true);
setDialogContent(
<ErrorDialog closeDialog={() => setShowDialog(false)}>
<p></p>
<p>
<br />
{error.message}
</p>
</ErrorDialog>
);
} else {
setShowDialog(true);
setDialogContent(
<ErrorDialog closeDialog={() => setShowDialog(false)} errorCode="UNKNOWN_ERROR">
<p></p>
<p>
<br />
<pre className="break-all">{JSON.stringify(error)}</pre>
</p>
</ErrorDialog>
);
}
}
};