This commit is contained in:
Alex Holliday committed 2025-12-28 19:36:14 +00:00
1 parent a2454cbab6
commit 8fb13273bf
7 files changed
+69 -83

No files matched your search

@@ -1,11 +1,12 @@
import { systemSettingsSchema } from "@/validation/zod";
import { systemSettingsSchema } from "@/validation";
import { useMemo } from "react";
import type { FormValues } from "@/pages/settings/SettingsForm";
import { z } from "zod";
export const useInitForm = ({
initialData,
}: {
initialData: Partial<z.infer<typeof systemSettingsSchema>> | undefined;
initialData: Partial<FormValues> | undefined;
}) => {
return useMemo(() => {
const defaults: z.infer<typeof systemSettingsSchema> = {
+1 -4
View File
@@ -7,12 +7,9 @@ import {} from "react-i18next";
import { useAppSelector } from "@/hooks/AppHooks";
import { useGet, usePatch, usePost } from "@/hooks/UseApi";
import type { ApiResponse } from "@/types/api";
import { z } from "zod";
import { systemSettingsSchema } from "@/validation/zod";
import type { FormValues } from "@/pages/settings/SettingsForm";
const AdminSettingsPage = () => {
type FormValues = z.infer<typeof systemSettingsSchema>;
const user = useAppSelector((state) => state.auth.user);
const orgPermissions = user?.org?.permissions || [];
+2 -2
View File
@@ -6,14 +6,14 @@ import { ConfigBox } from "@/components/design-elements";
import { useTheme } from "@mui/material/styles";
import { useTranslation } from "react-i18next";
import { systemSettingsSchema } from "@/validation/zod";
import { systemSettingsSchema } from "@/validation";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm, Controller, type SubmitHandler } from "react-hook-form";
import { useInitForm } from "@/hooks/forms/UseInitSettingsForm";
import { useEffect } from "react";
type FormValues = z.infer<typeof systemSettingsSchema>;
export type FormValues = z.infer<typeof systemSettingsSchema>;
export const SettingsForm = ({
initialData,
+1
View File
@@ -8,3 +8,4 @@ export * from "./utils";
export * from "./notification-channel";
export * from "./maintenance";
export * from "./team";
export * from "./system-settings";
+55
View File
@@ -0,0 +1,55 @@
import { z } from "zod";
import { optionalString } from "@/validation/utils";
export const systemSettingsSchema = z.object({
systemEmailHost: optionalString(
z.string().trim().min(1, { message: "SMTP host is required" }).max(255, {
message: "SMTP host must be 255 characters or fewer",
})
),
systemEmailPort: optionalString(
z
.union([z.number().int(), z.string().regex(/^\d+$/)])
.transform((val) => (typeof val === "string" ? Number(val) : val))
.refine(
(val) =>
val === undefined ||
(Number.isInteger(val) && val > 0 && val < 65536),
{
message: "Port must be a number between 1 and 65535",
}
)
),
systemEmailAddress: optionalString(
z.string().email({ message: "SMTP email must be valid" })
),
systemEmailPassword: optionalString(
z.string().max(255, {
message: "SMTP password must be 255 characters or fewer",
})
),
systemEmailUser: optionalString(
z.string().trim().max(255, {
message: "SMTP username must be 255 characters or fewer",
})
),
systemEmailConnectionHost: optionalString(
z.string().trim().max(255, {
message: "Connection host must be 255 characters or fewer",
})
),
systemEmailTLSServername: optionalString(
z.string().trim().max(255, {
message: "TLS server name must be 255 characters or fewer",
})
),
systemEmailSecure: z.boolean().optional(),
systemEmailPool: z.boolean().optional(),
systemEmailIgnoreTLS: z.boolean().optional(),
systemEmailRequireTLS: z.boolean().optional(),
systemEmailRejectUnauthorized: z.boolean().optional(),
checksRetentionDays: z.coerce
.number({ message: "Number required" })
.min(1, "Retention days must be at least 1")
.max(365, "Retention days cannot exceed 365 days"),
});
+7
View File
@@ -21,3 +21,10 @@ export const durationSchema = (minInterval: number) =>
});
}
});
export const optionalString = <T extends z.ZodTypeAny>(schema: T) =>
z.preprocess(
(val) =>
val === "" || val === null || val === undefined ? undefined : val,
schema.optional()
);
-75
View File
@@ -1,26 +1,4 @@
import { z } from "zod";
// humanInterval moved to split schema files
// MaintenanceRepeats moved to maintenance schema
// urlRegex moved to client/src/validation/utils.ts
// monitor duration schema moved to client/src/validation/monitor.ts
// pagespeed duration schema moved to client/src/validation/pagespeed.ts
// infra duration schema moved to client/src/validation/infra.ts
const optionalString = <T extends z.ZodTypeAny>(schema: T) =>
z.preprocess(
(val) =>
val === "" || val === null || val === undefined ? undefined : val,
schema.optional()
);
// moved to client/src/validation/monitor.ts
// moved to client/src/validation/pagespeed.ts
// moved to client/src/validation/infra.ts
// moved to client/src/validation/team.ts
export const inviteSchema = z.object({
email: z.email("Invalid email address"),
@@ -29,59 +7,6 @@ export const inviteSchema = z.object({
orgRoleId: z.string().optional(),
});
export const systemSettingsSchema = z.object({
systemEmailHost: optionalString(
z.string().trim().min(1, { message: "SMTP host is required" }).max(255, {
message: "SMTP host must be 255 characters or fewer",
})
),
systemEmailPort: optionalString(
z
.union([z.number().int(), z.string().regex(/^\d+$/)])
.transform((val) => (typeof val === "string" ? Number(val) : val))
.refine(
(val) =>
val === undefined ||
(Number.isInteger(val) && val > 0 && val < 65536),
{
message: "Port must be a number between 1 and 65535",
}
)
),
systemEmailAddress: optionalString(
z.string().email({ message: "SMTP email must be valid" })
),
systemEmailPassword: optionalString(
z.string().max(255, {
message: "SMTP password must be 255 characters or fewer",
})
),
systemEmailUser: optionalString(
z.string().trim().max(255, {
message: "SMTP username must be 255 characters or fewer",
})
),
systemEmailConnectionHost: optionalString(
z.string().trim().max(255, {
message: "Connection host must be 255 characters or fewer",
})
),
systemEmailTLSServername: optionalString(
z.string().trim().max(255, {
message: "TLS server name must be 255 characters or fewer",
})
),
systemEmailSecure: z.boolean().optional(),
systemEmailPool: z.boolean().optional(),
systemEmailIgnoreTLS: z.boolean().optional(),
systemEmailRequireTLS: z.boolean().optional(),
systemEmailRejectUnauthorized: z.boolean().optional(),
checksRetentionDays: z.coerce
.number({ message: "Number required" })
.min(1, "Retention days must be at least 1")
.max(365, "Retention days cannot exceed 365 days"),
});
const statusPageUrlRegex = /^[A-Za-z0-9]+$/;
export const statusPageSchema = z.object({