refactor contexts, format zod errors, and more refactoring

This commit is contained in:
Milo Schwartz 2024-11-03 13:57:51 -05:00
parent 2635443105
commit 2852d62258
No known key found for this signature in database
83 changed files with 2150 additions and 1264 deletions

View file

@ -120,7 +120,8 @@ if (!parsedConfig.success) {
throw new Error(`Invalid configuration file: ${errors}`);
}
process.env.SERVER_EXTERNAL_PORT = parsedConfig.data.server.external_port.toString();
process.env.SERVER_EXTERNAL_PORT =
parsedConfig.data.server.external_port.toString();
process.env.FLAGS_EMAIL_VERIFICATION_REQUIRED = parsedConfig.data.flags
?.require_email_verification
? "true"

View file

@ -47,7 +47,7 @@ authenticated.get("/org/:orgId/sites", verifyOrgAccess, site.listSites);
authenticated.get("/org/:orgId/site/:niceId", verifyOrgAccess, site.getSite);
authenticated.get(
"/org/:orgId/pickSiteDefaults",
"/org/:orgId/pick-site-defaults",
verifyOrgAccess,
site.pickSiteDefaults
);
@ -118,12 +118,12 @@ authenticated.delete(
target.deleteTarget
);
authenticated.put(
"/org/:orgId/role",
verifyOrgAccess,
verifySuperuser,
role.createRole
);
// authenticated.put(
// "/org/:orgId/role",
// verifyOrgAccess,
// verifySuperuser,
// role.createRole
// );
authenticated.get("/org/:orgId/roles", verifyOrgAccess, role.listRoles);
authenticated.get(
"/role/:roleId",
@ -131,18 +131,18 @@ authenticated.get(
verifyUserInRole,
role.getRole
);
authenticated.post(
"/role/:roleId",
verifyRoleAccess,
verifySuperuser,
role.updateRole
);
authenticated.delete(
"/role/:roleId",
verifyRoleAccess,
verifySuperuser,
role.deleteRole
);
// authenticated.post(
// "/role/:roleId",
// verifyRoleAccess,
// verifySuperuser,
// role.updateRole
// );
// authenticated.delete(
// "/role/:roleId",
// verifyRoleAccess,
// verifySuperuser,
// role.deleteRole
// );
authenticated.put(
"/role/:roleId/site",
@ -210,12 +210,6 @@ authenticated.delete(
verifyUserAccess,
user.removeUserOrg
);
authenticated.put(
"/org/:orgId/user/:userId",
verifyOrgAccess,
verifyUserAccess,
user.addUserOrg
);
authenticated.put(
"/user/:userId/site",

View file

@ -10,6 +10,7 @@ import logger from '@server/logger';
import config from "@server/config";
import { getUniqueExitNodeEndpointName } from '@server/db/names';
import { findNextAvailableCidr } from "@server/utils/ip";
import { fromError } from 'zod-validation-error';
// Define Zod schema for request validation
const getConfigSchema = z.object({
publicKey: z.string(),
@ -33,7 +34,7 @@ export async function getConfig(req: Request, res: Response, next: NextFunction)
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}

View file

@ -1,32 +1,38 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { orgs } from '@server/db/schema';
import { eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { orgs } from "@server/db/schema";
import { eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const getOrgSchema = z.object({
orgId: z.string()
orgId: z.string(),
});
export async function checkId(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function checkId(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedQuery = getOrgSchema.safeParse(req.query);
if (!parsedQuery.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedQuery.error.errors.map(e => e.message).join(', ')
fromError(parsedQuery.error).toString()
)
);
}
const { orgId } = parsedQuery.data;
const org = await db.select()
const org = await db
.select()
.from(orgs)
.where(eq(orgs.orgId, orgId))
.limit(1);
@ -50,6 +56,11 @@ export async function checkId(req: Request, res: Response, next: NextFunction):
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -10,6 +10,7 @@ import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import { createSuperuserRole } from '@server/db/ensureActions';
import config, { APP_PATH } from "@server/config";
import { fromError } from 'zod-validation-error';
const createOrgSchema = z.object({
orgId: z.string(),
@ -26,7 +27,7 @@ export async function createOrg(req: Request, res: Response, next: NextFunction)
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
fromError(parsedBody.error).toString()
)
);
}

View file

@ -8,6 +8,7 @@ import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import { fromError } from 'zod-validation-error';
const deleteOrgSchema = z.object({
orgId: z.string()
@ -20,7 +21,7 @@ export async function deleteOrg(req: Request, res: Response, next: NextFunction)
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}

View file

@ -8,6 +8,7 @@ import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import { fromError } from 'zod-validation-error';
const updateOrgParamsSchema = z.object({
orgId: z.string()
@ -27,7 +28,7 @@ export async function updateOrg(req: Request, res: Response, next: NextFunction)
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -37,7 +38,7 @@ export async function updateOrg(req: Request, res: Response, next: NextFunction)
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
fromError(parsedBody.error).toString()
)
);
}

View file

@ -9,6 +9,7 @@ import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import { eq, and } from 'drizzle-orm';
import stoi from '@server/utils/stoi';
import { fromError } from 'zod-validation-error';
const createResourceParamsSchema = z.object({
siteId: z.string().optional().transform(stoi).pipe(z.number().int().positive().optional()),
@ -29,7 +30,7 @@ export async function createResource(req: Request, res: Response, next: NextFunc
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
fromError(parsedBody.error).toString()
)
);
}
@ -42,7 +43,7 @@ export async function createResource(req: Request, res: Response, next: NextFunc
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}

View file

@ -1,20 +1,25 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { resources } from '@server/db/schema';
import { eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { resources } from "@server/db/schema";
import { eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
// Define Zod schema for request parameters validation
const deleteResourceSchema = z.object({
resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
});
export async function deleteResource(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function deleteResource(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
// Validate request parameters
const parsedParams = deleteResourceSchema.safeParse(req.params);
@ -22,7 +27,7 @@ export async function deleteResource(req: Request, res: Response, next: NextFunc
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -30,13 +35,22 @@ export async function deleteResource(req: Request, res: Response, next: NextFunc
const { resourceId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.deleteResource, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.deleteResource,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
// Delete the resource from the database
const deletedResource = await db.delete(resources)
const deletedResource = await db
.delete(resources)
.where(eq(resources.resourceId, resourceId))
.returning();
@ -58,6 +72,11 @@ export async function deleteResource(req: Request, res: Response, next: NextFunc
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,13 +1,14 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { resources } from '@server/db/schema';
import { eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { resources } from "@server/db/schema";
import { eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
// Define Zod schema for request parameters validation
const getResourceSchema = z.object({
@ -19,9 +20,13 @@ export type GetResourceResponse = {
siteId: number;
orgId: string;
name: string;
}
};
export async function getResource(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function getResource(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
// Validate request parameters
const parsedParams = getResourceSchema.safeParse(req.params);
@ -29,7 +34,7 @@ export async function getResource(req: Request, res: Response, next: NextFunctio
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -37,13 +42,22 @@ export async function getResource(req: Request, res: Response, next: NextFunctio
const { resourceId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.getResource, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.getResource,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
// Fetch the resource from the database
const resource = await db.select()
const resource = await db
.select()
.from(resources)
.where(eq(resources.resourceId, resourceId))
.limit(1);
@ -62,7 +76,7 @@ export async function getResource(req: Request, res: Response, next: NextFunctio
resourceId: resource[0].resourceId,
siteId: resource[0].siteId,
orgId: resource[0].orgId,
name: resource[0].name
name: resource[0].name,
},
success: true,
error: false,
@ -71,6 +85,11 @@ export async function getResource(req: Request, res: Response, next: NextFunctio
});
} catch (error) {
throw error;
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,26 +1,31 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { roleResources, roles } from '@server/db/schema';
import { eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { roleResources, roles } from "@server/db/schema";
import { eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const listResourceRolesSchema = z.object({
resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
});
export async function listResourceRoles(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function listResourceRoles(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = listResourceRolesSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -28,9 +33,17 @@ export async function listResourceRoles(req: Request, res: Response, next: NextF
const { resourceId } = parsedParams.data;
// Check if the user has permission to list resource roles
const hasPermission = await checkUserActionPermission(ActionsEnum.listResourceRoles, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.listResourceRoles,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const resourceRolesList = await db
@ -53,6 +66,11 @@ export async function listResourceRoles(req: Request, res: Response, next: NextF
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,13 +1,14 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { resources } from '@server/db/schema';
import { eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { resources } from "@server/db/schema";
import { eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
// Define Zod schema for request parameters validation
const updateResourceParamsSchema = z.object({
@ -15,14 +16,20 @@ const updateResourceParamsSchema = z.object({
});
// Define Zod schema for request body validation
const updateResourceBodySchema = z.object({
const updateResourceBodySchema = z
.object({
name: z.string().min(1).max(255).optional(),
subdomain: z.string().min(1).max(255).optional(),
}).refine(data => Object.keys(data).length > 0, {
message: "At least one field must be provided for update"
})
.refine((data) => Object.keys(data).length > 0, {
message: "At least one field must be provided for update",
});
export async function updateResource(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function updateResource(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
// Validate request parameters
const parsedParams = updateResourceParamsSchema.safeParse(req.params);
@ -30,7 +37,7 @@ export async function updateResource(req: Request, res: Response, next: NextFunc
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -41,7 +48,7 @@ export async function updateResource(req: Request, res: Response, next: NextFunc
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
fromError(parsedBody.error).toString()
)
);
}
@ -50,13 +57,22 @@ export async function updateResource(req: Request, res: Response, next: NextFunc
const updateData = parsedBody.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.updateResource, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.updateResource,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
// Update the resource in the database
const updatedResource = await db.update(resources)
const updatedResource = await db
.update(resources)
.set(updateData)
.where(eq(resources.resourceId, resourceId))
.returning();
@ -79,6 +95,11 @@ export async function updateResource(req: Request, res: Response, next: NextFunc
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,13 +1,14 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { roleActions, roles } from '@server/db/schema';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { roleActions, roles } from "@server/db/schema";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import { eq } from 'drizzle-orm';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { eq } from "drizzle-orm";
import { fromError } from "zod-validation-error";
const addRoleActionParamSchema = z.object({
roleId: z.string().transform(Number).pipe(z.number().int().positive()),
@ -17,14 +18,18 @@ const addRoleActionSchema = z.object({
actionId: z.string(),
});
export async function addRoleAction(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function addRoleAction(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedBody = addRoleActionSchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
fromError(parsedBody.error).toString()
)
);
}
@ -36,7 +41,7 @@ export async function addRoleAction(req: Request, res: Response, next: NextFunct
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -44,22 +49,42 @@ export async function addRoleAction(req: Request, res: Response, next: NextFunct
const { roleId } = parsedParams.data;
// Check if the user has permission to add role actions
const hasPermission = await checkUserActionPermission(ActionsEnum.addRoleAction, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.addRoleAction,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
// Get the orgId for the role
const role = await db.select({ orgId: roles.orgId }).from(roles).where(eq(roles.roleId, roleId)).limit(1);
const role = await db
.select({ orgId: roles.orgId })
.from(roles)
.where(eq(roles.roleId, roleId))
.limit(1);
if (role.length === 0) {
return next(createHttpError(HttpCode.NOT_FOUND, `Role with ID ${roleId} not found`));
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Role with ID ${roleId} not found`
)
);
}
const newRoleAction = await db.insert(roleActions).values({
const newRoleAction = await db
.insert(roleActions)
.values({
roleId,
actionId,
orgId: role[0].orgId!,
}).returning();
})
.returning();
return response(res, {
data: newRoleAction[0],
@ -70,6 +95,11 @@ export async function addRoleAction(req: Request, res: Response, next: NextFunct
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,12 +1,13 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { roleResources } from '@server/db/schema';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { roleResources } from "@server/db/schema";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const addRoleResourceParamsSchema = z.object({
roleId: z.string().transform(Number).pipe(z.number().int().positive()),
@ -16,14 +17,18 @@ const addRoleResourceSchema = z.object({
resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
});
export async function addRoleResource(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function addRoleResource(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedBody = addRoleResourceSchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
fromError(parsedBody.error).toString()
)
);
}
@ -35,7 +40,7 @@ export async function addRoleResource(req: Request, res: Response, next: NextFun
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -43,15 +48,26 @@ export async function addRoleResource(req: Request, res: Response, next: NextFun
const { roleId } = parsedParams.data;
// Check if the user has permission to add role resources
const hasPermission = await checkUserActionPermission(ActionsEnum.addRoleResource, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.addRoleResource,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const newRoleResource = await db.insert(roleResources).values({
const newRoleResource = await db
.insert(roleResources)
.values({
roleId,
resourceId,
}).returning();
})
.returning();
return response(res, {
data: newRoleResource[0],
@ -62,6 +78,11 @@ export async function addRoleResource(req: Request, res: Response, next: NextFun
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,13 +1,14 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { resources, roleResources, roleSites } from '@server/db/schema';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { resources, roleResources, roleSites } from "@server/db/schema";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import { eq } from 'drizzle-orm';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { eq } from "drizzle-orm";
import { fromError } from "zod-validation-error";
const addRoleSiteParamsSchema = z.object({
roleId: z.string().transform(Number).pipe(z.number().int().positive()),
@ -17,14 +18,18 @@ const addRoleSiteSchema = z.object({
siteId: z.string().transform(Number).pipe(z.number().int().positive()),
});
export async function addRoleSite(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function addRoleSite(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedBody = addRoleSiteSchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
fromError(parsedBody.error).toString()
)
);
}
@ -36,7 +41,7 @@ export async function addRoleSite(req: Request, res: Response, next: NextFunctio
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -44,17 +49,29 @@ export async function addRoleSite(req: Request, res: Response, next: NextFunctio
const { roleId } = parsedParams.data;
// Check if the user has permission to add role sites
const hasPermission = await checkUserActionPermission(ActionsEnum.addRoleSite, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.addRoleSite,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const newRoleSite = await db.insert(roleSites).values({
const newRoleSite = await db
.insert(roleSites)
.values({
roleId,
siteId,
}).returning();
})
.returning();
const siteResources = await db.select()
const siteResources = await db
.select()
.from(resources)
.where(eq(resources.siteId, siteId));
@ -74,6 +91,11 @@ export async function addRoleSite(req: Request, res: Response, next: NextFunctio
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,15 +1,16 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { roles } from '@server/db/schema';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { roles } from "@server/db/schema";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const createRoleParamsSchema = z.object({
orgId: z.string()
orgId: z.string(),
});
const createRoleSchema = z.object({
@ -17,14 +18,18 @@ const createRoleSchema = z.object({
description: z.string().optional(),
});
export async function createRole(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function createRole(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedBody = createRoleSchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
fromError(parsedBody.error).toString()
)
);
}
@ -36,7 +41,7 @@ export async function createRole(req: Request, res: Response, next: NextFunction
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -44,15 +49,26 @@ export async function createRole(req: Request, res: Response, next: NextFunction
const { orgId } = parsedParams.data;
// Check if the user has permission to create roles
const hasPermission = await checkUserActionPermission(ActionsEnum.createRole, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.createRole,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const newRole = await db.insert(roles).values({
const newRole = await db
.insert(roles)
.values({
...roleData,
orgId,
}).returning();
})
.returning();
return response(res, {
data: newRole[0],
@ -63,6 +79,11 @@ export async function createRole(req: Request, res: Response, next: NextFunction
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,26 +1,31 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { roles } from '@server/db/schema';
import { eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { roles } from "@server/db/schema";
import { eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const deleteRoleSchema = z.object({
roleId: z.string().transform(Number).pipe(z.number().int().positive())
roleId: z.string().transform(Number).pipe(z.number().int().positive()),
});
export async function deleteRole(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function deleteRole(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = deleteRoleSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -28,12 +33,21 @@ export async function deleteRole(req: Request, res: Response, next: NextFunction
const { roleId } = parsedParams.data;
// Check if the user has permission to delete roles
const hasPermission = await checkUserActionPermission(ActionsEnum.deleteRole, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.deleteRole,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const role = await db.select()
const role = await db
.select()
.from(roles)
.where(eq(roles.roleId, roleId))
.limit(1);
@ -56,7 +70,8 @@ export async function deleteRole(req: Request, res: Response, next: NextFunction
);
}
const deletedRole = await db.delete(roles)
const deletedRole = await db
.delete(roles)
.where(eq(roles.roleId, roleId))
.returning();
@ -78,6 +93,11 @@ export async function deleteRole(req: Request, res: Response, next: NextFunction
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,26 +1,31 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { roles } from '@server/db/schema';
import { eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { roles } from "@server/db/schema";
import { eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const getRoleSchema = z.object({
roleId: z.string().transform(Number).pipe(z.number().int().positive())
roleId: z.string().transform(Number).pipe(z.number().int().positive()),
});
export async function getRole(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function getRole(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = getRoleSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -28,12 +33,21 @@ export async function getRole(req: Request, res: Response, next: NextFunction):
const { roleId } = parsedParams.data;
// Check if the user has permission to get roles
const hasPermission = await checkUserActionPermission(ActionsEnum.getRole, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.getRole,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const role = await db.select()
const role = await db
.select()
.from(roles)
.where(eq(roles.roleId, roleId))
.limit(1);
@ -56,6 +70,11 @@ export async function getRole(req: Request, res: Response, next: NextFunction):
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,26 +1,31 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { roleActions, actions } from '@server/db/schema';
import { eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { roleActions, actions } from "@server/db/schema";
import { eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const listRoleActionsSchema = z.object({
roleId: z.string().transform(Number).pipe(z.number().int().positive()),
});
export async function listRoleActions(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function listRoleActions(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = listRoleActionsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -28,9 +33,17 @@ export async function listRoleActions(req: Request, res: Response, next: NextFun
const { roleId } = parsedParams.data;
// Check if the user has permission to list role actions
const hasPermission = await checkUserActionPermission(ActionsEnum.listRoleActions, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.listRoleActions,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const roleActionsList = await db
@ -54,6 +67,11 @@ export async function listRoleActions(req: Request, res: Response, next: NextFun
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,26 +1,31 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { roleResources, resources } from '@server/db/schema';
import { eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { roleResources, resources } from "@server/db/schema";
import { eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const listRoleResourcesSchema = z.object({
roleId: z.string().transform(Number).pipe(z.number().int().positive()),
});
export async function listRoleResources(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function listRoleResources(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = listRoleResourcesSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -28,9 +33,17 @@ export async function listRoleResources(req: Request, res: Response, next: NextF
const { roleId } = parsedParams.data;
// Check if the user has permission to list role resources
const hasPermission = await checkUserActionPermission(ActionsEnum.listRoleResources, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.listRoleResources,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const roleResourcesList = await db
@ -40,7 +53,10 @@ export async function listRoleResources(req: Request, res: Response, next: NextF
subdomain: resources.subdomain,
})
.from(roleResources)
.innerJoin(resources, eq(roleResources.resourceId, resources.resourceId))
.innerJoin(
resources,
eq(roleResources.resourceId, resources.resourceId)
)
.where(eq(roleResources.roleId, roleId));
// TODO: Do we need to filter out what the user can see?
@ -54,6 +70,11 @@ export async function listRoleResources(req: Request, res: Response, next: NextF
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,26 +1,31 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { roleSites, sites } from '@server/db/schema';
import { eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { roleSites, sites } from "@server/db/schema";
import { eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const listRoleSitesSchema = z.object({
roleId: z.string().transform(Number).pipe(z.number().int().positive()),
});
export async function listRoleSites(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function listRoleSites(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = listRoleSitesSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -28,9 +33,17 @@ export async function listRoleSites(req: Request, res: Response, next: NextFunct
const { roleId } = parsedParams.data;
// Check if the user has permission to list role sites
const hasPermission = await checkUserActionPermission(ActionsEnum.listRoleSites, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.listRoleSites,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const roleSitesList = await db
@ -53,6 +66,11 @@ export async function listRoleSites(req: Request, res: Response, next: NextFunct
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,32 +1,49 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { roles, orgs } from '@server/db/schema';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { roles, orgs } 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';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { sql, eq } from "drizzle-orm";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const listRolesParamsSchema = z.object({
orgId: z.string()
orgId: z.string(),
});
const listRolesSchema = 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()),
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 listRoles(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function listRoles(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedQuery = listRolesSchema.safeParse(req.query);
if (!parsedQuery.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedQuery.error.errors.map(e => e.message).join(', ')
fromError(parsedQuery.error).toString()
)
);
}
@ -38,7 +55,7 @@ export async function listRoles(req: Request, res: Response, next: NextFunction)
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -46,9 +63,17 @@ export async function listRoles(req: Request, res: Response, next: NextFunction)
const { orgId } = parsedParams.data;
// Check if the user has permission to list roles
const hasPermission = await checkUserActionPermission(ActionsEnum.listRoles, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.listRoles,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
let baseQuery: any = db
@ -64,7 +89,9 @@ export async function listRoles(req: Request, res: Response, next: NextFunction)
.leftJoin(orgs, eq(roles.orgId, orgs.orgId))
.where(eq(roles.orgId, orgId));
let countQuery: any = db.select({ count: sql<number>`cast(count(*) as integer)` }).from(roles)
let countQuery: any = db
.select({ count: sql<number>`cast(count(*) as integer)` })
.from(roles)
.where(eq(roles.orgId, orgId));
const rolesList = await baseQuery.limit(limit).offset(offset);
@ -87,6 +114,11 @@ export async function listRoles(req: Request, res: Response, next: NextFunction)
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,13 +1,14 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { roleActions } from '@server/db/schema';
import { and, eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { roleActions } from "@server/db/schema";
import { and, eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const removeRoleActionParamsSchema = z.object({
roleId: z.string().transform(Number).pipe(z.number().int().positive()),
@ -17,14 +18,18 @@ const removeRoleActionSchema = z.object({
actionId: z.string(),
});
export async function removeRoleAction(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function removeRoleAction(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = removeRoleActionSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -36,7 +41,7 @@ export async function removeRoleAction(req: Request, res: Response, next: NextFu
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
fromError(parsedBody.error).toString()
)
);
}
@ -44,13 +49,27 @@ export async function removeRoleAction(req: Request, res: Response, next: NextFu
const { roleId } = parsedBody.data;
// Check if the user has permission to remove role actions
const hasPermission = await checkUserActionPermission(ActionsEnum.removeRoleAction, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.removeRoleAction,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const deletedRoleAction = await db.delete(roleActions)
.where(and(eq(roleActions.roleId, roleId), eq(roleActions.actionId, actionId)))
const deletedRoleAction = await db
.delete(roleActions)
.where(
and(
eq(roleActions.roleId, roleId),
eq(roleActions.actionId, actionId)
)
)
.returning();
if (deletedRoleAction.length === 0) {
@ -71,6 +90,11 @@ export async function removeRoleAction(req: Request, res: Response, next: NextFu
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,13 +1,14 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { roleResources } from '@server/db/schema';
import { and, eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { roleResources } from "@server/db/schema";
import { and, eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const removeRoleResourceParamsSchema = z.object({
roleId: z.string().transform(Number).pipe(z.number().int().positive()),
@ -17,14 +18,18 @@ const removeRoleResourceSchema = z.object({
resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
});
export async function removeRoleResource(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function removeRoleResource(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = removeRoleResourceSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -36,7 +41,7 @@ export async function removeRoleResource(req: Request, res: Response, next: Next
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
fromError(parsedBody.error).toString()
)
);
}
@ -44,13 +49,27 @@ export async function removeRoleResource(req: Request, res: Response, next: Next
const { roleId } = parsedBody.data;
// Check if the user has permission to remove role resources
const hasPermission = await checkUserActionPermission(ActionsEnum.removeRoleResource, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.removeRoleResource,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const deletedRoleResource = await db.delete(roleResources)
.where(and(eq(roleResources.roleId, roleId), eq(roleResources.resourceId, resourceId)))
const deletedRoleResource = await db
.delete(roleResources)
.where(
and(
eq(roleResources.roleId, roleId),
eq(roleResources.resourceId, resourceId)
)
)
.returning();
if (deletedRoleResource.length === 0) {
@ -71,6 +90,11 @@ export async function removeRoleResource(req: Request, res: Response, next: Next
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,13 +1,14 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { resources, roleResources, roleSites } from '@server/db/schema';
import { and, eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { resources, roleResources, roleSites } from "@server/db/schema";
import { and, eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const removeRoleSiteParamsSchema = z.object({
roleId: z.string().transform(Number).pipe(z.number().int().positive()),
@ -17,14 +18,18 @@ const removeRoleSiteSchema = z.object({
siteId: z.string().transform(Number).pipe(z.number().int().positive()),
});
export async function removeRoleSite(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function removeRoleSite(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = removeRoleSiteSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -36,7 +41,7 @@ export async function removeRoleSite(req: Request, res: Response, next: NextFunc
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
fromError(parsedBody.error).toString()
)
);
}
@ -44,13 +49,24 @@ export async function removeRoleSite(req: Request, res: Response, next: NextFunc
const { roleId } = parsedBody.data;
// Check if the user has permission to remove role sites
const hasPermission = await checkUserActionPermission(ActionsEnum.removeRoleSite, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.removeRoleSite,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const deletedRoleSite = await db.delete(roleSites)
.where(and(eq(roleSites.roleId, roleId), eq(roleSites.siteId, siteId)))
const deletedRoleSite = await db
.delete(roleSites)
.where(
and(eq(roleSites.roleId, roleId), eq(roleSites.siteId, siteId))
)
.returning();
if (deletedRoleSite.length === 0) {
@ -62,13 +78,20 @@ export async function removeRoleSite(req: Request, res: Response, next: NextFunc
);
}
const siteResources = await db.select()
const siteResources = await db
.select()
.from(resources)
.where(eq(resources.siteId, siteId));
for (const resource of siteResources) {
await db.delete(roleResources)
.where(and(eq(roleResources.roleId, roleId), eq(roleResources.resourceId, resource.resourceId)))
await db
.delete(roleResources)
.where(
and(
eq(roleResources.roleId, roleId),
eq(roleResources.resourceId, resource.resourceId)
)
)
.returning();
}
@ -81,6 +104,11 @@ export async function removeRoleSite(req: Request, res: Response, next: NextFunc
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,33 +1,40 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { roles } from '@server/db/schema';
import { eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { roles } from "@server/db/schema";
import { eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const updateRoleParamsSchema = z.object({
roleId: z.string().transform(Number).pipe(z.number().int().positive())
roleId: z.string().transform(Number).pipe(z.number().int().positive()),
});
const updateRoleBodySchema = z.object({
const updateRoleBodySchema = z
.object({
name: z.string().min(1).max(255).optional(),
description: z.string().optional(),
}).refine(data => Object.keys(data).length > 0, {
message: "At least one field must be provided for update"
})
.refine((data) => Object.keys(data).length > 0, {
message: "At least one field must be provided for update",
});
export async function updateRole(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function updateRole(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = updateRoleParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -37,7 +44,7 @@ export async function updateRole(req: Request, res: Response, next: NextFunction
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
fromError(parsedBody.error).toString()
)
);
}
@ -46,12 +53,21 @@ export async function updateRole(req: Request, res: Response, next: NextFunction
const updateData = parsedBody.data;
// Check if the user has permission to update roles
const hasPermission = await checkUserActionPermission(ActionsEnum.updateRole, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.updateRole,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const role = await db.select()
const role = await db
.select()
.from(roles)
.where(eq(roles.roleId, roleId))
.limit(1);
@ -74,7 +90,8 @@ export async function updateRole(req: Request, res: Response, next: NextFunction
);
}
const updatedRole = await db.update(roles)
const updatedRole = await db
.update(roles)
.set(updateData)
.where(eq(roles.roleId, roleId))
.returning();
@ -97,6 +114,11 @@ export async function updateRole(req: Request, res: Response, next: NextFunction
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,20 +1,25 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { roles, userSites, sites, roleSites, exitNodes } from '@server/db/schema';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import {
roles,
userSites,
sites,
roleSites,
exitNodes,
} from "@server/db/schema";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import { eq, and } from 'drizzle-orm';
import { getUniqueSiteName } from '@server/db/names';
import { addPeer } from '../gerbil/peers';
const API_BASE_URL = "http://localhost:3000";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { eq, and } from "drizzle-orm";
import { getUniqueSiteName } from "@server/db/names";
import { addPeer } from "../gerbil/peers";
import { fromError } from "zod-validation-error";
const createSiteParamsSchema = z.object({
orgId: z.string()
orgId: z.string(),
});
// Define Zod schema for request body validation
@ -36,7 +41,11 @@ export type CreateSiteResponse = {
// subnet: string;
};
export async function createSite(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function createSite(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
// Validate request body
const parsedBody = createSiteSchema.safeParse(req.body);
@ -44,7 +53,7 @@ export async function createSite(req: Request, res: Response, next: NextFunction
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
fromError(parsedBody.error).toString()
)
);
}
@ -57,7 +66,7 @@ export async function createSite(req: Request, res: Response, next: NextFunction
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -65,38 +74,49 @@ export async function createSite(req: Request, res: Response, next: NextFunction
const { orgId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.createSite, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.createSite,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission perform this action"
)
);
}
if (!req.userOrgRoleId) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have a role'));
return next(
createHttpError(HttpCode.FORBIDDEN, "User does not have a role")
);
}
const niceId = await getUniqueSiteName(orgId);
// Create new site in the database
const [newSite] = await db.insert(sites).values({
const [newSite] = await db
.insert(sites)
.values({
orgId,
exitNodeId,
name,
niceId,
pubKey,
subnet,
}).returning();
})
.returning();
// find the superuser roleId and also add the resource to the superuser role
const superuserRole = await db.select()
const superuserRole = await db
.select()
.from(roles)
.where(and(eq(roles.isSuperuserRole, true), eq(roles.orgId, orgId)))
.limit(1);
if (superuserRole.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Superuser role not found`
)
createHttpError(HttpCode.NOT_FOUND, `Superuser role not found`)
);
}
@ -135,6 +155,11 @@ export async function createSite(req: Request, res: Response, next: NextFunction
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,23 +1,28 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { sites } from '@server/db/schema';
import { eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { sites } from "@server/db/schema";
import { eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import { deletePeer } from '../gerbil/peers';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { deletePeer } from "../gerbil/peers";
import { fromError } from "zod-validation-error";
const API_BASE_URL = "http://localhost:3000";
// Define Zod schema for request parameters validation
const deleteSiteSchema = z.object({
siteId: z.string().transform(Number).pipe(z.number().int().positive())
siteId: z.string().transform(Number).pipe(z.number().int().positive()),
});
export async function deleteSite(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function deleteSite(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
// Validate request parameters
const parsedParams = deleteSiteSchema.safeParse(req.params);
@ -25,7 +30,7 @@ export async function deleteSite(req: Request, res: Response, next: NextFunction
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -33,13 +38,22 @@ export async function deleteSite(req: Request, res: Response, next: NextFunction
const { siteId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.deleteSite, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.deleteSite,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
// Delete the site from the database
const [deletedSite] = await db.delete(sites)
const [deletedSite] = await db
.delete(sites)
.where(eq(sites.siteId, siteId))
.returning();
@ -63,26 +77,33 @@ export async function deleteSite(req: Request, res: Response, next: NextFunction
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}
async function removePeer(publicKey: string) {
try {
const response = await fetch(`${API_BASE_URL}/peer?public_key=${encodeURIComponent(publicKey)}`, {
method: 'DELETE',
});
const response = await fetch(
`${API_BASE_URL}/peer?public_key=${encodeURIComponent(publicKey)}`,
{
method: "DELETE",
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log('Peer removed successfully:', data.status);
console.log("Peer removed successfully:", data.status);
return data;
} catch (error: any) {
console.error('Error removing peer:', error.message);
console.error("Error removing peer:", error.message);
throw error;
}
}

View file

@ -1,18 +1,24 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { sites } from '@server/db/schema';
import { eq, and } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { sites } from "@server/db/schema";
import { eq, and } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import stoi from '@server/utils/stoi';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import stoi from "@server/utils/stoi";
import { fromError } from "zod-validation-error";
// Define Zod schema for request parameters validation
const getSiteSchema = z.object({
siteId: z.string().optional().transform(stoi).pipe(z.number().int().positive().optional()).optional(),
siteId: z
.string()
.optional()
.transform(stoi)
.pipe(z.number().int().positive().optional())
.optional(),
niceId: z.string().optional(),
orgId: z.string().optional(),
});
@ -22,9 +28,13 @@ export type GetSiteResponse = {
name: string;
subdomain: string;
subnet: string;
}
};
export async function getSite(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function getSite(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
// Validate request parameters
const parsedParams = getSiteSchema.safeParse(req.params);
@ -32,7 +42,7 @@ export async function getSite(req: Request, res: Response, next: NextFunction):
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -40,27 +50,37 @@ export async function getSite(req: Request, res: Response, next: NextFunction):
const { siteId, niceId, orgId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.updateSite, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.updateSite,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
let site;
// Fetch the site from the database
if (siteId) {
site = await db.select()
site = await db
.select()
.from(sites)
.where(eq(sites.siteId, siteId))
.limit(1);
} else if (niceId && orgId) {
site = await db.select()
site = await db
.select()
.from(sites)
.where(and(eq(sites.niceId, niceId), eq(sites.orgId, orgId)))
.limit(1);
}
if (!site) {
return next(createHttpError(HttpCode.NOT_FOUND, 'Site not found'));
return next(createHttpError(HttpCode.NOT_FOUND, "Site not found"));
}
if (site.length === 0) {
@ -86,6 +106,11 @@ export async function getSite(req: Request, res: Response, next: NextFunction):
});
} catch (error) {
logger.error("Error from getSite: ", error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,26 +1,31 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { roleSites, roles } from '@server/db/schema';
import { eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { roleSites, roles } from "@server/db/schema";
import { eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const listSiteRolesSchema = z.object({
siteId: z.string().transform(Number).pipe(z.number().int().positive()),
});
export async function listSiteRoles(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function listSiteRoles(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = listSiteRolesSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -28,9 +33,17 @@ export async function listSiteRoles(req: Request, res: Response, next: NextFunct
const { siteId } = parsedParams.data;
// Check if the user has permission to list site roles
const hasPermission = await checkUserActionPermission(ActionsEnum.listSiteRoles, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.listSiteRoles,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const siteRolesList = await db
@ -53,6 +66,11 @@ export async function listSiteRoles(req: Request, res: Response, next: NextFunct
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -18,26 +18,25 @@ export type PickSiteDefaultsResponse = {
listenPort: number;
endpoint: string;
subnet: string;
}
};
export async function pickSiteDefaults(
req: Request,
res: Response,
next: NextFunction,
next: NextFunction
): Promise<any> {
try {
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(
ActionsEnum.createSite,
req,
req
);
if (!hasPermission) {
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action",
),
"User does not have permission to perform this action"
)
);
}
@ -47,10 +46,7 @@ export async function pickSiteDefaults(
const nodes = await db.select().from(exitNodes);
if (nodes.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
"No exit nodes available",
),
createHttpError(HttpCode.NOT_FOUND, "No exit nodes available")
);
}
@ -59,8 +55,9 @@ export async function pickSiteDefaults(
// TODO: this probably can be optimized...
// list all of the sites on that exit node
const sitesQuery = await db.select({
subnet: sites.subnet
const sitesQuery = await db
.select({
subnet: sites.subnet,
})
.from(sites)
.where(eq(sites.exitNodeId, exitNode.exitNodeId));
@ -74,8 +71,8 @@ export async function pickSiteDefaults(
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"No available subnets",
),
"No available subnets"
)
);
}
@ -97,11 +94,7 @@ export async function pickSiteDefaults(
} catch (error) {
logger.error(error);
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred...",
),
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
);
}
}

View file

@ -1,21 +1,23 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { sites } from '@server/db/schema';
import { eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { sites } from "@server/db/schema";
import { eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
// Define Zod schema for request parameters validation
const updateSiteParamsSchema = z.object({
siteId: z.string().transform(Number).pipe(z.number().int().positive())
siteId: z.string().transform(Number).pipe(z.number().int().positive()),
});
// Define Zod schema for request body validation
const updateSiteBodySchema = z.object({
const updateSiteBodySchema = z
.object({
name: z.string().min(1).max(255).optional(),
subdomain: z.string().min(1).max(255).optional(),
pubKey: z.string().optional(),
@ -23,11 +25,16 @@ const updateSiteBodySchema = z.object({
exitNode: z.number().int().positive().optional(),
megabytesIn: z.number().int().nonnegative().optional(),
megabytesOut: z.number().int().nonnegative().optional(),
}).refine(data => Object.keys(data).length > 0, {
message: "At least one field must be provided for update"
})
.refine((data) => Object.keys(data).length > 0, {
message: "At least one field must be provided for update",
});
export async function updateSite(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function updateSite(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
// Validate request parameters
const parsedParams = updateSiteParamsSchema.safeParse(req.params);
@ -35,7 +42,7 @@ export async function updateSite(req: Request, res: Response, next: NextFunction
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -46,7 +53,7 @@ export async function updateSite(req: Request, res: Response, next: NextFunction
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
fromError(parsedBody.error).toString()
)
);
}
@ -55,13 +62,22 @@ export async function updateSite(req: Request, res: Response, next: NextFunction
const updateData = parsedBody.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.updateSite, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.updateSite,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
// Update the site in the database
const updatedSite = await db.update(sites)
const updatedSite = await db
.update(sites)
.set(updateData)
.where(eq(sites.siteId, siteId))
.returning();
@ -84,6 +100,11 @@ export async function updateSite(req: Request, res: Response, next: NextFunction
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,15 +1,16 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { resources, sites, targets } from '@server/db/schema';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { resources, sites, targets } from "@server/db/schema";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import { addPeer } from '../gerbil/peers';
import { eq, and } from 'drizzle-orm';
import { isIpInCidr } from '@server/utils/ip';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { addPeer } from "../gerbil/peers";
import { eq, and } from "drizzle-orm";
import { isIpInCidr } from "@server/utils/ip";
import { fromError } from "zod-validation-error";
const createTargetParamsSchema = z.object({
resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
@ -23,14 +24,18 @@ const createTargetSchema = z.object({
enabled: z.boolean().default(true),
});
export async function createTarget(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function createTarget(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedBody = createTargetSchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
fromError(parsedBody.error).toString()
)
);
}
@ -42,7 +47,7 @@ export async function createTarget(req: Request, res: Response, next: NextFuncti
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -50,13 +55,22 @@ export async function createTarget(req: Request, res: Response, next: NextFuncti
const { resourceId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.createTarget, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.createTarget,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
// get the resource
const [resource] = await db.select({
const [resource] = await db
.select({
siteId: resources.siteId,
})
.from(resources)
@ -74,7 +88,8 @@ export async function createTarget(req: Request, res: Response, next: NextFuncti
// TODO: is this all inefficient?
// get the site
const [site] = await db.select()
const [site] = await db
.select()
.from(sites)
.where(eq(sites.siteId, resource.siteId!))
.limit(1);
@ -98,10 +113,13 @@ export async function createTarget(req: Request, res: Response, next: NextFuncti
);
}
const newTarget = await db.insert(targets).values({
const newTarget = await db
.insert(targets)
.values({
resourceId,
...targetData
}).returning();
...targetData,
})
.returning();
// Fetch resources for this site
const resourcesRes = await db.query.resources.findMany({
@ -109,16 +127,18 @@ export async function createTarget(req: Request, res: Response, next: NextFuncti
});
// Fetch targets for all resources of this site
const targetIps = await Promise.all(resourcesRes.map(async (resource) => {
const targetIps = await Promise.all(
resourcesRes.map(async (resource) => {
const targetsRes = await db.query.targets.findMany({
where: eq(targets.resourceId, resource.resourceId),
});
return targetsRes.map(target => `${target.ip}/32`);
}));
return targetsRes.map((target) => `${target.ip}/32`);
})
);
await addPeer(site.exitNodeId!, {
publicKey: site.pubKey,
allowedIps: targetIps.flat()
allowedIps: targetIps.flat(),
});
return response(res, {
@ -130,6 +150,11 @@ export async function createTarget(req: Request, res: Response, next: NextFuncti
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,27 +1,32 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { resources, sites, targets } from '@server/db/schema';
import { eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { resources, sites, targets } from "@server/db/schema";
import { eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import { addPeer } from '../gerbil/peers';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { addPeer } from "../gerbil/peers";
import { fromError } from "zod-validation-error";
const deleteTargetSchema = z.object({
targetId: z.string().transform(Number).pipe(z.number().int().positive())
targetId: z.string().transform(Number).pipe(z.number().int().positive()),
});
export async function deleteTarget(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function deleteTarget(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = deleteTargetSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -29,13 +34,21 @@ export async function deleteTarget(req: Request, res: Response, next: NextFuncti
const { targetId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.deleteTarget, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.deleteTarget,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const [deletedTarget] = await db.delete(targets)
const [deletedTarget] = await db
.delete(targets)
.where(eq(targets.targetId, targetId))
.returning();
@ -48,7 +61,8 @@ export async function deleteTarget(req: Request, res: Response, next: NextFuncti
);
}
// get the resource
const [resource] = await db.select({
const [resource] = await db
.select({
siteId: resources.siteId,
})
.from(resources)
@ -66,7 +80,8 @@ export async function deleteTarget(req: Request, res: Response, next: NextFuncti
// TODO: is this all inefficient?
// get the site
const [site] = await db.select()
const [site] = await db
.select()
.from(sites)
.where(eq(sites.siteId, resource.siteId!))
.limit(1);
@ -86,16 +101,18 @@ export async function deleteTarget(req: Request, res: Response, next: NextFuncti
});
// Fetch targets for all resources of this site
const targetIps = await Promise.all(resourcesRes.map(async (resource) => {
const targetIps = await Promise.all(
resourcesRes.map(async (resource) => {
const targetsRes = await db.query.targets.findMany({
where: eq(targets.resourceId, resource.resourceId),
});
return targetsRes.map(target => `${target.ip}/32`);
}));
return targetsRes.map((target) => `${target.ip}/32`);
})
);
await addPeer(site.exitNodeId!, {
publicKey: site.pubKey,
allowedIps: targetIps.flat()
allowedIps: targetIps.flat(),
});
return response(res, {
@ -107,6 +124,11 @@ export async function deleteTarget(req: Request, res: Response, next: NextFuncti
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,26 +1,31 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { targets } from '@server/db/schema';
import { eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { targets } from "@server/db/schema";
import { eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const getTargetSchema = z.object({
targetId: z.string().transform(Number).pipe(z.number().int().positive())
targetId: z.string().transform(Number).pipe(z.number().int().positive()),
});
export async function getTarget(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function getTarget(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = getTargetSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -28,12 +33,21 @@ export async function getTarget(req: Request, res: Response, next: NextFunction)
const { targetId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.getTarget, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.getTarget,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const target = await db.select()
const target = await db
.select()
.from(targets)
.where(eq(targets.targetId, targetId))
.limit(1);
@ -56,6 +70,11 @@ export async function getTarget(req: Request, res: Response, next: NextFunction)
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,36 +1,43 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { targets } from '@server/db/schema';
import { eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { targets } from "@server/db/schema";
import { eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const updateTargetParamsSchema = z.object({
targetId: z.string().transform(Number).pipe(z.number().int().positive())
targetId: z.string().transform(Number).pipe(z.number().int().positive()),
});
const updateTargetBodySchema = z.object({
const updateTargetBodySchema = z
.object({
// ip: z.string().ip().optional(), // for now we cant update the ip; you will have to delete
method: z.string().min(1).max(10).optional(),
port: z.number().int().min(1).max(65535).optional(),
protocol: z.string().optional(),
enabled: z.boolean().optional(),
}).refine(data => Object.keys(data).length > 0, {
message: "At least one field must be provided for update"
})
.refine((data) => Object.keys(data).length > 0, {
message: "At least one field must be provided for update",
});
export async function updateTarget(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function updateTarget(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = updateTargetParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -40,7 +47,7 @@ export async function updateTarget(req: Request, res: Response, next: NextFuncti
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
fromError(parsedBody.error).toString()
)
);
}
@ -49,12 +56,21 @@ export async function updateTarget(req: Request, res: Response, next: NextFuncti
const updateData = parsedBody.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.updateTarget, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.updateTarget,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const updatedTarget = await db.update(targets)
const updatedTarget = await db
.update(targets)
.set(updateData)
.where(eq(targets.targetId, targetId))
.returning();
@ -77,6 +93,11 @@ export async function updateTarget(req: Request, res: Response, next: NextFuncti
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,13 +1,14 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { userActions, users } from '@server/db/schema';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { userActions, users } from "@server/db/schema";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import { eq } from 'drizzle-orm';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { eq } from "drizzle-orm";
import { fromError } from "zod-validation-error";
const addUserActionSchema = z.object({
userId: z.string(),
@ -15,14 +16,18 @@ const addUserActionSchema = z.object({
orgId: z.string(),
});
export async function addUserAction(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function addUserAction(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedBody = addUserActionSchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
fromError(parsedBody.error).toString()
)
);
}
@ -30,22 +35,42 @@ export async function addUserAction(req: Request, res: Response, next: NextFunct
const { userId, actionId, orgId } = parsedBody.data;
// Check if the user has permission to add user actions
const hasPermission = await checkUserActionPermission(ActionsEnum.addUserAction, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.addUserAction,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
// Check if the user exists
const user = await db.select().from(users).where(eq(users.userId, userId)).limit(1);
const user = await db
.select()
.from(users)
.where(eq(users.userId, userId))
.limit(1);
if (user.length === 0) {
return next(createHttpError(HttpCode.NOT_FOUND, `User with ID ${userId} not found`));
return next(
createHttpError(
HttpCode.NOT_FOUND,
`User with ID ${userId} not found`
)
);
}
const newUserAction = await db.insert(userActions).values({
const newUserAction = await db
.insert(userActions)
.values({
userId,
actionId,
orgId,
}).returning();
})
.returning();
return response(res, {
data: newUserAction[0],
@ -56,6 +81,11 @@ export async function addUserAction(req: Request, res: Response, next: NextFunct
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,87 +0,0 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { userOrgs, users, roles } from '@server/db/schema';
import { and, eq } from 'drizzle-orm';
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
const addUserParamsSchema = z.object({
userId: z.string().uuid(),
orgId: z.string()
});
const addUserSchema = z.object({
roleId: z.number().int().positive(),
});
export async function addUserOrg(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
const parsedParams = addUserParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
const { userId, orgId } = parsedParams.data;
const parsedBody = addUserSchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
)
);
}
const { roleId } = parsedBody.data;
// Check if the user has permission to add users
const hasPermission = await checkUserActionPermission(ActionsEnum.addUser, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
}
// Check if the user exists
const user = await db.select().from(users).where(eq(users.userId, userId)).limit(1);
if (user.length === 0) {
return next(createHttpError(HttpCode.NOT_FOUND, `User with ID ${userId} not found`));
}
// Check if the user is already in the organization
const existingUserOrg = await db.select()
.from(userOrgs)
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, orgId)))
.limit(1);
if (existingUserOrg.length > 0) {
return next(createHttpError(HttpCode.CONFLICT, 'User is already a member of this organization'));
}
// Add the user to the userOrgs table
const newUserOrg = await db.insert(userOrgs).values({
userId,
orgId,
roleId
}).returning();
return response(res, {
data: newUserOrg[0],
success: true,
error: false,
message: "User added to organization successfully",
status: HttpCode.CREATED,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
}

View file

@ -1,26 +1,31 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { userResources } from '@server/db/schema';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { userResources } from "@server/db/schema";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const addUserResourceSchema = z.object({
userId: z.string(),
resourceId: z.string().transform(Number).pipe(z.number().int().positive())
resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
});
export async function addUserResource(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function addUserResource(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedBody = addUserResourceSchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
fromError(parsedBody.error).toString()
)
);
}
@ -28,15 +33,26 @@ export async function addUserResource(req: Request, res: Response, next: NextFun
const { userId, resourceId } = parsedBody.data;
// Check if the user has permission to add user resources
const hasPermission = await checkUserActionPermission(ActionsEnum.addUserResource, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.addUserResource,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const newUserResource = await db.insert(userResources).values({
const newUserResource = await db
.insert(userResources)
.values({
userId,
resourceId,
}).returning();
})
.returning();
return response(res, {
data: newUserResource[0],
@ -47,6 +63,11 @@ export async function addUserResource(req: Request, res: Response, next: NextFun
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,27 +1,32 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { resources, userResources, userSites } from '@server/db/schema';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { resources, userResources, userSites } from "@server/db/schema";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import { eq } from 'drizzle-orm';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { eq } from "drizzle-orm";
import { fromError } from "zod-validation-error";
const addUserSiteSchema = z.object({
userId: z.string(),
siteId: z.string().transform(Number).pipe(z.number().int().positive()),
});
export async function addUserSite(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function addUserSite(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedBody = addUserSiteSchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
fromError(parsedBody.error).toString()
)
);
}
@ -29,18 +34,30 @@ export async function addUserSite(req: Request, res: Response, next: NextFunctio
const { userId, siteId } = parsedBody.data;
// Check if the user has permission to add user sites
const hasPermission = await checkUserActionPermission(ActionsEnum.addUserSite, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.addUserSite,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const newUserSite = await db.insert(userSites).values({
const newUserSite = await db
.insert(userSites)
.values({
userId,
siteId,
}).returning();
})
.returning();
// Add all resources associated with the site to the user
const siteResources = await db.select()
const siteResources = await db
.select()
.from(resources)
.where(eq(resources.siteId, siteId));
@ -60,6 +77,11 @@ export async function addUserSite(req: Request, res: Response, next: NextFunctio
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,6 +1,5 @@
export * from "./getUser";
export * from "./removeUserOrg";
export * from "./addUserOrg";
export * from "./listUsers";
export * from "./setUserRole";
export * from "./inviteUser";

View file

@ -1,13 +1,14 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { userActions } from '@server/db/schema';
import { and, eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { userActions } from "@server/db/schema";
import { and, eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const removeUserActionParamsSchema = z.object({
userId: z.string(),
@ -15,17 +16,21 @@ const removeUserActionParamsSchema = z.object({
const removeUserActionSchema = z.object({
actionId: z.string(),
orgId: z.string()
orgId: z.string(),
});
export async function removeUserAction(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function removeUserAction(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = removeUserActionParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -37,7 +42,7 @@ export async function removeUserAction(req: Request, res: Response, next: NextFu
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
fromError(parsedBody.error).toString()
)
);
}
@ -45,17 +50,28 @@ export async function removeUserAction(req: Request, res: Response, next: NextFu
const { actionId, orgId } = parsedBody.data;
// Check if the user has permission to remove user actions
const hasPermission = await checkUserActionPermission(ActionsEnum.removeUserAction, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.removeUserAction,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const deletedUserAction = await db.delete(userActions)
.where(and(
const deletedUserAction = await db
.delete(userActions)
.where(
and(
eq(userActions.userId, userId),
eq(userActions.actionId, actionId),
eq(userActions.orgId, orgId)
))
)
)
.returning();
if (deletedUserAction.length === 0) {
@ -76,6 +92,11 @@ export async function removeUserAction(req: Request, res: Response, next: NextFu
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,27 +1,32 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { userOrgs, users } from '@server/db/schema';
import { and, eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { userOrgs, users } from "@server/db/schema";
import { and, eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const removeUserSchema = z.object({
userId: z.string().uuid(),
orgId: z.string()
orgId: z.string(),
});
export async function removeUserOrg(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function removeUserOrg(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = removeUserSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -29,13 +34,22 @@ export async function removeUserOrg(req: Request, res: Response, next: NextFunct
const { userId, orgId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.removeUser, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.removeUser,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
// remove the user from the userOrgs table
await db.delete(userOrgs)
await db
.delete(userOrgs)
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, orgId)));
return response(res, {
@ -47,6 +61,11 @@ export async function removeUserOrg(req: Request, res: Response, next: NextFunct
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,27 +1,32 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { userResources } from '@server/db/schema';
import { and, eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { userResources } from "@server/db/schema";
import { and, eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const removeUserResourceSchema = z.object({
userId: z.string(),
resourceId: z.string().transform(Number).pipe(z.number().int().positive())
resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
});
export async function removeUserResource(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function removeUserResource(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = removeUserResourceSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -29,13 +34,27 @@ export async function removeUserResource(req: Request, res: Response, next: Next
const { userId, resourceId } = parsedParams.data;
// Check if the user has permission to remove user resources
const hasPermission = await checkUserActionPermission(ActionsEnum.removeUserResource, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.removeUserResource,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const deletedUserResource = await db.delete(userResources)
.where(and(eq(userResources.userId, userId), eq(userResources.resourceId, resourceId)))
const deletedUserResource = await db
.delete(userResources)
.where(
and(
eq(userResources.userId, userId),
eq(userResources.resourceId, resourceId)
)
)
.returning();
if (deletedUserResource.length === 0) {
@ -56,6 +75,11 @@ export async function removeUserResource(req: Request, res: Response, next: Next
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,13 +1,14 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { resources, userResources, userSites } from '@server/db/schema';
import { and, eq } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { resources, userResources, userSites } from "@server/db/schema";
import { and, eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const removeUserSiteParamsSchema = z.object({
userId: z.string(),
@ -17,14 +18,18 @@ const removeUserSiteSchema = z.object({
siteId: z.number().int().positive(),
});
export async function removeUserSite(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function removeUserSite(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = removeUserSiteParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
fromError(parsedParams.error).toString()
)
);
}
@ -36,7 +41,7 @@ export async function removeUserSite(req: Request, res: Response, next: NextFunc
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
fromError(parsedBody.error).toString()
)
);
}
@ -44,13 +49,24 @@ export async function removeUserSite(req: Request, res: Response, next: NextFunc
const { siteId } = parsedBody.data;
// Check if the user has permission to remove user sites
const hasPermission = await checkUserActionPermission(ActionsEnum.removeUserSite, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.removeUserSite,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const deletedUserSite = await db.delete(userSites)
.where(and(eq(userSites.userId, userId), eq(userSites.siteId, siteId)))
const deletedUserSite = await db
.delete(userSites)
.where(
and(eq(userSites.userId, userId), eq(userSites.siteId, siteId))
)
.returning();
if (deletedUserSite.length === 0) {
@ -62,13 +78,20 @@ export async function removeUserSite(req: Request, res: Response, next: NextFunc
);
}
const siteResources = await db.select()
const siteResources = await db
.select()
.from(resources)
.where(eq(resources.siteId, siteId));
for (const resource of siteResources) {
await db.delete(userResources)
.where(and(eq(userResources.userId, userId), eq(userResources.resourceId, resource.resourceId)))
await db
.delete(userResources)
.where(
and(
eq(userResources.userId, userId),
eq(userResources.resourceId, resource.resourceId)
)
)
.returning();
}
@ -81,6 +104,11 @@ export async function removeUserSite(req: Request, res: Response, next: NextFunc
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -1,28 +1,33 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { db } from '@server/db';
import { userOrgs, roles } from '@server/db/schema';
import { eq, and } from 'drizzle-orm';
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { userOrgs, roles } from "@server/db/schema";
import { eq, and } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
const addUserRoleSchema = z.object({
userId: z.string(),
roleId: z.number().int().positive(),
orgId: z.string()
orgId: z.string(),
});
export async function addUserRole(req: Request, res: Response, next: NextFunction): Promise<any> {
export async function addUserRole(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedBody = addUserRoleSchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
fromError(parsedBody.error).toString()
)
);
}
@ -30,22 +35,37 @@ export async function addUserRole(req: Request, res: Response, next: NextFunctio
const { userId, roleId, orgId } = parsedBody.data;
// Check if the user has permission to add user roles
const hasPermission = await checkUserActionPermission(ActionsEnum.addUserRole, req);
const hasPermission = await checkUserActionPermission(
ActionsEnum.addUserRole,
req
);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action'));
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
// Check if the role exists and belongs to the specified org
const roleExists = await db.select()
const roleExists = await db
.select()
.from(roles)
.where(and(eq(roles.roleId, roleId), eq(roles.orgId, orgId)))
.limit(1);
if (roleExists.length === 0) {
return next(createHttpError(HttpCode.NOT_FOUND, 'Role not found or does not belong to the specified organization'));
return next(
createHttpError(
HttpCode.NOT_FOUND,
"Role not found or does not belong to the specified organization"
)
);
}
const newUserRole = await db.update(userOrgs)
const newUserRole = await db
.update(userOrgs)
.set({ roleId })
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, orgId)))
.returning();
@ -59,6 +79,11 @@ export async function addUserRole(req: Request, res: Response, next: NextFunctio
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
);
}
}

View file

@ -20,7 +20,7 @@ import {
SelectTrigger,
SelectValue,
} from "@app/components/ui/select";
import { useToast } from "@app/hooks/use-toast";
import { useToast } from "@app/hooks/useToast";
import { ListOrgsResponse } from "@server/routers/org";
import Link from "next/link";
import { useRouter } from "next/navigation";

View file

@ -8,6 +8,9 @@ import { internal } from "@app/api";
import { AxiosResponse } from "axios";
import { GetOrgResponse, ListOrgsResponse } from "@server/routers/org";
import { authCookieHeader } from "@app/api/cookies";
import { cache } from "react";
export const dynamic = "force-dynamic";
export const metadata: Metadata = {
title: `Settings - Pangolin`,
@ -47,7 +50,8 @@ export default async function SettingsLayout(props: SettingsLayoutProps) {
const { children } = props;
const user = await verifySession();
const getUser = cache(verifySession);
const user = await getUser();
if (!user) {
redirect("/auth/login");
@ -56,20 +60,23 @@ export default async function SettingsLayout(props: SettingsLayoutProps) {
const cookie = await authCookieHeader();
try {
await internal.get<AxiosResponse<GetOrgResponse>>(
const getOrg = cache(() =>
internal.get<AxiosResponse<GetOrgResponse>>(
`/org/${params.orgId}`,
cookie
)
);
const org = await getOrg();
} catch {
redirect(`/`);
}
let orgs: ListOrgsResponse["orgs"] = [];
try {
const res = await internal.get<AxiosResponse<ListOrgsResponse>>(
`/orgs`,
cookie
const getOrgs = cache(() =>
internal.get<AxiosResponse<ListOrgsResponse>>(`/orgs`, cookie)
);
const res = await getOrgs();
if (res && res.data.data.orgs) {
orgs = res.data.data.orgs;
}

View file

@ -8,7 +8,7 @@ import {
import { useForm } from "react-hook-form";
import { z } from "zod";
import { cn } from "@/lib/utils";
import { toast } from "@/hooks/use-toast";
import { toast } from "@/hooks/useToast";
import { Button} from "@/components/ui/button";
import {
Form,

View file

@ -36,6 +36,8 @@ import { AxiosResponse } from "axios";
import api from "@app/api";
import { useParams } from "next/navigation";
import { useForm } from "react-hook-form";
import { GetResourceResponse } from "@server/routers/resource";
import { useToast } from "@app/hooks/useToast";
const GeneralFormSchema = z.object({
name: z.string(),
@ -49,6 +51,7 @@ export function GeneralForm() {
const orgId = params.orgId;
const { resource, updateResource } = useResourceContext();
const [sites, setSites] = useState<ListSitesResponse["sites"]>([]);
const { toast } = useToast();
const form = useForm<GeneralFormValues>({
resolver: zodResolver(GeneralFormSchema),
@ -72,7 +75,24 @@ export function GeneralForm() {
}, []);
async function onSubmit(data: GeneralFormValues) {
await updateResource({ name: data.name, siteId: data.siteId });
updateResource({ name: data.name, siteId: data.siteId });
await api
.post<AxiosResponse<GetResourceResponse>>(
`resource/${resource?.resourceId}`,
{
name: data.name,
siteId: data.siteId,
}
)
.catch((e) => {
toast({
variant: "destructive",
title: "Failed to update resource",
description:
e.response?.data?.message ||
"An error occurred while updating the resource",
});
});
}
return (

View file

@ -5,7 +5,7 @@ import { ChevronDownIcon } from "@radix-ui/react-icons";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { cn } from "@/lib/utils";
import { toast } from "@/hooks/use-toast";
import { toast } from "@/hooks/useToast";
import { Button, buttonVariants } from "@/components/ui/button";
import {
Form,
@ -78,7 +78,7 @@ export function CreateSiteForm() {
setKeypair(generatedKeypair);
setIsLoading(false);
api.get(`/org/${orgId}/pickSiteDefaults`)
api.get(`/org/${orgId}/pick-site-defaults`)
.catch((e) => {
toast({
title: "Error creating site...",

View file

@ -15,6 +15,8 @@ import {
import { Input } from "@/components/ui/input";
import { useSiteContext } from "@app/hooks/useSiteContext";
import { useForm } from "react-hook-form";
import api from "@app/api";
import { useToast } from "@app/hooks/useToast";
const GeneralFormSchema = z.object({
name: z.string(),
@ -24,6 +26,7 @@ type GeneralFormValues = z.infer<typeof GeneralFormSchema>;
export function GeneralForm() {
const { site, updateSite } = useSiteContext();
const { toast } = useToast();
const form = useForm<GeneralFormValues>({
resolver: zodResolver(GeneralFormSchema),
@ -34,7 +37,21 @@ export function GeneralForm() {
});
async function onSubmit(data: GeneralFormValues) {
await updateSite({ name: data.name });
updateSite({ name: data.name });
await api
.post(`/site/${site?.siteId}`, {
name: data.name,
})
.catch((e) => {
toast({
variant: "destructive",
title: "Failed to update site",
description:
e.message ||
"An error occurred while updating the site.",
});
});
}
return (

View file

@ -92,7 +92,7 @@ export const columns: ColumnDef<SiteRow>[] = [
<DropdownMenuContent align="end">
<DropdownMenuItem>
<Link
href={`/${siteRow.orgId}/settings/sites/${siteRow.id}`}
href={`/${siteRow.orgId}/settings/sites/${siteRow.nice}`}
>
View settings
</Link>

View file

@ -18,15 +18,30 @@ import {
SelectTrigger,
SelectValue,
} from "@app/components/ui/select";
import { useToast } from "@app/hooks/use-toast";
import { useToast } from "@app/hooks/useToast";
import { zodResolver } from "@hookform/resolvers/zod";
import { InviteUserBody, InviteUserResponse } from "@server/routers/user";
import { AxiosResponse } from "axios";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { useParams } from "next/navigation";
import CopyTextBox from "@app/components/CopyTextBox";
import {
Credenza,
CredenzaBody,
CredenzaClose,
CredenzaContent,
CredenzaDescription,
CredenzaFooter,
CredenzaHeader,
CredenzaTitle,
} from "@app/components/Credenza";
import { useOrgContext } from "@app/hooks/useOrgContext";
type InviteUserFormProps = {
open: boolean;
setOpen: (open: boolean) => void;
};
const formSchema = z.object({
email: z.string().email({ message: "Invalid email address" }),
@ -34,9 +49,9 @@ const formSchema = z.object({
roleId: z.string(),
});
export default function InviteUserForm() {
export default function InviteUserForm({ open, setOpen }: InviteUserFormProps) {
const { toast } = useToast();
const { orgId } = useParams();
const { org } = useOrgContext();
const [inviteLink, setInviteLink] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
@ -74,7 +89,7 @@ export default function InviteUserForm() {
const res = await api
.post<AxiosResponse<InviteUserResponse>>(
`/org/${orgId}/create-invite`,
`/org/${org?.org.orgId}/create-invite`,
{
email: values.email,
roleId: parseInt(values.roleId),
@ -107,11 +122,21 @@ export default function InviteUserForm() {
return (
<>
<Credenza open={open} onOpenChange={setOpen}>
<CredenzaContent>
<CredenzaHeader>
<CredenzaTitle>Invite User</CredenzaTitle>
<CredenzaDescription>
Give new users access to your organization
</CredenzaDescription>
</CredenzaHeader>
<CredenzaBody>
{!inviteLink && (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-4"
id="invite-user-form"
>
<FormField
control={form.control}
@ -136,7 +161,9 @@ export default function InviteUserForm() {
<FormItem>
<FormLabel>Role</FormLabel>
<Select
onValueChange={field.onChange}
onValueChange={
field.onChange
}
defaultValue={field.value.toString()}
>
<FormControl>
@ -147,7 +174,9 @@ export default function InviteUserForm() {
<SelectContent>
{roles.map((role) => (
<SelectItem
key={role.roleId}
key={
role.roleId
}
value={role.roleId.toString()}
>
{role.name}
@ -166,7 +195,9 @@ export default function InviteUserForm() {
<FormItem>
<FormLabel>Valid For</FormLabel>
<Select
onValueChange={field.onChange}
onValueChange={
field.onChange
}
defaultValue={field.value.toString()}
>
<FormControl>
@ -175,28 +206,26 @@ export default function InviteUserForm() {
</SelectTrigger>
</FormControl>
<SelectContent>
{validFor.map((option) => (
{validFor.map(
(option) => (
<SelectItem
key={option.hours}
key={
option.hours
}
value={option.hours.toString()}
>
{option.name}
{
option.name
}
</SelectItem>
))}
)
)}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<Button
type="submit"
className="w-full"
loading={loading}
disabled={inviteLink !== null}
>
Invite User
</Button>
</form>
</Form>
)}
@ -204,8 +233,9 @@ export default function InviteUserForm() {
{inviteLink && (
<div className="max-w-md">
<p className="mb-4">
The user has been successfully invited. They must access
the link below to accept the invitation.
The user has been successfully invited. They
must access the link below to accept the
invitation.
</p>
<p className="mb-4">
The invite will expire in{" "}
@ -215,9 +245,28 @@ export default function InviteUserForm() {
</b>
.
</p>
<CopyTextBox text={inviteLink} wrapText={false} />
<CopyTextBox
text={inviteLink}
wrapText={false}
/>
</div>
)}
</CredenzaBody>
<CredenzaFooter>
<Button
type="submit"
form="invite-user-form"
loading={loading}
disabled={inviteLink !== null}
>
Create Invitation
</Button>
<CredenzaClose asChild>
<Button variant="outline">Close</Button>
</CredenzaClose>
</CredenzaFooter>
</CredenzaContent>
</Credenza>
</>
);
}

View file

@ -10,16 +10,8 @@ import {
import { Button } from "@app/components/ui/button";
import { ArrowUpDown, MoreHorizontal } from "lucide-react";
import { UsersDataTable } from "./UsersDataTable";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@app/components/ui/dialog";
import { useState } from "react";
import InviteUserForm from "./InviteUserForm";
import { Credenza, CredenzaTitle, CredenzaDescription, CredenzaHeader, CredenzaClose, CredenzaFooter, CredenzaContent, CredenzaBody } from "@app/components/Credenza";
export type UserRow = {
id: string;
@ -74,19 +66,10 @@ export default function UsersTable({ users }: UsersTableProps) {
return (
<>
<Credenza open={isInviteModalOpen} onOpenChange={setIsInviteModalOpen}>
<CredenzaContent>
<CredenzaHeader>
<CredenzaTitle>Invite User</CredenzaTitle>
<CredenzaDescription>
Give new users access to your organization
</CredenzaDescription>
</CredenzaHeader>
<CredenzaBody>
<InviteUserForm />
</CredenzaBody>
</CredenzaContent>
</Credenza>
<InviteUserForm
open={isInviteModalOpen}
setOpen={setIsInviteModalOpen}
/>
<UsersDataTable
columns={columns}

View file

@ -3,6 +3,9 @@ import { authCookieHeader } from "@app/api/cookies";
import { ListUsersResponse } from "@server/routers/user";
import { AxiosResponse } from "axios";
import UsersTable, { UserRow } from "./components/UsersTable";
import { GetOrgResponse } from "@server/routers/org";
import { cache } from "react";
import OrgProvider from "@app/providers/OrgProvider";
type UsersPageProps = {
params: Promise<{ orgId: string }>;
@ -10,15 +13,36 @@ type UsersPageProps = {
export default async function UsersPage(props: UsersPageProps) {
const params = await props.params;
let users: ListUsersResponse["users"] = [];
try {
const res = await internal.get<AxiosResponse<ListUsersResponse>>(
const res = await internal
.get<AxiosResponse<ListUsersResponse>>(
`/org/${params.orgId}/users`,
await authCookieHeader()
);
)
.catch((e) => {
console.error(e);
});
if (res && res.status === 200) {
users = res.data.data.users;
} catch (e) {
console.error("Error fetching users", e);
}
let org: GetOrgResponse | null = null;
const getOrg = cache(async () =>
internal
.get<AxiosResponse<GetOrgResponse>>(
`/org/${params.orgId}`,
await authCookieHeader()
)
.catch((e) => {
console.error(e);
})
);
const orgRes = await getOrg();
if (orgRes && orgRes.status === 200) {
org = orgRes.data.data;
}
const userRows: UserRow[] = users.map((user) => {
@ -40,7 +64,9 @@ export default async function UsersPage(props: UsersPageProps) {
</p>
</div>
<OrgProvider org={org}>
<UsersTable users={userRows} />
</OrgProvider>
</>
);
}

View file

@ -2,12 +2,16 @@ import LoginForm from "@app/app/auth/login/LoginForm";
import { verifySession } from "@app/lib/auth/verifySession";
import Link from "next/link";
import { redirect } from "next/navigation";
import { cache } from "react";
export const dynamic = 'force-dynamic';
export default async function Page(props: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}) {
const searchParams = await props.searchParams;
const user = await verifySession();
const getUser = cache(verifySession);
const user = await getUser();
if (user) {
redirect("/");

View file

@ -2,12 +2,16 @@ import SignupForm from "@app/app/auth/signup/SignupForm";
import { verifySession } from "@app/lib/auth/verifySession";
import Link from "next/link";
import { redirect } from "next/navigation";
import { cache } from "react";
export const dynamic = 'force-dynamic';
export default async function Page(props: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}) {
const searchParams = await props.searchParams;
const user = await verifySession();
const getUser = cache(verifySession);
const user = await getUser();
if (user) {
redirect("/");

View file

@ -32,7 +32,7 @@ import { AxiosResponse } from "axios";
import { VerifyEmailResponse } from "@server/routers/auth";
import { Loader2 } from "lucide-react";
import { Alert, AlertDescription } from "../../../components/ui/alert";
import { useToast } from "@app/hooks/use-toast";
import { useToast } from "@app/hooks/useToast";
import { useRouter } from "next/navigation";
const FormSchema = z.object({

View file

@ -1,6 +1,9 @@
import VerifyEmailForm from "@app/app/auth/verify-email/VerifyEmailForm";
import { verifySession } from "@app/lib/auth/verifySession";
import { redirect } from "next/navigation";
import { cache } from "react";
export const dynamic = "force-dynamic";
export default async function Page(props: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
@ -10,7 +13,8 @@ export default async function Page(props: {
}
const searchParams = await props.searchParams;
const user = await verifySession();
const getUser = cache(verifySession);
const user = await getUser();
if (!user) {
redirect("/");

View file

@ -7,12 +7,16 @@ import { AxiosResponse } from "axios";
import { ArrowUpRight } from "lucide-react";
import Link from "next/link";
import { redirect } from "next/navigation";
import { cache } from "react";
export const dynamic = "force-dynamic";
export default async function Page(props: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}) {
const params = await props.searchParams; // this is needed to prevent static optimization
const user = await verifySession();
const getUser = cache(verifySession);
const user = await getUser();
if (!user) {
redirect("/auth/login");

View file

@ -6,7 +6,7 @@ import { useForm } from "react-hook-form"
import { z } from "zod"
import { cn } from "@/lib/utils"
import { toast } from "@/hooks/use-toast"
import { toast } from "@/hooks/useToast"
import { Button } from "@/components/ui/button"
import {
Command,

View file

@ -6,7 +6,7 @@ import { useForm } from "react-hook-form"
import { z } from "zod"
import { cn } from "@/lib/utils"
import { toast } from "@/hooks/use-toast"
import { toast } from "@/hooks/useToast"
import { Button, buttonVariants } from "@/components/ui/button"
import {
Form,

View file

@ -4,7 +4,7 @@ import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { z } from "zod"
import { toast } from "@/hooks/use-toast"
import { toast } from "@/hooks/useToast"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import {

View file

@ -5,7 +5,7 @@ import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { z } from "zod"
import { toast } from "@/hooks/use-toast"
import { toast } from "@/hooks/useToast"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import {

View file

@ -6,7 +6,7 @@ import { useFieldArray, useForm } from "react-hook-form"
import { z } from "zod"
import { cn } from "@/lib/utils"
import { toast } from "@/hooks/use-toast"
import { toast } from "@/hooks/useToast"
import { Button } from "@/components/ui/button"
import {

View file

@ -5,7 +5,7 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import Link from "next/link";
import api from "@app/api";
import { toast } from "@app/hooks/use-toast";
import { toast } from "@app/hooks/useToast";
import { useCallback, useEffect, useState } from "react";
import {
Card,

View file

@ -6,7 +6,7 @@ import { useForm } from "react-hook-form"
import { z } from "zod"
import { cn } from "@/lib/utils"
import { toast } from "@/hooks/use-toast"
import { toast } from "@/hooks/useToast"
import { Button } from "@/components/ui/button"
import {
Command,

View file

@ -1,13 +1,13 @@
"use client"
"use client";
import { zodResolver } from "@hookform/resolvers/zod"
import { ChevronDownIcon } from "@radix-ui/react-icons"
import { useForm } from "react-hook-form"
import { z } from "zod"
import { zodResolver } from "@hookform/resolvers/zod";
import { ChevronDownIcon } from "@radix-ui/react-icons";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { cn } from "@/lib/utils"
import { toast } from "@/hooks/use-toast"
import { Button, buttonVariants } from "@/components/ui/button"
import { cn } from "@/lib/utils";
import { toast } from "@/hooks/useToast";
import { Button, buttonVariants } from "@/components/ui/button";
import {
Form,
FormControl,
@ -16,9 +16,9 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import { useSiteContext } from "@app/hooks/useSiteContext"
} from "@/components/ui/form";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { useSiteContext } from "@app/hooks/useSiteContext";
const appearanceFormSchema = z.object({
theme: z.enum(["light", "dark"], {
@ -28,14 +28,14 @@ const appearanceFormSchema = z.object({
invalid_type_error: "Select a font",
required_error: "Please select a font.",
}),
})
});
type AppearanceFormValues = z.infer<typeof appearanceFormSchema>
type AppearanceFormValues = z.infer<typeof appearanceFormSchema>;
// This can come from your database or API.
const defaultValues: Partial<AppearanceFormValues> = {
theme: "light",
}
};
export function AppearanceForm() {
const site = useSiteContext();
@ -45,17 +45,19 @@ export function AppearanceForm() {
const form = useForm<AppearanceFormValues>({
resolver: zodResolver(appearanceFormSchema),
defaultValues,
})
});
function onSubmit(data: AppearanceFormValues) {
toast({
title: "You submitted the following values:",
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
<code className="text-white">
{JSON.stringify(data, null, 2)}
</code>
</pre>
),
})
});
}
return (
@ -71,7 +73,9 @@ export function AppearanceForm() {
<FormControl>
<select
className={cn(
buttonVariants({ variant: "outline" }),
buttonVariants({
variant: "outline",
}),
"w-[200px] appearance-none font-normal"
)}
{...field}
@ -108,7 +112,10 @@ export function AppearanceForm() {
<FormItem>
<FormLabel className="[&:has([data-state=checked])>div]:border-primary">
<FormControl>
<RadioGroupItem value="light" className="sr-only" />
<RadioGroupItem
value="light"
className="sr-only"
/>
</FormControl>
<div className="items-center rounded-md border-2 border-muted p-1 hover:border-accent">
<div className="space-y-2 rounded-sm bg-[#ecedef] p-2">
@ -134,7 +141,10 @@ export function AppearanceForm() {
<FormItem>
<FormLabel className="[&:has([data-state=checked])>div]:border-primary">
<FormControl>
<RadioGroupItem value="dark" className="sr-only" />
<RadioGroupItem
value="dark"
className="sr-only"
/>
</FormControl>
<div className="items-center rounded-md border-2 border-muted bg-popover p-1 hover:bg-accent hover:text-accent-foreground">
<div className="space-y-2 rounded-sm bg-slate-950 p-2">
@ -165,5 +175,5 @@ export function AppearanceForm() {
<Button type="submit">Update preferences</Button>
</form>
</Form>
)
);
}

View file

@ -4,7 +4,7 @@ import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { z } from "zod"
import { toast } from "@/hooks/use-toast"
import { toast } from "@/hooks/useToast"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import {

View file

@ -5,7 +5,7 @@ import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { z } from "zod"
import { toast } from "@/hooks/use-toast"
import { toast } from "@/hooks/useToast"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import {

View file

@ -6,7 +6,7 @@ import { useFieldArray, useForm } from "react-hook-form"
import { z } from "zod"
import { cn } from "@/lib/utils"
import { toast } from "@/hooks/use-toast"
import { toast } from "@/hooks/useToast"
import { Button } from "@/components/ui/button"
import {

View file

@ -1,6 +1,6 @@
"use client"
import { useToast } from "@/hooks/use-toast"
import { useToast } from "@/hooks/useToast"
import {
Toast,
ToastClose,

View file

@ -0,0 +1,11 @@
import { GetOrgResponse } from "@server/routers/org";
import { createContext } from "react";
interface OrgContextType {
org: GetOrgResponse | null;
updateOrg: (updateOrg: Partial<GetOrgResponse>) => void;
}
const OrgContext = createContext<OrgContextType | undefined>(undefined);
export default OrgContext;

View file

@ -3,9 +3,11 @@ import { createContext } from "react";
interface ResourceContextType {
resource: GetResourceResponse | null;
updateResource: (updatedResource: Partial<GetResourceResponse>) => Promise<void>;
updateResource: (updatedResource: Partial<GetResourceResponse>) => void;
}
const ResourceContext = createContext<ResourceContextType | undefined>(undefined);
const ResourceContext = createContext<ResourceContextType | undefined>(
undefined
);
export default ResourceContext;

View file

@ -3,7 +3,7 @@ import { createContext } from "react";
interface SiteContextType {
site: GetSiteResponse | null;
updateSite: (updatedSite: Partial<GetSiteResponse>) => Promise<void>;
updateSite: (updatedSite: Partial<GetSiteResponse>) => void;
}
const SiteContext = createContext<SiteContextType | undefined>(undefined);

View file

@ -0,0 +1,10 @@
import OrgContext from "@app/contexts/orgContext";
import { useContext } from "react";
export function useOrgContext() {
const context = useContext(OrgContext);
if (context === undefined) {
throw new Error("useOrgContext must be used within a OrgProvider");
}
return context;
}

View file

@ -4,7 +4,9 @@ import { useContext } from "react";
export function useResourceContext() {
const context = useContext(ResourceContext);
if (context === undefined) {
throw new Error('useResourceContext must be used within a ResourceProvider');
throw new Error(
"useResourceContext must be used within a ResourceProvider"
);
}
return context;
}

View file

@ -0,0 +1,39 @@
"use client";
import OrgContext from "@app/contexts/orgContext";
import { GetOrgResponse } from "@server/routers/org";
import { useState } from "react";
interface OrgProviderProps {
children: React.ReactNode;
org: GetOrgResponse | null;
}
export function OrgProvider({ children, org: serverOrg }: OrgProviderProps) {
const [org, setOrg] = useState<GetOrgResponse | null>(serverOrg);
const updateOrg = (updatedOrg: Partial<GetOrgResponse>) => {
if (!org) {
throw new Error("No org to update");
}
setOrg((prev) => {
if (!prev) {
return prev;
}
return {
...prev,
...updatedOrg,
};
});
};
return (
<OrgContext.Provider value={{ org, updateOrg }}>
{children}
</OrgContext.Provider>
);
}
export default OrgProvider;

View file

@ -1,10 +1,7 @@
"use client";
import api from "@app/api";
import ResourceContext from "@app/contexts/resourceContext";
import { toast } from "@app/hooks/use-toast";
import { GetResourceResponse } from "@server/routers/resource/getResource";
import { AxiosResponse } from "axios";
import { useState } from "react";
interface ResourceProviderProps {
@ -12,34 +9,36 @@ interface ResourceProviderProps {
resource: GetResourceResponse | null;
}
export function ResourceProvider({ children, resource: serverResource }: ResourceProviderProps) {
const [resource, setResource] = useState<GetResourceResponse | null>(serverResource);
export function ResourceProvider({
children,
resource: serverResource,
}: ResourceProviderProps) {
const [resource, setResource] = useState<GetResourceResponse | null>(
serverResource
);
const updateResource = async (updatedResource: Partial<GetResourceResponse>) => {
try {
const updateResource = (updatedResource: Partial<GetResourceResponse>) => {
if (!resource) {
throw new Error("No resource to update");
}
const res = await api.post<AxiosResponse<GetResourceResponse>>(
`resource/${resource.resourceId}`,
updatedResource,
);
setResource(res.data.data);
toast({
title: "Resource updated!",
});
} catch (error) {
console.error(error);
toast({
variant: "destructive",
title: "Error updating resource...",
})
setResource((prev) => {
if (!prev) {
return prev;
}
return {
...prev,
...updatedResource,
};
});
};
return <ResourceContext.Provider value={{ resource, updateResource }}>{children}</ResourceContext.Provider>;
return (
<ResourceContext.Provider value={{ resource, updateResource }}>
{children}
</ResourceContext.Provider>
);
}
export default ResourceProvider;

View file

@ -1,10 +1,7 @@
"use client";
import api from "@app/api";
import SiteContext from "@app/contexts/siteContext";
import { toast } from "@app/hooks/use-toast";
import { GetSiteResponse } from "@server/routers/site/getSite";
import { AxiosResponse } from "axios";
import { useState } from "react";
interface SiteProviderProps {
@ -12,34 +9,32 @@ interface SiteProviderProps {
site: GetSiteResponse | null;
}
export function SiteProvider({ children, site: serverSite }: SiteProviderProps) {
export function SiteProvider({
children,
site: serverSite,
}: SiteProviderProps) {
const [site, setSite] = useState<GetSiteResponse | null>(serverSite);
const updateSite = async (updatedSite: Partial<GetSiteResponse>) => {
try {
const updateSite = (updatedSite: Partial<GetSiteResponse>) => {
if (!site) {
throw new Error("No site to update");
}
const res = await api.post<AxiosResponse<GetSiteResponse>>(
`site/${site.siteId}`,
updatedSite,
);
setSite(res.data.data);
toast({
title: "Site updated!",
});
} catch (error) {
console.error(error);
toast({
variant: "destructive",
title: "Error updating site...",
})
setSite((prev) => {
if (!prev) {
return prev;
}
return {
...prev,
...updatedSite,
};
});
};
return <SiteContext.Provider value={{ site, updateSite }}>{children}</SiteContext.Provider>;
return (
<SiteContext.Provider value={{ site, updateSite }}>
{children}
</SiteContext.Provider>
);
}
export default SiteProvider;