diff --git a/server/routers/auth/verifySiteAccess.ts b/server/routers/auth/verifySiteAccess.ts index f0443baa..dc1a87d5 100644 --- a/server/routers/auth/verifySiteAccess.ts +++ b/server/routers/auth/verifySiteAccess.ts @@ -10,6 +10,7 @@ import { import { and, eq, or } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; +import logger from "@server/logger"; export async function verifySiteAccess( req: Request, @@ -28,6 +29,7 @@ export async function verifySiteAccess( } if (isNaN(siteId)) { + logger.debug(JSON.stringify(req.body)); return next(createHttpError(HttpCode.BAD_REQUEST, "Invalid site ID")); } diff --git a/server/routers/external.ts b/server/routers/external.ts index 47e9a4b0..5d22c753 100644 --- a/server/routers/external.ts +++ b/server/routers/external.ts @@ -150,7 +150,6 @@ authenticated.get( authenticated.post( "/resource/:resourceId", verifyResourceAccess, - verifySiteAccess, verifyUserHasAction(ActionsEnum.updateResource), resource.updateResource ); diff --git a/server/routers/org/createOrg.ts b/server/routers/org/createOrg.ts index 4401f0b2..07ee2240 100644 --- a/server/routers/org/createOrg.ts +++ b/server/routers/org/createOrg.ts @@ -12,11 +12,13 @@ import config from "@server/config"; import { fromError } from "zod-validation-error"; import { defaultRoleAllowedActions } from "../role"; -const createOrgSchema = z.object({ - orgId: z.string(), - name: z.string().min(1).max(255), - // domain: z.string().min(1).max(255).optional(), -}); +const createOrgSchema = z + .object({ + orgId: z.string(), + name: z.string().min(1).max(255), + // domain: z.string().min(1).max(255).optional(), + }) + .strict(); const MAX_ORGS = 5; diff --git a/server/routers/org/updateOrg.ts b/server/routers/org/updateOrg.ts index e3dfe2b8..4e700617 100644 --- a/server/routers/org/updateOrg.ts +++ b/server/routers/org/updateOrg.ts @@ -18,6 +18,7 @@ const updateOrgBodySchema = z name: z.string().min(1).max(255).optional(), domain: z.string().min(1).max(255).optional(), }) + .strict() .refine((data) => Object.keys(data).length > 0, { message: "At least one field must be provided for update", }); diff --git a/server/routers/resource/createResource.ts b/server/routers/resource/createResource.ts index c39929c8..f5021417 100644 --- a/server/routers/resource/createResource.ts +++ b/server/routers/resource/createResource.ts @@ -25,10 +25,12 @@ const createResourceParamsSchema = z.object({ orgId: z.string(), }); -const createResourceSchema = z.object({ - name: z.string().min(1).max(255), - subdomain: z.string().min(1).max(255).optional(), -}); +const createResourceSchema = z + .object({ + name: z.string().min(1).max(255), + subdomain: z.string().min(1).max(255).optional(), + }) + .strict(); export type CreateResourceResponse = Resource; diff --git a/server/routers/resource/updateResource.ts b/server/routers/resource/updateResource.ts index 719aad97..23ba87e9 100644 --- a/server/routers/resource/updateResource.ts +++ b/server/routers/resource/updateResource.ts @@ -18,8 +18,9 @@ const updateResourceBodySchema = z name: z.string().min(1).max(255).optional(), subdomain: z.string().min(1).max(255).optional(), ssl: z.boolean().optional(), - siteId: z.number(), + // siteId: z.number(), }) + .strict() .refine((data) => Object.keys(data).length > 0, { message: "At least one field must be provided for update", }); diff --git a/server/routers/role/createRole.ts b/server/routers/role/createRole.ts index 60992c03..b63deabe 100644 --- a/server/routers/role/createRole.ts +++ b/server/routers/role/createRole.ts @@ -14,10 +14,12 @@ const createRoleParamsSchema = z.object({ orgId: z.string(), }); -const createRoleSchema = z.object({ - name: z.string().min(1).max(255), - description: z.string().optional(), -}); +const createRoleSchema = z + .object({ + name: z.string().min(1).max(255), + description: z.string().optional(), + }) + .strict(); export const defaultRoleAllowedActions: ActionsEnum[] = [ ActionsEnum.getOrg, diff --git a/server/routers/role/updateRole.ts b/server/routers/role/updateRole.ts index 796678b4..574e1c32 100644 --- a/server/routers/role/updateRole.ts +++ b/server/routers/role/updateRole.ts @@ -18,6 +18,7 @@ const updateRoleBodySchema = z name: z.string().min(1).max(255).optional(), description: z.string().optional(), }) + .strict() .refine((data) => Object.keys(data).length > 0, { message: "At least one field must be provided for update", }); diff --git a/server/routers/site/createSite.ts b/server/routers/site/createSite.ts index ce513040..13689b6e 100644 --- a/server/routers/site/createSite.ts +++ b/server/routers/site/createSite.ts @@ -15,13 +15,15 @@ const createSiteParamsSchema = z.object({ orgId: z.string(), }); -const createSiteSchema = z.object({ - name: z.string().min(1).max(255), - exitNodeId: z.number().int().positive(), - subdomain: z.string().min(1).max(255).optional(), - pubKey: z.string(), - subnet: z.string(), -}); +const createSiteSchema = z + .object({ + name: z.string().min(1).max(255), + exitNodeId: z.number().int().positive(), + subdomain: z.string().min(1).max(255).optional(), + pubKey: z.string(), + subnet: z.string(), + }) + .strict(); export type CreateSiteResponse = { name: string; diff --git a/server/routers/site/updateSite.ts b/server/routers/site/updateSite.ts index c436b07f..71b36144 100644 --- a/server/routers/site/updateSite.ts +++ b/server/routers/site/updateSite.ts @@ -23,6 +23,7 @@ const updateSiteBodySchema = z megabytesIn: z.number().int().nonnegative().optional(), megabytesOut: z.number().int().nonnegative().optional(), }) + .strict() .refine((data) => Object.keys(data).length > 0, { message: "At least one field must be provided for update", }); diff --git a/server/routers/target/createTarget.ts b/server/routers/target/createTarget.ts index 63398e72..d98437f1 100644 --- a/server/routers/target/createTarget.ts +++ b/server/routers/target/createTarget.ts @@ -15,13 +15,15 @@ const createTargetParamsSchema = z.object({ resourceId: z.string().transform(Number).pipe(z.number().int().positive()), }); -const createTargetSchema = z.object({ - ip: z.string().ip(), - method: z.string().min(1).max(10), - port: z.number().int().min(1).max(65535), - protocol: z.string().optional(), - enabled: z.boolean().default(true), -}); +const createTargetSchema = z + .object({ + ip: z.string().ip(), + method: z.string().min(1).max(10), + port: z.number().int().min(1).max(65535), + protocol: z.string().optional(), + enabled: z.boolean().default(true), + }) + .strict(); export type CreateTargetResponse = Target; @@ -104,6 +106,7 @@ export async function createTarget( .insert(targets) .values({ resourceId, + protocol: "tcp", // hard code for now ...targetData, }) .returning(); diff --git a/server/routers/target/updateTarget.ts b/server/routers/target/updateTarget.ts index 52e033b1..58fa4914 100644 --- a/server/routers/target/updateTarget.ts +++ b/server/routers/target/updateTarget.ts @@ -15,12 +15,12 @@ const updateTargetParamsSchema = z.object({ const updateTargetBodySchema = z .object({ - // ip: z.string().ip().optional(), // for now we cant update the ip; you will have to delete + ip: z.string().ip().optional(), // for now we cant update the ip; you will have to delete method: z.string().min(1).max(10).optional(), port: z.number().int().min(1).max(65535).optional(), - protocol: z.string().optional(), enabled: z.boolean().optional(), }) + .strict() .refine((data) => Object.keys(data).length > 0, { message: "At least one field must be provided for update", }); diff --git a/src/app/[orgId]/settings/resources/[resourceId]/connectivity/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/connectivity/page.tsx index 75f38e47..7dc262b5 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/connectivity/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/connectivity/page.tsx @@ -17,7 +17,7 @@ import { AxiosResponse } from "axios"; import { ListTargetsResponse } from "@server/routers/target/listTargets"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; -import { set, z } from "zod"; +import { z } from "zod"; import { Form, FormControl, @@ -27,7 +27,7 @@ import { FormLabel, FormMessage, } from "@app/components/ui/form"; -import { CreateTargetResponse, updateTarget } from "@server/routers/target"; +import { CreateTargetResponse } from "@server/routers/target"; import { ColumnDef, getFilteredRowModel, @@ -51,7 +51,6 @@ import { useResourceContext } from "@app/hooks/useResourceContext"; import { ArrayElement } from "@server/types/ArrayElement"; import { Dot } from "lucide-react"; import { formatAxiosError } from "@app/lib/utils"; -import { escape } from "querystring"; const addTargetSchema = z.object({ ip: z.string().ip(), @@ -62,15 +61,18 @@ const addTargetSchema = z.object({ message: "Port must be a number", }) .transform((val) => Number(val)), - protocol: z.string(), + // protocol: z.string(), }); type AddTargetFormValues = z.infer; -type LocalTarget = ArrayElement & { - new?: boolean; - updated?: boolean; -}; +type LocalTarget = Omit< + ArrayElement & { + new?: boolean; + updated?: boolean; + }, + "protocol" +>; export default function ReverseProxyTargets(props: { params: Promise<{ resourceId: number }>; @@ -84,13 +86,15 @@ export default function ReverseProxyTargets(props: { const [targetsToRemove, setTargetsToRemove] = useState([]); const [sslEnabled, setSslEnabled] = useState(resource.ssl); + const [loading, setLoading] = useState(false); + const addTargetForm = useForm({ resolver: zodResolver(addTargetSchema), defaultValues: { ip: "", method: "http", port: "80", - protocol: "TCP", + // protocol: "TCP", }, }); @@ -153,109 +157,72 @@ export default function ReverseProxyTargets(props: { } async function saveAll() { - const res = await api - .post(`/resource/${params.resourceId}`, { ssl: sslEnabled }) - .catch((err) => { - console.error(err); - toast({ - variant: "destructive", - title: "Failed to update resource", - description: formatAxiosError( - err, - "Failed to update resource" - ), - }); - }) - .then(() => { - updateResource({ ssl: sslEnabled }); + try { + setLoading(true); + + const res = await api.post(`/resource/${params.resourceId}`, { + ssl: sslEnabled, }); - for (const target of targets) { - const data = { - ip: target.ip, - port: target.port, - method: target.method, - protocol: target.protocol, - enabled: target.enabled, - }; + updateResource({ ssl: sslEnabled }); - if (target.new) { - await api - .put>( - `/resource/${params.resourceId}/target`, + for (const target of targets) { + const data = { + ip: target.ip, + port: target.port, + // protocol: target.protocol, + method: target.method, + enabled: target.enabled, + }; + + if (target.new) { + const res = await api.put< + AxiosResponse + >(`/resource/${params.resourceId}/target`, data); + } else if (target.updated) { + const res = await api.post( + `/target/${target.targetId}`, data - ) - .then((res) => { - setTargets( - targets.map((t) => { - if ( - t.new && - t.targetId === res.data.data.targetId - ) { - return { - ...t, - new: false, - }; - } - return t; - }) - ); - }) - .catch((err) => { - console.error(err); - toast({ - variant: "destructive", - title: "Failed to add target", - description: formatAxiosError( - err, - "Failed to add target" - ), - }); - }); - } else if (target.updated) { - const res = await api - .post(`/target/${target.targetId}`, data) - .catch((err) => { - console.error(err); - toast({ - variant: "destructive", - title: "Failed to update target", - description: formatAxiosError( - err, - "Failed to update target" - ), - }); - }); - } - } - - for (const targetId of targetsToRemove) { - await api - .delete(`/target/${targetId}`) - .catch((err) => { - console.error(err); - toast({ - variant: "destructive", - title: "Failed to remove target", - description: formatAxiosError( - err, - "Failed to remove target" - ), - }); - }) - .then((res) => { - setTargets( - targets.filter((target) => target.targetId !== targetId) ); - }); + } + + setTargets([ + ...targets.map((t) => { + return { + ...t, + new: false, + updated: false, + }; + }), + ]); + } + + for (const targetId of targetsToRemove) { + await api.delete(`/target/${targetId}`); + setTargets( + targets.filter((target) => target.targetId !== targetId) + ); + } + + toast({ + title: "Resource updated", + description: "Resource and targets updated successfully", + }); + + setTargetsToRemove([]); + } catch (err) { + console.error(err); + toast({ + variant: "destructive", + title: "Operation failed", + description: formatAxiosError( + err, + "An error occurred during the save operation" + ), + }); } - toast({ - title: "Resource updated", - description: "Resource and targets updated successfully", - }); - - setTargetsToRemove([]); + setLoading(false); } const columns: ColumnDef[] = [ @@ -306,24 +273,24 @@ export default function ReverseProxyTargets(props: { ), }, - { - accessorKey: "protocol", - header: "Protocol", - cell: ({ row }) => ( - - ), - }, + // { + // accessorKey: "protocol", + // header: "Protocol", + // cell: ({ row }) => ( + // + // ), + // }, { accessorKey: "enabled", header: "Enabled", @@ -341,7 +308,14 @@ export default function ReverseProxyTargets(props: { cell: ({ row }) => ( <>
- {row.original.new && } + +
+ ); diff --git a/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx index b865e126..d3d1bb03 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx @@ -61,7 +61,7 @@ export default function GeneralForm() { resolver: zodResolver(GeneralFormSchema), defaultValues: { name: resource.name, - siteId: resource.siteId!, + // siteId: resource.siteId!, }, mode: "onChange", }); @@ -84,7 +84,7 @@ export default function GeneralForm() { `resource/${resource?.resourceId}`, { name: data.name, - siteId: data.siteId, + // siteId: data.siteId, } ) .catch((e) => { @@ -137,7 +137,7 @@ export default function GeneralForm() { )} /> - ( @@ -213,7 +213,7 @@ export default function GeneralForm() { )} - /> + /> */}