diff --git a/package-lock.json b/package-lock.json index c9e0a33..09e5063 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "license": "SEE LICENSE IN LICENSE AND README.md", "dependencies": { + "@asteasolutions/zod-to-openapi": "^7.3.0", "@hookform/resolvers": "3.9.1", "@node-rs/argon2": "2.0.2", "@oslojs/crypto": "1.0.1", @@ -128,6 +129,18 @@ "node": ">=6.0.0" } }, + "node_modules/@asteasolutions/zod-to-openapi": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-7.3.0.tgz", + "integrity": "sha512-7tE/r1gXwMIvGnXVUdIqUhCU1RevEFC4Jk6Bussa0fk1ecbnnINkZzj1EOAJyE/M3AI25DnHT/zKQL1/FPFi8Q==", + "license": "MIT", + "dependencies": { + "openapi3-ts": "^4.1.2" + }, + "peerDependencies": { + "zod": "^3.20.2" + } + }, "node_modules/@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", @@ -12455,6 +12468,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openapi3-ts": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-4.4.0.tgz", + "integrity": "sha512-9asTNB9IkKEzWMcHmVZE7Ts3kC9G7AFHfs8i7caD8HbI76gEjdkId4z/AkP83xdZsH7PLAnnbl47qZkXuxpArw==", + "license": "MIT", + "dependencies": { + "yaml": "^2.5.0" + } + }, "node_modules/optimist": { "version": "0.3.7", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", diff --git a/package.json b/package.json index 08cb73a..b493471 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "email": "email dev --dir server/emails/templates --port 3005" }, "dependencies": { + "@asteasolutions/zod-to-openapi": "^7.3.0", "@hookform/resolvers": "3.9.1", "@node-rs/argon2": "2.0.2", "@oslojs/crypto": "1.0.1", diff --git a/server/extendZod.ts b/server/extendZod.ts new file mode 100644 index 0000000..513992e --- /dev/null +++ b/server/extendZod.ts @@ -0,0 +1,6 @@ +import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi"; +import { z } from "zod"; + +extendZodWithOpenApi(z); + +export default function extendZod() {} diff --git a/server/index.ts b/server/index.ts index b3dc044..0168535 100644 --- a/server/index.ts +++ b/server/index.ts @@ -1,3 +1,5 @@ +import "./extendZod.ts"; + import { runSetupFunctions } from "./setup"; import { createApiServer } from "./apiServer"; import { createNextServer } from "./nextServer"; diff --git a/server/openApi.ts b/server/openApi.ts new file mode 100644 index 0000000..50820a9 --- /dev/null +++ b/server/openApi.ts @@ -0,0 +1,17 @@ +import { OpenAPIRegistry } from "@asteasolutions/zod-to-openapi"; + +export const registry = new OpenAPIRegistry(); + +export const bearerAuth = registry.registerComponent( + "securitySchemes", + "Bearer Auth", + { + type: "http", + scheme: "bearer" + } +); + +export enum OpenAPITags { + Site = "Site", + Org = "Organization" +} diff --git a/server/routers/site/createSite.ts b/server/routers/site/createSite.ts index 8d6d001..fbe6fc1 100644 --- a/server/routers/site/createSite.ts +++ b/server/routers/site/createSite.ts @@ -13,29 +13,29 @@ import { fromError } from "zod-validation-error"; import { hash } from "@node-rs/argon2"; import { newts } from "@server/db/schemas"; import moment from "moment"; -import { hashPassword } from "@server/auth/password"; +import { bearerAuth, OpenAPITags, registry } from "@server/openApi"; const createSiteParamsSchema = z .object({ - orgId: z.string() + orgId: z.string().openapi({}) }) .strict(); const createSiteSchema = z .object({ - name: z.string().min(1).max(255), - exitNodeId: z.number().int().positive().optional(), + name: z.string().min(1).max(255).openapi({}), + exitNodeId: z.number().int().positive().optional().openapi({}), // subdomain: z // .string() // .min(1) // .max(255) // .transform((val) => val.toLowerCase()) // .optional(), - pubKey: z.string().optional(), - subnet: z.string().optional(), - newtId: z.string().optional(), - secret: z.string().optional(), - type: z.string() + pubKey: z.string().optional().openapi({}), + subnet: z.string().optional().openapi({}), + newtId: z.string().optional().openapi({}), + secret: z.string().optional().openapi({}), + type: z.enum(["newt", "wireguard", "local"]).openapi({}) }) .strict(); @@ -43,6 +43,25 @@ export type CreateSiteBody = z.infer; export type CreateSiteResponse = Site; +registry.registerPath({ + method: "put", + path: "/org/{orgId}/site", + description: "Create a new site", + security: [{ [bearerAuth.name]: [] }], + tags: [OpenAPITags.Site, OpenAPITags.Org], + request: { + params: createSiteParamsSchema, + body: { + content: { + "application/json": { + schema: createSiteSchema + } + } + } + }, + responses: {} +}); + export async function createSite( req: Request, res: Response,