diff --git a/.gitignore b/.gitignore index dd935c30..a3851bef 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,4 @@ installer bin .secrets test_event.json +swagger.ts diff --git a/package-lock.json b/package-lock.json index 09e50637..59392137 100644 --- a/package-lock.json +++ b/package-lock.json @@ -67,6 +67,7 @@ "react-icons": "^5.5.0", "rebuild": "0.1.2", "semver": "7.6.3", + "swagger-ui-express": "^5.0.1", "tailwind-merge": "2.6.0", "tailwindcss-animate": "1.0.7", "vaul": "1.1.2", @@ -89,6 +90,7 @@ "@types/react": "19.0.2", "@types/react-dom": "19.0.2", "@types/semver": "7.5.8", + "@types/swagger-ui-express": "^4.1.8", "@types/ws": "8.5.13", "@types/yargs": "17.0.33", "drizzle-kit": "0.30.1", @@ -4164,6 +4166,13 @@ "integrity": "sha512-kkKUDVlII2DQiKy7UstOR1ErJP8kUKAQ4oa+SQtM0K+lPdmmjj0YnnxBgtTVYH7mUKtbsxeFC9y0AmK7Yb78/A==", "license": "MIT" }, + "node_modules/@scarf/scarf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", + "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", + "hasInstallScript": true, + "license": "Apache-2.0" + }, "node_modules/@selderee/plugin-htmlparser2": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", @@ -4442,6 +4451,17 @@ "@types/send": "*" } }, + "node_modules/@types/swagger-ui-express": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@types/swagger-ui-express/-/swagger-ui-express-4.1.8.tgz", + "integrity": "sha512-AhZV8/EIreHFmBV5wAs0gzJUNq9JbbSXgJLQubCC0jtIo6prnI9MIRRxnU4MZX9RB9yXxF1V4R7jtLl/Wcj31g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*", + "@types/serve-static": "*" + } + }, "node_modules/@types/triple-beam": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", @@ -15562,6 +15582,30 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-ui-dist": { + "version": "5.20.6", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.20.6.tgz", + "integrity": "sha512-q/1dwcCOQb+qsNkb+1VWRdGEEVdBtOTH4vv9rICjPwJXOwq/JSRkBbuEMjMe161Oxsp589+8Ff5nE4HTPLWIAw==", + "license": "Apache-2.0", + "dependencies": { + "@scarf/scarf": "=1.4.0" + } + }, + "node_modules/swagger-ui-express": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz", + "integrity": "sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==", + "license": "MIT", + "dependencies": { + "swagger-ui-dist": ">=5.0.0" + }, + "engines": { + "node": ">= v0.10.32" + }, + "peerDependencies": { + "express": ">=4.0.0 || >=5.0.0-beta" + } + }, "node_modules/tailwind-merge": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz", diff --git a/package.json b/package.json index b493471e..8ef20ebf 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "react-icons": "^5.5.0", "rebuild": "0.1.2", "semver": "7.6.3", + "swagger-ui-express": "^5.0.1", "tailwind-merge": "2.6.0", "tailwindcss-animate": "1.0.7", "vaul": "1.1.2", @@ -100,6 +101,7 @@ "@types/react": "19.0.2", "@types/react-dom": "19.0.2", "@types/semver": "7.5.8", + "@types/swagger-ui-express": "^4.1.8", "@types/ws": "8.5.13", "@types/yargs": "17.0.33", "drizzle-kit": "0.30.1", diff --git a/server/openApi.ts b/server/openApi.ts index 50820a95..6ffbbdef 100644 --- a/server/openApi.ts +++ b/server/openApi.ts @@ -2,16 +2,13 @@ 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" + Org = "Organization", + Resource = "Resource", + Role = "Role", + User = "User", + Target = "Target", + Rule = "Rule", + AccessToken = "Access Token" } diff --git a/server/routers/accessToken/deleteAccessToken.ts b/server/routers/accessToken/deleteAccessToken.ts index b7aa83d1..783c5fc8 100644 --- a/server/routers/accessToken/deleteAccessToken.ts +++ b/server/routers/accessToken/deleteAccessToken.ts @@ -8,6 +8,7 @@ import { fromError } from "zod-validation-error"; import { resourceAccessToken } from "@server/db/schemas"; import { and, eq } from "drizzle-orm"; import db from "@server/db"; +import { OpenAPITags, registry } from "@server/openApi"; const deleteAccessTokenParamsSchema = z .object({ @@ -15,6 +16,17 @@ const deleteAccessTokenParamsSchema = z }) .strict(); +registry.registerPath({ + method: "delete", + path: "/access-token/{accessTokenId}", + description: "Delete a access token.", + tags: [OpenAPITags.AccessToken], + request: { + params: deleteAccessTokenParamsSchema + }, + responses: {} +}); + export async function deleteAccessToken( req: Request, res: Response, diff --git a/server/routers/accessToken/generateAccessToken.ts b/server/routers/accessToken/generateAccessToken.ts index bb67387f..738c230e 100644 --- a/server/routers/accessToken/generateAccessToken.ts +++ b/server/routers/accessToken/generateAccessToken.ts @@ -22,6 +22,7 @@ import { createDate, TimeSpan } from "oslo"; import { hashPassword } from "@server/auth/password"; import { encodeHexLowerCase } from "@oslojs/encoding"; import { sha256 } from "@oslojs/crypto/sha2"; +import { OpenAPITags, registry } from "@server/openApi"; export const generateAccessTokenBodySchema = z .object({ @@ -45,6 +46,24 @@ export type GenerateAccessTokenResponse = Omit< "tokenHash" > & { accessToken: string }; +registry.registerPath({ + method: "post", + path: "/resource/{resourceId}/access-token", + description: "Generate a new access token for a resource.", + tags: [OpenAPITags.Resource, OpenAPITags.AccessToken], + request: { + params: generateAccssTokenParamsSchema, + body: { + content: { + "application/json": { + schema: generateAccessTokenBodySchema + } + } + } + }, + responses: {} +}); + export async function generateAccessToken( req: Request, res: Response, diff --git a/server/routers/accessToken/listAccessTokens.ts b/server/routers/accessToken/listAccessTokens.ts index a6dcff6c..1ed7b14a 100644 --- a/server/routers/accessToken/listAccessTokens.ts +++ b/server/routers/accessToken/listAccessTokens.ts @@ -15,6 +15,7 @@ import { sql, eq, or, inArray, and, count, isNull, lt, gt } from "drizzle-orm"; import logger from "@server/logger"; import stoi from "@server/lib/stoi"; import { fromZodError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; const listAccessTokensParamsSchema = z .object({ @@ -73,10 +74,7 @@ function queryAccessTokens( resources, eq(resourceAccessToken.resourceId, resources.resourceId) ) - .leftJoin( - sites, - eq(resources.resourceId, sites.siteId) - ) + .leftJoin(sites, eq(resources.resourceId, sites.siteId)) .where( and( inArray( @@ -98,10 +96,7 @@ function queryAccessTokens( resources, eq(resourceAccessToken.resourceId, resources.resourceId) ) - .leftJoin( - sites, - eq(resources.resourceId, sites.siteId) - ) + .leftJoin(sites, eq(resources.resourceId, sites.siteId)) .where( and( inArray( @@ -123,6 +118,34 @@ export type ListAccessTokensResponse = { pagination: { total: number; limit: number; offset: number }; }; +registry.registerPath({ + method: "get", + path: "/org/{orgId}/access-tokens", + description: "List all access tokens in an organization.", + tags: [OpenAPITags.Org, OpenAPITags.AccessToken], + request: { + params: z.object({ + orgId: z.string() + }), + query: listAccessTokensSchema + }, + responses: {} +}); + +registry.registerPath({ + method: "get", + path: "/resource/{resourceId}/access-tokens", + description: "List all access tokens in an organization.", + tags: [OpenAPITags.Resource, OpenAPITags.AccessToken], + request: { + params: z.object({ + resourceId: z.number() + }), + query: listAccessTokensSchema + }, + responses: {} +}); + export async function listAccessTokens( req: Request, res: Response, diff --git a/server/routers/domain/listDomains.ts b/server/routers/domain/listDomains.ts index a1cbbb3f..c525e1d8 100644 --- a/server/routers/domain/listDomains.ts +++ b/server/routers/domain/listDomains.ts @@ -8,6 +8,7 @@ import createHttpError from "http-errors"; import { eq, sql } from "drizzle-orm"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; const listDomainsParamsSchema = z .object({ @@ -51,6 +52,20 @@ export type ListDomainsResponse = { pagination: { total: number; limit: number; offset: number }; }; +registry.registerPath({ + method: "get", + path: "/org/{orgId}/domains", + description: "List all domains for a organization.", + tags: [OpenAPITags.Org], + request: { + params: z.object({ + orgId: z.string() + }), + query: listDomainsSchema + }, + responses: {} +}); + export async function listDomains( req: Request, res: Response, diff --git a/server/routers/org/createOrg.ts b/server/routers/org/createOrg.ts index d264eac8..d382151b 100644 --- a/server/routers/org/createOrg.ts +++ b/server/routers/org/createOrg.ts @@ -19,6 +19,7 @@ import { createAdminRole } from "@server/setup/ensureActions"; import config from "@server/lib/config"; import { fromError } from "zod-validation-error"; import { defaultRoleAllowedActions } from "../role"; +import { OpenAPITags, registry } from "@server/openApi"; const createOrgSchema = z .object({ @@ -29,6 +30,23 @@ const createOrgSchema = z // const MAX_ORGS = 5; +registry.registerPath({ + method: "put", + path: "/org", + description: "Create a new organization", + tags: [OpenAPITags.Org], + request: { + body: { + content: { + "application/json": { + schema: createOrgSchema + } + } + } + }, + responses: {} +}); + export async function createOrg( req: Request, res: Response, diff --git a/server/routers/org/deleteOrg.ts b/server/routers/org/deleteOrg.ts index 5ffdd739..030588c5 100644 --- a/server/routers/org/deleteOrg.ts +++ b/server/routers/org/deleteOrg.ts @@ -17,6 +17,7 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { sendToClient } from "../ws"; import { deletePeer } from "../gerbil/peers"; +import { OpenAPITags, registry } from "@server/openApi"; const deleteOrgSchema = z .object({ @@ -26,6 +27,17 @@ const deleteOrgSchema = z export type DeleteOrgResponse = {}; +registry.registerPath({ + method: "delete", + path: "/org/{orgId}", + description: "Delete an organization", + tags: [OpenAPITags.Org], + request: { + params: deleteOrgSchema + }, + responses: {} +}); + export async function deleteOrg( req: Request, res: Response, diff --git a/server/routers/org/getOrg.ts b/server/routers/org/getOrg.ts index 21a6fa2a..c112ab7a 100644 --- a/server/routers/org/getOrg.ts +++ b/server/routers/org/getOrg.ts @@ -8,6 +8,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromZodError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; const getOrgSchema = z .object({ @@ -19,6 +20,17 @@ export type GetOrgResponse = { org: Org; }; +registry.registerPath({ + method: "get", + path: "/org/{orgId}", + description: "Get an organization", + tags: [OpenAPITags.Org], + request: { + params: getOrgSchema + }, + responses: {} +}); + export async function getOrg( req: Request, res: Response, diff --git a/server/routers/org/listOrgs.ts b/server/routers/org/listOrgs.ts index 106c58e4..f299e3f2 100644 --- a/server/routers/org/listOrgs.ts +++ b/server/routers/org/listOrgs.ts @@ -8,6 +8,7 @@ import createHttpError from "http-errors"; import { sql, inArray } from "drizzle-orm"; import logger from "@server/logger"; import { fromZodError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; const listOrgsSchema = z.object({ limit: z @@ -21,7 +22,18 @@ const listOrgsSchema = z.object({ .optional() .default("0") .transform(Number) - .pipe(z.number().int().nonnegative()), + .pipe(z.number().int().nonnegative()) +}); + +registry.registerPath({ + method: "get", + path: "/orgs", + description: "List all organizations in the system", + tags: [OpenAPITags.Org], + request: { + query: listOrgsSchema + }, + responses: {} }); export type ListOrgsResponse = { @@ -57,13 +69,13 @@ export async function listOrgs( pagination: { total: 0, limit, - offset, - }, + offset + } }, success: true, error: false, message: "No organizations found for the user", - status: HttpCode.OK, + status: HttpCode.OK }); } @@ -86,13 +98,13 @@ export async function listOrgs( pagination: { total: totalCount, limit, - offset, - }, + offset + } }, success: true, error: false, message: "Organizations retrieved successfully", - status: HttpCode.OK, + status: HttpCode.OK }); } catch (error) { logger.error(error); diff --git a/server/routers/org/updateOrg.ts b/server/routers/org/updateOrg.ts index 2c4a4cf0..0f0aa89a 100644 --- a/server/routers/org/updateOrg.ts +++ b/server/routers/org/updateOrg.ts @@ -8,6 +8,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; const updateOrgParamsSchema = z .object({ @@ -17,7 +18,7 @@ const updateOrgParamsSchema = z const updateOrgBodySchema = z .object({ - name: z.string().min(1).max(255).optional(), + name: z.string().min(1).max(255).optional() // domain: z.string().min(1).max(255).optional(), }) .strict() @@ -25,6 +26,24 @@ const updateOrgBodySchema = z message: "At least one field must be provided for update" }); +registry.registerPath({ + method: "post", + path: "/org/{orgId}", + description: "Update an organization", + tags: [OpenAPITags.Org], + request: { + params: updateOrgParamsSchema, + body: { + content: { + "application/json": { + schema: updateOrgBodySchema + } + } + } + }, + responses: {} +}); + export async function updateOrg( req: Request, res: Response, diff --git a/server/routers/resource/createResource.ts b/server/routers/resource/createResource.ts index d4001de2..d43a4fdd 100644 --- a/server/routers/resource/createResource.ts +++ b/server/routers/resource/createResource.ts @@ -20,6 +20,7 @@ import { fromError } from "zod-validation-error"; import logger from "@server/logger"; import { subdomainSchema } from "@server/lib/schemas"; import config from "@server/lib/config"; +import { OpenAPITags, registry } from "@server/openApi"; const createResourceParamsSchema = z .object({ @@ -90,6 +91,26 @@ const createRawResourceSchema = z export type CreateResourceResponse = Resource; +registry.registerPath({ + method: "put", + path: "/org/{orgId}/site/{siteId}/resource", + description: "Create a resource.", + tags: [OpenAPITags.Org, OpenAPITags.Resource], + request: { + params: createResourceParamsSchema, + body: { + content: { + "application/json": { + schema: createHttpResourceSchema.or( + createRawResourceSchema + ) + } + } + } + }, + responses: {} +}); + export async function createResource( req: Request, res: Response, diff --git a/server/routers/resource/createResourceRule.ts b/server/routers/resource/createResourceRule.ts index d52f294f..b52713d1 100644 --- a/server/routers/resource/createResourceRule.ts +++ b/server/routers/resource/createResourceRule.ts @@ -13,6 +13,7 @@ import { isValidIP, isValidUrlGlobPattern } from "@server/lib/validators"; +import { OpenAPITags, registry } from "@server/openApi"; const createResourceRuleSchema = z .object({ @@ -33,6 +34,24 @@ const createResourceRuleParamsSchema = z }) .strict(); +registry.registerPath({ + method: "put", + path: "/resource/{resourceId}/rule", + description: "Create a resource rule.", + tags: [OpenAPITags.Resource, OpenAPITags.Rule], + request: { + params: createResourceRuleParamsSchema, + body: { + content: { + "application/json": { + schema: createResourceRuleSchema + } + } + } + }, + responses: {} +}); + export async function createResourceRule( req: Request, res: Response, diff --git a/server/routers/resource/deleteResource.ts b/server/routers/resource/deleteResource.ts index f1d2f206..8b58f688 100644 --- a/server/routers/resource/deleteResource.ts +++ b/server/routers/resource/deleteResource.ts @@ -11,6 +11,7 @@ import { fromError } from "zod-validation-error"; import { addPeer } from "../gerbil/peers"; import { removeTargets } from "../newt/targets"; import { getAllowedIps } from "../target/helpers"; +import { OpenAPITags, registry } from "@server/openApi"; // Define Zod schema for request parameters validation const deleteResourceSchema = z @@ -22,6 +23,17 @@ const deleteResourceSchema = z }) .strict(); +registry.registerPath({ + method: "delete", + path: "/resource/{resourceId}", + description: "Delete a resource.", + tags: [OpenAPITags.Resource], + request: { + params: deleteResourceSchema + }, + responses: {} +}); + export async function deleteResource( req: Request, res: Response, @@ -88,7 +100,11 @@ export async function deleteResource( .where(eq(newts.siteId, site.siteId)) .limit(1); - removeTargets(newt.newtId, targetsToBeRemoved, deletedResource.protocol); + removeTargets( + newt.newtId, + targetsToBeRemoved, + deletedResource.protocol + ); } } diff --git a/server/routers/resource/deleteResourceRule.ts b/server/routers/resource/deleteResourceRule.ts index 7583c311..573825b0 100644 --- a/server/routers/resource/deleteResourceRule.ts +++ b/server/routers/resource/deleteResourceRule.ts @@ -8,13 +8,11 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; const deleteResourceRuleSchema = z .object({ - ruleId: z - .string() - .transform(Number) - .pipe(z.number().int().positive()), + ruleId: z.string().transform(Number).pipe(z.number().int().positive()), resourceId: z .string() .transform(Number) @@ -22,6 +20,17 @@ const deleteResourceRuleSchema = z }) .strict(); +registry.registerPath({ + method: "delete", + path: "/resource/{resourceId}/rule/{ruleId}", + description: "Delete a resource rule.", + tags: [OpenAPITags.Resource, OpenAPITags.Rule], + request: { + params: deleteResourceRuleSchema + }, + responses: {} +}); + export async function deleteResourceRule( req: Request, res: Response, diff --git a/server/routers/resource/getResource.ts b/server/routers/resource/getResource.ts index 4fa3acac..ae3c87d3 100644 --- a/server/routers/resource/getResource.ts +++ b/server/routers/resource/getResource.ts @@ -8,6 +8,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import { fromError } from "zod-validation-error"; import logger from "@server/logger"; +import { OpenAPITags, registry } from "@server/openApi"; const getResourceSchema = z .object({ @@ -22,6 +23,17 @@ export type GetResourceResponse = Resource & { siteName: string; }; +registry.registerPath({ + method: "get", + path: "/resource/{resourceId}", + description: "Get a resource.", + tags: [OpenAPITags.Resource], + request: { + params: getResourceSchema + }, + responses: {} +}); + export async function getResource( req: Request, res: Response, diff --git a/server/routers/resource/getResourceWhitelist.ts b/server/routers/resource/getResourceWhitelist.ts index b99decd3..321fd331 100644 --- a/server/routers/resource/getResourceWhitelist.ts +++ b/server/routers/resource/getResourceWhitelist.ts @@ -8,6 +8,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; const getResourceWhitelistSchema = z .object({ @@ -31,6 +32,17 @@ export type GetResourceWhitelistResponse = { whitelist: NonNullable>>; }; +registry.registerPath({ + method: "get", + path: "/resource/{resourceId}/whitelist", + description: "Get the whitelist of emails for a specific resource.", + tags: [OpenAPITags.Resource], + request: { + params: getResourceWhitelistSchema + }, + responses: {} +}); + export async function getResourceWhitelist( req: Request, res: Response, diff --git a/server/routers/resource/listResourceRoles.ts b/server/routers/resource/listResourceRoles.ts index 8b80568f..c173cacb 100644 --- a/server/routers/resource/listResourceRoles.ts +++ b/server/routers/resource/listResourceRoles.ts @@ -8,6 +8,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; const listResourceRolesSchema = z .object({ @@ -35,6 +36,17 @@ export type ListResourceRolesResponse = { roles: NonNullable>>; }; +registry.registerPath({ + method: "get", + path: "/resource/{resourceId}/roles", + description: "List all roles for a resource.", + tags: [OpenAPITags.Resource, OpenAPITags.Role], + request: { + params: listResourceRolesSchema + }, + responses: {} +}); + export async function listResourceRoles( req: Request, res: Response, diff --git a/server/routers/resource/listResourceRules.ts b/server/routers/resource/listResourceRules.ts index 6a9fe0bc..f0a0d84c 100644 --- a/server/routers/resource/listResourceRules.ts +++ b/server/routers/resource/listResourceRules.ts @@ -8,6 +8,7 @@ import createHttpError from "http-errors"; import { z } from "zod"; import { fromError } from "zod-validation-error"; import logger from "@server/logger"; +import { OpenAPITags, registry } from "@server/openApi"; const listResourceRulesParamsSchema = z .object({ @@ -56,6 +57,18 @@ export type ListResourceRulesResponse = { pagination: { total: number; limit: number; offset: number }; }; +registry.registerPath({ + method: "get", + path: "/resource/{resourceId}/rules", + description: "List rules for a resource.", + tags: [OpenAPITags.Resource, OpenAPITags.Rule], + request: { + params: listResourceRulesParamsSchema, + query: listResourceRulesSchema + }, + responses: {} +}); + export async function listResourceRules( req: Request, res: Response, diff --git a/server/routers/resource/listResourceUsers.ts b/server/routers/resource/listResourceUsers.ts index 6ca79748..20411b1a 100644 --- a/server/routers/resource/listResourceUsers.ts +++ b/server/routers/resource/listResourceUsers.ts @@ -8,6 +8,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; const listResourceUsersSchema = z .object({ @@ -33,6 +34,17 @@ export type ListResourceUsersResponse = { users: NonNullable>>; }; +registry.registerPath({ + method: "get", + path: "/resource/{resourceId}/users", + description: "List all users for a resource.", + tags: [OpenAPITags.Resource, OpenAPITags.User], + request: { + params: listResourceUsersSchema + }, + responses: {} +}); + export async function listResourceUsers( req: Request, res: Response, diff --git a/server/routers/resource/listResources.ts b/server/routers/resource/listResources.ts index 1dba4119..a367ca3e 100644 --- a/server/routers/resource/listResources.ts +++ b/server/routers/resource/listResources.ts @@ -16,6 +16,7 @@ import { sql, eq, or, inArray, and, count } from "drizzle-orm"; import logger from "@server/logger"; import stoi from "@server/lib/stoi"; import { fromZodError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; const listResourcesParamsSchema = z .object({ @@ -128,6 +129,34 @@ export type ListResourcesResponse = { pagination: { total: number; limit: number; offset: number }; }; +registry.registerPath({ + method: "get", + path: "/site/{siteId}/resources", + description: "List resources for a site.", + tags: [OpenAPITags.Site, OpenAPITags.Resource], + request: { + params: z.object({ + siteId: z.number() + }), + query: listResourcesSchema + }, + responses: {} +}); + +registry.registerPath({ + method: "get", + path: "/org/{orgId}/resources", + description: "List resources for an organization.", + tags: [OpenAPITags.Org, OpenAPITags.Resource], + request: { + params: z.object({ + orgId: z.string() + }), + query: listResourcesSchema + }, + responses: {} +}); + export async function listResources( req: Request, res: Response, diff --git a/server/routers/resource/setResourcePassword.ts b/server/routers/resource/setResourcePassword.ts index 77628807..29eb89cb 100644 --- a/server/routers/resource/setResourcePassword.ts +++ b/server/routers/resource/setResourcePassword.ts @@ -10,6 +10,7 @@ import { hash } from "@node-rs/argon2"; import { response } from "@server/lib"; import logger from "@server/logger"; import { hashPassword } from "@server/auth/password"; +import { OpenAPITags, registry } from "@server/openApi"; const setResourceAuthMethodsParamsSchema = z.object({ resourceId: z.string().transform(Number).pipe(z.number().int().positive()) @@ -21,6 +22,25 @@ const setResourceAuthMethodsBodySchema = z }) .strict(); +registry.registerPath({ + method: "post", + path: "/resource/{resourceId}/password", + description: + "Set the password for a resource. Setting the password to null will remove it.", + tags: [OpenAPITags.Resource], + request: { + params: setResourceAuthMethodsParamsSchema, + body: { + content: { + "application/json": { + schema: setResourceAuthMethodsBodySchema + } + } + } + }, + responses: {} +}); + export async function setResourcePassword( req: Request, res: Response, diff --git a/server/routers/resource/setResourcePincode.ts b/server/routers/resource/setResourcePincode.ts index 8530b90c..2a1b7c1f 100644 --- a/server/routers/resource/setResourcePincode.ts +++ b/server/routers/resource/setResourcePincode.ts @@ -11,9 +11,10 @@ import { response } from "@server/lib"; import stoi from "@server/lib/stoi"; import logger from "@server/logger"; import { hashPassword } from "@server/auth/password"; +import { OpenAPITags, registry } from "@server/openApi"; const setResourceAuthMethodsParamsSchema = z.object({ - resourceId: z.string().transform(Number).pipe(z.number().int().positive()), + resourceId: z.string().transform(Number).pipe(z.number().int().positive()) }); const setResourceAuthMethodsBodySchema = z @@ -21,25 +22,44 @@ const setResourceAuthMethodsBodySchema = z pincode: z .string() .regex(/^\d{6}$/) - .or(z.null()), + .or(z.null()) }) .strict(); +registry.registerPath({ + method: "post", + path: "/resource/{resourceId}/pincode", + description: + "Set the PIN code for a resource. Setting the PIN code to null will remove it.", + tags: [OpenAPITags.Resource], + request: { + params: setResourceAuthMethodsParamsSchema, + body: { + content: { + "application/json": { + schema: setResourceAuthMethodsBodySchema + } + } + } + }, + responses: {} +}); + export async function setResourcePincode( req: Request, res: Response, - next: NextFunction, + next: NextFunction ): Promise { try { const parsedParams = setResourceAuthMethodsParamsSchema.safeParse( - req.params, + req.params ); if (!parsedParams.success) { return next( createHttpError( HttpCode.BAD_REQUEST, - fromError(parsedParams.error).toString(), - ), + fromError(parsedParams.error).toString() + ) ); } @@ -48,8 +68,8 @@ export async function setResourcePincode( return next( createHttpError( HttpCode.BAD_REQUEST, - fromError(parsedBody.error).toString(), - ), + fromError(parsedBody.error).toString() + ) ); } @@ -75,15 +95,12 @@ export async function setResourcePincode( success: true, error: false, message: "Resource PIN code set successfully", - status: HttpCode.CREATED, + status: HttpCode.CREATED }); } catch (error) { logger.error(error); return next( - createHttpError( - HttpCode.INTERNAL_SERVER_ERROR, - "An error occurred", - ), + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") ); } } diff --git a/server/routers/resource/setResourceRoles.ts b/server/routers/resource/setResourceRoles.ts index 3f09e765..2699be1f 100644 --- a/server/routers/resource/setResourceRoles.ts +++ b/server/routers/resource/setResourceRoles.ts @@ -8,6 +8,7 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { eq, and, ne } from "drizzle-orm"; +import { OpenAPITags, registry } from "@server/openApi"; const setResourceRolesBodySchema = z .object({ @@ -24,6 +25,25 @@ const setResourceRolesParamsSchema = z }) .strict(); +registry.registerPath({ + method: "post", + path: "/resource/{resourceId}/roles", + description: + "Set roles for a resource. This will replace all existing roles.", + tags: [OpenAPITags.Resource, OpenAPITags.Role], + request: { + params: setResourceRolesParamsSchema, + body: { + content: { + "application/json": { + schema: setResourceRolesBodySchema + } + } + } + }, + responses: {} +}); + export async function setResourceRoles( req: Request, res: Response, diff --git a/server/routers/resource/setResourceUsers.ts b/server/routers/resource/setResourceUsers.ts index 8878fd7d..3080ae45 100644 --- a/server/routers/resource/setResourceUsers.ts +++ b/server/routers/resource/setResourceUsers.ts @@ -8,6 +8,7 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { eq } from "drizzle-orm"; +import { OpenAPITags, registry } from "@server/openApi"; const setUserResourcesBodySchema = z .object({ @@ -24,6 +25,25 @@ const setUserResourcesParamsSchema = z }) .strict(); +registry.registerPath({ + method: "post", + path: "/resource/{resourceId}/users", + description: + "Set users for a resource. This will replace all existing users.", + tags: [OpenAPITags.Resource, OpenAPITags.User], + request: { + params: setUserResourcesParamsSchema, + body: { + content: { + "application/json": { + schema: setUserResourcesBodySchema + } + } + } + }, + responses: {} +}); + export async function setResourceUsers( req: Request, res: Response, diff --git a/server/routers/resource/setResourceWhitelist.ts b/server/routers/resource/setResourceWhitelist.ts index 390c2c29..ceec816c 100644 --- a/server/routers/resource/setResourceWhitelist.ts +++ b/server/routers/resource/setResourceWhitelist.ts @@ -8,6 +8,7 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { and, eq } from "drizzle-orm"; +import { OpenAPITags, registry } from "@server/openApi"; const setResourceWhitelistBodySchema = z .object({ @@ -37,6 +38,25 @@ const setResourceWhitelistParamsSchema = z }) .strict(); +registry.registerPath({ + method: "post", + path: "/resource/{resourceId}/whitelist", + description: + "Set email whitelist for a resource. This will replace all existing emails.", + tags: [OpenAPITags.Resource], + request: { + params: setResourceWhitelistParamsSchema, + body: { + content: { + "application/json": { + schema: setResourceWhitelistBodySchema + } + } + } + }, + responses: {} +}); + export async function setResourceWhitelist( req: Request, res: Response, diff --git a/server/routers/resource/transferResource.ts b/server/routers/resource/transferResource.ts index 17c1dcbe..9b21abb2 100644 --- a/server/routers/resource/transferResource.ts +++ b/server/routers/resource/transferResource.ts @@ -11,6 +11,7 @@ import { fromError } from "zod-validation-error"; import { addPeer } from "../gerbil/peers"; import { addTargets, removeTargets } from "../newt/targets"; import { getAllowedIps } from "../target/helpers"; +import { OpenAPITags, registry } from "@server/openApi"; const transferResourceParamsSchema = z .object({ @@ -27,6 +28,25 @@ const transferResourceBodySchema = z }) .strict(); +registry.registerPath({ + method: "post", + path: "/resource/{resourceId}/transfer", + description: + "Transfer a resource to a different site. This will also transfer the targets associated with the resource.", + tags: [OpenAPITags.Resource], + request: { + params: transferResourceParamsSchema, + body: { + content: { + "application/json": { + schema: transferResourceBodySchema + } + } + } + }, + responses: {} +}); + export async function transferResource( req: Request, res: Response, diff --git a/server/routers/resource/updateResource.ts b/server/routers/resource/updateResource.ts index 121b34ed..df4a41e7 100644 --- a/server/routers/resource/updateResource.ts +++ b/server/routers/resource/updateResource.ts @@ -17,6 +17,8 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import config from "@server/lib/config"; import { subdomainSchema } from "@server/lib/schemas"; +import { registry } from "@server/openApi"; +import { OpenAPITags } from "@server/openApi"; const updateResourceParamsSchema = z .object({ @@ -93,6 +95,26 @@ const updateRawResourceBodySchema = z { message: "Cannot update proxyPort" } ); +registry.registerPath({ + method: "post", + path: "/resource/{resourceId}", + description: "Update a resource.", + tags: [OpenAPITags.Resource], + request: { + params: updateResourceParamsSchema, + body: { + content: { + "application/json": { + schema: updateHttpResourceBodySchema.and( + updateRawResourceBodySchema + ) + } + } + } + }, + responses: {} +}); + export async function updateResource( req: Request, res: Response, diff --git a/server/routers/resource/updateResourceRule.ts b/server/routers/resource/updateResourceRule.ts index 9070d451..9a953500 100644 --- a/server/routers/resource/updateResourceRule.ts +++ b/server/routers/resource/updateResourceRule.ts @@ -13,6 +13,7 @@ import { isValidIP, isValidUrlGlobPattern } from "@server/lib/validators"; +import { OpenAPITags, registry } from "@server/openApi"; // Define Zod schema for request parameters validation const updateResourceRuleParamsSchema = z @@ -39,6 +40,24 @@ const updateResourceRuleSchema = z message: "At least one field must be provided for update" }); +registry.registerPath({ + method: "post", + path: "/resource/{resourceId}/rule/{ruleId}", + description: "Update a resource rule.", + tags: [OpenAPITags.Resource, OpenAPITags.Rule], + request: { + params: updateResourceRuleParamsSchema, + body: { + content: { + "application/json": { + schema: updateResourceRuleSchema + } + } + } + }, + responses: {} +}); + export async function updateResourceRule( req: Request, res: Response, diff --git a/server/routers/role/createRole.ts b/server/routers/role/createRole.ts index b9bdc37e..3bc363f6 100644 --- a/server/routers/role/createRole.ts +++ b/server/routers/role/createRole.ts @@ -9,6 +9,7 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { ActionsEnum } from "@server/auth/actions"; import { eq, and } from "drizzle-orm"; +import { OpenAPITags, registry } from "@server/openApi"; const createRoleParamsSchema = z .object({ @@ -33,6 +34,24 @@ export type CreateRoleBody = z.infer; export type CreateRoleResponse = Role; +registry.registerPath({ + method: "put", + path: "/org/{orgId}/role", + description: "Create a role.", + tags: [OpenAPITags.Org, OpenAPITags.Role], + request: { + params: createRoleParamsSchema, + body: { + content: { + "application/json": { + schema: createRoleSchema + } + } + } + }, + responses: {} +}); + export async function createRole( req: Request, res: Response, diff --git a/server/routers/role/deleteRole.ts b/server/routers/role/deleteRole.ts index 0e709ff2..a89428d5 100644 --- a/server/routers/role/deleteRole.ts +++ b/server/routers/role/deleteRole.ts @@ -8,6 +8,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; const deleteRoleSchema = z .object({ @@ -21,6 +22,24 @@ const deelteRoleBodySchema = z }) .strict(); +registry.registerPath({ + method: "delete", + path: "/role/{roleId}", + description: "Delete a role.", + tags: [OpenAPITags.Role], + request: { + params: deleteRoleSchema, + body: { + content: { + "application/json": { + schema: deelteRoleBodySchema + } + } + } + }, + responses: {} +}); + export async function deleteRole( req: Request, res: Response, diff --git a/server/routers/role/listRoles.ts b/server/routers/role/listRoles.ts index 2740484a..73834b53 100644 --- a/server/routers/role/listRoles.ts +++ b/server/routers/role/listRoles.ts @@ -9,6 +9,7 @@ import { sql, eq } from "drizzle-orm"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import stoi from "@server/lib/stoi"; +import { OpenAPITags, registry } from "@server/openApi"; const listRolesParamsSchema = z .object({ @@ -57,6 +58,18 @@ export type ListRolesResponse = { }; }; +registry.registerPath({ + method: "get", + path: "/orgs/{orgId}/roles", + description: "List roles.", + tags: [OpenAPITags.Org, OpenAPITags.Role], + request: { + params: listRolesParamsSchema, + query: listRolesSchema + }, + responses: {} +}); + export async function listRoles( req: Request, res: Response, diff --git a/server/routers/site/createSite.ts b/server/routers/site/createSite.ts index fbe6fc1a..03b0d747 100644 --- a/server/routers/site/createSite.ts +++ b/server/routers/site/createSite.ts @@ -13,29 +13,30 @@ import { fromError } from "zod-validation-error"; import { hash } from "@node-rs/argon2"; import { newts } from "@server/db/schemas"; import moment from "moment"; -import { bearerAuth, OpenAPITags, registry } from "@server/openApi"; +import { OpenAPITags, registry } from "@server/openApi"; +import { hashPassword } from "@server/auth/password"; const createSiteParamsSchema = z .object({ - orgId: z.string().openapi({}) + orgId: z.string() }) .strict(); const createSiteSchema = z .object({ - name: z.string().min(1).max(255).openapi({}), - exitNodeId: z.number().int().positive().optional().openapi({}), + name: z.string().min(1).max(255), + exitNodeId: z.number().int().positive().optional(), // subdomain: z // .string() // .min(1) // .max(255) // .transform((val) => val.toLowerCase()) // .optional(), - 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({}) + pubKey: z.string().optional(), + subnet: z.string().optional(), + newtId: z.string().optional(), + secret: z.string().optional(), + type: z.enum(["newt", "wireguard", "local"]) }) .strict(); @@ -46,8 +47,7 @@ export type CreateSiteResponse = Site; registry.registerPath({ method: "put", path: "/org/{orgId}/site", - description: "Create a new site", - security: [{ [bearerAuth.name]: [] }], + description: "Create a new site.", tags: [OpenAPITags.Site, OpenAPITags.Org], request: { params: createSiteParamsSchema, diff --git a/server/routers/site/deleteSite.ts b/server/routers/site/deleteSite.ts index 1c237efe..667ab5c8 100644 --- a/server/routers/site/deleteSite.ts +++ b/server/routers/site/deleteSite.ts @@ -10,6 +10,7 @@ import logger from "@server/logger"; import { deletePeer } from "../gerbil/peers"; import { fromError } from "zod-validation-error"; import { sendToClient } from "../ws"; +import { OpenAPITags, registry } from "@server/openApi"; const deleteSiteSchema = z .object({ @@ -17,6 +18,17 @@ const deleteSiteSchema = z }) .strict(); +registry.registerPath({ + method: "delete", + path: "/site/{siteId}", + description: "Delete a site and all its associated data.", + tags: [OpenAPITags.Site], + request: { + params: deleteSiteSchema + }, + responses: {} +}); + export async function deleteSite( req: Request, res: Response, diff --git a/server/routers/site/getSite.ts b/server/routers/site/getSite.ts index 98cd6ef4..4baa85cc 100644 --- a/server/routers/site/getSite.ts +++ b/server/routers/site/getSite.ts @@ -9,6 +9,7 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import stoi from "@server/lib/stoi"; import { fromError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; const getSiteSchema = z .object({ @@ -43,6 +44,34 @@ async function query(siteId?: number, niceId?: string, orgId?: string) { export type GetSiteResponse = NonNullable>>; +registry.registerPath({ + method: "get", + path: "/org/{orgId}/site/{niceId}", + description: + "Get a site by orgId and niceId. NiceId is a readable ID for the site and unique on a per org basis.", + tags: [OpenAPITags.Org, OpenAPITags.Site], + request: { + params: z.object({ + orgId: z.string(), + niceId: z.string() + }) + }, + responses: {} +}); + +registry.registerPath({ + method: "get", + path: "/site/{siteId}", + description: "Get a site by siteId.", + tags: [OpenAPITags.Site], + request: { + params: z.object({ + siteId: z.number() + }) + }, + responses: {} +}); + export async function getSite( req: Request, res: Response, diff --git a/server/routers/site/listSites.ts b/server/routers/site/listSites.ts index c40b7fe7..ae8b4fe3 100644 --- a/server/routers/site/listSites.ts +++ b/server/routers/site/listSites.ts @@ -8,6 +8,7 @@ import { NextFunction, Request, Response } from "express"; import createHttpError from "http-errors"; import { z } from "zod"; import { fromError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; const listSitesParamsSchema = z .object({ @@ -59,6 +60,18 @@ export type ListSitesResponse = { pagination: { total: number; limit: number; offset: number }; }; +registry.registerPath({ + method: "get", + path: "/org/{orgId}/sites", + description: "List all sites in an organization", + tags: [OpenAPITags.Org, OpenAPITags.Site], + request: { + params: listSitesParamsSchema, + query: listSitesSchema + }, + responses: {} +}); + export async function listSites( req: Request, res: Response, diff --git a/server/routers/site/pickSiteDefaults.ts b/server/routers/site/pickSiteDefaults.ts index 38f2ec9c..92b93e3c 100644 --- a/server/routers/site/pickSiteDefaults.ts +++ b/server/routers/site/pickSiteDefaults.ts @@ -9,6 +9,8 @@ import logger from "@server/logger"; import { findNextAvailableCidr } from "@server/lib/ip"; import { generateId } from "@server/auth/sessions/app"; import config from "@server/lib/config"; +import { OpenAPITags, registry } from "@server/openApi"; +import { z } from "zod"; export type PickSiteDefaultsResponse = { exitNodeId: number; @@ -22,6 +24,20 @@ export type PickSiteDefaultsResponse = { newtSecret: string; }; +registry.registerPath({ + method: "get", + path: "/org/{orgId}/pick-site-defaults", + description: + "Return pre-requisite data for creating a site, such as the exit node, subnet, Newt credentials, etc.", + tags: [OpenAPITags.Org, OpenAPITags.Site], + request: { + params: z.object({ + orgId: z.string() + }) + }, + responses: {} +}); + export async function pickSiteDefaults( req: Request, res: Response, @@ -45,7 +61,7 @@ export async function pickSiteDefaults( // list all of the sites on that exit node const sitesQuery = await db .select({ - subnet: sites.subnet, + subnet: sites.subnet }) .from(sites) .where(eq(sites.exitNodeId, exitNode.exitNodeId)); @@ -53,8 +69,17 @@ export async function pickSiteDefaults( // TODO: we need to lock this subnet for some time so someone else does not take it let subnets = sitesQuery.map((site) => site.subnet); // exclude the exit node address by replacing after the / with a site block size - subnets.push(exitNode.address.replace(/\/\d+$/, `/${config.getRawConfig().gerbil.site_block_size}`)); - const newSubnet = findNextAvailableCidr(subnets, config.getRawConfig().gerbil.site_block_size, exitNode.address); + subnets.push( + exitNode.address.replace( + /\/\d+$/, + `/${config.getRawConfig().gerbil.site_block_size}` + ) + ); + const newSubnet = findNextAvailableCidr( + subnets, + config.getRawConfig().gerbil.site_block_size, + exitNode.address + ); if (!newSubnet) { return next( createHttpError( @@ -77,12 +102,12 @@ export async function pickSiteDefaults( endpoint: exitNode.endpoint, subnet: newSubnet, newtId, - newtSecret: secret, + newtSecret: secret }, success: true, error: false, message: "Organization retrieved successfully", - status: HttpCode.OK, + status: HttpCode.OK }); } catch (error) { logger.error(error); diff --git a/server/routers/site/updateSite.ts b/server/routers/site/updateSite.ts index 2309fa3d..43cd848a 100644 --- a/server/routers/site/updateSite.ts +++ b/server/routers/site/updateSite.ts @@ -8,6 +8,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; const updateSiteParamsSchema = z .object({ @@ -35,6 +36,25 @@ const updateSiteBodySchema = z message: "At least one field must be provided for update" }); +registry.registerPath({ + method: "post", + path: "/site/{siteId}", + description: + "Update a site.", + tags: [OpenAPITags.Site], + request: { + params: updateSiteParamsSchema, + body: { + content: { + "application/json": { + schema: updateSiteBodySchema + } + } + } + }, + responses: {} +}); + export async function updateSite( req: Request, res: Response, diff --git a/server/routers/target/createTarget.ts b/server/routers/target/createTarget.ts index 86e9ff63..810ee409 100644 --- a/server/routers/target/createTarget.ts +++ b/server/routers/target/createTarget.ts @@ -13,6 +13,7 @@ import { addTargets } from "../newt/targets"; import { eq } from "drizzle-orm"; import { pickPort } from "./helpers"; import { isTargetValid } from "@server/lib/validators"; +import { OpenAPITags, registry } from "@server/openApi"; const createTargetParamsSchema = z .object({ @@ -34,6 +35,24 @@ const createTargetSchema = z export type CreateTargetResponse = Target; +registry.registerPath({ + method: "put", + path: "/resource/{resourceId}/target", + description: "Create a target for a resource.", + tags: [OpenAPITags.Resource, OpenAPITags.Target], + request: { + params: createTargetParamsSchema, + body: { + content: { + "application/json": { + schema: createTargetSchema + } + } + } + }, + responses: {} +}); + export async function createTarget( req: Request, res: Response, diff --git a/server/routers/target/deleteTarget.ts b/server/routers/target/deleteTarget.ts index 57534bd3..979740dd 100644 --- a/server/routers/target/deleteTarget.ts +++ b/server/routers/target/deleteTarget.ts @@ -11,6 +11,7 @@ import { addPeer } from "../gerbil/peers"; import { fromError } from "zod-validation-error"; import { removeTargets } from "../newt/targets"; import { getAllowedIps } from "./helpers"; +import { OpenAPITags, registry } from "@server/openApi"; const deleteTargetSchema = z .object({ @@ -18,6 +19,17 @@ const deleteTargetSchema = z }) .strict(); +registry.registerPath({ + method: "delete", + path: "/target/{targetId}", + description: "Delete a target.", + tags: [OpenAPITags.Target], + request: { + params: deleteTargetSchema + }, + responses: {} +}); + export async function deleteTarget( req: Request, res: Response, diff --git a/server/routers/target/getTarget.ts b/server/routers/target/getTarget.ts index 69ade88f..a268629c 100644 --- a/server/routers/target/getTarget.ts +++ b/server/routers/target/getTarget.ts @@ -8,6 +8,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; const getTargetSchema = z .object({ @@ -15,6 +16,17 @@ const getTargetSchema = z }) .strict(); +registry.registerPath({ + method: "get", + path: "/target/{targetId}", + description: "Get a target.", + tags: [OpenAPITags.Target], + request: { + params: getTargetSchema + }, + responses: {} +}); + export async function getTarget( req: Request, res: Response, diff --git a/server/routers/target/listTargets.ts b/server/routers/target/listTargets.ts index 154787a0..3d4c573b 100644 --- a/server/routers/target/listTargets.ts +++ b/server/routers/target/listTargets.ts @@ -8,6 +8,7 @@ import createHttpError from "http-errors"; import { z } from "zod"; import { fromError } from "zod-validation-error"; import logger from "@server/logger"; +import { OpenAPITags, registry } from "@server/openApi"; const listTargetsParamsSchema = z .object({ @@ -56,6 +57,18 @@ export type ListTargetsResponse = { pagination: { total: number; limit: number; offset: number }; }; +registry.registerPath({ + method: "get", + path: "/resource/{resourceId}/targets", + description: "List targets for a resource.", + tags: [OpenAPITags.Resource, OpenAPITags.Target], + request: { + params: listTargetsParamsSchema, + query: listTargetsSchema + }, + responses: {} +}); + export async function listTargets( req: Request, res: Response, diff --git a/server/routers/target/updateTarget.ts b/server/routers/target/updateTarget.ts index e675034d..284b1a31 100644 --- a/server/routers/target/updateTarget.ts +++ b/server/routers/target/updateTarget.ts @@ -12,6 +12,7 @@ import { addPeer } from "../gerbil/peers"; import { addTargets } from "../newt/targets"; import { pickPort } from "./helpers"; import { isTargetValid } from "@server/lib/validators"; +import { OpenAPITags, registry } from "@server/openApi"; const updateTargetParamsSchema = z .object({ @@ -31,6 +32,24 @@ const updateTargetBodySchema = z message: "At least one field must be provided for update" }); +registry.registerPath({ + method: "post", + path: "/target/{targetId}", + description: "Update a target.", + tags: [OpenAPITags.Target], + request: { + params: updateTargetParamsSchema, + body: { + content: { + "application/json": { + schema: updateTargetBodySchema + } + } + } + }, + responses: {} +}); + export async function updateTarget( req: Request, res: Response, diff --git a/server/routers/user/addUserRole.ts b/server/routers/user/addUserRole.ts index 22527d09..b482e39a 100644 --- a/server/routers/user/addUserRole.ts +++ b/server/routers/user/addUserRole.ts @@ -9,6 +9,7 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import stoi from "@server/lib/stoi"; +import { OpenAPITags, registry } from "@server/openApi"; const addUserRoleParamsSchema = z .object({ @@ -19,6 +20,17 @@ const addUserRoleParamsSchema = z export type AddUserRoleResponse = z.infer; +registry.registerPath({ + method: "post", + path: "/role/{roleId}/add/{userId}", + description: "Add a role to a user.", + tags: [OpenAPITags.Role, OpenAPITags.User], + request: { + params: addUserRoleParamsSchema + }, + responses: {} +}); + export async function addUserRole( req: Request, res: Response, diff --git a/server/routers/user/getOrgUser.ts b/server/routers/user/getOrgUser.ts index fd4defe3..2763f809 100644 --- a/server/routers/user/getOrgUser.ts +++ b/server/routers/user/getOrgUser.ts @@ -9,6 +9,7 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions"; +import { OpenAPITags, registry } from "@server/openApi"; async function queryUser(orgId: string, userId: string) { const [user] = await db @@ -40,6 +41,17 @@ const getOrgUserParamsSchema = z }) .strict(); +registry.registerPath({ + method: "get", + path: "/org/{orgId}/user/{userId}", + description: "Get a user in an organization.", + tags: [OpenAPITags.Org, OpenAPITags.User], + request: { + params: getOrgUserParamsSchema + }, + responses: {} +}); + export async function getOrgUser( req: Request, res: Response, diff --git a/server/routers/user/inviteUser.ts b/server/routers/user/inviteUser.ts index 078c1ab7..589c5b38 100644 --- a/server/routers/user/inviteUser.ts +++ b/server/routers/user/inviteUser.ts @@ -14,6 +14,7 @@ import { hashPassword } from "@server/auth/password"; import { fromError } from "zod-validation-error"; import { sendEmail } from "@server/emails"; import SendInviteLink from "@server/emails/templates/SendInviteLink"; +import { OpenAPITags, registry } from "@server/openApi"; const inviteUserParamsSchema = z .object({ @@ -42,6 +43,24 @@ export type InviteUserResponse = { const inviteTracker: Record = {}; +registry.registerPath({ + method: "post", + path: "/org/{orgId}/create-invite", + description: "Invite a user to join an organization.", + tags: [OpenAPITags.Org], + request: { + params: inviteUserParamsSchema, + body: { + content: { + "application/json": { + schema: inviteUserBodySchema + } + } + } + }, + responses: {} +}); + export async function inviteUser( req: Request, res: Response, diff --git a/server/routers/user/listUsers.ts b/server/routers/user/listUsers.ts index e4429597..c4215181 100644 --- a/server/routers/user/listUsers.ts +++ b/server/routers/user/listUsers.ts @@ -8,6 +8,7 @@ import createHttpError from "http-errors"; import { sql } from "drizzle-orm"; import logger from "@server/logger"; import { fromZodError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; const listUsersParamsSchema = z .object({ @@ -57,6 +58,18 @@ export type ListUsersResponse = { pagination: { total: number; limit: number; offset: number }; }; +registry.registerPath({ + method: "get", + path: "/org/{orgId}/users", + description: "List users in an organization.", + tags: [OpenAPITags.Org, OpenAPITags.User], + request: { + params: listUsersParamsSchema, + query: listUsersSchema + }, + responses: {} +}); + export async function listUsers( req: Request, res: Response, diff --git a/server/routers/user/removeUserOrg.ts b/server/routers/user/removeUserOrg.ts index 6043989e..b344978c 100644 --- a/server/routers/user/removeUserOrg.ts +++ b/server/routers/user/removeUserOrg.ts @@ -8,6 +8,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; const removeUserSchema = z .object({ @@ -16,6 +17,17 @@ const removeUserSchema = z }) .strict(); +registry.registerPath({ + method: "delete", + path: "/org/{orgId}/user/{userId}", + description: "Remove a user from an organization.", + tags: [OpenAPITags.Org, OpenAPITags.User], + request: { + params: removeUserSchema + }, + responses: {} +}); + export async function removeUserOrg( req: Request, res: Response,