Merge branch 'Lokowitz-fix-zod-new' into dev

This commit is contained in:
Owen
2025-11-17 10:46:40 -05:00
202 changed files with 892 additions and 1378 deletions

81
package-lock.json generated
View File

@@ -9,7 +9,7 @@
"version": "0.0.0",
"license": "SEE LICENSE IN LICENSE AND README.md",
"dependencies": {
"@asteasolutions/zod-to-openapi": "^7.3.4",
"@asteasolutions/zod-to-openapi": "8.1.0",
"@aws-sdk/client-s3": "3.922.0",
"@faker-js/faker": "^10.1.0",
"@headlessui/react": "^2.2.9",
@@ -108,15 +108,15 @@
"ws": "8.18.3",
"yaml": "^2.8.1",
"yargs": "18.0.0",
"zod": "3.25.76",
"zod-validation-error": "3.5.2"
"zod": "4.1.12",
"zod-validation-error": "5.0.0"
},
"devDependencies": {
"@dotenvx/dotenvx": "1.51.1",
"@esbuild-plugins/tsconfig-paths": "0.1.2",
"@react-email/preview-server": "4.3.2",
"@tanstack/react-query-devtools": "^5.90.2",
"@tailwindcss/postcss": "^4.1.17",
"@tanstack/react-query-devtools": "^5.90.2",
"@types/better-sqlite3": "7.6.12",
"@types/cookie-parser": "1.4.10",
"@types/cors": "2.8.19",
@@ -175,15 +175,15 @@
}
},
"node_modules/@asteasolutions/zod-to-openapi": {
"version": "7.3.4",
"resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-7.3.4.tgz",
"integrity": "sha512-/2rThQ5zPi9OzVwes6U7lK1+Yvug0iXu25olp7S0XsYmOqnyMfxH7gdSQjn/+DSOHRg7wnotwGJSyL+fBKdnEA==",
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-8.1.0.tgz",
"integrity": "sha512-tQFxVs05J/6QXXqIzj6rTRk3nj1HFs4pe+uThwE95jL5II2JfpVXkK+CqkO7aT0Do5AYqO6LDrKpleLUFXgY+g==",
"license": "MIT",
"dependencies": {
"openapi3-ts": "^4.1.2"
},
"peerDependencies": {
"zod": "^3.20.2"
"zod": "^4.0.0"
}
},
"node_modules/@aws-crypto/crc32": {
@@ -1644,6 +1644,7 @@
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz",
"integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.26.2",
@@ -4073,6 +4074,7 @@
"integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": "^14.21.3 || >=16"
},
@@ -6969,6 +6971,7 @@
"integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.0.2"
}
@@ -7174,6 +7177,7 @@
"integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -7184,6 +7188,7 @@
"integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.25.0"
},
@@ -8618,6 +8623,7 @@
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.6.tgz",
"integrity": "sha512-gB1sljYjcobZKxjPbKSa31FUTyr+ROaBdoH+wSSs9Dk+yDCmMs+TkTV3PybRRVLC7ax7q0erJ9LvRWnMktnRAw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@tanstack/query-core": "5.90.6"
},
@@ -8723,6 +8729,7 @@
"integrity": "sha512-fnQmj8lELIj7BSrZQAdBMHEHX8OZLYIHXqAKT1O7tDfLxaINzf00PMjw22r3N/xXh0w/sGHlO6SVaCQ2mj78lg==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/node": "*"
}
@@ -8809,6 +8816,7 @@
"integrity": "sha512-LuIQOcb6UmnF7C1PCFmEU1u2hmiHL43fgFQX67sN3H4Z+0Yk0Neo++mFsBjhOAuLzvlQeqAAkeDOZrJs9rzumQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^5.0.0",
@@ -8902,6 +8910,7 @@
"integrity": "sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"undici-types": "~7.16.0"
}
@@ -8937,6 +8946,7 @@
"integrity": "sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/node": "*",
"pg-protocol": "*",
@@ -8970,6 +8980,7 @@
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.0.2"
}
@@ -8980,6 +8991,7 @@
"integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==",
"devOptional": true,
"license": "MIT",
"peer": true,
"peerDependencies": {
"@types/react": "^19.2.0"
}
@@ -9123,6 +9135,7 @@
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.3.tgz",
"integrity": "sha512-6m1I5RmHBGTnUGS113G04DMu3CpSdxCAU/UvtjNWL4Nuf3MW9tQhiJqRlHzChIkhy6kZSAQmc+I1bcGjE3yNKg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.46.3",
"@typescript-eslint/types": "8.46.3",
@@ -9796,6 +9809,7 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -10325,6 +10339,7 @@
"integrity": "sha512-mXpa5jnIKKHeoGzBrUJrc65cXFKcILGZpU3FXR0pradUEm9MA7UZz02qfEejaMcm9iXrSOCenwwYMJ/tZ1y5Ig==",
"hasInstallScript": true,
"license": "MIT",
"peer": true,
"dependencies": {
"bindings": "^1.5.0",
"prebuild-install": "^7.1.1"
@@ -10437,6 +10452,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.8.9",
"caniuse-lite": "^1.0.30001746",
@@ -11437,8 +11453,7 @@
"version": "3.1.7",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.7.tgz",
"integrity": "sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==",
"license": "(MPL-2.0 OR Apache-2.0)",
"peer": true
"license": "(MPL-2.0 OR Apache-2.0)"
},
"node_modules/domutils": {
"version": "3.2.2",
@@ -12094,6 +12109,7 @@
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"peer": true,
"bin": {
"esbuild": "bin/esbuild"
},
@@ -12190,6 +12206,7 @@
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.0.tgz",
"integrity": "sha512-iy2GE3MHrYTL5lrCtMZ0X1KLEKKUjmK0kzwcnefhR66txcEmXZD2YWgR5GNdcEwkNx3a0siYkSvl0vIC+Svjmg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -12367,6 +12384,7 @@
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@rtsao/scc": "^1.1.0",
"array-includes": "^3.1.9",
@@ -12493,6 +12511,18 @@
"eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
}
},
"node_modules/eslint-plugin-react-hooks/node_modules/zod-validation-error": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
"integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
"license": "MIT",
"engines": {
"node": ">=18.0.0"
},
"peerDependencies": {
"zod": "^3.25.0 || ^4.0.0"
}
},
"node_modules/eslint-plugin-react/node_modules/resolve": {
"version": "2.0.0-next.5",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
@@ -12663,6 +12693,7 @@
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
"license": "MIT",
"peer": true,
"dependencies": {
"accepts": "^2.0.0",
"body-parser": "^2.2.0",
@@ -15272,7 +15303,6 @@
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.54.0.tgz",
"integrity": "sha512-hx45SEUoLatgWxHKCmlLJH81xBo0uXP4sRkESUpmDQevfi+e7K1VuiSprK6UpQ8u4zOcKNiH0pMvHvlMWA/4cw==",
"license": "MIT",
"peer": true,
"dependencies": {
"dompurify": "3.1.7",
"marked": "14.0.0"
@@ -15283,7 +15313,6 @@
"resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz",
"integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==",
"license": "MIT",
"peer": true,
"bin": {
"marked": "bin/marked.js"
},
@@ -15406,6 +15435,7 @@
"resolved": "https://registry.npmjs.org/next/-/next-15.5.6.tgz",
"integrity": "sha512-zTxsnI3LQo3c9HSdSf91O1jMNsEzIXDShXd4wVdg9y5shwLqBXi4ZtUUJyB86KGVSJLZx0PFONvO54aheGX8QQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@next/env": "15.5.6",
"@swc/helpers": "0.5.15",
@@ -17852,6 +17882,7 @@
"version": "4.0.3",
"inBundle": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -18836,6 +18867,7 @@
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
"integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==",
"license": "MIT",
"peer": true,
"dependencies": {
"pg-connection-string": "^2.9.1",
"pg-pool": "^3.10.1",
@@ -19012,6 +19044,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -19469,6 +19502,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -19499,6 +19533,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -19790,6 +19825,7 @@
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.66.0.tgz",
"integrity": "sha512-xXBqsWGKrY46ZqaHDo+ZUYiMUgi8suYu5kdrS20EG8KiL7VRQitEbNjm+UcrDYrNi1YLyfpmAeGjCZYXLT9YBw==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=18.0.0"
},
@@ -20283,6 +20319,7 @@
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@@ -21502,7 +21539,8 @@
"version": "4.1.17",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz",
"integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/tapable": {
"version": "2.3.0",
@@ -22032,6 +22070,7 @@
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -22545,6 +22584,7 @@
"resolved": "https://registry.npmjs.org/winston/-/winston-3.18.3.tgz",
"integrity": "sha512-NoBZauFNNWENgsnC9YpgyYwOVrl2m58PpQ8lNHjV3kosGs7KJ7Npk9pCUE+WJlawVSe8mykWDKWFSVfs3QO9ww==",
"license": "MIT",
"peer": true,
"dependencies": {
"@colors/colors": "^1.6.0",
"@dabh/diagnostics": "^2.0.8",
@@ -22847,24 +22887,25 @@
}
},
"node_modules/zod": {
"version": "3.25.76",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz",
"integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==",
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
},
"node_modules/zod-validation-error": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.5.2.tgz",
"integrity": "sha512-mdi7YOLtram5dzJ5aDtm1AG9+mxRma1iaMrZdYIpFO7epdKBUwLHIxTF8CPDeCQ828zAXYtizrKlEJAtzgfgrw==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-5.0.0.tgz",
"integrity": "sha512-hmk+pkyKq7Q71PiWVSDUc3VfpzpvcRHZ3QPw9yEMVvmtCekaMeOHnbr3WbxfrgEnQTv6haGP4cmv0Ojmihzsxw==",
"license": "MIT",
"engines": {
"node": ">=18.0.0"
},
"peerDependencies": {
"zod": "^3.25.0"
"zod": "^3.25.0 || ^4.0.0"
}
}
}

View File

@@ -32,7 +32,7 @@
"build:cli": "node esbuild.mjs -e cli/index.ts -o dist/cli.mjs"
},
"dependencies": {
"@asteasolutions/zod-to-openapi": "^7.3.4",
"@asteasolutions/zod-to-openapi": "8.1.0",
"@aws-sdk/client-s3": "3.922.0",
"@faker-js/faker": "^10.1.0",
"@headlessui/react": "^2.2.9",
@@ -131,8 +131,8 @@
"ws": "8.18.3",
"yaml": "^2.8.1",
"yargs": "18.0.0",
"zod": "3.25.76",
"zod-validation-error": "3.5.2"
"zod": "4.1.12",
"zod-validation-error": "5.0.0"
},
"devDependencies": {
"@dotenvx/dotenvx": "1.51.1",

View File

@@ -7,20 +7,20 @@ export const SiteSchema = z.object({
export const TargetHealthCheckSchema = z.object({
hostname: z.string(),
port: z.number().int().min(1).max(65535),
port: z.int().min(1).max(65535),
enabled: z.boolean().optional().default(true),
path: z.string().optional(),
scheme: z.string().optional(),
mode: z.string().default("http"),
interval: z.number().int().default(30),
"unhealthy-interval": z.number().int().default(30),
unhealthyInterval: z.number().int().optional(), // deprecated alias
timeout: z.number().int().default(5),
interval: z.int().default(30),
"unhealthy-interval": z.int().default(30),
unhealthyInterval: z.int().optional(), // deprecated alias
timeout: z.int().default(5),
headers: z.array(z.object({ name: z.string(), value: z.string() })).nullable().optional().default(null),
"follow-redirects": z.boolean().default(true),
followRedirects: z.boolean().optional(), // deprecated alias
method: z.string().default("GET"),
status: z.number().int().optional()
status: z.int().optional()
});
// Schema for individual target within a resource
@@ -28,16 +28,16 @@ export const TargetSchema = z.object({
site: z.string().optional(),
method: z.enum(["http", "https", "h2c"]).optional(),
hostname: z.string(),
port: z.number().int().min(1).max(65535),
port: z.int().min(1).max(65535),
enabled: z.boolean().optional().default(true),
"internal-port": z.number().int().min(1).max(65535).optional(),
"internal-port": z.int().min(1).max(65535).optional(),
path: z.string().optional(),
"path-match": z.enum(["exact", "prefix", "regex"]).optional().nullable(),
healthcheck: TargetHealthCheckSchema.optional(),
rewritePath: z.string().optional(), // deprecated alias
"rewrite-path": z.string().optional(),
"rewrite-match": z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable(),
priority: z.number().int().min(1).max(1000).optional().default(100)
priority: z.int().min(1).max(1000).optional().default(100)
});
export type TargetData = z.infer<typeof TargetSchema>;
@@ -55,10 +55,10 @@ export const AuthSchema = z.object({
.optional()
.default([])
.refine((roles) => !roles.includes("Admin"), {
message: "Admin role cannot be included in sso-roles"
error: "Admin role cannot be included in sso-roles"
}),
"sso-users": z.array(z.string().email()).optional().default([]),
"whitelist-users": z.array(z.string().email()).optional().default([]),
"sso-users": z.array(z.email()).optional().default([]),
"whitelist-users": z.array(z.email()).optional().default([]),
});
export const RuleSchema = z.object({
@@ -79,7 +79,7 @@ export const ResourceSchema = z
protocol: z.enum(["http", "tcp", "udp"]).optional(),
ssl: z.boolean().optional(),
"full-domain": z.string().optional(),
"proxy-port": z.number().int().min(1).max(65535).optional(),
"proxy-port": z.int().min(1).max(65535).optional(),
enabled: z.boolean().optional(),
targets: z.array(TargetSchema.nullable()).optional().default([]),
auth: AuthSchema.optional(),
@@ -100,9 +100,8 @@ export const ResourceSchema = z
);
},
{
message:
"Resource must either be targets-only (only 'targets' field) or have both 'name' and 'protocol' fields at a minimum",
path: ["name", "protocol"]
path: ["name", "protocol"],
error: "Resource must either be targets-only (only 'targets' field) or have both 'name' and 'protocol' fields at a minimum"
}
)
.refine(
@@ -117,6 +116,20 @@ export const ResourceSchema = z
(target) => target == null || target.method !== undefined
);
}
return true;
},
{
path: ["targets"],
error: "When protocol is 'http', all targets must have a 'method' field"
}
)
.refine(
(resource) => {
if (isTargetsOnlyResource(resource)) {
return true;
}
// If protocol is tcp or udp, no target should have method field
if (resource.protocol === "tcp" || resource.protocol === "udp") {
return resource.targets.every(
@@ -125,19 +138,9 @@ export const ResourceSchema = z
}
return true;
},
(resource) => {
if (resource.protocol === "http") {
return {
message:
"When protocol is 'http', all targets must have a 'method' field",
path: ["targets"]
};
}
return {
message:
"When protocol is 'tcp' or 'udp', targets must not have a 'method' field",
path: ["targets"]
};
{
path: ["targets"],
error: "When protocol is 'tcp' or 'udp', targets must not have a 'method' field"
}
)
.refine(
@@ -156,9 +159,8 @@ export const ResourceSchema = z
return true;
},
{
message:
"When protocol is 'http', a 'full-domain' must be provided",
path: ["full-domain"]
path: ["full-domain"],
error: "When protocol is 'http', a 'full-domain' must be provided"
}
)
.refine(
@@ -174,9 +176,8 @@ export const ResourceSchema = z
return true;
},
{
message:
"When protocol is 'tcp' or 'udp', 'proxy-port' must be provided",
path: ["proxy-port", "exit-node"]
path: ["proxy-port", "exit-node"],
error: "When protocol is 'tcp' or 'udp', 'proxy-port' must be provided"
}
)
.refine(
@@ -193,9 +194,8 @@ export const ResourceSchema = z
return true;
},
{
message:
"When protocol is 'tcp' or 'udp', 'auth' must not be provided",
path: ["auth"]
path: ["auth"],
error: "When protocol is 'tcp' or 'udp', 'auth' must not be provided"
}
);
@@ -216,36 +216,12 @@ export const ClientResourceSchema = z.object({
// Schema for the entire configuration object
export const ConfigSchema = z
.object({
"proxy-resources": z.record(z.string(), ResourceSchema).optional().default({}),
"client-resources": z.record(z.string(), ClientResourceSchema).optional().default({}),
sites: z.record(z.string(), SiteSchema).optional().default({})
"proxy-resources": z.record(z.string(), ResourceSchema).optional().prefault({}),
"client-resources": z.record(z.string(), ClientResourceSchema).optional().prefault({}),
sites: z.record(z.string(), SiteSchema).optional().prefault({})
})
.refine(
// Enforce the full-domain uniqueness across resources in the same stack
(config) => {
// Extract all full-domain values with their resource keys
const fullDomainMap = new Map<string, string[]>();
Object.entries(config["proxy-resources"]).forEach(
([resourceKey, resource]) => {
const fullDomain = resource["full-domain"];
if (fullDomain) {
// Only process if full-domain is defined
if (!fullDomainMap.has(fullDomain)) {
fullDomainMap.set(fullDomain, []);
}
fullDomainMap.get(fullDomain)!.push(resourceKey);
}
}
);
// Find duplicates
const duplicates = Array.from(fullDomainMap.entries()).filter(
([_, resourceKeys]) => resourceKeys.length > 1
);
return duplicates.length === 0;
},
(config) => {
// Extract duplicates for error message
const fullDomainMap = new Map<string, string[]>();
@@ -271,38 +247,16 @@ export const ConfigSchema = z
)
.join("; ");
return {
message: `Duplicate 'full-domain' values found: ${duplicates}`,
path: ["resources"]
};
if (duplicates.length !== 0) {
return {
path: ["resources"],
error: `Duplicate 'full-domain' values found: ${duplicates}`
};
}
}
)
.refine(
// Enforce proxy-port uniqueness within proxy-resources per protocol
(config) => {
const protocolPortMap = new Map<string, string[]>();
Object.entries(config["proxy-resources"]).forEach(
([resourceKey, resource]) => {
const proxyPort = resource["proxy-port"];
const protocol = resource.protocol;
if (proxyPort !== undefined && protocol !== undefined) {
const key = `${protocol}:${proxyPort}`;
if (!protocolPortMap.has(key)) {
protocolPortMap.set(key, []);
}
protocolPortMap.get(key)!.push(resourceKey);
}
}
);
// Find duplicates
const duplicates = Array.from(protocolPortMap.entries()).filter(
([_, resourceKeys]) => resourceKeys.length > 1
);
return duplicates.length === 0;
},
(config) => {
// Extract duplicates for error message
const protocolPortMap = new Map<string, string[]>();
@@ -331,36 +285,16 @@ export const ConfigSchema = z
)
.join("; ");
return {
message: `Duplicate 'proxy-port' values found in proxy-resources: ${duplicates}`,
path: ["proxy-resources"]
};
if (duplicates.length !== 0) {
return {
path: ["proxy-resources"],
error: `Duplicate 'proxy-port' values found in proxy-resources: ${duplicates}`
};
}
}
)
.refine(
// Enforce proxy-port uniqueness within client-resources
(config) => {
const proxyPortMap = new Map<number, string[]>();
Object.entries(config["client-resources"]).forEach(
([resourceKey, resource]) => {
const proxyPort = resource["proxy-port"];
if (proxyPort !== undefined) {
if (!proxyPortMap.has(proxyPort)) {
proxyPortMap.set(proxyPort, []);
}
proxyPortMap.get(proxyPort)!.push(resourceKey);
}
}
);
// Find duplicates
const duplicates = Array.from(proxyPortMap.entries()).filter(
([_, resourceKeys]) => resourceKeys.length > 1
);
return duplicates.length === 0;
},
(config) => {
// Extract duplicates for error message
const proxyPortMap = new Map<number, string[]>();
@@ -385,10 +319,12 @@ export const ConfigSchema = z
)
.join("; ");
return {
message: `Duplicate 'proxy-port' values found in client-resources: ${duplicates}`,
path: ["client-resources"]
};
if (duplicates.length !== 0) {
return {
path: ["client-resources"],
error: `Duplicate 'proxy-port' values found in client-resources: ${duplicates}`
};
}
}
);

View File

@@ -14,10 +14,8 @@ export const configSchema = z
.object({
app: z
.object({
dashboard_url: z
.string()
.url()
.pipe(z.string().url())
dashboard_url: z.url()
.pipe(z.url())
.transform((url) => url.toLowerCase())
.optional(),
log_level: z
@@ -31,14 +29,14 @@ export const configSchema = z
anonymous_usage: z.boolean().optional().default(true)
})
.optional()
.default({}),
.prefault({}),
notifications: z
.object({
product_updates: z.boolean().optional().default(true),
new_releases: z.boolean().optional().default(true)
})
.optional()
.default({})
.prefault({})
})
.optional()
.default({
@@ -107,7 +105,7 @@ export const configSchema = z
token: z.string().optional().default("P-Access-Token")
})
.optional()
.default({}),
.prefault({}),
resource_session_request_param: z
.string()
.optional()
@@ -132,7 +130,7 @@ export const configSchema = z
credentials: z.boolean().optional()
})
.optional(),
trust_proxy: z.number().int().gte(0).optional().default(1),
trust_proxy: z.int().gte(0).optional().default(1),
secret: z.string().pipe(z.string().min(8)).optional(),
maxmind_db_path: z.string().optional()
})
@@ -189,7 +187,7 @@ export const configSchema = z
.default(5000)
})
.optional()
.default({})
.prefault({})
})
.optional(),
traefik: z
@@ -222,7 +220,7 @@ export const configSchema = z
.default("pp-transport-v")
})
.optional()
.default({}),
.prefault({}),
gerbil: z
.object({
exit_node_name: z.string().optional(),
@@ -247,7 +245,7 @@ export const configSchema = z
.default(30)
})
.optional()
.default({}),
.prefault({}),
orgs: z
.object({
block_size: z.number().positive().gt(0).optional().default(24),
@@ -276,7 +274,7 @@ export const configSchema = z
.default(500)
})
.optional()
.default({}),
.prefault({}),
auth: z
.object({
window_minutes: z
@@ -293,10 +291,10 @@ export const configSchema = z
.default(500)
})
.optional()
.default({})
.prefault({})
})
.optional()
.default({}),
.prefault({}),
email: z
.object({
smtp_host: z.string().optional(),
@@ -308,7 +306,7 @@ export const configSchema = z
.transform(getEnvOrYaml("EMAIL_SMTP_PASS")),
smtp_secure: z.boolean().optional(),
smtp_tls_reject_unauthorized: z.boolean().optional(),
no_reply: z.string().email().optional()
no_reply: z.email().optional()
})
.optional(),
flags: z
@@ -340,7 +338,7 @@ export const configSchema = z
.default("cname.pangolin.net")
})
.optional()
.default({})
.prefault({})
})
.refine(
(data) => {
@@ -355,7 +353,7 @@ export const configSchema = z
return true;
},
{
message: "At least one domain must be defined"
error: "At least one domain must be defined"
}
)
.refine(
@@ -370,7 +368,7 @@ export const configSchema = z
);
},
{
message: "Server secret must be defined"
error: "Server secret must be defined"
}
)
.refine(
@@ -382,7 +380,7 @@ export const configSchema = z
);
},
{
message: "Dashboard URL must be defined"
error: "Dashboard URL must be defined"
}
);

View File

@@ -2,11 +2,11 @@ import z from "zod";
import ipaddr from "ipaddr.js";
export function isValidCIDR(cidr: string): boolean {
return z.string().cidr().safeParse(cidr).success;
return z.cidrv4().safeParse(cidr).success || z.cidrv6().safeParse(cidr).success;
}
export function isValidIP(ip: string): boolean {
return z.string().ip().safeParse(ip).success;
return z.ipv4().safeParse(ip).success || z.ipv6().safeParse(ip).success;
}
export function isValidUrlGlobPattern(pattern: string): boolean {

View File

@@ -50,14 +50,14 @@ export const privateConfigSchema = z.object({
host: z.string(),
port: portSchema,
password: z.string().optional(),
db: z.number().int().nonnegative().optional().default(0),
db: z.int().nonnegative().optional().default(0),
replicas: z
.array(
z.object({
host: z.string(),
port: portSchema,
password: z.string().optional(),
db: z.number().int().nonnegative().optional().default(0)
db: z.int().nonnegative().optional().default(0)
})
)
.optional()
@@ -79,14 +79,14 @@ export const privateConfigSchema = z.object({
.default("http://gerbil:3004")
})
.optional()
.default({}),
.prefault({}),
flags: z
.object({
enable_redis: z.boolean().optional().default(false),
use_pangolin_dns: z.boolean().optional().default(false)
})
.optional()
.default({}),
.prefault({}),
branding: z
.object({
app_name: z.string().optional(),

View File

@@ -30,17 +30,17 @@ export const queryAccessAuditLogsQuery = z.object({
timeStart: z
.string()
.refine((val) => !isNaN(Date.parse(val)), {
message: "timeStart must be a valid ISO date string"
error: "timeStart must be a valid ISO date string"
})
.transform((val) => Math.floor(new Date(val).getTime() / 1000)),
timeEnd: z
.string()
.refine((val) => !isNaN(Date.parse(val)), {
message: "timeEnd must be a valid ISO date string"
error: "timeEnd must be a valid ISO date string"
})
.transform((val) => Math.floor(new Date(val).getTime() / 1000))
.optional()
.default(new Date().toISOString()),
.prefault(new Date().toISOString()),
action: z
.union([z.boolean(), z.string()])
.transform((val) => (typeof val === "string" ? val === "true" : val))
@@ -51,7 +51,7 @@ export const queryAccessAuditLogsQuery = z.object({
.string()
.optional()
.transform(Number)
.pipe(z.number().int().positive())
.pipe(z.int().positive())
.optional(),
actor: z.string().optional(),
type: z.string().optional(),
@@ -61,13 +61,13 @@ export const queryAccessAuditLogsQuery = z.object({
.optional()
.default("1000")
.transform(Number)
.pipe(z.number().int().positive()),
.pipe(z.int().positive()),
offset: z
.string()
.optional()
.default("0")
.transform(Number)
.pipe(z.number().int().nonnegative())
.pipe(z.int().nonnegative())
});
export const queryAccessAuditLogsParams = z.object({

View File

@@ -30,17 +30,17 @@ export const queryActionAuditLogsQuery = z.object({
timeStart: z
.string()
.refine((val) => !isNaN(Date.parse(val)), {
message: "timeStart must be a valid ISO date string"
error: "timeStart must be a valid ISO date string"
})
.transform((val) => Math.floor(new Date(val).getTime() / 1000)),
timeEnd: z
.string()
.refine((val) => !isNaN(Date.parse(val)), {
message: "timeEnd must be a valid ISO date string"
error: "timeEnd must be a valid ISO date string"
})
.transform((val) => Math.floor(new Date(val).getTime() / 1000))
.optional()
.default(new Date().toISOString()),
.prefault(new Date().toISOString()),
action: z.string().optional(),
actorType: z.string().optional(),
actorId: z.string().optional(),
@@ -50,13 +50,13 @@ export const queryActionAuditLogsQuery = z.object({
.optional()
.default("1000")
.transform(Number)
.pipe(z.number().int().positive()),
.pipe(z.int().positive()),
offset: z
.string()
.optional()
.default("0")
.transform(Number)
.pipe(z.number().int().nonnegative())
.pipe(z.int().nonnegative())
});
export const queryActionAuditLogsParams = z.object({

View File

@@ -28,7 +28,7 @@ import { response } from "@server/lib/response";
import { encrypt } from "@server/lib/crypto";
import config from "@server/lib/config";
const paramsSchema = z.object({}).strict();
const paramsSchema = z.strictObject({});
export type GetSessionTransferTokenRenponse = {
token: string;

View File

@@ -62,10 +62,10 @@ import { isTargetValid } from "@server/lib/validators";
import { listExitNodes } from "#private/lib/exitNodes";
const bodySchema = z.object({
email: z.string().toLowerCase().email(),
email: z.email().toLowerCase(),
ip: z.string().refine(isTargetValid),
method: z.enum(["http", "https"]),
port: z.number().int().min(1).max(65535),
port: z.int().min(1).max(65535),
pincode: z
.string()
.regex(/^\d{6}$/)

View File

@@ -25,11 +25,9 @@ import stripe from "#private/lib/stripe";
import { getLineItems, getStandardFeaturePriceSet } from "@server/lib/billing";
import { getTierPriceSet, TierId } from "@server/lib/billing/tiers";
const createCheckoutSessionSchema = z
.object({
const createCheckoutSessionSchema = z.strictObject({
orgId: z.string()
})
.strict();
});
export async function createCheckoutSession(
req: Request,

View File

@@ -23,11 +23,9 @@ import config from "@server/lib/config";
import { fromError } from "zod-validation-error";
import stripe from "#private/lib/stripe";
const createPortalSessionSchema = z
.object({
const createPortalSessionSchema = z.strictObject({
orgId: z.string()
})
.strict();
});
export async function createPortalSession(
req: Request,

View File

@@ -33,11 +33,9 @@ import {
SubscriptionItem
} from "@server/db";
const getOrgSchema = z
.object({
const getOrgSchema = z.strictObject({
orgId: z.string()
})
.strict();
});
registry.registerPath({
method: "get",

View File

@@ -27,11 +27,9 @@ import { usageService } from "@server/lib/billing/usageService";
import { FeatureId } from "@server/lib/billing";
import { GetOrgUsageResponse } from "@server/routers/billing/types";
const getOrgSchema = z
.object({
const getOrgSchema = z.strictObject({
orgId: z.string()
})
.strict();
});
registry.registerPath({
method: "get",

View File

@@ -21,11 +21,9 @@ import { fromZodError } from "zod-validation-error";
import { getOrgTierData } from "#private/lib/billing";
import { GetOrgTierResponse } from "@server/routers/billing/types";
const getOrgSchema = z
.object({
const getOrgSchema = z.strictObject({
orgId: z.string()
})
.strict();
});
export async function getOrgTier(
req: Request,

View File

@@ -23,13 +23,11 @@ import { fromError } from "zod-validation-error";
import { registry } from "@server/openApi";
import { GetCertificateResponse } from "@server/routers/certificates/types";
const getCertificateSchema = z
.object({
const getCertificateSchema = z.strictObject({
domainId: z.string(),
domain: z.string().min(1).max(255),
orgId: z.string()
})
.strict();
});
async function query(domainId: string, domain: string) {
const [domainRecord] = await db

View File

@@ -24,12 +24,10 @@ import stoi from "@server/lib/stoi";
import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
const restartCertificateParamsSchema = z
.object({
certId: z.string().transform(stoi).pipe(z.number().int().positive()),
const restartCertificateParamsSchema = z.strictObject({
certId: z.string().transform(stoi).pipe(z.int().positive()),
orgId: z.string()
})
.strict();
});
registry.registerPath({
method: "post",
@@ -41,7 +39,7 @@ registry.registerPath({
certId: z
.string()
.transform(stoi)
.pipe(z.number().int().positive()),
.pipe(z.int().positive()),
orgId: z.string()
})
},

View File

@@ -23,13 +23,11 @@ import { db, domainNamespaces, resources } from "@server/db";
import { inArray } from "drizzle-orm";
import { CheckDomainAvailabilityResponse } from "@server/routers/domain/types";
const paramsSchema = z.object({}).strict();
const paramsSchema = z.strictObject({});
const querySchema = z
.object({
const querySchema = z.strictObject({
subdomain: z.string()
})
.strict();
});
registry.registerPath({
method: "get",

View File

@@ -23,24 +23,22 @@ import logger from "@server/logger";
import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
const paramsSchema = z.object({}).strict();
const paramsSchema = z.strictObject({});
const querySchema = z
.object({
const querySchema = z.strictObject({
limit: z
.string()
.optional()
.default("1000")
.transform(Number)
.pipe(z.number().int().nonnegative()),
.pipe(z.int().nonnegative()),
offset: z
.string()
.optional()
.default("0")
.transform(Number)
.pipe(z.number().int().nonnegative())
})
.strict();
.pipe(z.int().nonnegative())
});
async function query(limit: number, offset: number) {
const res = await db

View File

@@ -78,105 +78,78 @@ import { verifyResourceAccessToken } from "@server/auth/verifyResourceAccessToke
import semver from "semver";
// Zod schemas for request validation
const getResourceByDomainParamsSchema = z
.object({
const getResourceByDomainParamsSchema = z.strictObject({
domain: z.string().min(1, "Domain is required")
})
.strict();
});
const getUserSessionParamsSchema = z
.object({
const getUserSessionParamsSchema = z.strictObject({
userSessionId: z.string().min(1, "User session ID is required")
})
.strict();
});
const getUserOrgRoleParamsSchema = z
.object({
const getUserOrgRoleParamsSchema = z.strictObject({
userId: z.string().min(1, "User ID is required"),
orgId: z.string().min(1, "Organization ID is required")
})
.strict();
});
const getRoleResourceAccessParamsSchema = z
.object({
const getRoleResourceAccessParamsSchema = z.strictObject({
roleId: z
.string()
.transform(Number)
.pipe(
z.number().int().positive("Role ID must be a positive integer")
z.int().positive("Role ID must be a positive integer")
),
resourceId: z
.string()
.transform(Number)
.pipe(
z
.number()
.int()
z.int()
.positive("Resource ID must be a positive integer")
)
})
.strict();
});
const getUserResourceAccessParamsSchema = z
.object({
const getUserResourceAccessParamsSchema = z.strictObject({
userId: z.string().min(1, "User ID is required"),
resourceId: z
.string()
.transform(Number)
.pipe(
z
.number()
.int()
z.int()
.positive("Resource ID must be a positive integer")
)
})
.strict();
});
const getResourceRulesParamsSchema = z
.object({
const getResourceRulesParamsSchema = z.strictObject({
resourceId: z
.string()
.transform(Number)
.pipe(
z
.number()
.int()
z.int()
.positive("Resource ID must be a positive integer")
)
})
.strict();
});
const validateResourceSessionTokenParamsSchema = z
.object({
const validateResourceSessionTokenParamsSchema = z.strictObject({
resourceId: z
.string()
.transform(Number)
.pipe(
z
.number()
.int()
z.int()
.positive("Resource ID must be a positive integer")
)
})
.strict();
});
const validateResourceSessionTokenBodySchema = z
.object({
const validateResourceSessionTokenBodySchema = z.strictObject({
token: z.string().min(1, "Token is required")
})
.strict();
});
const validateResourceAccessTokenBodySchema = z
.object({
const validateResourceAccessTokenBodySchema = z.strictObject({
accessTokenId: z.string().optional(),
resourceId: z.number().optional(),
accessToken: z.string()
})
.strict();
});
// Certificates by domains query validation
const getCertificatesByDomainsQuerySchema = z
.object({
const getCertificatesByDomainsQuerySchema = z.strictObject({
// Accept domains as string or array (domains or domains[])
domains: z
.union([z.array(z.string().min(1)), z.string().min(1)])
@@ -185,8 +158,7 @@ const getCertificatesByDomainsQuerySchema = z
"domains[]": z
.union([z.array(z.string().min(1)), z.string().min(1)])
.optional()
})
.strict();
});
// Type exports for request schemas
export type GetResourceByDomainParams = z.infer<
@@ -591,11 +563,9 @@ hybridRouter.get(
}
);
const getOrgLoginPageParamsSchema = z
.object({
const getOrgLoginPageParamsSchema = z.strictObject({
orgId: z.string().min(1)
})
.strict();
});
hybridRouter.get(
"/org/:orgId/login-page",
@@ -1217,7 +1187,7 @@ hybridRouter.post(
);
const geoIpLookupParamsSchema = z.object({
ip: z.string().ip()
ip: z.union([z.ipv4(), z.ipv6()])
});
hybridRouter.get(
"/geoip/:ip",

View File

@@ -20,11 +20,9 @@ import license from "#private/license/license";
import { z } from "zod";
import { fromError } from "zod-validation-error";
const bodySchema = z
.object({
const bodySchema = z.strictObject({
licenseKey: z.string().min(1).max(255)
})
.strict();
});
export async function activateLicense(
req: Request,

View File

@@ -23,11 +23,9 @@ import { eq } from "drizzle-orm";
import { licenseKey } from "@server/db";
import license from "#private/license/license";
const paramsSchema = z
.object({
const paramsSchema = z.strictObject({
licenseKey: z.string().min(1).max(255)
})
.strict();
});
export async function deleteLicenseKey(
req: Request,

View File

@@ -35,18 +35,14 @@ import { TierId } from "@server/lib/billing/tiers";
import { build } from "@server/build";
import { CreateLoginPageResponse } from "@server/routers/loginPage/types";
const paramsSchema = z
.object({
const paramsSchema = z.strictObject({
orgId: z.string()
})
.strict();
});
const bodySchema = z
.object({
const bodySchema = z.strictObject({
subdomain: z.string().nullable().optional(),
domainId: z.string()
})
.strict();
});
export type CreateLoginPageBody = z.infer<typeof bodySchema>;

View File

@@ -25,7 +25,7 @@ import { DeleteLoginPageResponse } from "@server/routers/loginPage/types";
const paramsSchema = z
.object({
orgId: z.string(),
loginPageId: z.coerce.number()
loginPageId: z.coerce.number<number>()
})
.strict();

View File

@@ -22,11 +22,9 @@ import logger from "@server/logger";
import { fromError } from "zod-validation-error";
import { GetLoginPageResponse } from "@server/routers/loginPage/types";
const paramsSchema = z
.object({
const paramsSchema = z.strictObject({
orgId: z.string()
})
.strict();
});
async function query(orgId: string) {
const [res] = await db

View File

@@ -23,8 +23,8 @@ import { fromError } from "zod-validation-error";
import { LoadLoginPageResponse } from "@server/routers/loginPage/types";
const querySchema = z.object({
resourceId: z.coerce.number().int().positive().optional(),
idpId: z.coerce.number().int().positive().optional(),
resourceId: z.coerce.number<number>().int().positive().optional(),
idpId: z.coerce.number<number>().int().positive().optional(),
orgId: z.string().min(1).optional(),
fullDomain: z.string().min(1)
});

View File

@@ -31,18 +31,16 @@ import { UpdateLoginPageResponse } from "@server/routers/loginPage/types";
const paramsSchema = z
.object({
orgId: z.string(),
loginPageId: z.coerce.number()
loginPageId: z.coerce.number<number>()
})
.strict();
const bodySchema = z
.object({
const bodySchema = z.strictObject({
subdomain: subdomainSchema.nullable().optional(),
domainId: z.string().optional()
})
.strict()
.refine((data) => Object.keys(data).length > 0, {
message: "At least one field must be provided for update"
error: "At least one field must be provided for update"
})
.refine(
(data) => {
@@ -51,7 +49,9 @@ const bodySchema = z
}
return true;
},
{ message: "Invalid subdomain" }
{
error: "Invalid subdomain"
}
);
export type UpdateLoginPageBody = z.infer<typeof bodySchema>;

View File

@@ -22,12 +22,10 @@ import { sendEmail } from "@server/emails";
import SupportEmail from "@server/emails/templates/SupportEmail";
import config from "@server/lib/config";
const bodySchema = z
.object({
const bodySchema = z.strictObject({
body: z.string().min(1),
subject: z.string().min(1).max(255)
})
.strict();
});
export async function sendSupportEmail(
req: Request,

View File

@@ -29,15 +29,14 @@ import { getOrgTierData } from "#private/lib/billing";
import { TierId } from "@server/lib/billing/tiers";
import { CreateOrgIdpResponse } from "@server/routers/orgIdp/types";
const paramsSchema = z.object({ orgId: z.string().nonempty() }).strict();
const paramsSchema = z.strictObject({ orgId: z.string().nonempty() });
const bodySchema = z
.object({
const bodySchema = z.strictObject({
name: z.string().nonempty(),
clientId: z.string().nonempty(),
clientSecret: z.string().nonempty(),
authUrl: z.string().url(),
tokenUrl: z.string().url(),
authUrl: z.url(),
tokenUrl: z.url(),
identifierPath: z.string().nonempty(),
emailPath: z.string().optional(),
namePath: z.string().optional(),
@@ -45,8 +44,7 @@ const bodySchema = z
autoProvision: z.boolean().optional(),
variant: z.enum(["oidc", "google", "azure"]).optional().default("oidc"),
roleMapping: z.string().optional()
})
.strict();
});
// registry.registerPath({
// method: "put",

View File

@@ -26,7 +26,7 @@ import { OpenAPITags, registry } from "@server/openApi";
const paramsSchema = z
.object({
orgId: z.string().optional(), // Optional; used with org idp in saas
idpId: z.coerce.number()
idpId: z.coerce.number<number>()
})
.strict();

View File

@@ -30,7 +30,7 @@ import { GetOrgIdpResponse } from "@server/routers/orgIdp/types";
const paramsSchema = z
.object({
orgId: z.string().nonempty(),
idpId: z.coerce.number()
idpId: z.coerce.number<number>()
})
.strict();

View File

@@ -24,28 +24,24 @@ import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
import { ListOrgIdpsResponse } from "@server/routers/orgIdp/types";
const querySchema = z
.object({
const querySchema = z.strictObject({
limit: z
.string()
.optional()
.default("1000")
.transform(Number)
.pipe(z.number().int().nonnegative()),
.pipe(z.int().nonnegative()),
offset: z
.string()
.optional()
.default("0")
.transform(Number)
.pipe(z.number().int().nonnegative())
})
.strict();
.pipe(z.int().nonnegative())
});
const paramsSchema = z
.object({
const paramsSchema = z.strictObject({
orgId: z.string().nonempty()
})
.strict();
});
async function query(orgId: string, limit: number, offset: number) {
const res = await db

View File

@@ -31,12 +31,11 @@ import { TierId } from "@server/lib/billing/tiers";
const paramsSchema = z
.object({
orgId: z.string().nonempty(),
idpId: z.coerce.number()
idpId: z.coerce.number<number>()
})
.strict();
const bodySchema = z
.object({
const bodySchema = z.strictObject({
name: z.string().optional(),
clientId: z.string().optional(),
clientSecret: z.string().optional(),
@@ -48,8 +47,7 @@ const bodySchema = z
scopes: z.string().optional(),
autoProvision: z.boolean().optional(),
roleMapping: z.string().optional()
})
.strict();
});
export type UpdateOrgIdpResponse = {
idpId: number;

View File

@@ -24,19 +24,15 @@ import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
import { hashPassword } from "@server/auth/password";
const reGenerateSecretParamsSchema = z
.object({
clientId: z.string().transform(Number).pipe(z.number().int().positive())
})
.strict();
const reGenerateSecretParamsSchema = z.strictObject({
clientId: z.string().transform(Number).pipe(z.int().positive())
});
const reGenerateSecretBodySchema = z
.object({
const reGenerateSecretBodySchema = z.strictObject({
olmId: z.string().min(1).optional(),
secret: z.string().min(1).optional(),
})
.strict();
});
export type ReGenerateSecretBody = z.infer<typeof reGenerateSecretBodySchema>;

View File

@@ -29,12 +29,10 @@ export const paramsSchema = z.object({
orgId: z.string()
});
const bodySchema = z
.object({
const bodySchema = z.strictObject({
remoteExitNodeId: z.string().length(15),
secret: z.string().length(48)
})
.strict();
});
registry.registerPath({

View File

@@ -25,22 +25,18 @@ import { hashPassword } from "@server/auth/password";
import { addPeer } from "@server/routers/gerbil/peers";
const updateSiteParamsSchema = z
.object({
siteId: z.string().transform(Number).pipe(z.number().int().positive())
})
.strict();
const updateSiteParamsSchema = z.strictObject({
siteId: z.string().transform(Number).pipe(z.int().positive())
});
const updateSiteBodySchema = z
.object({
const updateSiteBodySchema = z.strictObject({
type: z.enum(["newt", "wireguard"]),
newtId: z.string().min(1).max(255).optional(),
newtSecret: z.string().min(1).max(255).optional(),
exitNodeId: z.number().int().positive().optional(),
exitNodeId: z.int().positive().optional(),
pubKey: z.string().optional(),
subnet: z.string().optional(),
})
.strict();
});
registry.registerPath({
method: "post",

View File

@@ -35,12 +35,10 @@ export const paramsSchema = z.object({
orgId: z.string()
});
const bodySchema = z
.object({
const bodySchema = z.strictObject({
remoteExitNodeId: z.string().length(15),
secret: z.string().length(48)
})
.strict();
});
export type CreateRemoteExitNodeBody = z.infer<typeof bodySchema>;

View File

@@ -24,12 +24,10 @@ import { fromError } from "zod-validation-error";
import { usageService } from "@server/lib/billing/usageService";
import { FeatureId } from "@server/lib/billing";
const paramsSchema = z
.object({
const paramsSchema = z.strictObject({
orgId: z.string().min(1),
remoteExitNodeId: z.string().min(1)
})
.strict();
});
export async function deleteRemoteExitNode(
req: Request,

View File

@@ -23,12 +23,10 @@ import logger from "@server/logger";
import { fromError } from "zod-validation-error";
import { GetRemoteExitNodeResponse } from "@server/routers/remoteExitNode/types";
const getRemoteExitNodeSchema = z
.object({
const getRemoteExitNodeSchema = z.strictObject({
orgId: z.string().min(1),
remoteExitNodeId: z.string().min(1)
})
.strict();
});
async function query(remoteExitNodeId: string) {
const [remoteExitNode] = await db

View File

@@ -23,11 +23,9 @@ import logger from "@server/logger";
import { fromError } from "zod-validation-error";
import { ListRemoteExitNodesResponse } from "@server/routers/remoteExitNode/types";
const listRemoteExitNodesParamsSchema = z
.object({
const listRemoteExitNodesParamsSchema = z.strictObject({
orgId: z.string()
})
.strict();
});
const listRemoteExitNodesSchema = z.object({
limit: z
@@ -35,13 +33,13 @@ const listRemoteExitNodesSchema = z.object({
.optional()
.default("1000")
.transform(Number)
.pipe(z.number().int().positive()),
.pipe(z.int().positive()),
offset: z
.string()
.optional()
.default("0")
.transform(Number)
.pipe(z.number().int().nonnegative())
.pipe(z.int().nonnegative())
});
export function queryRemoteExitNodes(orgId: string) {

View File

@@ -21,11 +21,9 @@ import { fromError } from "zod-validation-error";
import { z } from "zod";
import { PickRemoteExitNodeDefaultsResponse } from "@server/routers/remoteExitNode/types";
const paramsSchema = z
.object({
const paramsSchema = z.strictObject({
orgId: z.string()
})
.strict();
});
export async function pickRemoteExitNodeDefaults(
req: Request,

View File

@@ -10,11 +10,9 @@ import { and, eq } from "drizzle-orm";
import { db } from "@server/db";
import { OpenAPITags, registry } from "@server/openApi";
const deleteAccessTokenParamsSchema = z
.object({
const deleteAccessTokenParamsSchema = z.strictObject({
accessTokenId: z.string()
})
.strict();
});
registry.registerPath({
method: "delete",

View File

@@ -24,22 +24,18 @@ import { encodeHexLowerCase } from "@oslojs/encoding";
import { sha256 } from "@oslojs/crypto/sha2";
import { OpenAPITags, registry } from "@server/openApi";
export const generateAccessTokenBodySchema = z
.object({
validForSeconds: z.number().int().positive().optional(), // seconds
export const generateAccessTokenBodySchema = z.strictObject({
validForSeconds: z.int().positive().optional(), // seconds
title: z.string().optional(),
description: z.string().optional()
})
.strict();
});
export const generateAccssTokenParamsSchema = z
.object({
export const generateAccssTokenParamsSchema = z.strictObject({
resourceId: z
.string()
.transform(Number)
.pipe(z.number().int().positive())
})
.strict();
.pipe(z.int().positive())
});
export type GenerateAccessTokenResponse = Omit<
ResourceAccessToken,

View File

@@ -17,18 +17,16 @@ import stoi from "@server/lib/stoi";
import { fromZodError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
const listAccessTokensParamsSchema = z
.object({
const listAccessTokensParamsSchema = z.strictObject({
resourceId: z
.string()
.optional()
.transform(stoi)
.pipe(z.number().int().positive().optional()),
.pipe(z.int().positive().optional()),
orgId: z.string().optional()
})
.strict()
.refine((data) => !!data.resourceId !== !!data.orgId, {
message: "Either resourceId or orgId must be provided, but not both"
error: "Either resourceId or orgId must be provided, but not both"
});
const listAccessTokensSchema = z.object({
@@ -37,14 +35,14 @@ const listAccessTokensSchema = z.object({
.optional()
.default("1000")
.transform(Number)
.pipe(z.number().int().nonnegative()),
.pipe(z.int().nonnegative()),
offset: z
.string()
.optional()
.default("0")
.transform(Number)
.pipe(z.number().int().nonnegative())
.pipe(z.int().nonnegative())
});
function queryAccessTokens(

View File

@@ -14,11 +14,9 @@ import {
import logger from "@server/logger";
import { hashPassword } from "@server/auth/password";
const bodySchema = z
.object({
const bodySchema = z.strictObject({
name: z.string().min(1).max(255)
})
.strict();
});
export type CreateRootApiKeyBody = z.infer<typeof bodySchema>;

View File

@@ -20,13 +20,13 @@ const querySchema = z.object({
.optional()
.default("1000")
.transform(Number)
.pipe(z.number().int().positive()),
.pipe(z.int().positive()),
offset: z
.string()
.optional()
.default("0")
.transform(Number)
.pipe(z.number().int().nonnegative())
.pipe(z.int().nonnegative())
});
function queryActions(apiKeyId: string) {

View File

@@ -16,13 +16,13 @@ const querySchema = z.object({
.optional()
.default("1000")
.transform(Number)
.pipe(z.number().int().positive()),
.pipe(z.int().positive()),
offset: z
.string()
.optional()
.default("0")
.transform(Number)
.pipe(z.number().int().nonnegative())
.pipe(z.int().nonnegative())
});
const paramsSchema = z.object({

View File

@@ -15,13 +15,13 @@ const querySchema = z.object({
.optional()
.default("1000")
.transform(Number)
.pipe(z.number().int().positive()),
.pipe(z.int().positive()),
offset: z
.string()
.optional()
.default("0")
.transform(Number)
.pipe(z.number().int().nonnegative())
.pipe(z.int().nonnegative())
});
function queryApiKeys() {

View File

@@ -10,13 +10,10 @@ import { fromError } from "zod-validation-error";
import { eq, and, inArray } from "drizzle-orm";
import { OpenAPITags, registry } from "@server/openApi";
const bodySchema = z
.object({
actionIds: z
.array(z.string().nonempty())
const bodySchema = z.strictObject({
actionIds: z.tuple([z.string()], z.string())
.transform((v) => Array.from(new Set(v)))
})
.strict();
});
const paramsSchema = z.object({
apiKeyId: z.string().nonempty()

View File

@@ -9,13 +9,10 @@ import logger from "@server/logger";
import { fromError } from "zod-validation-error";
import { eq, and, inArray } from "drizzle-orm";
const bodySchema = z
.object({
orgIds: z
.array(z.string().nonempty())
const bodySchema = z.strictObject({
orgIds: z.tuple([z.string()], z.string())
.transform((v) => Array.from(new Set(v)))
})
.strict();
});
const paramsSchema = z.object({
apiKeyId: z.string().nonempty()

View File

@@ -17,17 +17,17 @@ export const queryAccessAuditLogsQuery = z.object({
timeStart: z
.string()
.refine((val) => !isNaN(Date.parse(val)), {
message: "timeStart must be a valid ISO date string"
error: "timeStart must be a valid ISO date string"
})
.transform((val) => Math.floor(new Date(val).getTime() / 1000)),
timeEnd: z
.string()
.refine((val) => !isNaN(Date.parse(val)), {
message: "timeEnd must be a valid ISO date string"
error: "timeEnd must be a valid ISO date string"
})
.transform((val) => Math.floor(new Date(val).getTime() / 1000))
.optional()
.default(new Date().toISOString()),
.prefault(new Date().toISOString()),
action: z
.union([z.boolean(), z.string()])
.transform((val) => (typeof val === "string" ? val === "true" : val))
@@ -37,13 +37,13 @@ export const queryAccessAuditLogsQuery = z.object({
.string()
.optional()
.transform(Number)
.pipe(z.number().int().positive())
.pipe(z.int().positive())
.optional(),
resourceId: z
.string()
.optional()
.transform(Number)
.pipe(z.number().int().positive())
.pipe(z.int().positive())
.optional(),
actor: z.string().optional(),
location: z.string().optional(),
@@ -54,13 +54,13 @@ export const queryAccessAuditLogsQuery = z.object({
.optional()
.default("1000")
.transform(Number)
.pipe(z.number().int().positive()),
.pipe(z.int().positive()),
offset: z
.string()
.optional()
.default("0")
.transform(Number)
.pipe(z.number().int().nonnegative())
.pipe(z.int().nonnegative())
});
export const queryRequestAuditLogsParams = z.object({

View File

@@ -22,13 +22,11 @@ import { sendEmail } from "@server/emails";
import ConfirmPasswordReset from "@server/emails/templates/NotifyResetPassword";
import config from "@server/lib/config";
export const changePasswordBody = z
.object({
export const changePasswordBody = z.strictObject({
oldPassword: z.string(),
newPassword: passwordSchema,
code: z.string().optional()
})
.strict();
});
export type ChangePasswordBody = z.infer<typeof changePasswordBody>;

View File

@@ -7,10 +7,10 @@ import { response } from "@server/lib/response";
import { validateResourceSessionToken } from "@server/auth/sessions/resource";
import logger from "@server/logger";
export const params = z.object({
export const params = z.strictObject({
token: z.string(),
resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
}).strict();
resourceId: z.string().transform(Number).pipe(z.int().positive()),
});
export type CheckResourceSessionParams = z.infer<typeof params>;

View File

@@ -16,12 +16,10 @@ import config from "@server/lib/config";
import { unauthorized } from "@server/auth/unauthorizedResponse";
import { UserType } from "@server/types/UserTypes";
export const disable2faBody = z
.object({
export const disable2faBody = z.strictObject({
password: z.string(),
code: z.string().optional()
})
.strict();
});
export type Disable2faBody = z.infer<typeof disable2faBody>;

View File

@@ -20,14 +20,12 @@ import { verifySession } from "@server/auth/sessions/verifySession";
import { UserType } from "@server/types/UserTypes";
import { logAccessAudit } from "#dynamic/lib/logAccessAudit";
export const loginBodySchema = z
.object({
email: z.string().toLowerCase().email(),
export const loginBodySchema = z.strictObject({
email: z.email().toLowerCase(),
password: z.string(),
code: z.string().optional(),
resourceGuid: z.string().optional()
})
.strict();
});
export type LoginBody = z.infer<typeof loginBodySchema>;

View File

@@ -17,11 +17,9 @@ import ResetPasswordCode from "@server/emails/templates/ResetPasswordCode";
import { hashPassword } from "@server/auth/password";
import { UserType } from "@server/types/UserTypes";
export const requestPasswordResetBody = z
.object({
email: z.string().toLowerCase().email()
})
.strict();
export const requestPasswordResetBody = z.strictObject({
email: z.email().toLowerCase()
});
export type RequestPasswordResetBody = z.infer<typeof requestPasswordResetBody>;

View File

@@ -16,12 +16,10 @@ import { UserType } from "@server/types/UserTypes";
import { verifySession } from "@server/auth/sessions/verifySession";
import config from "@server/lib/config";
export const requestTotpSecretBody = z
.object({
export const requestTotpSecretBody = z.strictObject({
password: z.string(),
email: z.string().email().optional()
})
.strict();
email: z.email().optional()
});
export type RequestTotpSecretBody = z.infer<typeof requestTotpSecretBody>;

View File

@@ -17,14 +17,12 @@ import ConfirmPasswordReset from "@server/emails/templates/NotifyResetPassword";
import { sendEmail } from "@server/emails";
import { passwordSchema } from "@server/auth/passwordSchema";
export const resetPasswordBody = z
.object({
email: z.string().toLowerCase().email(),
export const resetPasswordBody = z.strictObject({
email: z.email().toLowerCase(),
token: z.string(), // reset secret code
newPassword: passwordSchema,
code: z.string().optional() // 2fa code
})
.strict();
});
export type ResetPasswordBody = z.infer<typeof resetPasswordBody>;

View File

@@ -99,28 +99,28 @@ async function clearChallenge(sessionId: string) {
await db.delete(webauthnChallenge).where(eq(webauthnChallenge.sessionId, sessionId));
}
export const registerSecurityKeyBody = z.object({
export const registerSecurityKeyBody = z.strictObject({
name: z.string().min(1),
password: z.string().min(1),
code: z.string().optional()
}).strict();
});
export const verifyRegistrationBody = z.object({
export const verifyRegistrationBody = z.strictObject({
credential: z.any()
}).strict();
});
export const startAuthenticationBody = z.object({
email: z.string().email().optional()
}).strict();
export const startAuthenticationBody = z.strictObject({
email: z.email().optional()
});
export const verifyAuthenticationBody = z.object({
export const verifyAuthenticationBody = z.strictObject({
credential: z.any()
}).strict();
});
export const deleteSecurityKeyBody = z.object({
export const deleteSecurityKeyBody = z.strictObject({
password: z.string().min(1),
code: z.string().optional()
}).strict();
});
export async function startRegistration(
req: Request,

View File

@@ -14,7 +14,7 @@ import { UserType } from "@server/types/UserTypes";
import moment from "moment";
export const bodySchema = z.object({
email: z.string().toLowerCase().email(),
email: z.email().toLowerCase(),
password: passwordSchema,
setupToken: z.string().min(1, "Setup token is required")
});

View File

@@ -26,7 +26,7 @@ import { build } from "@server/build";
import resend, { AudienceIds, moveEmailToAudience } from "#dynamic/lib/resend";
export const signupBodySchema = z.object({
email: z.string().toLowerCase().email(),
email: z.email().toLowerCase(),
password: passwordSchema,
inviteToken: z.string().optional(),
inviteId: z.string().optional(),

View File

@@ -8,11 +8,9 @@ import createHttpError from "http-errors";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const validateSetupTokenSchema = z
.object({
const validateSetupTokenSchema = z.strictObject({
token: z.string().min(1, "Token is required")
})
.strict();
});
export type ValidateSetupTokenResponse = {
valid: boolean;

View File

@@ -13,11 +13,9 @@ import logger from "@server/logger";
import { freeLimitSet, limitsService } from "@server/lib/billing";
import { build } from "@server/build";
export const verifyEmailBody = z
.object({
export const verifyEmailBody = z.strictObject({
code: z.string()
})
.strict();
});
export type VerifyEmailBody = z.infer<typeof verifyEmailBody>;

View File

@@ -18,13 +18,11 @@ import { generateBackupCodes } from "@server/lib/totp";
import { verifySession } from "@server/auth/sessions/verifySession";
import { unauthorized } from "@server/auth/unauthorizedResponse";
export const verifyTotpBody = z
.object({
email: z.string().email().optional(),
export const verifyTotpBody = z.strictObject({
email: z.email().optional(),
password: z.string().optional(),
code: z.string()
})
.strict();
});
export type VerifyTotpBody = z.infer<typeof verifyTotpBody>;

View File

@@ -40,10 +40,10 @@ import { logRequestAudit } from "./logRequestAudit";
import cache from "@server/lib/cache";
const verifyResourceSessionSchema = z.object({
sessions: z.record(z.string()).optional(),
headers: z.record(z.string()).optional(),
query: z.record(z.string()).optional(),
originalRequestURL: z.string().url(),
sessions: z.record(z.string(), z.string()).optional(),
headers: z.record(z.string(), z.string()).optional(),
query: z.record(z.string(), z.string()).optional(),
originalRequestURL: z.url(),
scheme: z.string(),
host: z.string(),
path: z.string(),

View File

@@ -8,17 +8,13 @@ import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
import { applyBlueprint } from "@server/lib/blueprints/applyBlueprint";
const applyBlueprintSchema = z
.object({
const applyBlueprintSchema = z.strictObject({
blueprint: z.string()
})
.strict();
});
const applyBlueprintParamsSchema = z
.object({
const applyBlueprintParamsSchema = z.strictObject({
orgId: z.string()
})
.strict();
});
registry.registerPath({
method: "put",

View File

@@ -12,15 +12,13 @@ import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
import { BlueprintData } from "./types";
const getBlueprintSchema = z
.object({
const getBlueprintSchema = z.strictObject({
blueprintId: z
.string()
.transform(stoi)
.pipe(z.number().int().positive()),
.pipe(z.int().positive()),
orgId: z.string()
})
.strict();
});
async function query(blueprintId: number, orgId: string) {
// Get the client

View File

@@ -10,28 +10,24 @@ import { fromZodError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
import { BlueprintData } from "./types";
const listBluePrintsParamsSchema = z
.object({
const listBluePrintsParamsSchema = z.strictObject({
orgId: z.string()
})
.strict();
});
const listBluePrintsSchema = z
.object({
const listBluePrintsSchema = z.strictObject({
limit: z
.string()
.optional()
.default("1000")
.transform(Number)
.pipe(z.number().int().nonnegative()),
.pipe(z.int().nonnegative()),
offset: z
.string()
.optional()
.default("0")
.transform(Number)
.pipe(z.number().int().nonnegative())
})
.strict();
.pipe(z.int().nonnegative())
});
async function queryBlueprints(orgId: string, limit: number, offset: number) {
const res = await db

View File

@@ -26,22 +26,18 @@ import { isIpInCidr } from "@server/lib/ip";
import { OpenAPITags, registry } from "@server/openApi";
import { listExitNodes } from "#dynamic/lib/exitNodes";
const createClientParamsSchema = z
.object({
const createClientParamsSchema = z.strictObject({
orgId: z.string()
})
.strict();
});
const createClientSchema = z
.object({
const createClientSchema = z.strictObject({
name: z.string().min(1).max(255),
siteIds: z.array(z.number().int().positive()),
siteIds: z.array(z.int().positive()),
olmId: z.string(),
secret: z.string(),
subnet: z.string(),
type: z.enum(["olm"])
})
.strict();
});
export type CreateClientBody = z.infer<typeof createClientSchema>;

View File

@@ -10,11 +10,9 @@ import logger from "@server/logger";
import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
const deleteClientSchema = z
.object({
clientId: z.string().transform(Number).pipe(z.number().int().positive())
})
.strict();
const deleteClientSchema = z.strictObject({
clientId: z.string().transform(Number).pipe(z.int().positive())
});
registry.registerPath({
method: "delete",

View File

@@ -11,11 +11,9 @@ import stoi from "@server/lib/stoi";
import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
const getClientSchema = z
.object({
clientId: z.string().transform(stoi).pipe(z.number().int().positive())
})
.strict();
const getClientSchema = z.strictObject({
clientId: z.string().transform(stoi).pipe(z.int().positive())
});
async function query(clientId: number) {
// Get the client

View File

@@ -78,11 +78,9 @@ async function getLatestOlmVersion(): Promise<string | null> {
}
const listClientsParamsSchema = z
.object({
const listClientsParamsSchema = z.strictObject({
orgId: z.string()
})
.strict();
});
const listClientsSchema = z.object({
limit: z
@@ -90,13 +88,13 @@ const listClientsSchema = z.object({
.optional()
.default("1000")
.transform(Number)
.pipe(z.number().int().positive()),
.pipe(z.int().positive()),
offset: z
.string()
.optional()
.default("0")
.transform(Number)
.pipe(z.number().int().nonnegative())
.pipe(z.int().nonnegative())
});
function queryClients(orgId: string, accessibleClientIds: number[]) {

View File

@@ -15,11 +15,9 @@ export type PickClientDefaultsResponse = {
subnet: string;
};
const pickClientDefaultsSchema = z
.object({
const pickClientDefaultsSchema = z.strictObject({
orgId: z.string()
})
.strict();
});
registry.registerPath({
method: "get",

View File

@@ -20,20 +20,16 @@ import {
import { sendToExitNode } from "#dynamic/lib/exitNodes";
import { hashPassword } from "@server/auth/password";
const updateClientParamsSchema = z
.object({
clientId: z.string().transform(Number).pipe(z.number().int().positive())
})
.strict();
const updateClientParamsSchema = z.strictObject({
clientId: z.string().transform(Number).pipe(z.int().positive())
});
const updateClientSchema = z
.object({
const updateClientSchema = z.strictObject({
name: z.string().min(1).max(255).optional(),
siteIds: z
.array(z.number().int().positive())
.array(z.int().positive())
.optional(),
})
.strict();
});
export type UpdateClientBody = z.infer<typeof updateClientSchema>;

View File

@@ -15,20 +15,16 @@ import { isSecondLevelDomain, isValidDomain } from "@server/lib/validators";
import { build } from "@server/build";
import config from "@server/lib/config";
const paramsSchema = z
.object({
const paramsSchema = z.strictObject({
orgId: z.string()
})
.strict();
});
const bodySchema = z
.object({
const bodySchema = z.strictObject({
type: z.enum(["ns", "cname", "wildcard"]),
baseDomain: subdomainSchema,
certResolver: z.string().optional().nullable(),
preferWildcardCert: z.boolean().optional().nullable() // optional, only for wildcard
})
.strict();
});
export type CreateDomainResponse = {

View File

@@ -10,12 +10,10 @@ import { and, eq } from "drizzle-orm";
import { usageService } from "@server/lib/billing/usageService";
import { FeatureId } from "@server/lib/billing";
const paramsSchema = z
.object({
const paramsSchema = z.strictObject({
domainId: z.string(),
orgId: z.string()
})
.strict();
});
export type DeleteAccountDomainResponse = {
success: boolean;

View File

@@ -10,12 +10,10 @@ import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
import { getServerIp } from "@server/lib/serverIpService"; // your in-memory IP module
const getDNSRecordsSchema = z
.object({
const getDNSRecordsSchema = z.strictObject({
domainId: z.string(),
orgId: z.string()
})
.strict();
});
async function query(domainId: string) {
const records = await db

View File

@@ -10,14 +10,12 @@ import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
import { domain } from "zod/v4/core/regexes";
const getDomainSchema = z
.object({
const getDomainSchema = z.strictObject({
domainId: z
.string()
.optional(),
orgId: z.string().optional()
})
.strict();
});
async function query(domainId?: string, orgId?: string) {
if (domainId) {

View File

@@ -10,28 +10,24 @@ import logger from "@server/logger";
import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
const listDomainsParamsSchema = z
.object({
const listDomainsParamsSchema = z.strictObject({
orgId: z.string()
})
.strict();
});
const listDomainsSchema = z
.object({
const listDomainsSchema = z.strictObject({
limit: z
.string()
.optional()
.default("1000")
.transform(Number)
.pipe(z.number().int().nonnegative()),
.pipe(z.int().nonnegative()),
offset: z
.string()
.optional()
.default("0")
.transform(Number)
.pipe(z.number().int().nonnegative())
})
.strict();
.pipe(z.int().nonnegative())
});
async function queryDomains(orgId: string, limit: number, offset: number) {
const res = await db

View File

@@ -8,12 +8,10 @@ import logger from "@server/logger";
import { fromError } from "zod-validation-error";
import { and, eq } from "drizzle-orm";
const paramsSchema = z
.object({
const paramsSchema = z.strictObject({
domainId: z.string(),
orgId: z.string()
})
.strict();
});
export type RestartOrgDomainResponse = {
success: boolean;

View File

@@ -9,19 +9,15 @@ import { fromError } from "zod-validation-error";
import { eq, and } from "drizzle-orm";
import { OpenAPITags, registry } from "@server/openApi";
const paramsSchema = z
.object({
const paramsSchema = z.strictObject({
orgId: z.string(),
domainId: z.string()
})
.strict();
});
const bodySchema = z
.object({
const bodySchema = z.strictObject({
certResolver: z.string().optional().nullable(),
preferWildcardCert: z.boolean().optional().nullable()
})
.strict();
});
export type UpdateDomainResponse = {
domainId: string;

View File

@@ -11,19 +11,15 @@ import config from "@server/lib/config";
import { eq, and } from "drizzle-orm";
import { idp, idpOrg } from "@server/db";
const paramsSchema = z
.object({
idpId: z.coerce.number(),
const paramsSchema = z.strictObject({
idpId: z.coerce.number<number>(),
orgId: z.string()
})
.strict();
});
const bodySchema = z
.object({
const bodySchema = z.strictObject({
roleMapping: z.string().optional(),
orgMapping: z.string().optional()
})
.strict();
});
export type CreateIdpOrgPolicyResponse = {};

View File

@@ -12,22 +12,20 @@ import { generateOidcRedirectUrl } from "@server/lib/idp/generateRedirectUrl";
import { encrypt } from "@server/lib/crypto";
import config from "@server/lib/config";
const paramsSchema = z.object({}).strict();
const paramsSchema = z.strictObject({});
const bodySchema = z
.object({
const bodySchema = z.strictObject({
name: z.string().nonempty(),
clientId: z.string().nonempty(),
clientSecret: z.string().nonempty(),
authUrl: z.string().url(),
tokenUrl: z.string().url(),
authUrl: z.url(),
tokenUrl: z.url(),
identifierPath: z.string().nonempty(),
emailPath: z.string().optional(),
namePath: z.string().optional(),
scopes: z.string().nonempty(),
autoProvision: z.boolean().optional()
})
.strict();
});
export type CreateIdpResponse = {
idpId: number;

View File

@@ -13,7 +13,7 @@ import { OpenAPITags, registry } from "@server/openApi";
const paramsSchema = z
.object({
orgId: z.string().optional(), // Optional; used with org idp in saas
idpId: z.coerce.number()
idpId: z.coerce.number<number>()
})
.strict();

View File

@@ -10,12 +10,10 @@ import { idp, idpOrg } from "@server/db";
import { eq, and } from "drizzle-orm";
import { OpenAPITags, registry } from "@server/openApi";
const paramsSchema = z
.object({
idpId: z.coerce.number(),
const paramsSchema = z.strictObject({
idpId: z.coerce.number<number>(),
orgId: z.string()
})
.strict();
});
registry.registerPath({
method: "delete",

View File

@@ -19,15 +19,13 @@ import { TierId } from "@server/lib/billing/tiers";
const paramsSchema = z
.object({
idpId: z.coerce.number()
idpId: z.coerce.number<number>()
})
.strict();
const bodySchema = z
.object({
const bodySchema = z.strictObject({
redirectUrl: z.string()
})
.strict();
});
const querySchema = z.object({
orgId: z.string().optional() // check what actuall calls it

View File

@@ -14,7 +14,7 @@ import { decrypt } from "@server/lib/crypto";
const paramsSchema = z
.object({
idpId: z.coerce.number()
idpId: z.coerce.number<number>()
})
.strict();

View File

@@ -11,25 +11,23 @@ import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
const paramsSchema = z.object({
idpId: z.coerce.number()
idpId: z.coerce.number<number>()
});
const querySchema = z
.object({
const querySchema = z.strictObject({
limit: z
.string()
.optional()
.default("1000")
.transform(Number)
.pipe(z.number().int().nonnegative()),
.pipe(z.int().nonnegative()),
offset: z
.string()
.optional()
.default("0")
.transform(Number)
.pipe(z.number().int().nonnegative())
})
.strict();
.pipe(z.int().nonnegative())
});
async function query(idpId: number, limit: number, offset: number) {
const res = await db

View File

@@ -10,22 +10,20 @@ import logger from "@server/logger";
import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
const querySchema = z
.object({
const querySchema = z.strictObject({
limit: z
.string()
.optional()
.default("1000")
.transform(Number)
.pipe(z.number().int().nonnegative()),
.pipe(z.int().nonnegative()),
offset: z
.string()
.optional()
.default("0")
.transform(Number)
.pipe(z.number().int().nonnegative())
})
.strict();
.pipe(z.int().nonnegative())
});
async function query(limit: number, offset: number) {
const res = await db

View File

@@ -10,19 +10,15 @@ import { OpenAPITags, registry } from "@server/openApi";
import { eq, and } from "drizzle-orm";
import { idp, idpOrg } from "@server/db";
const paramsSchema = z
.object({
idpId: z.coerce.number(),
const paramsSchema = z.strictObject({
idpId: z.coerce.number<number>(),
orgId: z.string()
})
.strict();
});
const bodySchema = z
.object({
const bodySchema = z.strictObject({
roleMapping: z.string().optional(),
orgMapping: z.string().optional()
})
.strict();
});
export type UpdateIdpOrgPolicyResponse = {};

View File

@@ -14,12 +14,11 @@ import config from "@server/lib/config";
const paramsSchema = z
.object({
idpId: z.coerce.number()
idpId: z.coerce.number<number>()
})
.strict();
const bodySchema = z
.object({
const bodySchema = z.strictObject({
name: z.string().optional(),
clientId: z.string().optional(),
clientSecret: z.string().optional(),
@@ -32,8 +31,7 @@ const bodySchema = z
autoProvision: z.boolean().optional(),
defaultRoleMapping: z.string().optional(),
defaultOrgMapping: z.string().optional()
})
.strict();
});
export type UpdateIdpResponse = {
idpId: number;

View File

@@ -40,7 +40,7 @@ const ensureTrailingSlash = (url: string): string => {
const paramsSchema = z
.object({
idpId: z.coerce.number()
idpId: z.coerce.number<number>()
})
.strict();
@@ -51,7 +51,7 @@ const bodySchema = z.object({
});
const querySchema = z.object({
loginPageId: z.coerce.number().optional()
loginPageId: z.coerce.number<number>().optional()
});
export type ValidateOidcUrlCallbackResponse = {

View File

@@ -23,12 +23,10 @@ export type CreateNewtResponse = {
secret: string;
};
const createNewtSchema = z
.object({
const createNewtSchema = z.strictObject({
newtId: z.string(),
secret: z.string()
})
.strict();
});
export async function createNewt(
req: Request,

View File

@@ -18,7 +18,7 @@ import { sendToExitNode } from "#dynamic/lib/exitNodes";
const inputSchema = z.object({
publicKey: z.string(),
port: z.number().int().positive()
port: z.int().positive()
});
type Input = z.infer<typeof inputSchema>;

View File

@@ -23,12 +23,10 @@ export type CreateNewtResponse = {
secret: string;
};
const createNewtSchema = z
.object({
const createNewtSchema = z.strictObject({
newtId: z.string(),
secret: z.string()
})
.strict();
});
export async function createNewt(
req: Request,

View File

@@ -9,11 +9,9 @@ import createHttpError from "http-errors";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const getOrgSchema = z
.object({
const getOrgSchema = z.strictObject({
orgId: z.string()
})
.strict();
});
export async function checkId(
req: Request,

View File

@@ -27,13 +27,11 @@ import { usageService } from "@server/lib/billing/usageService";
import { FeatureId } from "@server/lib/billing";
import { build } from "@server/build";
const createOrgSchema = z
.object({
const createOrgSchema = z.strictObject({
orgId: z.string(),
name: z.string().min(1).max(255),
subnet: z.string()
})
.strict();
});
registry.registerPath({
method: "put",

View File

@@ -13,11 +13,9 @@ import { sendToClient } from "#dynamic/routers/ws";
import { deletePeer } from "../gerbil/peers";
import { OpenAPITags, registry } from "@server/openApi";
const deleteOrgSchema = z
.object({
const deleteOrgSchema = z.strictObject({
orgId: z.string()
})
.strict();
});
export type DeleteOrgResponse = {};

View File

@@ -10,11 +10,9 @@ import logger from "@server/logger";
import { fromZodError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
const getOrgSchema = z
.object({
const getOrgSchema = z.strictObject({
orgId: z.string()
})
.strict();
});
export type GetOrgResponse = {
org: Org;

View File

@@ -18,11 +18,9 @@ import createHttpError from "http-errors";
import logger from "@server/logger";
import { fromZodError } from "zod-validation-error";
const getOrgParamsSchema = z
.object({
const getOrgParamsSchema = z.strictObject({
orgId: z.string()
})
.strict();
});
export type GetOrgOverviewResponse = {
orgName: string;

Some files were not shown because too many files have changed in this diff Show More