mirror of
https://github.com/fosrl/pangolin.git
synced 2025-07-31 08:04:54 +02:00
fix zod schemas
This commit is contained in:
parent
0e04e82b88
commit
20f659db89
7 changed files with 158 additions and 122 deletions
|
@ -13,7 +13,7 @@ experimental:
|
|||
plugins:
|
||||
badger:
|
||||
moduleName: "github.com/fosrl/badger"
|
||||
version: "v1.0.0-beta.2"
|
||||
version: "v1.0.0-beta.3"
|
||||
|
||||
log:
|
||||
level: "INFO"
|
||||
|
|
|
@ -17,6 +17,7 @@ import { eq, and } from "drizzle-orm";
|
|||
import stoi from "@server/lib/stoi";
|
||||
import { fromError } from "zod-validation-error";
|
||||
import logger from "@server/logger";
|
||||
import { subdomainSchema } from "@server/schemas/subdomainSchema";
|
||||
|
||||
const createResourceParamsSchema = z
|
||||
.object({
|
||||
|
@ -27,34 +28,41 @@ const createResourceParamsSchema = z
|
|||
|
||||
const createResourceSchema = z
|
||||
.object({
|
||||
subdomain: z
|
||||
.union([
|
||||
z
|
||||
.string()
|
||||
.regex(
|
||||
/^(?!:\/\/)([a-zA-Z0-9-_]+\.)*[a-zA-Z0-9-_]+$/,
|
||||
"Invalid subdomain format"
|
||||
)
|
||||
.min(1, "Subdomain must be at least 1 character long")
|
||||
.transform((val) => val.toLowerCase()),
|
||||
z.string().optional()
|
||||
])
|
||||
.optional(),
|
||||
subdomain: z.string().optional(),
|
||||
name: z.string().min(1).max(255),
|
||||
siteId: z.number(),
|
||||
http: z.boolean(),
|
||||
protocol: z.string(),
|
||||
proxyPort: z.number().int().min(1).max(65535).optional(),
|
||||
}).refine(
|
||||
proxyPort: z.number().optional()
|
||||
})
|
||||
.refine(
|
||||
(data) => {
|
||||
if (data.http === true) {
|
||||
return true;
|
||||
if (!data.http) {
|
||||
return z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(65535)
|
||||
.safeParse(data.proxyPort).success;
|
||||
}
|
||||
return !!data.proxyPort;
|
||||
return true;
|
||||
},
|
||||
{
|
||||
message: "Port number is required for non-HTTP resources",
|
||||
message: "Invalid port number",
|
||||
path: ["proxyPort"]
|
||||
}
|
||||
)
|
||||
.refine(
|
||||
(data) => {
|
||||
if (data.http) {
|
||||
return subdomainSchema.safeParse(data.subdomain).success;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
{
|
||||
message: "Invalid subdomain",
|
||||
path: ["subdomain"]
|
||||
}
|
||||
);
|
||||
|
||||
export type CreateResourceResponse = Resource;
|
||||
|
@ -134,7 +142,6 @@ export async function createResource(
|
|||
);
|
||||
}
|
||||
} else {
|
||||
|
||||
if (proxyPort === 443 || proxyPort === 80) {
|
||||
return next(
|
||||
createHttpError(
|
||||
|
@ -165,7 +172,7 @@ export async function createResource(
|
|||
.insert(resources)
|
||||
.values({
|
||||
siteId,
|
||||
fullDomain: http? fullDomain : null,
|
||||
fullDomain: http ? fullDomain : null,
|
||||
orgId,
|
||||
name,
|
||||
subdomain,
|
||||
|
|
|
@ -156,13 +156,15 @@ export async function traefikConfigProvider(
|
|||
: {})
|
||||
};
|
||||
|
||||
const additionalMiddlewares = config.getRawConfig().traefik.additional_middlewares || [];
|
||||
|
||||
config_output.http.routers![routerName] = {
|
||||
entryPoints: [
|
||||
resource.ssl
|
||||
? config.getRawConfig().traefik.https_entrypoint
|
||||
: config.getRawConfig().traefik.http_entrypoint
|
||||
],
|
||||
middlewares: [badgerMiddlewareName],
|
||||
middlewares: [badgerMiddlewareName, ...additionalMiddlewares],
|
||||
service: serviceName,
|
||||
rule: `Host(\`${fullDomain}\`)`,
|
||||
...(resource.ssl ? { tls } : {})
|
||||
|
|
|
@ -59,38 +59,39 @@ import {
|
|||
SelectTrigger,
|
||||
SelectValue
|
||||
} from "@app/components/ui/select";
|
||||
import { subdomainSchema } from "@server/schemas/subdomainSchema";
|
||||
|
||||
const createResourceFormSchema = z
|
||||
.object({
|
||||
subdomain: z
|
||||
.union([
|
||||
z
|
||||
.string()
|
||||
.regex(
|
||||
/^(?!:\/\/)([a-zA-Z0-9-_]+\.)*[a-zA-Z0-9-_]+$/,
|
||||
"Invalid subdomain format"
|
||||
)
|
||||
.min(1, "Subdomain must be at least 1 character long")
|
||||
.transform((val) => val.toLowerCase()),
|
||||
z.string().optional()
|
||||
])
|
||||
.optional(),
|
||||
subdomain: z.string().optional(),
|
||||
name: z.string().min(1).max(255),
|
||||
siteId: z.number(),
|
||||
http: z.boolean(),
|
||||
protocol: z.string(),
|
||||
proxyPort: z.number().int().min(1).max(65535).optional()
|
||||
proxyPort: z.number().optional(),
|
||||
})
|
||||
.refine(
|
||||
(data) => {
|
||||
if (data.http === true) {
|
||||
return true;
|
||||
if (!data.http) {
|
||||
return z.number().int().min(1).max(65535).safeParse(data.proxyPort).success;
|
||||
}
|
||||
return !!data.proxyPort;
|
||||
return true;
|
||||
},
|
||||
{
|
||||
message: "Port number is required for non-HTTP resources",
|
||||
path: ["proxyPort"]
|
||||
message: "Invalid port number",
|
||||
path: ["proxyPort"],
|
||||
}
|
||||
)
|
||||
.refine(
|
||||
(data) => {
|
||||
if (data.http) {
|
||||
return subdomainSchema.safeParse(data.subdomain).success;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
{
|
||||
message: "Invalid subdomain",
|
||||
path: ["subdomain"],
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@ import {
|
|||
} from "@app/components/Settings";
|
||||
import { SwitchInput } from "@app/components/SwitchInput";
|
||||
import { useSiteContext } from "@app/hooks/useSiteContext";
|
||||
import { InfoPopup } from "@app/components/ui/info-popup";
|
||||
|
||||
// Regular expressions for validation
|
||||
const DOMAIN_REGEX =
|
||||
|
@ -464,8 +465,8 @@ export default function ReverseProxyTargets(props: {
|
|||
SSL Configuration
|
||||
</SettingsSectionTitle>
|
||||
<SettingsSectionDescription>
|
||||
Setup SSL to secure your connections with LetsEncrypt
|
||||
certificates
|
||||
Setup SSL to secure your connections with
|
||||
LetsEncrypt certificates
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<SettingsSectionBody>
|
||||
|
@ -479,7 +480,7 @@ export default function ReverseProxyTargets(props: {
|
|||
/>
|
||||
</SettingsSectionBody>
|
||||
</SettingsSection>
|
||||
)}
|
||||
)}
|
||||
{/* Targets Section */}
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
|
@ -498,7 +499,6 @@ export default function ReverseProxyTargets(props: {
|
|||
>
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
|
||||
{resource.http && (
|
||||
|
||||
<FormField
|
||||
control={addTargetForm.control}
|
||||
name="method"
|
||||
|
@ -507,8 +507,13 @@ export default function ReverseProxyTargets(props: {
|
|||
<FormLabel>Method</FormLabel>
|
||||
<FormControl>
|
||||
<Select
|
||||
value={field.value || undefined}
|
||||
onValueChange={(value) => {
|
||||
value={
|
||||
field.value ||
|
||||
undefined
|
||||
}
|
||||
onValueChange={(
|
||||
value
|
||||
) => {
|
||||
addTargetForm.setValue(
|
||||
"method",
|
||||
value
|
||||
|
@ -647,9 +652,9 @@ export default function ReverseProxyTargets(props: {
|
|||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<SettingsSectionDescription>
|
||||
Multiple targets will get load balanced by Traefik. You can use this for high availability.
|
||||
</SettingsSectionDescription>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Adding more than one target above will enable load balancing.
|
||||
</p>
|
||||
</SettingsSectionBody>
|
||||
<SettingsSectionFooter>
|
||||
<Button
|
||||
|
|
|
@ -36,24 +36,44 @@ import { useOrgContext } from "@app/hooks/useOrgContext";
|
|||
import CustomDomainInput from "../CustomDomainInput";
|
||||
import { createApiClient } from "@app/lib/api";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
import { subdomainSchema } from "@server/schemas/subdomainSchema";
|
||||
|
||||
const GeneralFormSchema = z.object({
|
||||
subdomain: z
|
||||
.union([
|
||||
z
|
||||
.string()
|
||||
.regex(
|
||||
/^(?!:\/\/)([a-zA-Z0-9-_]+\.)*[a-zA-Z0-9-_]+$/,
|
||||
"Invalid subdomain format"
|
||||
)
|
||||
.min(1, "Subdomain must be at least 1 character long")
|
||||
.transform((val) => val.toLowerCase()),
|
||||
z.string().optional()
|
||||
])
|
||||
.optional(),
|
||||
const GeneralFormSchema = z
|
||||
.object({
|
||||
subdomain: z.string().optional(),
|
||||
name: z.string().min(1).max(255),
|
||||
proxyPort: z.number().int().min(1).max(65535).optional()
|
||||
});
|
||||
proxyPort: z.number().optional(),
|
||||
http: z.boolean()
|
||||
})
|
||||
.refine(
|
||||
(data) => {
|
||||
if (!data.http) {
|
||||
return z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(65535)
|
||||
.safeParse(data.proxyPort).success;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
{
|
||||
message: "Invalid port number",
|
||||
path: ["proxyPort"]
|
||||
}
|
||||
)
|
||||
.refine(
|
||||
(data) => {
|
||||
if (data.http) {
|
||||
return subdomainSchema.safeParse(data.subdomain).success;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
{
|
||||
message: "Invalid subdomain",
|
||||
path: ["subdomain"]
|
||||
}
|
||||
);
|
||||
|
||||
type GeneralFormValues = z.infer<typeof GeneralFormSchema>;
|
||||
|
||||
|
@ -77,7 +97,8 @@ export default function GeneralForm() {
|
|||
defaultValues: {
|
||||
name: resource.name,
|
||||
subdomain: resource.subdomain ? resource.subdomain : undefined,
|
||||
proxyPort: resource.proxyPort ? resource.proxyPort : undefined
|
||||
proxyPort: resource.proxyPort ? resource.proxyPort : undefined,
|
||||
http: resource.http
|
||||
},
|
||||
mode: "onChange"
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue