refactor: use yup custom validator

This commit is contained in:
ReaJason
2025-05-28 01:22:54 +08:00
parent 55e2b2f5a6
commit df6125c046
8 changed files with 176 additions and 72 deletions
+107 -24
View File
@@ -1,27 +1,110 @@
import * as z from "zod";
import { TFunction } from "i18next";
import { useCallback } from "react";
import { FieldErrors } from "react-hook-form";
import * as yup from "yup";
import { ShellToolType } from "./shell";
export const formSchema = z.object({
server: z.string().min(1),
targetJdkVersion: z.optional(z.string()),
debug: z.optional(z.boolean()),
bypassJavaModule: z.optional(z.boolean()),
shellClassName: z.string().optional(),
shellTool: z.string().min(1),
shellType: z.string().min(1),
urlPattern: z.optional(z.string()),
godzillaPass: z.optional(z.string()),
godzillaKey: z.optional(z.string()),
behinderPass: z.optional(z.string()),
antSwordPass: z.optional(z.string()),
commandParamName: z.optional(z.string()),
implementationClass: z.optional(z.string()),
headerName: z.optional(z.string()),
headerValue: z.optional(z.string()),
injectorClassName: z.optional(z.string()),
packingMethod: z.string().min(1),
shrink: z.optional(z.boolean()),
shellClassBase64: z.optional(z.string()),
encryptor: z.optional(z.string()),
export const formSchema = yup.object({
server: yup.string().required().min(1),
targetJdkVersion: yup.string().optional(),
debug: yup.boolean().optional(),
bypassJavaModule: yup.boolean().optional(),
shellClassName: yup.string().optional(),
shellTool: yup.string().required().min(1),
shellType: yup.string().required().min(1),
urlPattern: yup.string().optional(),
godzillaPass: yup.string().optional(),
godzillaKey: yup.string().optional(),
behinderPass: yup.string().optional(),
antSwordPass: yup.string().optional(),
commandParamName: yup.string().optional(),
implementationClass: yup.string().optional(),
headerName: yup.string().optional(),
headerValue: yup.string().optional(),
injectorClassName: yup.string().optional(),
packingMethod: yup.string().required().min(1),
shrink: yup.boolean().optional(),
shellClassBase64: yup.string().optional(),
encryptor: yup.string().optional(),
});
export type FormSchema = z.infer<typeof formSchema>;
interface ValidationResult {
values: FormSchema;
errors: FieldErrors<FormSchema>;
}
const urlPatternIsNeeded = (shellType: string) => {
return (
shellType.endsWith("Servlet") ||
shellType.endsWith("ControllerHandler") ||
shellType === "HandlerMethod" ||
shellType === "HandlerFunction" ||
shellType.endsWith("WebSocket")
);
};
const isInvalidUrl = (urlPattern: string | undefined) =>
urlPattern === "/" || urlPattern === "/*" || !urlPattern?.startsWith("/") || !urlPattern;
export const useYupValidationResolver = (validationSchema: yup.ObjectSchema<any>, t: TFunction) =>
useCallback(
async (data: FormSchema): Promise<ValidationResult> => {
try {
const values = (await validationSchema.validate(data, {
abortEarly: false,
})) as FormSchema;
const urlPattern: keyof FormSchema = "urlPattern";
const shellClassBase64: keyof FormSchema = "shellClassBase64";
const errors = {} as any;
if (urlPatternIsNeeded(values?.shellType) && isInvalidUrl(values?.urlPattern)) {
errors[urlPattern] = {
type: "custom",
message: t("tips.specificUrlPattern"),
};
}
if (values.shellTool === ShellToolType.Custom && !values.shellClassBase64) {
errors[shellClassBase64] = {
type: "custom",
message: t("tips.customShellClass"),
};
}
return {
values,
errors,
};
} catch (errors) {
if (errors instanceof yup.ValidationError) {
return {
values: {} as FormSchema,
errors: errors.inner.reduce(
(allErrors, currentError) => ({
// biome-ignore lint/performance/noAccumulatingSpread: <explanation>
...allErrors,
[currentError.path as keyof FormSchema]: {
type: currentError.type ?? "validation",
message: currentError.message,
},
}),
{},
),
};
}
return {
values: {} as FormSchema,
errors: {
server: {
type: "unknown",
message: "An unexpected validation error occurred",
},
},
};
}
},
[validationSchema, t],
);
export type FormSchema = yup.InferType<typeof formSchema>;