mirror of
https://github.com/fosrl/pangolin.git
synced 2025-07-07 20:45:00 +02:00
refactor contexts, format zod errors, and more refactoring
This commit is contained in:
parent
2635443105
commit
2852d62258
83 changed files with 2150 additions and 1264 deletions
|
@ -120,7 +120,8 @@ if (!parsedConfig.success) {
|
||||||
throw new Error(`Invalid configuration file: ${errors}`);
|
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
|
process.env.FLAGS_EMAIL_VERIFICATION_REQUIRED = parsedConfig.data.flags
|
||||||
?.require_email_verification
|
?.require_email_verification
|
||||||
? "true"
|
? "true"
|
||||||
|
|
|
@ -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/site/:niceId", verifyOrgAccess, site.getSite);
|
||||||
|
|
||||||
authenticated.get(
|
authenticated.get(
|
||||||
"/org/:orgId/pickSiteDefaults",
|
"/org/:orgId/pick-site-defaults",
|
||||||
verifyOrgAccess,
|
verifyOrgAccess,
|
||||||
site.pickSiteDefaults
|
site.pickSiteDefaults
|
||||||
);
|
);
|
||||||
|
@ -118,12 +118,12 @@ authenticated.delete(
|
||||||
target.deleteTarget
|
target.deleteTarget
|
||||||
);
|
);
|
||||||
|
|
||||||
authenticated.put(
|
// authenticated.put(
|
||||||
"/org/:orgId/role",
|
// "/org/:orgId/role",
|
||||||
verifyOrgAccess,
|
// verifyOrgAccess,
|
||||||
verifySuperuser,
|
// verifySuperuser,
|
||||||
role.createRole
|
// role.createRole
|
||||||
);
|
// );
|
||||||
authenticated.get("/org/:orgId/roles", verifyOrgAccess, role.listRoles);
|
authenticated.get("/org/:orgId/roles", verifyOrgAccess, role.listRoles);
|
||||||
authenticated.get(
|
authenticated.get(
|
||||||
"/role/:roleId",
|
"/role/:roleId",
|
||||||
|
@ -131,18 +131,18 @@ authenticated.get(
|
||||||
verifyUserInRole,
|
verifyUserInRole,
|
||||||
role.getRole
|
role.getRole
|
||||||
);
|
);
|
||||||
authenticated.post(
|
// authenticated.post(
|
||||||
"/role/:roleId",
|
// "/role/:roleId",
|
||||||
verifyRoleAccess,
|
// verifyRoleAccess,
|
||||||
verifySuperuser,
|
// verifySuperuser,
|
||||||
role.updateRole
|
// role.updateRole
|
||||||
);
|
// );
|
||||||
authenticated.delete(
|
// authenticated.delete(
|
||||||
"/role/:roleId",
|
// "/role/:roleId",
|
||||||
verifyRoleAccess,
|
// verifyRoleAccess,
|
||||||
verifySuperuser,
|
// verifySuperuser,
|
||||||
role.deleteRole
|
// role.deleteRole
|
||||||
);
|
// );
|
||||||
|
|
||||||
authenticated.put(
|
authenticated.put(
|
||||||
"/role/:roleId/site",
|
"/role/:roleId/site",
|
||||||
|
@ -210,12 +210,6 @@ authenticated.delete(
|
||||||
verifyUserAccess,
|
verifyUserAccess,
|
||||||
user.removeUserOrg
|
user.removeUserOrg
|
||||||
);
|
);
|
||||||
authenticated.put(
|
|
||||||
"/org/:orgId/user/:userId",
|
|
||||||
verifyOrgAccess,
|
|
||||||
verifyUserAccess,
|
|
||||||
user.addUserOrg
|
|
||||||
);
|
|
||||||
|
|
||||||
authenticated.put(
|
authenticated.put(
|
||||||
"/user/:userId/site",
|
"/user/:userId/site",
|
||||||
|
|
|
@ -10,6 +10,7 @@ import logger from '@server/logger';
|
||||||
import config from "@server/config";
|
import config from "@server/config";
|
||||||
import { getUniqueExitNodeEndpointName } from '@server/db/names';
|
import { getUniqueExitNodeEndpointName } from '@server/db/names';
|
||||||
import { findNextAvailableCidr } from "@server/utils/ip";
|
import { findNextAvailableCidr } from "@server/utils/ip";
|
||||||
|
import { fromError } from 'zod-validation-error';
|
||||||
// Define Zod schema for request validation
|
// Define Zod schema for request validation
|
||||||
const getConfigSchema = z.object({
|
const getConfigSchema = z.object({
|
||||||
publicKey: z.string(),
|
publicKey: z.string(),
|
||||||
|
@ -33,7 +34,7 @@ export async function getConfig(req: Request, res: Response, next: NextFunction)
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
HttpCode.BAD_REQUEST,
|
||||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
fromError(parsedParams.error).toString()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,38 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { orgs } from '@server/db/schema';
|
import { orgs } from "@server/db/schema";
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const getOrgSchema = z.object({
|
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 {
|
try {
|
||||||
const parsedQuery = getOrgSchema.safeParse(req.query);
|
const parsedQuery = getOrgSchema.safeParse(req.query);
|
||||||
if (!parsedQuery.success) {
|
if (!parsedQuery.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
HttpCode.BAD_REQUEST,
|
||||||
parsedQuery.error.errors.map(e => e.message).join(', ')
|
fromError(parsedQuery.error).toString()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { orgId } = parsedQuery.data;
|
const { orgId } = parsedQuery.data;
|
||||||
|
|
||||||
const org = await db.select()
|
const org = await db
|
||||||
|
.select()
|
||||||
.from(orgs)
|
.from(orgs)
|
||||||
.where(eq(orgs.orgId, orgId))
|
.where(eq(orgs.orgId, orgId))
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
@ -50,6 +56,11 @@ export async function checkId(req: Request, res: Response, next: NextFunction):
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||||
import logger from '@server/logger';
|
import logger from '@server/logger';
|
||||||
import { createSuperuserRole } from '@server/db/ensureActions';
|
import { createSuperuserRole } from '@server/db/ensureActions';
|
||||||
import config, { APP_PATH } from "@server/config";
|
import config, { APP_PATH } from "@server/config";
|
||||||
|
import { fromError } from 'zod-validation-error';
|
||||||
|
|
||||||
const createOrgSchema = z.object({
|
const createOrgSchema = z.object({
|
||||||
orgId: z.string(),
|
orgId: z.string(),
|
||||||
|
@ -26,7 +27,7 @@ export async function createOrg(req: Request, res: Response, next: NextFunction)
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
HttpCode.BAD_REQUEST,
|
||||||
parsedBody.error.errors.map(e => e.message).join(', ')
|
fromError(parsedBody.error).toString()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import HttpCode from '@server/types/HttpCode';
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from 'http-errors';
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||||
import logger from '@server/logger';
|
import logger from '@server/logger';
|
||||||
|
import { fromError } from 'zod-validation-error';
|
||||||
|
|
||||||
const deleteOrgSchema = z.object({
|
const deleteOrgSchema = z.object({
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
|
@ -20,7 +21,7 @@ export async function deleteOrg(req: Request, res: Response, next: NextFunction)
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
HttpCode.BAD_REQUEST,
|
||||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
fromError(parsedParams.error).toString()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import HttpCode from '@server/types/HttpCode';
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from 'http-errors';
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||||
import logger from '@server/logger';
|
import logger from '@server/logger';
|
||||||
|
import { fromError } from 'zod-validation-error';
|
||||||
|
|
||||||
const updateOrgParamsSchema = z.object({
|
const updateOrgParamsSchema = z.object({
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
|
@ -27,7 +28,7 @@ export async function updateOrg(req: Request, res: Response, next: NextFunction)
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
HttpCode.BAD_REQUEST,
|
||||||
parsedBody.error.errors.map(e => e.message).join(', ')
|
fromError(parsedBody.error).toString()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||||
import logger from '@server/logger';
|
import logger from '@server/logger';
|
||||||
import { eq, and } from 'drizzle-orm';
|
import { eq, and } from 'drizzle-orm';
|
||||||
import stoi from '@server/utils/stoi';
|
import stoi from '@server/utils/stoi';
|
||||||
|
import { fromError } from 'zod-validation-error';
|
||||||
|
|
||||||
const createResourceParamsSchema = z.object({
|
const createResourceParamsSchema = z.object({
|
||||||
siteId: z.string().optional().transform(stoi).pipe(z.number().int().positive().optional()),
|
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(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
HttpCode.BAD_REQUEST,
|
||||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
fromError(parsedParams.error).toString()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,25 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { resources } from '@server/db/schema';
|
import { resources } from "@server/db/schema";
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
// Define Zod schema for request parameters validation
|
// Define Zod schema for request parameters validation
|
||||||
const deleteResourceSchema = z.object({
|
const deleteResourceSchema = z.object({
|
||||||
resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
|
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 {
|
try {
|
||||||
// Validate request parameters
|
// Validate request parameters
|
||||||
const parsedParams = deleteResourceSchema.safeParse(req.params);
|
const parsedParams = deleteResourceSchema.safeParse(req.params);
|
||||||
|
@ -22,7 +27,7 @@ export async function deleteResource(req: Request, res: Response, next: NextFunc
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { resourceId } = parsedParams.data;
|
||||||
|
|
||||||
// Check if the user has permission to list sites
|
// 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) {
|
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
|
// Delete the resource from the database
|
||||||
const deletedResource = await db.delete(resources)
|
const deletedResource = await db
|
||||||
|
.delete(resources)
|
||||||
.where(eq(resources.resourceId, resourceId))
|
.where(eq(resources.resourceId, resourceId))
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
|
@ -58,6 +72,11 @@ export async function deleteResource(req: Request, res: Response, next: NextFunc
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { resources } from '@server/db/schema';
|
import { resources } from "@server/db/schema";
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
// Define Zod schema for request parameters validation
|
// Define Zod schema for request parameters validation
|
||||||
const getResourceSchema = z.object({
|
const getResourceSchema = z.object({
|
||||||
|
@ -19,9 +20,13 @@ export type GetResourceResponse = {
|
||||||
siteId: number;
|
siteId: number;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
name: 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 {
|
try {
|
||||||
// Validate request parameters
|
// Validate request parameters
|
||||||
const parsedParams = getResourceSchema.safeParse(req.params);
|
const parsedParams = getResourceSchema.safeParse(req.params);
|
||||||
|
@ -29,7 +34,7 @@ export async function getResource(req: Request, res: Response, next: NextFunctio
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { resourceId } = parsedParams.data;
|
||||||
|
|
||||||
// Check if the user has permission to list sites
|
// 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) {
|
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
|
// Fetch the resource from the database
|
||||||
const resource = await db.select()
|
const resource = await db
|
||||||
|
.select()
|
||||||
.from(resources)
|
.from(resources)
|
||||||
.where(eq(resources.resourceId, resourceId))
|
.where(eq(resources.resourceId, resourceId))
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
@ -62,7 +76,7 @@ export async function getResource(req: Request, res: Response, next: NextFunctio
|
||||||
resourceId: resource[0].resourceId,
|
resourceId: resource[0].resourceId,
|
||||||
siteId: resource[0].siteId,
|
siteId: resource[0].siteId,
|
||||||
orgId: resource[0].orgId,
|
orgId: resource[0].orgId,
|
||||||
name: resource[0].name
|
name: resource[0].name,
|
||||||
},
|
},
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
|
@ -71,6 +85,11 @@ export async function getResource(req: Request, res: Response, next: NextFunctio
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,31 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { roleResources, roles } from '@server/db/schema';
|
import { roleResources, roles } from "@server/db/schema";
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const listResourceRolesSchema = z.object({
|
const listResourceRolesSchema = z.object({
|
||||||
resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
|
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 {
|
try {
|
||||||
const parsedParams = listResourceRolesSchema.safeParse(req.params);
|
const parsedParams = listResourceRolesSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { resourceId } = parsedParams.data;
|
||||||
|
|
||||||
// Check if the user has permission to list resource roles
|
// 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) {
|
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
|
const resourceRolesList = await db
|
||||||
|
@ -53,6 +66,11 @@ export async function listResourceRoles(req: Request, res: Response, next: NextF
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { resources } from '@server/db/schema';
|
import { resources } from "@server/db/schema";
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
// Define Zod schema for request parameters validation
|
// Define Zod schema for request parameters validation
|
||||||
const updateResourceParamsSchema = z.object({
|
const updateResourceParamsSchema = z.object({
|
||||||
|
@ -15,14 +16,20 @@ const updateResourceParamsSchema = z.object({
|
||||||
});
|
});
|
||||||
|
|
||||||
// Define Zod schema for request body validation
|
// Define Zod schema for request body validation
|
||||||
const updateResourceBodySchema = z.object({
|
const updateResourceBodySchema = z
|
||||||
name: z.string().min(1).max(255).optional(),
|
.object({
|
||||||
subdomain: z.string().min(1).max(255).optional(),
|
name: z.string().min(1).max(255).optional(),
|
||||||
}).refine(data => Object.keys(data).length > 0, {
|
subdomain: z.string().min(1).max(255).optional(),
|
||||||
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 {
|
try {
|
||||||
// Validate request parameters
|
// Validate request parameters
|
||||||
const parsedParams = updateResourceParamsSchema.safeParse(req.params);
|
const parsedParams = updateResourceParamsSchema.safeParse(req.params);
|
||||||
|
@ -30,7 +37,7 @@ export async function updateResource(req: Request, res: Response, next: NextFunc
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const updateData = parsedBody.data;
|
||||||
|
|
||||||
// Check if the user has permission to list sites
|
// 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) {
|
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
|
// Update the resource in the database
|
||||||
const updatedResource = await db.update(resources)
|
const updatedResource = await db
|
||||||
|
.update(resources)
|
||||||
.set(updateData)
|
.set(updateData)
|
||||||
.where(eq(resources.resourceId, resourceId))
|
.where(eq(resources.resourceId, resourceId))
|
||||||
.returning();
|
.returning();
|
||||||
|
@ -79,6 +95,11 @@ export async function updateResource(req: Request, res: Response, next: NextFunc
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { roleActions, roles } from '@server/db/schema';
|
import { roleActions, roles } from "@server/db/schema";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from "drizzle-orm";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const addRoleActionParamSchema = z.object({
|
const addRoleActionParamSchema = z.object({
|
||||||
roleId: z.string().transform(Number).pipe(z.number().int().positive()),
|
roleId: z.string().transform(Number).pipe(z.number().int().positive()),
|
||||||
|
@ -17,14 +18,18 @@ const addRoleActionSchema = z.object({
|
||||||
actionId: z.string(),
|
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 {
|
try {
|
||||||
const parsedBody = addRoleActionSchema.safeParse(req.body);
|
const parsedBody = addRoleActionSchema.safeParse(req.body);
|
||||||
if (!parsedBody.success) {
|
if (!parsedBody.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { roleId } = parsedParams.data;
|
||||||
|
|
||||||
// Check if the user has permission to add role actions
|
// 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) {
|
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
|
// 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) {
|
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
|
||||||
roleId,
|
.insert(roleActions)
|
||||||
actionId,
|
.values({
|
||||||
orgId: role[0].orgId!,
|
roleId,
|
||||||
}).returning();
|
actionId,
|
||||||
|
orgId: role[0].orgId!,
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
|
||||||
return response(res, {
|
return response(res, {
|
||||||
data: newRoleAction[0],
|
data: newRoleAction[0],
|
||||||
|
@ -70,6 +95,11 @@ export async function addRoleAction(req: Request, res: Response, next: NextFunct
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { roleResources } from '@server/db/schema';
|
import { roleResources } from "@server/db/schema";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const addRoleResourceParamsSchema = z.object({
|
const addRoleResourceParamsSchema = z.object({
|
||||||
roleId: z.string().transform(Number).pipe(z.number().int().positive()),
|
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()),
|
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 {
|
try {
|
||||||
const parsedBody = addRoleResourceSchema.safeParse(req.body);
|
const parsedBody = addRoleResourceSchema.safeParse(req.body);
|
||||||
if (!parsedBody.success) {
|
if (!parsedBody.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { roleId } = parsedParams.data;
|
||||||
|
|
||||||
// Check if the user has permission to add role resources
|
// 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) {
|
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
|
||||||
roleId,
|
.insert(roleResources)
|
||||||
resourceId,
|
.values({
|
||||||
}).returning();
|
roleId,
|
||||||
|
resourceId,
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
|
||||||
return response(res, {
|
return response(res, {
|
||||||
data: newRoleResource[0],
|
data: newRoleResource[0],
|
||||||
|
@ -62,6 +78,11 @@ export async function addRoleResource(req: Request, res: Response, next: NextFun
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { resources, roleResources, roleSites } from '@server/db/schema';
|
import { resources, roleResources, roleSites } from "@server/db/schema";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from "drizzle-orm";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const addRoleSiteParamsSchema = z.object({
|
const addRoleSiteParamsSchema = z.object({
|
||||||
roleId: z.string().transform(Number).pipe(z.number().int().positive()),
|
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()),
|
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 {
|
try {
|
||||||
const parsedBody = addRoleSiteSchema.safeParse(req.body);
|
const parsedBody = addRoleSiteSchema.safeParse(req.body);
|
||||||
if (!parsedBody.success) {
|
if (!parsedBody.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { roleId } = parsedParams.data;
|
||||||
|
|
||||||
// Check if the user has permission to add role sites
|
// 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) {
|
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
|
||||||
roleId,
|
.insert(roleSites)
|
||||||
siteId,
|
.values({
|
||||||
}).returning();
|
roleId,
|
||||||
|
siteId,
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
|
||||||
const siteResources = await db.select()
|
const siteResources = await db
|
||||||
|
.select()
|
||||||
.from(resources)
|
.from(resources)
|
||||||
.where(eq(resources.siteId, siteId));
|
.where(eq(resources.siteId, siteId));
|
||||||
|
|
||||||
|
@ -74,6 +91,11 @@ export async function addRoleSite(req: Request, res: Response, next: NextFunctio
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { roles } from '@server/db/schema';
|
import { roles } from "@server/db/schema";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const createRoleParamsSchema = z.object({
|
const createRoleParamsSchema = z.object({
|
||||||
orgId: z.string()
|
orgId: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const createRoleSchema = z.object({
|
const createRoleSchema = z.object({
|
||||||
|
@ -17,14 +18,18 @@ const createRoleSchema = z.object({
|
||||||
description: z.string().optional(),
|
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 {
|
try {
|
||||||
const parsedBody = createRoleSchema.safeParse(req.body);
|
const parsedBody = createRoleSchema.safeParse(req.body);
|
||||||
if (!parsedBody.success) {
|
if (!parsedBody.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { orgId } = parsedParams.data;
|
||||||
|
|
||||||
// Check if the user has permission to create roles
|
// 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) {
|
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
|
||||||
...roleData,
|
.insert(roles)
|
||||||
orgId,
|
.values({
|
||||||
}).returning();
|
...roleData,
|
||||||
|
orgId,
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
|
||||||
return response(res, {
|
return response(res, {
|
||||||
data: newRole[0],
|
data: newRole[0],
|
||||||
|
@ -63,6 +79,11 @@ export async function createRole(req: Request, res: Response, next: NextFunction
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,31 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { roles } from '@server/db/schema';
|
import { roles } from "@server/db/schema";
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const deleteRoleSchema = z.object({
|
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 {
|
try {
|
||||||
const parsedParams = deleteRoleSchema.safeParse(req.params);
|
const parsedParams = deleteRoleSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
HttpCode.BAD_REQUEST,
|
||||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
fromError(parsedParams.error).toString()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -28,15 +33,24 @@ export async function deleteRole(req: Request, res: Response, next: NextFunction
|
||||||
const { roleId } = parsedParams.data;
|
const { roleId } = parsedParams.data;
|
||||||
|
|
||||||
// Check if the user has permission to delete roles
|
// 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) {
|
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
|
||||||
.from(roles)
|
.select()
|
||||||
.where(eq(roles.roleId, roleId))
|
.from(roles)
|
||||||
.limit(1);
|
.where(eq(roles.roleId, roleId))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
if (role.length === 0) {
|
if (role.length === 0) {
|
||||||
return next(
|
return next(
|
||||||
|
@ -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))
|
.where(eq(roles.roleId, roleId))
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
|
@ -78,6 +93,11 @@ export async function deleteRole(req: Request, res: Response, next: NextFunction
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,31 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { roles } from '@server/db/schema';
|
import { roles } from "@server/db/schema";
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const getRoleSchema = z.object({
|
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 {
|
try {
|
||||||
const parsedParams = getRoleSchema.safeParse(req.params);
|
const parsedParams = getRoleSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { roleId } = parsedParams.data;
|
||||||
|
|
||||||
// Check if the user has permission to get roles
|
// 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) {
|
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)
|
.from(roles)
|
||||||
.where(eq(roles.roleId, roleId))
|
.where(eq(roles.roleId, roleId))
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
@ -56,6 +70,11 @@ export async function getRole(req: Request, res: Response, next: NextFunction):
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,31 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { roleActions, actions } from '@server/db/schema';
|
import { roleActions, actions } from "@server/db/schema";
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const listRoleActionsSchema = z.object({
|
const listRoleActionsSchema = 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 listRoleActions(req: Request, res: Response, next: NextFunction): Promise<any> {
|
export async function listRoleActions(
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
): Promise<any> {
|
||||||
try {
|
try {
|
||||||
const parsedParams = listRoleActionsSchema.safeParse(req.params);
|
const parsedParams = listRoleActionsSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { roleId } = parsedParams.data;
|
||||||
|
|
||||||
// Check if the user has permission to list role actions
|
// 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) {
|
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
|
const roleActionsList = await db
|
||||||
|
@ -43,7 +56,7 @@ export async function listRoleActions(req: Request, res: Response, next: NextFun
|
||||||
.innerJoin(actions, eq(roleActions.actionId, actions.actionId))
|
.innerJoin(actions, eq(roleActions.actionId, actions.actionId))
|
||||||
.where(eq(roleActions.roleId, roleId));
|
.where(eq(roleActions.roleId, roleId));
|
||||||
|
|
||||||
// TODO: Do we need to filter out what the user can see?
|
// TODO: Do we need to filter out what the user can see?
|
||||||
|
|
||||||
return response(res, {
|
return response(res, {
|
||||||
data: roleActionsList,
|
data: roleActionsList,
|
||||||
|
@ -54,6 +67,11 @@ export async function listRoleActions(req: Request, res: Response, next: NextFun
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,31 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { roleResources, resources } from '@server/db/schema';
|
import { roleResources, resources } from "@server/db/schema";
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const listRoleResourcesSchema = z.object({
|
const listRoleResourcesSchema = 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 listRoleResources(req: Request, res: Response, next: NextFunction): Promise<any> {
|
export async function listRoleResources(
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
): Promise<any> {
|
||||||
try {
|
try {
|
||||||
const parsedParams = listRoleResourcesSchema.safeParse(req.params);
|
const parsedParams = listRoleResourcesSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { roleId } = parsedParams.data;
|
||||||
|
|
||||||
// Check if the user has permission to list role resources
|
// 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) {
|
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
|
const roleResourcesList = await db
|
||||||
|
@ -40,10 +53,13 @@ export async function listRoleResources(req: Request, res: Response, next: NextF
|
||||||
subdomain: resources.subdomain,
|
subdomain: resources.subdomain,
|
||||||
})
|
})
|
||||||
.from(roleResources)
|
.from(roleResources)
|
||||||
.innerJoin(resources, eq(roleResources.resourceId, resources.resourceId))
|
.innerJoin(
|
||||||
|
resources,
|
||||||
|
eq(roleResources.resourceId, resources.resourceId)
|
||||||
|
)
|
||||||
.where(eq(roleResources.roleId, roleId));
|
.where(eq(roleResources.roleId, roleId));
|
||||||
|
|
||||||
// TODO: Do we need to filter out what the user can see?
|
// TODO: Do we need to filter out what the user can see?
|
||||||
|
|
||||||
return response(res, {
|
return response(res, {
|
||||||
data: roleResourcesList,
|
data: roleResourcesList,
|
||||||
|
@ -54,6 +70,11 @@ export async function listRoleResources(req: Request, res: Response, next: NextF
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,31 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { roleSites, sites } from '@server/db/schema';
|
import { roleSites, sites } from "@server/db/schema";
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const listRoleSitesSchema = z.object({
|
const listRoleSitesSchema = 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 listRoleSites(req: Request, res: Response, next: NextFunction): Promise<any> {
|
export async function listRoleSites(
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
): Promise<any> {
|
||||||
try {
|
try {
|
||||||
const parsedParams = listRoleSitesSchema.safeParse(req.params);
|
const parsedParams = listRoleSitesSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { roleId } = parsedParams.data;
|
||||||
|
|
||||||
// Check if the user has permission to list role sites
|
// 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) {
|
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
|
const roleSitesList = await db
|
||||||
|
@ -42,7 +55,7 @@ export async function listRoleSites(req: Request, res: Response, next: NextFunct
|
||||||
.innerJoin(sites, eq(roleSites.siteId, sites.siteId))
|
.innerJoin(sites, eq(roleSites.siteId, sites.siteId))
|
||||||
.where(eq(roleSites.roleId, roleId));
|
.where(eq(roleSites.roleId, roleId));
|
||||||
|
|
||||||
// TODO: Do we need to filter out what the user can see?
|
// TODO: Do we need to filter out what the user can see?
|
||||||
|
|
||||||
return response(res, {
|
return response(res, {
|
||||||
data: roleSitesList,
|
data: roleSitesList,
|
||||||
|
@ -53,6 +66,11 @@ export async function listRoleSites(req: Request, res: Response, next: NextFunct
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,49 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { roles, orgs } from '@server/db/schema';
|
import { roles, orgs } from "@server/db/schema";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { sql, eq } from 'drizzle-orm';
|
import { sql, eq } from "drizzle-orm";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const listRolesParamsSchema = z.object({
|
const listRolesParamsSchema = z.object({
|
||||||
orgId: z.string()
|
orgId: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const listRolesSchema = z.object({
|
const listRolesSchema = z.object({
|
||||||
limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)),
|
limit: z
|
||||||
offset: z.string().optional().transform(Number).pipe(z.number().int().nonnegative().default(0)),
|
.string()
|
||||||
orgId: z.string().optional().transform(Number).pipe(z.number().int().positive()),
|
.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 {
|
try {
|
||||||
const parsedQuery = listRolesSchema.safeParse(req.query);
|
const parsedQuery = listRolesSchema.safeParse(req.query);
|
||||||
if (!parsedQuery.success) {
|
if (!parsedQuery.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { orgId } = parsedParams.data;
|
||||||
|
|
||||||
// Check if the user has permission to list roles
|
// 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) {
|
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
|
let baseQuery: any = db
|
||||||
|
@ -64,8 +89,10 @@ export async function listRoles(req: Request, res: Response, next: NextFunction)
|
||||||
.leftJoin(orgs, eq(roles.orgId, orgs.orgId))
|
.leftJoin(orgs, eq(roles.orgId, orgs.orgId))
|
||||||
.where(eq(roles.orgId, orgId));
|
.where(eq(roles.orgId, orgId));
|
||||||
|
|
||||||
let countQuery: any = db.select({ count: sql<number>`cast(count(*) as integer)` }).from(roles)
|
let countQuery: any = db
|
||||||
.where(eq(roles.orgId, orgId));
|
.select({ count: sql<number>`cast(count(*) as integer)` })
|
||||||
|
.from(roles)
|
||||||
|
.where(eq(roles.orgId, orgId));
|
||||||
|
|
||||||
const rolesList = await baseQuery.limit(limit).offset(offset);
|
const rolesList = await baseQuery.limit(limit).offset(offset);
|
||||||
const totalCountResult = await countQuery;
|
const totalCountResult = await countQuery;
|
||||||
|
@ -87,6 +114,11 @@ export async function listRoles(req: Request, res: Response, next: NextFunction)
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { roleActions } from '@server/db/schema';
|
import { roleActions } from "@server/db/schema";
|
||||||
import { and, eq } from 'drizzle-orm';
|
import { and, eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const removeRoleActionParamsSchema = z.object({
|
const removeRoleActionParamsSchema = z.object({
|
||||||
roleId: z.string().transform(Number).pipe(z.number().int().positive()),
|
roleId: z.string().transform(Number).pipe(z.number().int().positive()),
|
||||||
|
@ -17,14 +18,18 @@ const removeRoleActionSchema = z.object({
|
||||||
actionId: z.string(),
|
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 {
|
try {
|
||||||
const parsedParams = removeRoleActionSchema.safeParse(req.params);
|
const parsedParams = removeRoleActionSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { roleId } = parsedBody.data;
|
||||||
|
|
||||||
// Check if the user has permission to remove role actions
|
// 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) {
|
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)
|
const deletedRoleAction = await db
|
||||||
.where(and(eq(roleActions.roleId, roleId), eq(roleActions.actionId, actionId)))
|
.delete(roleActions)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(roleActions.roleId, roleId),
|
||||||
|
eq(roleActions.actionId, actionId)
|
||||||
|
)
|
||||||
|
)
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
if (deletedRoleAction.length === 0) {
|
if (deletedRoleAction.length === 0) {
|
||||||
|
@ -71,6 +90,11 @@ export async function removeRoleAction(req: Request, res: Response, next: NextFu
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { roleResources } from '@server/db/schema';
|
import { roleResources } from "@server/db/schema";
|
||||||
import { and, eq } from 'drizzle-orm';
|
import { and, eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const removeRoleResourceParamsSchema = z.object({
|
const removeRoleResourceParamsSchema = z.object({
|
||||||
roleId: z.string().transform(Number).pipe(z.number().int().positive()),
|
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()),
|
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 {
|
try {
|
||||||
const parsedParams = removeRoleResourceSchema.safeParse(req.params);
|
const parsedParams = removeRoleResourceSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { roleId } = parsedBody.data;
|
||||||
|
|
||||||
// Check if the user has permission to remove role resources
|
// 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) {
|
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)
|
const deletedRoleResource = await db
|
||||||
.where(and(eq(roleResources.roleId, roleId), eq(roleResources.resourceId, resourceId)))
|
.delete(roleResources)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(roleResources.roleId, roleId),
|
||||||
|
eq(roleResources.resourceId, resourceId)
|
||||||
|
)
|
||||||
|
)
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
if (deletedRoleResource.length === 0) {
|
if (deletedRoleResource.length === 0) {
|
||||||
|
@ -71,6 +90,11 @@ export async function removeRoleResource(req: Request, res: Response, next: Next
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { resources, roleResources, roleSites } from '@server/db/schema';
|
import { resources, roleResources, roleSites } from "@server/db/schema";
|
||||||
import { and, eq } from 'drizzle-orm';
|
import { and, eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const removeRoleSiteParamsSchema = z.object({
|
const removeRoleSiteParamsSchema = z.object({
|
||||||
roleId: z.string().transform(Number).pipe(z.number().int().positive()),
|
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()),
|
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 {
|
try {
|
||||||
const parsedParams = removeRoleSiteSchema.safeParse(req.params);
|
const parsedParams = removeRoleSiteSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { roleId } = parsedBody.data;
|
||||||
|
|
||||||
// Check if the user has permission to remove role sites
|
// 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) {
|
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)
|
const deletedRoleSite = await db
|
||||||
.where(and(eq(roleSites.roleId, roleId), eq(roleSites.siteId, siteId)))
|
.delete(roleSites)
|
||||||
|
.where(
|
||||||
|
and(eq(roleSites.roleId, roleId), eq(roleSites.siteId, siteId))
|
||||||
|
)
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
if (deletedRoleSite.length === 0) {
|
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
|
||||||
.from(resources)
|
.select()
|
||||||
.where(eq(resources.siteId, siteId));
|
.from(resources)
|
||||||
|
.where(eq(resources.siteId, siteId));
|
||||||
|
|
||||||
for (const resource of siteResources) {
|
for (const resource of siteResources) {
|
||||||
await db.delete(roleResources)
|
await db
|
||||||
.where(and(eq(roleResources.roleId, roleId), eq(roleResources.resourceId, resource.resourceId)))
|
.delete(roleResources)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(roleResources.roleId, roleId),
|
||||||
|
eq(roleResources.resourceId, resource.resourceId)
|
||||||
|
)
|
||||||
|
)
|
||||||
.returning();
|
.returning();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +104,11 @@ export async function removeRoleSite(req: Request, res: Response, next: NextFunc
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +1,40 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { roles } from '@server/db/schema';
|
import { roles } from "@server/db/schema";
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const updateRoleParamsSchema = z.object({
|
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
|
||||||
name: z.string().min(1).max(255).optional(),
|
.object({
|
||||||
description: z.string().optional(),
|
name: z.string().min(1).max(255).optional(),
|
||||||
}).refine(data => Object.keys(data).length > 0, {
|
description: z.string().optional(),
|
||||||
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 {
|
try {
|
||||||
const parsedParams = updateRoleParamsSchema.safeParse(req.params);
|
const parsedParams = updateRoleParamsSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
HttpCode.BAD_REQUEST,
|
||||||
parsedBody.error.errors.map(e => e.message).join(', ')
|
fromError(parsedBody.error).toString()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -46,15 +53,24 @@ export async function updateRole(req: Request, res: Response, next: NextFunction
|
||||||
const updateData = parsedBody.data;
|
const updateData = parsedBody.data;
|
||||||
|
|
||||||
// Check if the user has permission to update roles
|
// 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) {
|
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
|
||||||
.from(roles)
|
.select()
|
||||||
.where(eq(roles.roleId, roleId))
|
.from(roles)
|
||||||
.limit(1);
|
.where(eq(roles.roleId, roleId))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
if (role.length === 0) {
|
if (role.length === 0) {
|
||||||
return next(
|
return next(
|
||||||
|
@ -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)
|
.set(updateData)
|
||||||
.where(eq(roles.roleId, roleId))
|
.where(eq(roles.roleId, roleId))
|
||||||
.returning();
|
.returning();
|
||||||
|
@ -97,6 +114,11 @@ export async function updateRole(req: Request, res: Response, next: NextFunction
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,25 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { roles, userSites, sites, roleSites, exitNodes } from '@server/db/schema';
|
import {
|
||||||
|
roles,
|
||||||
|
userSites,
|
||||||
|
sites,
|
||||||
|
roleSites,
|
||||||
|
exitNodes,
|
||||||
|
} from "@server/db/schema";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
import { eq, and } from 'drizzle-orm';
|
import { eq, and } from "drizzle-orm";
|
||||||
import { getUniqueSiteName } from '@server/db/names';
|
import { getUniqueSiteName } from "@server/db/names";
|
||||||
import { addPeer } from '../gerbil/peers';
|
import { addPeer } from "../gerbil/peers";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
const API_BASE_URL = "http://localhost:3000";
|
|
||||||
|
|
||||||
const createSiteParamsSchema = z.object({
|
const createSiteParamsSchema = z.object({
|
||||||
orgId: z.string()
|
orgId: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Define Zod schema for request body validation
|
// Define Zod schema for request body validation
|
||||||
|
@ -36,7 +41,11 @@ export type CreateSiteResponse = {
|
||||||
// subnet: string;
|
// 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 {
|
try {
|
||||||
// Validate request body
|
// Validate request body
|
||||||
const parsedBody = createSiteSchema.safeParse(req.body);
|
const parsedBody = createSiteSchema.safeParse(req.body);
|
||||||
|
@ -44,7 +53,7 @@ export async function createSite(req: Request, res: Response, next: NextFunction
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { orgId } = parsedParams.data;
|
||||||
|
|
||||||
// Check if the user has permission to list sites
|
// 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) {
|
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) {
|
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);
|
const niceId = await getUniqueSiteName(orgId);
|
||||||
|
|
||||||
// Create new site in the database
|
// Create new site in the database
|
||||||
const [newSite] = await db.insert(sites).values({
|
const [newSite] = await db
|
||||||
orgId,
|
.insert(sites)
|
||||||
exitNodeId,
|
.values({
|
||||||
name,
|
orgId,
|
||||||
niceId,
|
exitNodeId,
|
||||||
pubKey,
|
name,
|
||||||
subnet,
|
niceId,
|
||||||
}).returning();
|
pubKey,
|
||||||
|
subnet,
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
// find the superuser roleId and also add the resource to the superuser role
|
// find the superuser roleId and also add the resource to the superuser role
|
||||||
const superuserRole = await db.select()
|
const superuserRole = await db
|
||||||
.from(roles)
|
.select()
|
||||||
.where(and(eq(roles.isSuperuserRole, true), eq(roles.orgId, orgId)))
|
.from(roles)
|
||||||
.limit(1);
|
.where(and(eq(roles.isSuperuserRole, true), eq(roles.orgId, orgId)))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
if (superuserRole.length === 0) {
|
if (superuserRole.length === 0) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(HttpCode.NOT_FOUND, `Superuser role not found`)
|
||||||
HttpCode.NOT_FOUND,
|
|
||||||
`Superuser role not found`
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,6 +155,11 @@ export async function createSite(req: Request, res: Response, next: NextFunction
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,28 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { sites } from '@server/db/schema';
|
import { sites } from "@server/db/schema";
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
import { deletePeer } from '../gerbil/peers';
|
import { deletePeer } from "../gerbil/peers";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const API_BASE_URL = "http://localhost:3000";
|
const API_BASE_URL = "http://localhost:3000";
|
||||||
|
|
||||||
// Define Zod schema for request parameters validation
|
// Define Zod schema for request parameters validation
|
||||||
const deleteSiteSchema = z.object({
|
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 {
|
try {
|
||||||
// Validate request parameters
|
// Validate request parameters
|
||||||
const parsedParams = deleteSiteSchema.safeParse(req.params);
|
const parsedParams = deleteSiteSchema.safeParse(req.params);
|
||||||
|
@ -25,7 +30,7 @@ export async function deleteSite(req: Request, res: Response, next: NextFunction
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { siteId } = parsedParams.data;
|
||||||
|
|
||||||
// Check if the user has permission to list sites
|
// 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) {
|
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
|
// Delete the site from the database
|
||||||
const [deletedSite] = await db.delete(sites)
|
const [deletedSite] = await db
|
||||||
|
.delete(sites)
|
||||||
.where(eq(sites.siteId, siteId))
|
.where(eq(sites.siteId, siteId))
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
|
@ -63,26 +77,33 @@ export async function deleteSite(req: Request, res: Response, next: NextFunction
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(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) {
|
async function removePeer(publicKey: string) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${API_BASE_URL}/peer?public_key=${encodeURIComponent(publicKey)}`, {
|
const response = await fetch(
|
||||||
method: 'DELETE',
|
`${API_BASE_URL}/peer?public_key=${encodeURIComponent(publicKey)}`,
|
||||||
});
|
{
|
||||||
|
method: "DELETE",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
console.log('Peer removed successfully:', data.status);
|
console.log("Peer removed successfully:", data.status);
|
||||||
return data;
|
return data;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Error removing peer:', error.message);
|
console.error("Error removing peer:", error.message);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,24 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { sites } from '@server/db/schema';
|
import { sites } from "@server/db/schema";
|
||||||
import { eq, and } from 'drizzle-orm';
|
import { eq, and } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
import stoi from '@server/utils/stoi';
|
import stoi from "@server/utils/stoi";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
// Define Zod schema for request parameters validation
|
// Define Zod schema for request parameters validation
|
||||||
const getSiteSchema = z.object({
|
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(),
|
niceId: z.string().optional(),
|
||||||
orgId: z.string().optional(),
|
orgId: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
@ -22,9 +28,13 @@ export type GetSiteResponse = {
|
||||||
name: string;
|
name: string;
|
||||||
subdomain: string;
|
subdomain: string;
|
||||||
subnet: 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 {
|
try {
|
||||||
// Validate request parameters
|
// Validate request parameters
|
||||||
const parsedParams = getSiteSchema.safeParse(req.params);
|
const parsedParams = getSiteSchema.safeParse(req.params);
|
||||||
|
@ -32,7 +42,7 @@ export async function getSite(req: Request, res: Response, next: NextFunction):
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { siteId, niceId, orgId } = parsedParams.data;
|
||||||
|
|
||||||
// Check if the user has permission to list sites
|
// 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) {
|
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;
|
let site;
|
||||||
// Fetch the site from the database
|
// Fetch the site from the database
|
||||||
if (siteId) {
|
if (siteId) {
|
||||||
site = await db.select()
|
site = await db
|
||||||
|
.select()
|
||||||
.from(sites)
|
.from(sites)
|
||||||
.where(eq(sites.siteId, siteId))
|
.where(eq(sites.siteId, siteId))
|
||||||
.limit(1);
|
.limit(1);
|
||||||
} else if (niceId && orgId) {
|
} else if (niceId && orgId) {
|
||||||
site = await db.select()
|
site = await db
|
||||||
|
.select()
|
||||||
.from(sites)
|
.from(sites)
|
||||||
.where(and(eq(sites.niceId, niceId), eq(sites.orgId, orgId)))
|
.where(and(eq(sites.niceId, niceId), eq(sites.orgId, orgId)))
|
||||||
.limit(1);
|
.limit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!site) {
|
if (!site) {
|
||||||
return next(createHttpError(HttpCode.NOT_FOUND, 'Site not found'));
|
return next(createHttpError(HttpCode.NOT_FOUND, "Site not found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (site.length === 0) {
|
if (site.length === 0) {
|
||||||
|
@ -86,6 +106,11 @@ export async function getSite(req: Request, res: Response, next: NextFunction):
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("Error from getSite: ", 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..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,31 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { roleSites, roles } from '@server/db/schema';
|
import { roleSites, roles } from "@server/db/schema";
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const listSiteRolesSchema = z.object({
|
const listSiteRolesSchema = 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 listSiteRoles(req: Request, res: Response, next: NextFunction): Promise<any> {
|
export async function listSiteRoles(
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
): Promise<any> {
|
||||||
try {
|
try {
|
||||||
const parsedParams = listSiteRolesSchema.safeParse(req.params);
|
const parsedParams = listSiteRolesSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { siteId } = parsedParams.data;
|
||||||
|
|
||||||
// Check if the user has permission to list site roles
|
// 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) {
|
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
|
const siteRolesList = await db
|
||||||
|
@ -53,6 +66,11 @@ export async function listSiteRoles(req: Request, res: Response, next: NextFunct
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,26 +18,25 @@ export type PickSiteDefaultsResponse = {
|
||||||
listenPort: number;
|
listenPort: number;
|
||||||
endpoint: string;
|
endpoint: string;
|
||||||
subnet: string;
|
subnet: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export async function pickSiteDefaults(
|
export async function pickSiteDefaults(
|
||||||
req: Request,
|
req: Request,
|
||||||
res: Response,
|
res: Response,
|
||||||
next: NextFunction,
|
next: NextFunction
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// Check if the user has permission to list sites
|
// Check if the user has permission to list sites
|
||||||
const hasPermission = await checkUserActionPermission(
|
const hasPermission = await checkUserActionPermission(
|
||||||
ActionsEnum.createSite,
|
ActionsEnum.createSite,
|
||||||
req,
|
req
|
||||||
);
|
);
|
||||||
if (!hasPermission) {
|
if (!hasPermission) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.FORBIDDEN,
|
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);
|
const nodes = await db.select().from(exitNodes);
|
||||||
if (nodes.length === 0) {
|
if (nodes.length === 0) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(HttpCode.NOT_FOUND, "No exit nodes available")
|
||||||
HttpCode.NOT_FOUND,
|
|
||||||
"No exit nodes available",
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,9 +55,10 @@ export async function pickSiteDefaults(
|
||||||
|
|
||||||
// TODO: this probably can be optimized...
|
// TODO: this probably can be optimized...
|
||||||
// list all of the sites on that exit node
|
// list all of the sites on that exit node
|
||||||
const sitesQuery = await db.select({
|
const sitesQuery = await db
|
||||||
subnet: sites.subnet
|
.select({
|
||||||
})
|
subnet: sites.subnet,
|
||||||
|
})
|
||||||
.from(sites)
|
.from(sites)
|
||||||
.where(eq(sites.exitNodeId, exitNode.exitNodeId));
|
.where(eq(sites.exitNodeId, exitNode.exitNodeId));
|
||||||
|
|
||||||
|
@ -74,8 +71,8 @@ export async function pickSiteDefaults(
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.INTERNAL_SERVER_ERROR,
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
"No available subnets",
|
"No available subnets"
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,11 +94,7 @@ export async function pickSiteDefaults(
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
|
||||||
HttpCode.INTERNAL_SERVER_ERROR,
|
|
||||||
"An error occurred...",
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,33 +1,40 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { sites } from '@server/db/schema';
|
import { sites } from "@server/db/schema";
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
// Define Zod schema for request parameters validation
|
// Define Zod schema for request parameters validation
|
||||||
const updateSiteParamsSchema = z.object({
|
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
|
// Define Zod schema for request body validation
|
||||||
const updateSiteBodySchema = z.object({
|
const updateSiteBodySchema = z
|
||||||
name: z.string().min(1).max(255).optional(),
|
.object({
|
||||||
subdomain: z.string().min(1).max(255).optional(),
|
name: z.string().min(1).max(255).optional(),
|
||||||
pubKey: z.string().optional(),
|
subdomain: z.string().min(1).max(255).optional(),
|
||||||
subnet: z.string().optional(),
|
pubKey: z.string().optional(),
|
||||||
exitNode: z.number().int().positive().optional(),
|
subnet: z.string().optional(),
|
||||||
megabytesIn: z.number().int().nonnegative().optional(),
|
exitNode: z.number().int().positive().optional(),
|
||||||
megabytesOut: z.number().int().nonnegative().optional(),
|
megabytesIn: z.number().int().nonnegative().optional(),
|
||||||
}).refine(data => Object.keys(data).length > 0, {
|
megabytesOut: z.number().int().nonnegative().optional(),
|
||||||
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 {
|
try {
|
||||||
// Validate request parameters
|
// Validate request parameters
|
||||||
const parsedParams = updateSiteParamsSchema.safeParse(req.params);
|
const parsedParams = updateSiteParamsSchema.safeParse(req.params);
|
||||||
|
@ -35,7 +42,7 @@ export async function updateSite(req: Request, res: Response, next: NextFunction
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const updateData = parsedBody.data;
|
||||||
|
|
||||||
// Check if the user has permission to list sites
|
// 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) {
|
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
|
// Update the site in the database
|
||||||
const updatedSite = await db.update(sites)
|
const updatedSite = await db
|
||||||
|
.update(sites)
|
||||||
.set(updateData)
|
.set(updateData)
|
||||||
.where(eq(sites.siteId, siteId))
|
.where(eq(sites.siteId, siteId))
|
||||||
.returning();
|
.returning();
|
||||||
|
@ -84,6 +100,11 @@ export async function updateSite(req: Request, res: Response, next: NextFunction
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { resources, sites, targets } from '@server/db/schema';
|
import { resources, sites, targets } from "@server/db/schema";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
import { addPeer } from '../gerbil/peers';
|
import { addPeer } from "../gerbil/peers";
|
||||||
import { eq, and } from 'drizzle-orm';
|
import { eq, and } from "drizzle-orm";
|
||||||
import { isIpInCidr } from '@server/utils/ip';
|
import { isIpInCidr } from "@server/utils/ip";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const createTargetParamsSchema = z.object({
|
const createTargetParamsSchema = z.object({
|
||||||
resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
|
resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
|
||||||
|
@ -23,14 +24,18 @@ const createTargetSchema = z.object({
|
||||||
enabled: z.boolean().default(true),
|
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 {
|
try {
|
||||||
const parsedBody = createTargetSchema.safeParse(req.body);
|
const parsedBody = createTargetSchema.safeParse(req.body);
|
||||||
if (!parsedBody.success) {
|
if (!parsedBody.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
HttpCode.BAD_REQUEST,
|
||||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
fromError(parsedParams.error).toString()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -50,15 +55,24 @@ export async function createTarget(req: Request, res: Response, next: NextFuncti
|
||||||
const { resourceId } = parsedParams.data;
|
const { resourceId } = parsedParams.data;
|
||||||
|
|
||||||
// Check if the user has permission to list sites
|
// 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) {
|
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
|
// get the resource
|
||||||
const [resource] = await db.select({
|
const [resource] = await db
|
||||||
siteId: resources.siteId,
|
.select({
|
||||||
})
|
siteId: resources.siteId,
|
||||||
|
})
|
||||||
.from(resources)
|
.from(resources)
|
||||||
.where(eq(resources.resourceId, resourceId));
|
.where(eq(resources.resourceId, resourceId));
|
||||||
|
|
||||||
|
@ -72,9 +86,10 @@ export async function createTarget(req: Request, res: Response, next: NextFuncti
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: is this all inefficient?
|
// TODO: is this all inefficient?
|
||||||
|
|
||||||
// get the site
|
// get the site
|
||||||
const [site] = await db.select()
|
const [site] = await db
|
||||||
|
.select()
|
||||||
.from(sites)
|
.from(sites)
|
||||||
.where(eq(sites.siteId, resource.siteId!))
|
.where(eq(sites.siteId, resource.siteId!))
|
||||||
.limit(1);
|
.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
|
||||||
resourceId,
|
.insert(targets)
|
||||||
...targetData
|
.values({
|
||||||
}).returning();
|
resourceId,
|
||||||
|
...targetData,
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
|
||||||
// Fetch resources for this site
|
// Fetch resources for this site
|
||||||
const resourcesRes = await db.query.resources.findMany({
|
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
|
// Fetch targets for all resources of this site
|
||||||
const targetIps = await Promise.all(resourcesRes.map(async (resource) => {
|
const targetIps = await Promise.all(
|
||||||
const targetsRes = await db.query.targets.findMany({
|
resourcesRes.map(async (resource) => {
|
||||||
where: eq(targets.resourceId, resource.resourceId),
|
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!, {
|
await addPeer(site.exitNodeId!, {
|
||||||
publicKey: site.pubKey,
|
publicKey: site.pubKey,
|
||||||
allowedIps: targetIps.flat()
|
allowedIps: targetIps.flat(),
|
||||||
});
|
});
|
||||||
|
|
||||||
return response(res, {
|
return response(res, {
|
||||||
|
@ -130,6 +150,11 @@ export async function createTarget(req: Request, res: Response, next: NextFuncti
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,32 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { resources, sites, targets } from '@server/db/schema';
|
import { resources, sites, targets } from "@server/db/schema";
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
import { addPeer } from '../gerbil/peers';
|
import { addPeer } from "../gerbil/peers";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const deleteTargetSchema = z.object({
|
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 {
|
try {
|
||||||
const parsedParams = deleteTargetSchema.safeParse(req.params);
|
const parsedParams = deleteTargetSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { targetId } = parsedParams.data;
|
||||||
|
|
||||||
// Check if the user has permission to list sites
|
// 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) {
|
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
|
||||||
const [deletedTarget] = await db.delete(targets)
|
.delete(targets)
|
||||||
.where(eq(targets.targetId, targetId))
|
.where(eq(targets.targetId, targetId))
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
|
@ -48,9 +61,10 @@ export async function deleteTarget(req: Request, res: Response, next: NextFuncti
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// get the resource
|
// get the resource
|
||||||
const [resource] = await db.select({
|
const [resource] = await db
|
||||||
siteId: resources.siteId,
|
.select({
|
||||||
})
|
siteId: resources.siteId,
|
||||||
|
})
|
||||||
.from(resources)
|
.from(resources)
|
||||||
.where(eq(resources.resourceId, deletedTarget.resourceId!));
|
.where(eq(resources.resourceId, deletedTarget.resourceId!));
|
||||||
|
|
||||||
|
@ -66,7 +80,8 @@ export async function deleteTarget(req: Request, res: Response, next: NextFuncti
|
||||||
// TODO: is this all inefficient?
|
// TODO: is this all inefficient?
|
||||||
|
|
||||||
// get the site
|
// get the site
|
||||||
const [site] = await db.select()
|
const [site] = await db
|
||||||
|
.select()
|
||||||
.from(sites)
|
.from(sites)
|
||||||
.where(eq(sites.siteId, resource.siteId!))
|
.where(eq(sites.siteId, resource.siteId!))
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
@ -86,16 +101,18 @@ export async function deleteTarget(req: Request, res: Response, next: NextFuncti
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fetch targets for all resources of this site
|
// Fetch targets for all resources of this site
|
||||||
const targetIps = await Promise.all(resourcesRes.map(async (resource) => {
|
const targetIps = await Promise.all(
|
||||||
const targetsRes = await db.query.targets.findMany({
|
resourcesRes.map(async (resource) => {
|
||||||
where: eq(targets.resourceId, resource.resourceId),
|
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!, {
|
await addPeer(site.exitNodeId!, {
|
||||||
publicKey: site.pubKey,
|
publicKey: site.pubKey,
|
||||||
allowedIps: targetIps.flat()
|
allowedIps: targetIps.flat(),
|
||||||
});
|
});
|
||||||
|
|
||||||
return response(res, {
|
return response(res, {
|
||||||
|
@ -107,6 +124,11 @@ export async function deleteTarget(req: Request, res: Response, next: NextFuncti
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,31 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { targets } from '@server/db/schema';
|
import { targets } from "@server/db/schema";
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const getTargetSchema = z.object({
|
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 {
|
try {
|
||||||
const parsedParams = getTargetSchema.safeParse(req.params);
|
const parsedParams = getTargetSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { targetId } = parsedParams.data;
|
||||||
|
|
||||||
// Check if the user has permission to list sites
|
// 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) {
|
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)
|
.from(targets)
|
||||||
.where(eq(targets.targetId, targetId))
|
.where(eq(targets.targetId, targetId))
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
@ -56,6 +70,11 @@ export async function getTarget(req: Request, res: Response, next: NextFunction)
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +1,43 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { targets } from '@server/db/schema';
|
import { targets } from "@server/db/schema";
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const updateTargetParamsSchema = z.object({
|
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
|
||||||
// ip: z.string().ip().optional(), // for now we cant update the ip; you will have to delete
|
.object({
|
||||||
method: z.string().min(1).max(10).optional(),
|
// ip: z.string().ip().optional(), // for now we cant update the ip; you will have to delete
|
||||||
port: z.number().int().min(1).max(65535).optional(),
|
method: z.string().min(1).max(10).optional(),
|
||||||
protocol: z.string().optional(),
|
port: z.number().int().min(1).max(65535).optional(),
|
||||||
enabled: z.boolean().optional(),
|
protocol: z.string().optional(),
|
||||||
}).refine(data => Object.keys(data).length > 0, {
|
enabled: z.boolean().optional(),
|
||||||
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 {
|
try {
|
||||||
const parsedParams = updateTargetParamsSchema.safeParse(req.params);
|
const parsedParams = updateTargetParamsSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const updateData = parsedBody.data;
|
||||||
|
|
||||||
// Check if the user has permission to list sites
|
// 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) {
|
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)
|
.set(updateData)
|
||||||
.where(eq(targets.targetId, targetId))
|
.where(eq(targets.targetId, targetId))
|
||||||
.returning();
|
.returning();
|
||||||
|
@ -77,6 +93,11 @@ export async function updateTarget(req: Request, res: Response, next: NextFuncti
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { userActions, users } from '@server/db/schema';
|
import { userActions, users } from "@server/db/schema";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from "drizzle-orm";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const addUserActionSchema = z.object({
|
const addUserActionSchema = z.object({
|
||||||
userId: z.string(),
|
userId: z.string(),
|
||||||
|
@ -15,14 +16,18 @@ const addUserActionSchema = z.object({
|
||||||
orgId: z.string(),
|
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 {
|
try {
|
||||||
const parsedBody = addUserActionSchema.safeParse(req.body);
|
const parsedBody = addUserActionSchema.safeParse(req.body);
|
||||||
if (!parsedBody.success) {
|
if (!parsedBody.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { userId, actionId, orgId } = parsedBody.data;
|
||||||
|
|
||||||
// Check if the user has permission to add user actions
|
// 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) {
|
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
|
// 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) {
|
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
|
||||||
userId,
|
.insert(userActions)
|
||||||
actionId,
|
.values({
|
||||||
orgId,
|
userId,
|
||||||
}).returning();
|
actionId,
|
||||||
|
orgId,
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
|
||||||
return response(res, {
|
return response(res, {
|
||||||
data: newUserAction[0],
|
data: newUserAction[0],
|
||||||
|
@ -56,6 +81,11 @@ export async function addUserAction(req: Request, res: Response, next: NextFunct
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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..."));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +1,31 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { userResources } from '@server/db/schema';
|
import { userResources } from "@server/db/schema";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const addUserResourceSchema = z.object({
|
const addUserResourceSchema = z.object({
|
||||||
userId: z.string(),
|
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 {
|
try {
|
||||||
const parsedBody = addUserResourceSchema.safeParse(req.body);
|
const parsedBody = addUserResourceSchema.safeParse(req.body);
|
||||||
if (!parsedBody.success) {
|
if (!parsedBody.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { userId, resourceId } = parsedBody.data;
|
||||||
|
|
||||||
// Check if the user has permission to add user resources
|
// 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) {
|
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
|
||||||
userId,
|
.insert(userResources)
|
||||||
resourceId,
|
.values({
|
||||||
}).returning();
|
userId,
|
||||||
|
resourceId,
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
|
||||||
return response(res, {
|
return response(res, {
|
||||||
data: newUserResource[0],
|
data: newUserResource[0],
|
||||||
|
@ -47,6 +63,11 @@ export async function addUserResource(req: Request, res: Response, next: NextFun
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,32 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { resources, userResources, userSites } from '@server/db/schema';
|
import { resources, userResources, userSites } from "@server/db/schema";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from "drizzle-orm";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const addUserSiteSchema = z.object({
|
const addUserSiteSchema = z.object({
|
||||||
userId: z.string(),
|
userId: z.string(),
|
||||||
siteId: z.string().transform(Number).pipe(z.number().int().positive()),
|
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 {
|
try {
|
||||||
const parsedBody = addUserSiteSchema.safeParse(req.body);
|
const parsedBody = addUserSiteSchema.safeParse(req.body);
|
||||||
if (!parsedBody.success) {
|
if (!parsedBody.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { userId, siteId } = parsedBody.data;
|
||||||
|
|
||||||
// Check if the user has permission to add user sites
|
// 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) {
|
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
|
||||||
userId,
|
.insert(userSites)
|
||||||
siteId,
|
.values({
|
||||||
}).returning();
|
userId,
|
||||||
|
siteId,
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
|
||||||
// Add all resources associated with the site to the user
|
// Add all resources associated with the site to the user
|
||||||
const siteResources = await db.select()
|
const siteResources = await db
|
||||||
|
.select()
|
||||||
.from(resources)
|
.from(resources)
|
||||||
.where(eq(resources.siteId, siteId));
|
.where(eq(resources.siteId, siteId));
|
||||||
|
|
||||||
|
@ -60,6 +77,11 @@ export async function addUserSite(req: Request, res: Response, next: NextFunctio
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
export * from "./getUser";
|
export * from "./getUser";
|
||||||
export * from "./removeUserOrg";
|
export * from "./removeUserOrg";
|
||||||
export * from "./addUserOrg";
|
|
||||||
export * from "./listUsers";
|
export * from "./listUsers";
|
||||||
export * from "./setUserRole";
|
export * from "./setUserRole";
|
||||||
export * from "./inviteUser";
|
export * from "./inviteUser";
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { userActions } from '@server/db/schema';
|
import { userActions } from "@server/db/schema";
|
||||||
import { and, eq } from 'drizzle-orm';
|
import { and, eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const removeUserActionParamsSchema = z.object({
|
const removeUserActionParamsSchema = z.object({
|
||||||
userId: z.string(),
|
userId: z.string(),
|
||||||
|
@ -15,17 +16,21 @@ const removeUserActionParamsSchema = z.object({
|
||||||
|
|
||||||
const removeUserActionSchema = z.object({
|
const removeUserActionSchema = z.object({
|
||||||
actionId: z.string(),
|
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 {
|
try {
|
||||||
const parsedParams = removeUserActionParamsSchema.safeParse(req.params);
|
const parsedParams = removeUserActionParamsSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { actionId, orgId } = parsedBody.data;
|
||||||
|
|
||||||
// Check if the user has permission to remove user actions
|
// 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) {
|
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)
|
const deletedUserAction = await db
|
||||||
.where(and(
|
.delete(userActions)
|
||||||
eq(userActions.userId, userId),
|
.where(
|
||||||
eq(userActions.actionId, actionId),
|
and(
|
||||||
eq(userActions.orgId, orgId)
|
eq(userActions.userId, userId),
|
||||||
))
|
eq(userActions.actionId, actionId),
|
||||||
|
eq(userActions.orgId, orgId)
|
||||||
|
)
|
||||||
|
)
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
if (deletedUserAction.length === 0) {
|
if (deletedUserAction.length === 0) {
|
||||||
|
@ -76,6 +92,11 @@ export async function removeUserAction(req: Request, res: Response, next: NextFu
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,32 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { userOrgs, users } from '@server/db/schema';
|
import { userOrgs, users } from "@server/db/schema";
|
||||||
import { and, eq } from 'drizzle-orm';
|
import { and, eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const removeUserSchema = z.object({
|
const removeUserSchema = z.object({
|
||||||
userId: z.string().uuid(),
|
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 {
|
try {
|
||||||
const parsedParams = removeUserSchema.safeParse(req.params);
|
const parsedParams = removeUserSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { userId, orgId } = parsedParams.data;
|
||||||
|
|
||||||
// Check if the user has permission to list sites
|
// 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) {
|
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
|
// 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)));
|
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, orgId)));
|
||||||
|
|
||||||
return response(res, {
|
return response(res, {
|
||||||
|
@ -47,6 +61,11 @@ export async function removeUserOrg(req: Request, res: Response, next: NextFunct
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,32 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { userResources } from '@server/db/schema';
|
import { userResources } from "@server/db/schema";
|
||||||
import { and, eq } from 'drizzle-orm';
|
import { and, eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const removeUserResourceSchema = z.object({
|
const removeUserResourceSchema = z.object({
|
||||||
userId: z.string(),
|
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 {
|
try {
|
||||||
const parsedParams = removeUserResourceSchema.safeParse(req.params);
|
const parsedParams = removeUserResourceSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { userId, resourceId } = parsedParams.data;
|
||||||
|
|
||||||
// Check if the user has permission to remove user resources
|
// 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) {
|
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)
|
const deletedUserResource = await db
|
||||||
.where(and(eq(userResources.userId, userId), eq(userResources.resourceId, resourceId)))
|
.delete(userResources)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(userResources.userId, userId),
|
||||||
|
eq(userResources.resourceId, resourceId)
|
||||||
|
)
|
||||||
|
)
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
if (deletedUserResource.length === 0) {
|
if (deletedUserResource.length === 0) {
|
||||||
|
@ -56,6 +75,11 @@ export async function removeUserResource(req: Request, res: Response, next: Next
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { resources, userResources, userSites } from '@server/db/schema';
|
import { resources, userResources, userSites } from "@server/db/schema";
|
||||||
import { and, eq } from 'drizzle-orm';
|
import { and, eq } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const removeUserSiteParamsSchema = z.object({
|
const removeUserSiteParamsSchema = z.object({
|
||||||
userId: z.string(),
|
userId: z.string(),
|
||||||
|
@ -17,14 +18,18 @@ const removeUserSiteSchema = z.object({
|
||||||
siteId: z.number().int().positive(),
|
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 {
|
try {
|
||||||
const parsedParams = removeUserSiteParamsSchema.safeParse(req.params);
|
const parsedParams = removeUserSiteParamsSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { siteId } = parsedBody.data;
|
||||||
|
|
||||||
// Check if the user has permission to remove user sites
|
// 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) {
|
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)
|
const deletedUserSite = await db
|
||||||
.where(and(eq(userSites.userId, userId), eq(userSites.siteId, siteId)))
|
.delete(userSites)
|
||||||
|
.where(
|
||||||
|
and(eq(userSites.userId, userId), eq(userSites.siteId, siteId))
|
||||||
|
)
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
if (deletedUserSite.length === 0) {
|
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)
|
.from(resources)
|
||||||
.where(eq(resources.siteId, siteId));
|
.where(eq(resources.siteId, siteId));
|
||||||
|
|
||||||
for (const resource of siteResources) {
|
for (const resource of siteResources) {
|
||||||
await db.delete(userResources)
|
await db
|
||||||
.where(and(eq(userResources.userId, userId), eq(userResources.resourceId, resource.resourceId)))
|
.delete(userResources)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(userResources.userId, userId),
|
||||||
|
eq(userResources.resourceId, resource.resourceId)
|
||||||
|
)
|
||||||
|
)
|
||||||
.returning();
|
.returning();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +104,11 @@ export async function removeUserSite(req: Request, res: Response, next: NextFunc
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +1,33 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { db } from '@server/db';
|
import { db } from "@server/db";
|
||||||
import { userOrgs, roles } from '@server/db/schema';
|
import { userOrgs, roles } from "@server/db/schema";
|
||||||
import { eq, and } from 'drizzle-orm';
|
import { eq, and } from "drizzle-orm";
|
||||||
import response from "@server/utils/response";
|
import response from "@server/utils/response";
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from "http-errors";
|
||||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||||
import logger from '@server/logger';
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const addUserRoleSchema = z.object({
|
const addUserRoleSchema = z.object({
|
||||||
userId: z.string(),
|
userId: z.string(),
|
||||||
roleId: z.number().int().positive(),
|
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 {
|
try {
|
||||||
const parsedBody = addUserRoleSchema.safeParse(req.body);
|
const parsedBody = addUserRoleSchema.safeParse(req.body);
|
||||||
if (!parsedBody.success) {
|
if (!parsedBody.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
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;
|
const { userId, roleId, orgId } = parsedBody.data;
|
||||||
|
|
||||||
// Check if the user has permission to add user roles
|
// 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) {
|
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
|
// Check if the role exists and belongs to the specified org
|
||||||
const roleExists = await db.select()
|
const roleExists = await db
|
||||||
|
.select()
|
||||||
.from(roles)
|
.from(roles)
|
||||||
.where(and(eq(roles.roleId, roleId), eq(roles.orgId, orgId)))
|
.where(and(eq(roles.roleId, roleId), eq(roles.orgId, orgId)))
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
if (roleExists.length === 0) {
|
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 })
|
.set({ roleId })
|
||||||
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, orgId)))
|
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, orgId)))
|
||||||
.returning();
|
.returning();
|
||||||
|
@ -59,6 +79,11 @@ export async function addUserRole(req: Request, res: Response, next: NextFunctio
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"An error occurred..."
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ import {
|
||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@app/components/ui/select";
|
} 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 { ListOrgsResponse } from "@server/routers/org";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
|
|
@ -8,6 +8,9 @@ import { internal } from "@app/api";
|
||||||
import { AxiosResponse } from "axios";
|
import { AxiosResponse } from "axios";
|
||||||
import { GetOrgResponse, ListOrgsResponse } from "@server/routers/org";
|
import { GetOrgResponse, ListOrgsResponse } from "@server/routers/org";
|
||||||
import { authCookieHeader } from "@app/api/cookies";
|
import { authCookieHeader } from "@app/api/cookies";
|
||||||
|
import { cache } from "react";
|
||||||
|
|
||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: `Settings - Pangolin`,
|
title: `Settings - Pangolin`,
|
||||||
|
@ -47,7 +50,8 @@ export default async function SettingsLayout(props: SettingsLayoutProps) {
|
||||||
|
|
||||||
const { children } = props;
|
const { children } = props;
|
||||||
|
|
||||||
const user = await verifySession();
|
const getUser = cache(verifySession);
|
||||||
|
const user = await getUser();
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
redirect("/auth/login");
|
redirect("/auth/login");
|
||||||
|
@ -56,20 +60,23 @@ export default async function SettingsLayout(props: SettingsLayoutProps) {
|
||||||
const cookie = await authCookieHeader();
|
const cookie = await authCookieHeader();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await internal.get<AxiosResponse<GetOrgResponse>>(
|
const getOrg = cache(() =>
|
||||||
`/org/${params.orgId}`,
|
internal.get<AxiosResponse<GetOrgResponse>>(
|
||||||
cookie
|
`/org/${params.orgId}`,
|
||||||
|
cookie
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
const org = await getOrg();
|
||||||
} catch {
|
} catch {
|
||||||
redirect(`/`);
|
redirect(`/`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let orgs: ListOrgsResponse["orgs"] = [];
|
let orgs: ListOrgsResponse["orgs"] = [];
|
||||||
try {
|
try {
|
||||||
const res = await internal.get<AxiosResponse<ListOrgsResponse>>(
|
const getOrgs = cache(() =>
|
||||||
`/orgs`,
|
internal.get<AxiosResponse<ListOrgsResponse>>(`/orgs`, cookie)
|
||||||
cookie
|
|
||||||
);
|
);
|
||||||
|
const res = await getOrgs();
|
||||||
if (res && res.data.data.orgs) {
|
if (res && res.data.data.orgs) {
|
||||||
orgs = res.data.data.orgs;
|
orgs = res.data.data.orgs;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { toast } from "@/hooks/use-toast";
|
import { toast } from "@/hooks/useToast";
|
||||||
import { Button} from "@/components/ui/button";
|
import { Button} from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
|
|
|
@ -36,6 +36,8 @@ import { AxiosResponse } from "axios";
|
||||||
import api from "@app/api";
|
import api from "@app/api";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
import { GetResourceResponse } from "@server/routers/resource";
|
||||||
|
import { useToast } from "@app/hooks/useToast";
|
||||||
|
|
||||||
const GeneralFormSchema = z.object({
|
const GeneralFormSchema = z.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
|
@ -49,6 +51,7 @@ export function GeneralForm() {
|
||||||
const orgId = params.orgId;
|
const orgId = params.orgId;
|
||||||
const { resource, updateResource } = useResourceContext();
|
const { resource, updateResource } = useResourceContext();
|
||||||
const [sites, setSites] = useState<ListSitesResponse["sites"]>([]);
|
const [sites, setSites] = useState<ListSitesResponse["sites"]>([]);
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
const form = useForm<GeneralFormValues>({
|
const form = useForm<GeneralFormValues>({
|
||||||
resolver: zodResolver(GeneralFormSchema),
|
resolver: zodResolver(GeneralFormSchema),
|
||||||
|
@ -72,7 +75,24 @@ export function GeneralForm() {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
async function onSubmit(data: GeneralFormValues) {
|
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 (
|
return (
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { ChevronDownIcon } from "@radix-ui/react-icons";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { toast } from "@/hooks/use-toast";
|
import { toast } from "@/hooks/useToast";
|
||||||
import { Button, buttonVariants } from "@/components/ui/button";
|
import { Button, buttonVariants } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
|
@ -78,7 +78,7 @@ export function CreateSiteForm() {
|
||||||
setKeypair(generatedKeypair);
|
setKeypair(generatedKeypair);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
|
||||||
api.get(`/org/${orgId}/pickSiteDefaults`)
|
api.get(`/org/${orgId}/pick-site-defaults`)
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
toast({
|
toast({
|
||||||
title: "Error creating site...",
|
title: "Error creating site...",
|
||||||
|
|
|
@ -15,6 +15,8 @@ import {
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { useSiteContext } from "@app/hooks/useSiteContext";
|
import { useSiteContext } from "@app/hooks/useSiteContext";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
import api from "@app/api";
|
||||||
|
import { useToast } from "@app/hooks/useToast";
|
||||||
|
|
||||||
const GeneralFormSchema = z.object({
|
const GeneralFormSchema = z.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
|
@ -24,6 +26,7 @@ type GeneralFormValues = z.infer<typeof GeneralFormSchema>;
|
||||||
|
|
||||||
export function GeneralForm() {
|
export function GeneralForm() {
|
||||||
const { site, updateSite } = useSiteContext();
|
const { site, updateSite } = useSiteContext();
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
const form = useForm<GeneralFormValues>({
|
const form = useForm<GeneralFormValues>({
|
||||||
resolver: zodResolver(GeneralFormSchema),
|
resolver: zodResolver(GeneralFormSchema),
|
||||||
|
@ -34,7 +37,21 @@ export function GeneralForm() {
|
||||||
});
|
});
|
||||||
|
|
||||||
async function onSubmit(data: GeneralFormValues) {
|
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 (
|
return (
|
||||||
|
|
|
@ -92,7 +92,7 @@ export const columns: ColumnDef<SiteRow>[] = [
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuItem>
|
<DropdownMenuItem>
|
||||||
<Link
|
<Link
|
||||||
href={`/${siteRow.orgId}/settings/sites/${siteRow.id}`}
|
href={`/${siteRow.orgId}/settings/sites/${siteRow.nice}`}
|
||||||
>
|
>
|
||||||
View settings
|
View settings
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
@ -18,15 +18,30 @@ import {
|
||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@app/components/ui/select";
|
} 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 { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { InviteUserBody, InviteUserResponse } from "@server/routers/user";
|
import { InviteUserBody, InviteUserResponse } from "@server/routers/user";
|
||||||
import { AxiosResponse } from "axios";
|
import { AxiosResponse } from "axios";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { useParams } from "next/navigation";
|
|
||||||
import CopyTextBox from "@app/components/CopyTextBox";
|
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({
|
const formSchema = z.object({
|
||||||
email: z.string().email({ message: "Invalid email address" }),
|
email: z.string().email({ message: "Invalid email address" }),
|
||||||
|
@ -34,9 +49,9 @@ const formSchema = z.object({
|
||||||
roleId: z.string(),
|
roleId: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function InviteUserForm() {
|
export default function InviteUserForm({ open, setOpen }: InviteUserFormProps) {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const { orgId } = useParams();
|
const { org } = useOrgContext();
|
||||||
|
|
||||||
const [inviteLink, setInviteLink] = useState<string | null>(null);
|
const [inviteLink, setInviteLink] = useState<string | null>(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
@ -74,7 +89,7 @@ export default function InviteUserForm() {
|
||||||
|
|
||||||
const res = await api
|
const res = await api
|
||||||
.post<AxiosResponse<InviteUserResponse>>(
|
.post<AxiosResponse<InviteUserResponse>>(
|
||||||
`/org/${orgId}/create-invite`,
|
`/org/${org?.org.orgId}/create-invite`,
|
||||||
{
|
{
|
||||||
email: values.email,
|
email: values.email,
|
||||||
roleId: parseInt(values.roleId),
|
roleId: parseInt(values.roleId),
|
||||||
|
@ -107,117 +122,151 @@ export default function InviteUserForm() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!inviteLink && (
|
<Credenza open={open} onOpenChange={setOpen}>
|
||||||
<Form {...form}>
|
<CredenzaContent>
|
||||||
<form
|
<CredenzaHeader>
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
<CredenzaTitle>Invite User</CredenzaTitle>
|
||||||
className="space-y-4"
|
<CredenzaDescription>
|
||||||
>
|
Give new users access to your organization
|
||||||
<FormField
|
</CredenzaDescription>
|
||||||
control={form.control}
|
</CredenzaHeader>
|
||||||
name="email"
|
<CredenzaBody>
|
||||||
render={({ field }) => (
|
{!inviteLink && (
|
||||||
<FormItem>
|
<Form {...form}>
|
||||||
<FormLabel>Email</FormLabel>
|
<form
|
||||||
<FormControl>
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
<Input
|
className="space-y-4"
|
||||||
placeholder="Enter an email"
|
id="invite-user-form"
|
||||||
{...field}
|
>
|
||||||
/>
|
<FormField
|
||||||
</FormControl>
|
control={form.control}
|
||||||
<FormMessage />
|
name="email"
|
||||||
</FormItem>
|
render={({ field }) => (
|
||||||
)}
|
<FormItem>
|
||||||
/>
|
<FormLabel>Email</FormLabel>
|
||||||
<FormField
|
<FormControl>
|
||||||
control={form.control}
|
<Input
|
||||||
name="roleId"
|
placeholder="Enter an email"
|
||||||
render={({ field }) => (
|
{...field}
|
||||||
<FormItem>
|
/>
|
||||||
<FormLabel>Role</FormLabel>
|
</FormControl>
|
||||||
<Select
|
<FormMessage />
|
||||||
onValueChange={field.onChange}
|
</FormItem>
|
||||||
defaultValue={field.value.toString()}
|
)}
|
||||||
>
|
/>
|
||||||
<FormControl>
|
<FormField
|
||||||
<SelectTrigger>
|
control={form.control}
|
||||||
<SelectValue placeholder="Select role" />
|
name="roleId"
|
||||||
</SelectTrigger>
|
render={({ field }) => (
|
||||||
</FormControl>
|
<FormItem>
|
||||||
<SelectContent>
|
<FormLabel>Role</FormLabel>
|
||||||
{roles.map((role) => (
|
<Select
|
||||||
<SelectItem
|
onValueChange={
|
||||||
key={role.roleId}
|
field.onChange
|
||||||
value={role.roleId.toString()}
|
}
|
||||||
|
defaultValue={field.value.toString()}
|
||||||
>
|
>
|
||||||
{role.name}
|
<FormControl>
|
||||||
</SelectItem>
|
<SelectTrigger>
|
||||||
))}
|
<SelectValue placeholder="Select role" />
|
||||||
</SelectContent>
|
</SelectTrigger>
|
||||||
</Select>
|
</FormControl>
|
||||||
<FormMessage />
|
<SelectContent>
|
||||||
</FormItem>
|
{roles.map((role) => (
|
||||||
)}
|
<SelectItem
|
||||||
/>
|
key={
|
||||||
<FormField
|
role.roleId
|
||||||
control={form.control}
|
}
|
||||||
name="validForHours"
|
value={role.roleId.toString()}
|
||||||
render={({ field }) => (
|
>
|
||||||
<FormItem>
|
{role.name}
|
||||||
<FormLabel>Valid For</FormLabel>
|
</SelectItem>
|
||||||
<Select
|
))}
|
||||||
onValueChange={field.onChange}
|
</SelectContent>
|
||||||
defaultValue={field.value.toString()}
|
</Select>
|
||||||
>
|
<FormMessage />
|
||||||
<FormControl>
|
</FormItem>
|
||||||
<SelectTrigger>
|
)}
|
||||||
<SelectValue placeholder="Select duration" />
|
/>
|
||||||
</SelectTrigger>
|
<FormField
|
||||||
</FormControl>
|
control={form.control}
|
||||||
<SelectContent>
|
name="validForHours"
|
||||||
{validFor.map((option) => (
|
render={({ field }) => (
|
||||||
<SelectItem
|
<FormItem>
|
||||||
key={option.hours}
|
<FormLabel>Valid For</FormLabel>
|
||||||
value={option.hours.toString()}
|
<Select
|
||||||
|
onValueChange={
|
||||||
|
field.onChange
|
||||||
|
}
|
||||||
|
defaultValue={field.value.toString()}
|
||||||
>
|
>
|
||||||
{option.name}
|
<FormControl>
|
||||||
</SelectItem>
|
<SelectTrigger>
|
||||||
))}
|
<SelectValue placeholder="Select duration" />
|
||||||
</SelectContent>
|
</SelectTrigger>
|
||||||
</Select>
|
</FormControl>
|
||||||
<FormMessage />
|
<SelectContent>
|
||||||
</FormItem>
|
{validFor.map(
|
||||||
)}
|
(option) => (
|
||||||
/>
|
<SelectItem
|
||||||
|
key={
|
||||||
|
option.hours
|
||||||
|
}
|
||||||
|
value={option.hours.toString()}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
option.name
|
||||||
|
}
|
||||||
|
</SelectItem>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{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.
|
||||||
|
</p>
|
||||||
|
<p className="mb-4">
|
||||||
|
The invite will expire in{" "}
|
||||||
|
<b>
|
||||||
|
{expiresInDays}{" "}
|
||||||
|
{expiresInDays === 1 ? "day" : "days"}
|
||||||
|
</b>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
<CopyTextBox
|
||||||
|
text={inviteLink}
|
||||||
|
wrapText={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CredenzaBody>
|
||||||
|
<CredenzaFooter>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="w-full"
|
form="invite-user-form"
|
||||||
loading={loading}
|
loading={loading}
|
||||||
disabled={inviteLink !== null}
|
disabled={inviteLink !== null}
|
||||||
>
|
>
|
||||||
Invite User
|
Create Invitation
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
<CredenzaClose asChild>
|
||||||
</Form>
|
<Button variant="outline">Close</Button>
|
||||||
)}
|
</CredenzaClose>
|
||||||
|
</CredenzaFooter>
|
||||||
{inviteLink && (
|
</CredenzaContent>
|
||||||
<div className="max-w-md">
|
</Credenza>
|
||||||
<p className="mb-4">
|
|
||||||
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{" "}
|
|
||||||
<b>
|
|
||||||
{expiresInDays}{" "}
|
|
||||||
{expiresInDays === 1 ? "day" : "days"}
|
|
||||||
</b>
|
|
||||||
.
|
|
||||||
</p>
|
|
||||||
<CopyTextBox text={inviteLink} wrapText={false} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,16 +10,8 @@ import {
|
||||||
import { Button } from "@app/components/ui/button";
|
import { Button } from "@app/components/ui/button";
|
||||||
import { ArrowUpDown, MoreHorizontal } from "lucide-react";
|
import { ArrowUpDown, MoreHorizontal } from "lucide-react";
|
||||||
import { UsersDataTable } from "./UsersDataTable";
|
import { UsersDataTable } from "./UsersDataTable";
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogDescription,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
} from "@app/components/ui/dialog";
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import InviteUserForm from "./InviteUserForm";
|
import InviteUserForm from "./InviteUserForm";
|
||||||
import { Credenza, CredenzaTitle, CredenzaDescription, CredenzaHeader, CredenzaClose, CredenzaFooter, CredenzaContent, CredenzaBody } from "@app/components/Credenza";
|
|
||||||
|
|
||||||
export type UserRow = {
|
export type UserRow = {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -74,19 +66,10 @@ export default function UsersTable({ users }: UsersTableProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Credenza open={isInviteModalOpen} onOpenChange={setIsInviteModalOpen}>
|
<InviteUserForm
|
||||||
<CredenzaContent>
|
open={isInviteModalOpen}
|
||||||
<CredenzaHeader>
|
setOpen={setIsInviteModalOpen}
|
||||||
<CredenzaTitle>Invite User</CredenzaTitle>
|
/>
|
||||||
<CredenzaDescription>
|
|
||||||
Give new users access to your organization
|
|
||||||
</CredenzaDescription>
|
|
||||||
</CredenzaHeader>
|
|
||||||
<CredenzaBody>
|
|
||||||
<InviteUserForm />
|
|
||||||
</CredenzaBody>
|
|
||||||
</CredenzaContent>
|
|
||||||
</Credenza>
|
|
||||||
|
|
||||||
<UsersDataTable
|
<UsersDataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
|
|
@ -3,6 +3,9 @@ import { authCookieHeader } from "@app/api/cookies";
|
||||||
import { ListUsersResponse } from "@server/routers/user";
|
import { ListUsersResponse } from "@server/routers/user";
|
||||||
import { AxiosResponse } from "axios";
|
import { AxiosResponse } from "axios";
|
||||||
import UsersTable, { UserRow } from "./components/UsersTable";
|
import UsersTable, { UserRow } from "./components/UsersTable";
|
||||||
|
import { GetOrgResponse } from "@server/routers/org";
|
||||||
|
import { cache } from "react";
|
||||||
|
import OrgProvider from "@app/providers/OrgProvider";
|
||||||
|
|
||||||
type UsersPageProps = {
|
type UsersPageProps = {
|
||||||
params: Promise<{ orgId: string }>;
|
params: Promise<{ orgId: string }>;
|
||||||
|
@ -10,15 +13,36 @@ type UsersPageProps = {
|
||||||
|
|
||||||
export default async function UsersPage(props: UsersPageProps) {
|
export default async function UsersPage(props: UsersPageProps) {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
|
|
||||||
let users: ListUsersResponse["users"] = [];
|
let users: ListUsersResponse["users"] = [];
|
||||||
try {
|
const res = await internal
|
||||||
const res = await internal.get<AxiosResponse<ListUsersResponse>>(
|
.get<AxiosResponse<ListUsersResponse>>(
|
||||||
`/org/${params.orgId}/users`,
|
`/org/${params.orgId}/users`,
|
||||||
await authCookieHeader()
|
await authCookieHeader()
|
||||||
);
|
)
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res && res.status === 200) {
|
||||||
users = res.data.data.users;
|
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) => {
|
const userRows: UserRow[] = users.map((user) => {
|
||||||
|
@ -40,7 +64,9 @@ export default async function UsersPage(props: UsersPageProps) {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UsersTable users={userRows} />
|
<OrgProvider org={org}>
|
||||||
|
<UsersTable users={userRows} />
|
||||||
|
</OrgProvider>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,16 @@ import LoginForm from "@app/app/auth/login/LoginForm";
|
||||||
import { verifySession } from "@app/lib/auth/verifySession";
|
import { verifySession } from "@app/lib/auth/verifySession";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
import { cache } from "react";
|
||||||
|
|
||||||
|
export const dynamic = 'force-dynamic';
|
||||||
|
|
||||||
export default async function Page(props: {
|
export default async function Page(props: {
|
||||||
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
|
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
|
||||||
}) {
|
}) {
|
||||||
const searchParams = await props.searchParams;
|
const searchParams = await props.searchParams;
|
||||||
const user = await verifySession();
|
const getUser = cache(verifySession);
|
||||||
|
const user = await getUser();
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
redirect("/");
|
redirect("/");
|
||||||
|
|
|
@ -2,12 +2,16 @@ import SignupForm from "@app/app/auth/signup/SignupForm";
|
||||||
import { verifySession } from "@app/lib/auth/verifySession";
|
import { verifySession } from "@app/lib/auth/verifySession";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
import { cache } from "react";
|
||||||
|
|
||||||
|
export const dynamic = 'force-dynamic';
|
||||||
|
|
||||||
export default async function Page(props: {
|
export default async function Page(props: {
|
||||||
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
|
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
|
||||||
}) {
|
}) {
|
||||||
const searchParams = await props.searchParams;
|
const searchParams = await props.searchParams;
|
||||||
const user = await verifySession();
|
const getUser = cache(verifySession);
|
||||||
|
const user = await getUser();
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
redirect("/");
|
redirect("/");
|
||||||
|
|
|
@ -32,7 +32,7 @@ import { AxiosResponse } from "axios";
|
||||||
import { VerifyEmailResponse } from "@server/routers/auth";
|
import { VerifyEmailResponse } from "@server/routers/auth";
|
||||||
import { Loader2 } from "lucide-react";
|
import { Loader2 } from "lucide-react";
|
||||||
import { Alert, AlertDescription } from "../../../components/ui/alert";
|
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";
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
const FormSchema = z.object({
|
const FormSchema = z.object({
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import VerifyEmailForm from "@app/app/auth/verify-email/VerifyEmailForm";
|
import VerifyEmailForm from "@app/app/auth/verify-email/VerifyEmailForm";
|
||||||
import { verifySession } from "@app/lib/auth/verifySession";
|
import { verifySession } from "@app/lib/auth/verifySession";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
import { cache } from "react";
|
||||||
|
|
||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
export default async function Page(props: {
|
export default async function Page(props: {
|
||||||
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
|
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
|
||||||
|
@ -10,7 +13,8 @@ export default async function Page(props: {
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchParams = await props.searchParams;
|
const searchParams = await props.searchParams;
|
||||||
const user = await verifySession();
|
const getUser = cache(verifySession);
|
||||||
|
const user = await getUser();
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
redirect("/");
|
redirect("/");
|
||||||
|
|
|
@ -7,12 +7,16 @@ import { AxiosResponse } from "axios";
|
||||||
import { ArrowUpRight } from "lucide-react";
|
import { ArrowUpRight } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
import { cache } from "react";
|
||||||
|
|
||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
export default async function Page(props: {
|
export default async function Page(props: {
|
||||||
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
|
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
|
||||||
}) {
|
}) {
|
||||||
const params = await props.searchParams; // this is needed to prevent static optimization
|
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) {
|
if (!user) {
|
||||||
redirect("/auth/login");
|
redirect("/auth/login");
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { useForm } from "react-hook-form"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { toast } from "@/hooks/use-toast"
|
import { toast } from "@/hooks/useToast"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import {
|
import {
|
||||||
Command,
|
Command,
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { useForm } from "react-hook-form"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { toast } from "@/hooks/use-toast"
|
import { toast } from "@/hooks/useToast"
|
||||||
import { Button, buttonVariants } from "@/components/ui/button"
|
import { Button, buttonVariants } from "@/components/ui/button"
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { useForm } from "react-hook-form"
|
import { useForm } from "react-hook-form"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
import { toast } from "@/hooks/use-toast"
|
import { toast } from "@/hooks/useToast"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { Checkbox } from "@/components/ui/checkbox"
|
import { Checkbox } from "@/components/ui/checkbox"
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { useForm } from "react-hook-form"
|
import { useForm } from "react-hook-form"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
import { toast } from "@/hooks/use-toast"
|
import { toast } from "@/hooks/useToast"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { Checkbox } from "@/components/ui/checkbox"
|
import { Checkbox } from "@/components/ui/checkbox"
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { useFieldArray, useForm } from "react-hook-form"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { toast } from "@/hooks/use-toast"
|
import { toast } from "@/hooks/useToast"
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import api from "@app/api";
|
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 { useCallback, useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { useForm } from "react-hook-form"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { toast } from "@/hooks/use-toast"
|
import { toast } from "@/hooks/useToast"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import {
|
import {
|
||||||
Command,
|
Command,
|
||||||
|
|
|
@ -1,169 +1,179 @@
|
||||||
"use client"
|
"use client";
|
||||||
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { ChevronDownIcon } from "@radix-ui/react-icons"
|
import { ChevronDownIcon } from "@radix-ui/react-icons";
|
||||||
import { useForm } from "react-hook-form"
|
import { useForm } from "react-hook-form";
|
||||||
import { z } from "zod"
|
import { z } from "zod";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils";
|
||||||
import { toast } from "@/hooks/use-toast"
|
import { toast } from "@/hooks/useToast";
|
||||||
import { Button, buttonVariants } from "@/components/ui/button"
|
import { Button, buttonVariants } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
FormDescription,
|
FormDescription,
|
||||||
FormField,
|
FormField,
|
||||||
FormItem,
|
FormItem,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from "@/components/ui/form"
|
} from "@/components/ui/form";
|
||||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
import { useSiteContext } from "@app/hooks/useSiteContext"
|
import { useSiteContext } from "@app/hooks/useSiteContext";
|
||||||
|
|
||||||
const appearanceFormSchema = z.object({
|
const appearanceFormSchema = z.object({
|
||||||
theme: z.enum(["light", "dark"], {
|
theme: z.enum(["light", "dark"], {
|
||||||
required_error: "Please select a theme.",
|
required_error: "Please select a theme.",
|
||||||
}),
|
}),
|
||||||
font: z.enum(["inter", "manrope", "system"], {
|
font: z.enum(["inter", "manrope", "system"], {
|
||||||
invalid_type_error: "Select a font",
|
invalid_type_error: "Select a font",
|
||||||
required_error: "Please 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.
|
// This can come from your database or API.
|
||||||
const defaultValues: Partial<AppearanceFormValues> = {
|
const defaultValues: Partial<AppearanceFormValues> = {
|
||||||
theme: "light",
|
theme: "light",
|
||||||
}
|
};
|
||||||
|
|
||||||
export function AppearanceForm() {
|
export function AppearanceForm() {
|
||||||
const site = useSiteContext();
|
const site = useSiteContext();
|
||||||
|
|
||||||
console.log(site);
|
console.log(site);
|
||||||
|
|
||||||
const form = useForm<AppearanceFormValues>({
|
|
||||||
resolver: zodResolver(appearanceFormSchema),
|
|
||||||
defaultValues,
|
|
||||||
})
|
|
||||||
|
|
||||||
function onSubmit(data: AppearanceFormValues) {
|
const form = useForm<AppearanceFormValues>({
|
||||||
toast({
|
resolver: zodResolver(appearanceFormSchema),
|
||||||
title: "You submitted the following values:",
|
defaultValues,
|
||||||
description: (
|
});
|
||||||
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
|
|
||||||
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
|
|
||||||
</pre>
|
|
||||||
),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
function onSubmit(data: AppearanceFormValues) {
|
||||||
<Form {...form}>
|
toast({
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
title: "You submitted the following values:",
|
||||||
<FormField
|
description: (
|
||||||
control={form.control}
|
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
|
||||||
name="font"
|
<code className="text-white">
|
||||||
render={({ field }) => (
|
{JSON.stringify(data, null, 2)}
|
||||||
<FormItem>
|
</code>
|
||||||
<FormLabel>Font</FormLabel>
|
</pre>
|
||||||
<div className="relative w-max">
|
),
|
||||||
<FormControl>
|
});
|
||||||
<select
|
}
|
||||||
className={cn(
|
|
||||||
buttonVariants({ variant: "outline" }),
|
return (
|
||||||
"w-[200px] appearance-none font-normal"
|
<Form {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="font"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Font</FormLabel>
|
||||||
|
<div className="relative w-max">
|
||||||
|
<FormControl>
|
||||||
|
<select
|
||||||
|
className={cn(
|
||||||
|
buttonVariants({
|
||||||
|
variant: "outline",
|
||||||
|
}),
|
||||||
|
"w-[200px] appearance-none font-normal"
|
||||||
|
)}
|
||||||
|
{...field}
|
||||||
|
>
|
||||||
|
<option value="inter">Inter</option>
|
||||||
|
<option value="manrope">Manrope</option>
|
||||||
|
<option value="system">System</option>
|
||||||
|
</select>
|
||||||
|
</FormControl>
|
||||||
|
<ChevronDownIcon className="absolute right-3 top-2.5 h-4 w-4 opacity-50" />
|
||||||
|
</div>
|
||||||
|
<FormDescription>
|
||||||
|
Set the font you want to use in the dashboard.
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
{...field}
|
/>
|
||||||
>
|
<FormField
|
||||||
<option value="inter">Inter</option>
|
control={form.control}
|
||||||
<option value="manrope">Manrope</option>
|
name="theme"
|
||||||
<option value="system">System</option>
|
render={({ field }) => (
|
||||||
</select>
|
<FormItem className="space-y-1">
|
||||||
</FormControl>
|
<FormLabel>Theme</FormLabel>
|
||||||
<ChevronDownIcon className="absolute right-3 top-2.5 h-4 w-4 opacity-50" />
|
<FormDescription>
|
||||||
</div>
|
Select the theme for the dashboard.
|
||||||
<FormDescription>
|
</FormDescription>
|
||||||
Set the font you want to use in the dashboard.
|
<FormMessage />
|
||||||
</FormDescription>
|
<RadioGroup
|
||||||
<FormMessage />
|
onValueChange={field.onChange}
|
||||||
</FormItem>
|
defaultValue={field.value}
|
||||||
)}
|
className="grid max-w-md grid-cols-2 gap-8 pt-2"
|
||||||
/>
|
>
|
||||||
<FormField
|
<FormItem>
|
||||||
control={form.control}
|
<FormLabel className="[&:has([data-state=checked])>div]:border-primary">
|
||||||
name="theme"
|
<FormControl>
|
||||||
render={({ field }) => (
|
<RadioGroupItem
|
||||||
<FormItem className="space-y-1">
|
value="light"
|
||||||
<FormLabel>Theme</FormLabel>
|
className="sr-only"
|
||||||
<FormDescription>
|
/>
|
||||||
Select the theme for the dashboard.
|
</FormControl>
|
||||||
</FormDescription>
|
<div className="items-center rounded-md border-2 border-muted p-1 hover:border-accent">
|
||||||
<FormMessage />
|
<div className="space-y-2 rounded-sm bg-[#ecedef] p-2">
|
||||||
<RadioGroup
|
<div className="space-y-2 rounded-md bg-white p-2 shadow-sm">
|
||||||
onValueChange={field.onChange}
|
<div className="h-2 w-[80px] rounded-lg bg-[#ecedef]" />
|
||||||
defaultValue={field.value}
|
<div className="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
|
||||||
className="grid max-w-md grid-cols-2 gap-8 pt-2"
|
</div>
|
||||||
>
|
<div className="flex items-center space-x-2 rounded-md bg-white p-2 shadow-sm">
|
||||||
<FormItem>
|
<div className="h-4 w-4 rounded-full bg-[#ecedef]" />
|
||||||
<FormLabel className="[&:has([data-state=checked])>div]:border-primary">
|
<div className="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
|
||||||
<FormControl>
|
</div>
|
||||||
<RadioGroupItem value="light" className="sr-only" />
|
<div className="flex items-center space-x-2 rounded-md bg-white p-2 shadow-sm">
|
||||||
</FormControl>
|
<div className="h-4 w-4 rounded-full bg-[#ecedef]" />
|
||||||
<div className="items-center rounded-md border-2 border-muted p-1 hover:border-accent">
|
<div className="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
|
||||||
<div className="space-y-2 rounded-sm bg-[#ecedef] p-2">
|
</div>
|
||||||
<div className="space-y-2 rounded-md bg-white p-2 shadow-sm">
|
</div>
|
||||||
<div className="h-2 w-[80px] rounded-lg bg-[#ecedef]" />
|
</div>
|
||||||
<div className="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
|
<span className="block w-full p-2 text-center font-normal">
|
||||||
</div>
|
Light
|
||||||
<div className="flex items-center space-x-2 rounded-md bg-white p-2 shadow-sm">
|
</span>
|
||||||
<div className="h-4 w-4 rounded-full bg-[#ecedef]" />
|
</FormLabel>
|
||||||
<div className="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
|
</FormItem>
|
||||||
</div>
|
<FormItem>
|
||||||
<div className="flex items-center space-x-2 rounded-md bg-white p-2 shadow-sm">
|
<FormLabel className="[&:has([data-state=checked])>div]:border-primary">
|
||||||
<div className="h-4 w-4 rounded-full bg-[#ecedef]" />
|
<FormControl>
|
||||||
<div className="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
|
<RadioGroupItem
|
||||||
</div>
|
value="dark"
|
||||||
</div>
|
className="sr-only"
|
||||||
</div>
|
/>
|
||||||
<span className="block w-full p-2 text-center font-normal">
|
</FormControl>
|
||||||
Light
|
<div className="items-center rounded-md border-2 border-muted bg-popover p-1 hover:bg-accent hover:text-accent-foreground">
|
||||||
</span>
|
<div className="space-y-2 rounded-sm bg-slate-950 p-2">
|
||||||
</FormLabel>
|
<div className="space-y-2 rounded-md bg-slate-800 p-2 shadow-sm">
|
||||||
</FormItem>
|
<div className="h-2 w-[80px] rounded-lg bg-slate-400" />
|
||||||
<FormItem>
|
<div className="h-2 w-[100px] rounded-lg bg-slate-400" />
|
||||||
<FormLabel className="[&:has([data-state=checked])>div]:border-primary">
|
</div>
|
||||||
<FormControl>
|
<div className="flex items-center space-x-2 rounded-md bg-slate-800 p-2 shadow-sm">
|
||||||
<RadioGroupItem value="dark" className="sr-only" />
|
<div className="h-4 w-4 rounded-full bg-slate-400" />
|
||||||
</FormControl>
|
<div className="h-2 w-[100px] rounded-lg bg-slate-400" />
|
||||||
<div className="items-center rounded-md border-2 border-muted bg-popover p-1 hover:bg-accent hover:text-accent-foreground">
|
</div>
|
||||||
<div className="space-y-2 rounded-sm bg-slate-950 p-2">
|
<div className="flex items-center space-x-2 rounded-md bg-slate-800 p-2 shadow-sm">
|
||||||
<div className="space-y-2 rounded-md bg-slate-800 p-2 shadow-sm">
|
<div className="h-4 w-4 rounded-full bg-slate-400" />
|
||||||
<div className="h-2 w-[80px] rounded-lg bg-slate-400" />
|
<div className="h-2 w-[100px] rounded-lg bg-slate-400" />
|
||||||
<div className="h-2 w-[100px] rounded-lg bg-slate-400" />
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2 rounded-md bg-slate-800 p-2 shadow-sm">
|
</div>
|
||||||
<div className="h-4 w-4 rounded-full bg-slate-400" />
|
<span className="block w-full p-2 text-center font-normal">
|
||||||
<div className="h-2 w-[100px] rounded-lg bg-slate-400" />
|
Dark
|
||||||
</div>
|
</span>
|
||||||
<div className="flex items-center space-x-2 rounded-md bg-slate-800 p-2 shadow-sm">
|
</FormLabel>
|
||||||
<div className="h-4 w-4 rounded-full bg-slate-400" />
|
</FormItem>
|
||||||
<div className="h-2 w-[100px] rounded-lg bg-slate-400" />
|
</RadioGroup>
|
||||||
</div>
|
</FormItem>
|
||||||
</div>
|
)}
|
||||||
</div>
|
/>
|
||||||
<span className="block w-full p-2 text-center font-normal">
|
|
||||||
Dark
|
|
||||||
</span>
|
|
||||||
</FormLabel>
|
|
||||||
</FormItem>
|
|
||||||
</RadioGroup>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Button type="submit">Update preferences</Button>
|
<Button type="submit">Update preferences</Button>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { useForm } from "react-hook-form"
|
import { useForm } from "react-hook-form"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
import { toast } from "@/hooks/use-toast"
|
import { toast } from "@/hooks/useToast"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { Checkbox } from "@/components/ui/checkbox"
|
import { Checkbox } from "@/components/ui/checkbox"
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { useForm } from "react-hook-form"
|
import { useForm } from "react-hook-form"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
import { toast } from "@/hooks/use-toast"
|
import { toast } from "@/hooks/useToast"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { Checkbox } from "@/components/ui/checkbox"
|
import { Checkbox } from "@/components/ui/checkbox"
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { useFieldArray, useForm } from "react-hook-form"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { toast } from "@/hooks/use-toast"
|
import { toast } from "@/hooks/useToast"
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useToast } from "@/hooks/use-toast"
|
import { useToast } from "@/hooks/useToast"
|
||||||
import {
|
import {
|
||||||
Toast,
|
Toast,
|
||||||
ToastClose,
|
ToastClose,
|
||||||
|
|
11
src/contexts/orgContext.ts
Normal file
11
src/contexts/orgContext.ts
Normal 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;
|
|
@ -3,9 +3,11 @@ import { createContext } from "react";
|
||||||
|
|
||||||
interface ResourceContextType {
|
interface ResourceContextType {
|
||||||
resource: GetResourceResponse | null;
|
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;
|
export default ResourceContext;
|
||||||
|
|
|
@ -3,9 +3,9 @@ import { createContext } from "react";
|
||||||
|
|
||||||
interface SiteContextType {
|
interface SiteContextType {
|
||||||
site: GetSiteResponse | null;
|
site: GetSiteResponse | null;
|
||||||
updateSite: (updatedSite: Partial<GetSiteResponse>) => Promise<void>;
|
updateSite: (updatedSite: Partial<GetSiteResponse>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SiteContext = createContext<SiteContextType | undefined>(undefined);
|
const SiteContext = createContext<SiteContextType | undefined>(undefined);
|
||||||
|
|
||||||
export default SiteContext;
|
export default SiteContext;
|
||||||
|
|
10
src/hooks/useOrgContext.ts
Normal file
10
src/hooks/useOrgContext.ts
Normal 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;
|
||||||
|
}
|
|
@ -4,7 +4,9 @@ import { useContext } from "react";
|
||||||
export function useResourceContext() {
|
export function useResourceContext() {
|
||||||
const context = useContext(ResourceContext);
|
const context = useContext(ResourceContext);
|
||||||
if (context === undefined) {
|
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;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,4 +14,4 @@ export async function verifySession(): Promise<GetUserResponse | null> {
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
39
src/providers/OrgProvider.tsx
Normal file
39
src/providers/OrgProvider.tsx
Normal 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;
|
|
@ -1,10 +1,7 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import api from "@app/api";
|
|
||||||
import ResourceContext from "@app/contexts/resourceContext";
|
import ResourceContext from "@app/contexts/resourceContext";
|
||||||
import { toast } from "@app/hooks/use-toast";
|
|
||||||
import { GetResourceResponse } from "@server/routers/resource/getResource";
|
import { GetResourceResponse } from "@server/routers/resource/getResource";
|
||||||
import { AxiosResponse } from "axios";
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
interface ResourceProviderProps {
|
interface ResourceProviderProps {
|
||||||
|
@ -12,34 +9,36 @@ interface ResourceProviderProps {
|
||||||
resource: GetResourceResponse | null;
|
resource: GetResourceResponse | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ResourceProvider({ children, resource: serverResource }: ResourceProviderProps) {
|
export function ResourceProvider({
|
||||||
const [resource, setResource] = useState<GetResourceResponse | null>(serverResource);
|
children,
|
||||||
|
resource: serverResource,
|
||||||
|
}: ResourceProviderProps) {
|
||||||
|
const [resource, setResource] = useState<GetResourceResponse | null>(
|
||||||
|
serverResource
|
||||||
|
);
|
||||||
|
|
||||||
const updateResource = async (updatedResource: Partial<GetResourceResponse>) => {
|
const updateResource = (updatedResource: Partial<GetResourceResponse>) => {
|
||||||
try {
|
if (!resource) {
|
||||||
if (!resource) {
|
throw new Error("No resource to update");
|
||||||
throw new Error("No resource to update");
|
}
|
||||||
|
|
||||||
|
setResource((prev) => {
|
||||||
|
if (!prev) {
|
||||||
|
return prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await api.post<AxiosResponse<GetResourceResponse>>(
|
return {
|
||||||
`resource/${resource.resourceId}`,
|
...prev,
|
||||||
updatedResource,
|
...updatedResource,
|
||||||
);
|
};
|
||||||
setResource(res.data.data);
|
});
|
||||||
toast({
|
|
||||||
title: "Resource updated!",
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
toast({
|
|
||||||
variant: "destructive",
|
|
||||||
title: "Error updating resource...",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
return <ResourceContext.Provider value={{ resource, updateResource }}>{children}</ResourceContext.Provider>;
|
<ResourceContext.Provider value={{ resource, updateResource }}>
|
||||||
|
{children}
|
||||||
|
</ResourceContext.Provider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ResourceProvider;
|
export default ResourceProvider;
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import api from "@app/api";
|
|
||||||
import SiteContext from "@app/contexts/siteContext";
|
import SiteContext from "@app/contexts/siteContext";
|
||||||
import { toast } from "@app/hooks/use-toast";
|
|
||||||
import { GetSiteResponse } from "@server/routers/site/getSite";
|
import { GetSiteResponse } from "@server/routers/site/getSite";
|
||||||
import { AxiosResponse } from "axios";
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
interface SiteProviderProps {
|
interface SiteProviderProps {
|
||||||
|
@ -12,34 +9,32 @@ interface SiteProviderProps {
|
||||||
site: GetSiteResponse | null;
|
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 [site, setSite] = useState<GetSiteResponse | null>(serverSite);
|
||||||
|
|
||||||
const updateSite = async (updatedSite: Partial<GetSiteResponse>) => {
|
const updateSite = (updatedSite: Partial<GetSiteResponse>) => {
|
||||||
try {
|
if (!site) {
|
||||||
if (!site) {
|
throw new Error("No site to update");
|
||||||
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 (
|
||||||
return <SiteContext.Provider value={{ site, updateSite }}>{children}</SiteContext.Provider>;
|
<SiteContext.Provider value={{ site, updateSite }}>
|
||||||
|
{children}
|
||||||
|
</SiteContext.Provider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SiteProvider;
|
export default SiteProvider;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue