diff --git a/server/routers/resource/updateResource.ts b/server/routers/resource/updateResource.ts index f7dcf6ea..cc48894b 100644 --- a/server/routers/resource/updateResource.ts +++ b/server/routers/resource/updateResource.ts @@ -29,7 +29,8 @@ const updateResourceBodySchema = z blockAccess: z.boolean().optional(), proxyPort: z.number().int().min(1).max(65535).optional(), emailWhitelistEnabled: z.boolean().optional(), - isBaseDomain: z.boolean().optional() + isBaseDomain: z.boolean().optional(), + applyRules: z.boolean().optional(), }) .strict() .refine((data) => Object.keys(data).length > 0, { diff --git a/src/app/[orgId]/settings/resources/[resourceId]/connectivity/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/connectivity/page.tsx index 7cc21914..0e853194 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/connectivity/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/connectivity/page.tsx @@ -131,7 +131,7 @@ export default function ReverseProxyTargets(props: { resolver: zodResolver(addTargetSchema), defaultValues: { ip: "", - method: resource.http ? "http" : null, + method: resource.http ? "http" : null // protocol: "TCP", } as z.infer }); @@ -316,17 +316,31 @@ export default function ReverseProxyTargets(props: { } async function saveSsl(val: boolean) { - const res = await api.post(`/resource/${params.resourceId}`, { - ssl: val - }); + const res = await api + .post(`/resource/${params.resourceId}`, { + ssl: val + }) + .catch((err) => { + console.error(err); + toast({ + variant: "destructive", + title: "Failed to update SSL configuration", + description: formatAxiosError( + err, + "An error occurred while updating the SSL configuration" + ) + }); + }); - setSslEnabled(val); - updateResource({ ssl: val }); + if (res && res.status === 200) { + setSslEnabled(val); + updateResource({ ssl: val }); - toast({ - title: "SSL Configuration", - description: "SSL configuration updated successfully" - }); + toast({ + title: "SSL Configuration", + description: "SSL configuration updated successfully" + }); + } } const columns: ColumnDef[] = [ @@ -652,7 +666,8 @@ export default function ReverseProxyTargets(props: {

- Adding more than one target above will enable load balancing. + Adding more than one target above will enable load + balancing.

diff --git a/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx index d74ba6e9..ac863c5a 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx @@ -56,6 +56,7 @@ import { SettingsSectionFooter } from "@app/components/Settings"; import { ListResourceRulesResponse } from "@server/routers/resource/listResourceRules"; +import { SwitchInput } from "@app/components/SwitchInput"; // Schema for rule validation const addRuleSchema = z.object({ @@ -74,12 +75,13 @@ export default function ResourceRules(props: { }) { const params = use(props.params); const { toast } = useToast(); - const { resource } = useResourceContext(); + const { resource, updateResource } = useResourceContext(); const api = createApiClient(useEnvContext()); const [rules, setRules] = useState([]); const [rulesToRemove, setRulesToRemove] = useState([]); const [loading, setLoading] = useState(false); const [pageLoading, setPageLoading] = useState(true); + const [rulesEnabled, setRulesEnabled] = useState(resource.applyRules); const addRuleForm = useForm({ resolver: zodResolver(addRuleSchema), @@ -180,6 +182,34 @@ export default function ResourceRules(props: { ); } + async function saveApplyRules(val: boolean) { + const res = await api + .post(`/resource/${params.resourceId}`, { + applyRules: val + }) + .catch((err) => { + console.error(err); + toast({ + variant: "destructive", + title: "Failed to update rules", + description: formatAxiosError( + err, + "An error occurred while updating rules" + ) + }); + }); + + if (res && res.status === 200) { + setRulesEnabled(val); + updateResource({ applyRules: val }); + + toast({ + title: "Enable Rules", + description: "Rule evaluation has been updated" + }); + } + } + async function saveRules() { try { setLoading(true); @@ -199,7 +229,10 @@ export default function ResourceRules(props: { setLoading(false); return; } - if (rule.match === "PATH" && !isValidUrlGlobPattern(rule.value)) { + if ( + rule.match === "PATH" && + !isValidUrlGlobPattern(rule.value) + ) { toast({ variant: "destructive", title: "Invalid URL path", @@ -334,6 +367,25 @@ export default function ResourceRules(props: { return ( + + + Enable Rules + + Enable or disable rule evaluation for this resource + + + + { + await saveApplyRules(val); + }} + /> + + + @@ -400,9 +452,11 @@ export default function ResourceRules(props: { CIDR - - PATH - + {resource.http && ( + + PATH + + )} @@ -421,14 +475,21 @@ export default function ResourceRules(props: { - Enter CIDR or path value based - on match type + Enter CIDR{" "} + {resource.http + ? "or path value" + : ""}{" "} + based on match type )} /> - @@ -518,35 +579,35 @@ function isValidCIDR(cidr: string): boolean { function isValidUrlGlobPattern(pattern: string): boolean { // Remove leading slash if present - pattern = pattern.startsWith('/') ? pattern.slice(1) : pattern; - + pattern = pattern.startsWith("/") ? pattern.slice(1) : pattern; + // Empty string is not valid if (!pattern) { - return false; + return false; } - + // Split path into segments - const segments = pattern.split('/'); - + const segments = pattern.split("/"); + // Check each segment for (let i = 0; i < segments.length; i++) { - const segment = segments[i]; - - // Empty segments are not allowed (double slashes) - if (!segment && i !== segments.length - 1) { - return false; - } - - // If segment contains *, it must be exactly * - if (segment.includes('*') && segment !== '*') { - return false; - } - - // Check for invalid characters - if (!/^[a-zA-Z0-9_*-]*$/.test(segment)) { - return false; - } + const segment = segments[i]; + + // Empty segments are not allowed (double slashes) + if (!segment && i !== segments.length - 1) { + return false; + } + + // If segment contains *, it must be exactly * + if (segment.includes("*") && segment !== "*") { + return false; + } + + // Check for invalid characters + if (!/^[a-zA-Z0-9_*-]*$/.test(segment)) { + return false; + } } - + return true; - } \ No newline at end of file +}