From ecdf2dfd043692c4da6b3861b9e9a0ce2da710df Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Wed, 2 Oct 2024 21:17:38 -0400 Subject: [PATCH] Fix return response and add list --- server/routers/org/createOrg.ts | 2 +- server/routers/org/deleteOrg.ts | 2 +- server/routers/org/getOrg.ts | 2 +- server/routers/org/listOrgs.ts | 57 +++++++++++++++++ server/routers/org/updateOrg.ts | 2 +- server/routers/resource/createResource.ts | 2 +- server/routers/resource/deleteResource.ts | 2 +- server/routers/resource/getResource.ts | 2 +- server/routers/resource/listResources.ts | 70 +++++++++++++++++++++ server/routers/resource/updateResource.ts | 2 +- server/routers/site/deleteSite.ts | 2 +- server/routers/site/getSite.ts | 2 +- server/routers/site/listSites.ts | 76 +++++++++++++++++++++++ server/routers/site/updateSite.ts | 2 +- server/routers/target/createTarget.ts | 2 +- server/routers/target/deleteTarget.ts | 2 +- server/routers/target/getTarget.ts | 2 +- server/routers/target/listTargets.ts | 73 ++++++++++++++++++++++ server/routers/target/updateTarget.ts | 2 +- server/routers/user/deleteUser.ts | 2 +- server/routers/user/getUser.ts | 2 +- server/routers/user/listUsers.ts | 61 ++++++++++++++++++ 22 files changed, 354 insertions(+), 17 deletions(-) create mode 100644 server/routers/org/listOrgs.ts create mode 100644 server/routers/resource/listResources.ts create mode 100644 server/routers/site/listSites.ts create mode 100644 server/routers/target/listTargets.ts create mode 100644 server/routers/user/listUsers.ts diff --git a/server/routers/org/createOrg.ts b/server/routers/org/createOrg.ts index ee8ce276..048f2dc9 100644 --- a/server/routers/org/createOrg.ts +++ b/server/routers/org/createOrg.ts @@ -31,7 +31,7 @@ export async function createOrg(req: Request, res: Response, next: NextFunction) }).returning(); return res.status(HttpCode.CREATED).send( - response({ + response(res, { data: newOrg[0], success: true, error: false, diff --git a/server/routers/org/deleteOrg.ts b/server/routers/org/deleteOrg.ts index aea15399..e4aaa819 100644 --- a/server/routers/org/deleteOrg.ts +++ b/server/routers/org/deleteOrg.ts @@ -39,7 +39,7 @@ export async function deleteOrg(req: Request, res: Response, next: NextFunction) } return res.status(HttpCode.OK).send( - response({ + response(res, { data: null, success: true, error: false, diff --git a/server/routers/org/getOrg.ts b/server/routers/org/getOrg.ts index acd81ef7..95d3f7d2 100644 --- a/server/routers/org/getOrg.ts +++ b/server/routers/org/getOrg.ts @@ -40,7 +40,7 @@ export async function getOrg(req: Request, res: Response, next: NextFunction): P } return res.status(HttpCode.OK).send( - response({ + response(res, { data: org[0], success: true, error: false, diff --git a/server/routers/org/listOrgs.ts b/server/routers/org/listOrgs.ts new file mode 100644 index 00000000..8ece3110 --- /dev/null +++ b/server/routers/org/listOrgs.ts @@ -0,0 +1,57 @@ +import { Request, Response, NextFunction } from 'express'; +import { z } from 'zod'; +import { db } from '@server/db'; +import { orgs } from '@server/db/schema'; +import response from "@server/utils/response"; +import HttpCode from '@server/types/HttpCode'; +import createHttpError from 'http-errors'; +import { sql } from 'drizzle-orm'; + +const listOrgsSchema = z.object({ + limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)), + offset: z.string().optional().transform(Number).pipe(z.number().int().nonnegative().default(0)), +}); + +export async function listOrgs(req: Request, res: Response, next: NextFunction): Promise { + try { + const parsedQuery = listOrgsSchema.safeParse(req.query); + if (!parsedQuery.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + parsedQuery.error.errors.map(e => e.message).join(', ') + ) + ); + } + + const { limit, offset } = parsedQuery.data; + + const organizations = await db.select() + .from(orgs) + .limit(limit) + .offset(offset); + + const totalCountResult = await db.select({ count: sql`cast(count(*) as integer)` }) + .from(orgs); + const totalCount = totalCountResult[0].count; + + return res.status(HttpCode.OK).send( + response(res, { + data: { + organizations, + pagination: { + total: totalCount, + limit, + offset, + }, + }, + success: true, + error: false, + message: "Organizations retrieved successfully", + status: HttpCode.OK, + }) + ); + } catch (error) { + next(error); + } +} \ No newline at end of file diff --git a/server/routers/org/updateOrg.ts b/server/routers/org/updateOrg.ts index 2311b412..cfd8bdac 100644 --- a/server/routers/org/updateOrg.ts +++ b/server/routers/org/updateOrg.ts @@ -58,7 +58,7 @@ export async function updateOrg(req: Request, res: Response, next: NextFunction) } return res.status(HttpCode.OK).send( - response({ + response(res, { data: updatedOrg[0], success: true, error: false, diff --git a/server/routers/resource/createResource.ts b/server/routers/resource/createResource.ts index 1c45f620..2dcd05b8 100644 --- a/server/routers/resource/createResource.ts +++ b/server/routers/resource/createResource.ts @@ -40,7 +40,7 @@ export async function createResource(req: Request, res: Response, next: NextFunc }).returning(); return res.status(HttpCode.CREATED).send( - response({ + response(res, { data: newResource[0], success: true, error: false, diff --git a/server/routers/resource/deleteResource.ts b/server/routers/resource/deleteResource.ts index dbc11c00..43e28094 100644 --- a/server/routers/resource/deleteResource.ts +++ b/server/routers/resource/deleteResource.ts @@ -42,7 +42,7 @@ export async function deleteResource(req: Request, res: Response, next: NextFunc } return res.status(HttpCode.OK).send( - response({ + response(res, { data: null, success: true, error: false, diff --git a/server/routers/resource/getResource.ts b/server/routers/resource/getResource.ts index 58decd69..8343056c 100644 --- a/server/routers/resource/getResource.ts +++ b/server/routers/resource/getResource.ts @@ -43,7 +43,7 @@ export async function getResource(req: Request, res: Response, next: NextFunctio } return res.status(HttpCode.OK).send( - response({ + response(res, { data: resource[0], success: true, error: false, diff --git a/server/routers/resource/listResources.ts b/server/routers/resource/listResources.ts new file mode 100644 index 00000000..adf560b9 --- /dev/null +++ b/server/routers/resource/listResources.ts @@ -0,0 +1,70 @@ +import { Request, Response, NextFunction } from 'express'; +import { z } from 'zod'; +import { db } from '@server/db'; +import { resources, sites } from '@server/db/schema'; +import response from "@server/utils/response"; +import HttpCode from '@server/types/HttpCode'; +import createHttpError from 'http-errors'; +import { sql, eq } from 'drizzle-orm'; + +const listResourcesSchema = z.object({ + limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)), + offset: z.string().optional().transform(Number).pipe(z.number().int().nonnegative().default(0)), + siteId: z.string().optional().transform(Number).pipe(z.number().int().positive()), +}); + +export async function listResources(req: Request, res: Response, next: NextFunction): Promise { + try { + const parsedQuery = listResourcesSchema.safeParse(req.query); + if (!parsedQuery.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + parsedQuery.error.errors.map(e => e.message).join(', ') + ) + ); + } + + const { limit, offset, siteId } = parsedQuery.data; + + let baseQuery = db + .select({ + resourceId: resources.resourceId, + name: resources.name, + subdomain: resources.subdomain, + siteName: sites.name, + }) + .from(resources) + .leftJoin(sites, eq(resources.siteId, sites.siteId)); + + let countQuery = db.select({ count: sql`cast(count(*) as integer)` }).from(resources); + + if (siteId) { + baseQuery = baseQuery.where(eq(resources.siteId, siteId)); + countQuery = countQuery.where(eq(resources.siteId, siteId)); + } + + const resourcesList = await baseQuery.limit(limit).offset(offset); + const totalCountResult = await countQuery; + const totalCount = totalCountResult[0].count; + + return res.status(HttpCode.OK).send( + response(res, { + data: { + resources: resourcesList, + pagination: { + total: totalCount, + limit, + offset, + }, + }, + success: true, + error: false, + message: "Resources retrieved successfully", + status: HttpCode.OK, + }) + ); + } catch (error) { + next(error); + } +} \ No newline at end of file diff --git a/server/routers/resource/updateResource.ts b/server/routers/resource/updateResource.ts index 09b6486f..96b14152 100644 --- a/server/routers/resource/updateResource.ts +++ b/server/routers/resource/updateResource.ts @@ -63,7 +63,7 @@ export async function updateResource(req: Request, res: Response, next: NextFunc } return res.status(HttpCode.OK).send( - response({ + response(res, { data: updatedResource[0], success: true, error: false, diff --git a/server/routers/site/deleteSite.ts b/server/routers/site/deleteSite.ts index a982169d..16d26eae 100644 --- a/server/routers/site/deleteSite.ts +++ b/server/routers/site/deleteSite.ts @@ -42,7 +42,7 @@ export async function deleteSite(req: Request, res: Response, next: NextFunction } return res.status(HttpCode.OK).send( - response({ + response(res, { data: null, success: true, error: false, diff --git a/server/routers/site/getSite.ts b/server/routers/site/getSite.ts index 2049a888..5ceead1c 100644 --- a/server/routers/site/getSite.ts +++ b/server/routers/site/getSite.ts @@ -43,7 +43,7 @@ export async function getSite(req: Request, res: Response, next: NextFunction): } return res.status(HttpCode.OK).send( - response({ + response(res, { data: site[0], success: true, error: false, diff --git a/server/routers/site/listSites.ts b/server/routers/site/listSites.ts new file mode 100644 index 00000000..f808804e --- /dev/null +++ b/server/routers/site/listSites.ts @@ -0,0 +1,76 @@ +import { Request, Response, NextFunction } from 'express'; +import { z } from 'zod'; +import { db } from '@server/db'; +import { sites, orgs, exitNodes } from '@server/db/schema'; +import response from "@server/utils/response"; +import HttpCode from '@server/types/HttpCode'; +import createHttpError from 'http-errors'; +import { sql, eq } from 'drizzle-orm'; + +const listSitesSchema = z.object({ + limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)), + offset: z.string().optional().transform(Number).pipe(z.number().int().nonnegative().default(0)), + orgId: z.string().optional().transform(Number).pipe(z.number().int().positive()), +}); + +export async function listSites(req: Request, res: Response, next: NextFunction): Promise { + try { + const parsedQuery = listSitesSchema.safeParse(req.query); + if (!parsedQuery.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + parsedQuery.error.errors.map(e => e.message).join(', ') + ) + ); + } + + const { limit, offset, orgId } = parsedQuery.data; + + let baseQuery: any = db + .select({ + siteId: sites.siteId, + name: sites.name, + subdomain: sites.subdomain, + pubKey: sites.pubKey, + subnet: sites.subnet, + megabytesIn: sites.megabytesIn, + megabytesOut: sites.megabytesOut, + orgName: orgs.name, + exitNodeName: exitNodes.name, + }) + .from(sites) + .leftJoin(orgs, eq(sites.orgId, orgs.orgId)) + .leftJoin(exitNodes, eq(sites.exitNode, exitNodes.exitNodeId)); + + let countQuery: any = db.select({ count: sql`cast(count(*) as integer)` }).from(sites); + + if (orgId) { + baseQuery = baseQuery.where(eq(sites.orgId, orgId)); + countQuery = countQuery.where(eq(sites.orgId, orgId)); + } + + const sitesList = await baseQuery.limit(limit).offset(offset); + const totalCountResult = await countQuery; + const totalCount = totalCountResult[0].count; + + return res.status(HttpCode.OK).send( + response(res, { + data: { + sites: sitesList, + pagination: { + total: totalCount, + limit, + offset, + }, + }, + success: true, + error: false, + message: "Sites retrieved successfully", + status: HttpCode.OK, + }) + ); + } catch (error) { + next(error); + } +} \ No newline at end of file diff --git a/server/routers/site/updateSite.ts b/server/routers/site/updateSite.ts index e32a9d8f..66b1e5aa 100644 --- a/server/routers/site/updateSite.ts +++ b/server/routers/site/updateSite.ts @@ -68,7 +68,7 @@ export async function updateSite(req: Request, res: Response, next: NextFunction } return res.status(HttpCode.OK).send( - response({ + response(res, { data: updatedSite[0], success: true, error: false, diff --git a/server/routers/target/createTarget.ts b/server/routers/target/createTarget.ts index c51caf93..71071764 100644 --- a/server/routers/target/createTarget.ts +++ b/server/routers/target/createTarget.ts @@ -32,7 +32,7 @@ export async function createTarget(req: Request, res: Response, next: NextFuncti const newTarget = await db.insert(targets).values(targetData).returning(); return res.status(HttpCode.CREATED).send( - response({ + response(res, { data: newTarget[0], success: true, error: false, diff --git a/server/routers/target/deleteTarget.ts b/server/routers/target/deleteTarget.ts index 934a8c00..a684fcec 100644 --- a/server/routers/target/deleteTarget.ts +++ b/server/routers/target/deleteTarget.ts @@ -39,7 +39,7 @@ export async function deleteTarget(req: Request, res: Response, next: NextFuncti } return res.status(HttpCode.OK).send( - response({ + response(res, { data: null, success: true, error: false, diff --git a/server/routers/target/getTarget.ts b/server/routers/target/getTarget.ts index 918dc8a7..96ce352e 100644 --- a/server/routers/target/getTarget.ts +++ b/server/routers/target/getTarget.ts @@ -40,7 +40,7 @@ export async function getTarget(req: Request, res: Response, next: NextFunction) } return res.status(HttpCode.OK).send( - response({ + response(res, { data: target[0], success: true, error: false, diff --git a/server/routers/target/listTargets.ts b/server/routers/target/listTargets.ts new file mode 100644 index 00000000..f8a32c85 --- /dev/null +++ b/server/routers/target/listTargets.ts @@ -0,0 +1,73 @@ +import { Request, Response, NextFunction } from 'express'; +import { z } from 'zod'; +import { db } from '@server/db'; +import { targets, resources } from '@server/db/schema'; +import response from "@server/utils/response"; +import HttpCode from '@server/types/HttpCode'; +import createHttpError from 'http-errors'; +import { sql, eq } from 'drizzle-orm'; + +const listTargetsSchema = z.object({ + limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)), + offset: z.string().optional().transform(Number).pipe(z.number().int().nonnegative().default(0)), + resourceId: z.string().optional(), +}); + +export async function listTargets(req: Request, res: Response, next: NextFunction): Promise { + try { + const parsedQuery = listTargetsSchema.safeParse(req.query); + if (!parsedQuery.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + parsedQuery.error.errors.map(e => e.message).join(', ') + ) + ); + } + + const { limit, offset, resourceId } = parsedQuery.data; + + let baseQuery: any = db + .select({ + targetId: targets.targetId, + ip: targets.ip, + method: targets.method, + port: targets.port, + protocol: targets.protocol, + enabled: targets.enabled, + resourceName: resources.name, + }) + .from(targets) + .leftJoin(resources, eq(targets.resourceId, resources.resourceId)); + + let countQuery: any = db.select({ count: sql`cast(count(*) as integer)` }).from(targets); + + if (resourceId) { + baseQuery = baseQuery.where(eq(targets.resourceId, resourceId)); + countQuery = countQuery.where(eq(targets.resourceId, resourceId)); + } + + const targetsList = await baseQuery.limit(limit).offset(offset); + const totalCountResult = await countQuery; + const totalCount = totalCountResult[0].count; + + return res.status(HttpCode.OK).send( + response(res, { + data: { + targets: targetsList, + pagination: { + total: totalCount, + limit, + offset, + }, + }, + success: true, + error: false, + message: "Targets retrieved successfully", + status: HttpCode.OK, + }) + ); + } catch (error) { + next(error); + } +} \ No newline at end of file diff --git a/server/routers/target/updateTarget.ts b/server/routers/target/updateTarget.ts index 6b4d1213..1654264c 100644 --- a/server/routers/target/updateTarget.ts +++ b/server/routers/target/updateTarget.ts @@ -61,7 +61,7 @@ export async function updateTarget(req: Request, res: Response, next: NextFuncti } return res.status(HttpCode.OK).send( - response({ + response(res, { data: updatedTarget[0], success: true, error: false, diff --git a/server/routers/user/deleteUser.ts b/server/routers/user/deleteUser.ts index 8d193312..6a241ece 100644 --- a/server/routers/user/deleteUser.ts +++ b/server/routers/user/deleteUser.ts @@ -39,7 +39,7 @@ export async function deleteUser(req: Request, res: Response, next: NextFunction } return res.status(HttpCode.OK).send( - response({ + response(res, { data: null, success: true, error: false, diff --git a/server/routers/user/getUser.ts b/server/routers/user/getUser.ts index ff98392c..6286819d 100644 --- a/server/routers/user/getUser.ts +++ b/server/routers/user/getUser.ts @@ -43,7 +43,7 @@ export async function getUser(req: Request, res: Response, next: NextFunction): const { passwordHash: _, ...userWithoutPassword } = user[0]; return res.status(HttpCode.OK).send( - response({ + response(res, { data: userWithoutPassword, success: true, error: false, diff --git a/server/routers/user/listUsers.ts b/server/routers/user/listUsers.ts new file mode 100644 index 00000000..43d636a5 --- /dev/null +++ b/server/routers/user/listUsers.ts @@ -0,0 +1,61 @@ +import { Request, Response, NextFunction } from 'express'; +import { z } from 'zod'; +import { db } from '@server/db'; +import { users } from '@server/db/schema'; +import response from "@server/utils/response"; +import HttpCode from '@server/types/HttpCode'; +import createHttpError from 'http-errors'; +import { sql } from 'drizzle-orm'; + +const listUsersSchema = z.object({ + limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)), + offset: z.string().optional().transform(Number).pipe(z.number().int().nonnegative().default(0)), +}); + +export async function listUsers(req: Request, res: Response, next: NextFunction): Promise { + try { + const parsedQuery = listUsersSchema.safeParse(req.query); + if (!parsedQuery.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + parsedQuery.error.errors.map(e => e.message).join(', ') + ) + ); + } + + const { limit, offset } = parsedQuery.data; + + const usersList = await db.select() + .from(users) + .limit(limit) + .offset(offset); + + const totalCountResult = await db + .select({ count: sql`cast(count(*) as integer)` }) + .from(users); + const totalCount = totalCountResult[0].count; + + // Remove passwordHash from each user object + const usersWithoutPassword = usersList.map(({ passwordHash, ...userWithoutPassword }) => userWithoutPassword); + + return res.status(HttpCode.OK).send( + response(res, { + data: { + users: usersWithoutPassword, + pagination: { + total: totalCount, + limit, + offset, + }, + }, + success: true, + error: false, + message: "Users retrieved successfully", + status: HttpCode.OK, + }) + ); + } catch (error) { + next(error); + } +} \ No newline at end of file