mirror of
https://github.com/fosrl/pangolin.git
synced 2025-08-28 13:48:13 +02:00
Merge branch 'main' of https://github.com/fosrl/pangolin
This commit is contained in:
commit
3e2085fbb3
57 changed files with 2604 additions and 2104 deletions
|
@ -1,3 +1,6 @@
|
||||||
{
|
{
|
||||||
"extends": ["next/core-web-vitals", "next/typescript"]
|
"extends": [
|
||||||
|
"next/core-web-vitals",
|
||||||
|
"next/typescript"
|
||||||
|
]
|
||||||
}
|
}
|
11
bruno/Auth/login.bru
Normal file
11
bruno/Auth/login.bru
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
meta {
|
||||||
|
name: login
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url:
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
11
bruno/Orgs/listOrgs.bru
Normal file
11
bruno/Orgs/listOrgs.bru
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
meta {
|
||||||
|
name: listOrgs
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url:
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
11
bruno/Resources/listResourcesByOrg.bru
Normal file
11
bruno/Resources/listResourcesByOrg.bru
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
meta {
|
||||||
|
name: listResourcesByOrg
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url:
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
16
bruno/Resources/listResourcesBySite.bru
Normal file
16
bruno/Resources/listResourcesBySite.bru
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
meta {
|
||||||
|
name: listResourcesBySite
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: http://localhost:3000/api/v1/site/1/resources?limit=10&offset=0
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
|
|
||||||
|
params:query {
|
||||||
|
limit: 10
|
||||||
|
offset: 0
|
||||||
|
}
|
11
bruno/Sites/listSites.bru
Normal file
11
bruno/Sites/listSites.bru
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
meta {
|
||||||
|
name: listSites
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url:
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
16
bruno/Targets/listTargets.bru
Normal file
16
bruno/Targets/listTargets.bru
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
meta {
|
||||||
|
name: listTargets
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: http://localhost:3000/api/v1/resource/web.main.localhost/targets?limit=10&offset=0
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
|
|
||||||
|
params:query {
|
||||||
|
limit: 10
|
||||||
|
offset: 0
|
||||||
|
}
|
9
bruno/bruno.json
Normal file
9
bruno/bruno.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"version": "1",
|
||||||
|
"name": "Pangolin",
|
||||||
|
"type": "collection",
|
||||||
|
"ignore": [
|
||||||
|
"node_modules",
|
||||||
|
".git"
|
||||||
|
]
|
||||||
|
}
|
96
server/auth/actions.ts
Normal file
96
server/auth/actions.ts
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
import { Request } from 'express';
|
||||||
|
import { db } from '@server/db';
|
||||||
|
import { userActions, roleActions, userOrgs } from '@server/db/schema';
|
||||||
|
import { and, eq } from 'drizzle-orm';
|
||||||
|
import createHttpError from 'http-errors';
|
||||||
|
import HttpCode from '@server/types/HttpCode';
|
||||||
|
|
||||||
|
export enum ActionsEnum {
|
||||||
|
createOrg = "createOrg",
|
||||||
|
deleteOrg = "deleteOrg",
|
||||||
|
getOrg = "getOrg",
|
||||||
|
listOrgs = "listOrgs",
|
||||||
|
updateOrg = "updateOrg",
|
||||||
|
createSite = "createSite",
|
||||||
|
deleteSite = "deleteSite",
|
||||||
|
getSite = "getSite",
|
||||||
|
listSites = "listSites",
|
||||||
|
updateSite = "updateSite",
|
||||||
|
createResource = "createResource",
|
||||||
|
deleteResource = "deleteResource",
|
||||||
|
getResource = "getResource",
|
||||||
|
listResources = "listResources",
|
||||||
|
updateResource = "updateResource",
|
||||||
|
createTarget = "createTarget",
|
||||||
|
deleteTarget = "deleteTarget",
|
||||||
|
getTarget = "getTarget",
|
||||||
|
listTargets = "listTargets",
|
||||||
|
updateTarget = "updateTarget",
|
||||||
|
getUser = "getUser",
|
||||||
|
deleteUser = "deleteUser",
|
||||||
|
listUsers = "listUsers"
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkUserActionPermission(actionId: string, req: Request): Promise<boolean> {
|
||||||
|
const userId = req.user?.id;
|
||||||
|
|
||||||
|
if (!userId) {
|
||||||
|
throw createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!req.userOrgId) {
|
||||||
|
throw createHttpError(HttpCode.BAD_REQUEST, 'Organization ID is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let userOrgRoleId = req.userOrgRoleId;
|
||||||
|
|
||||||
|
// If userOrgRoleId is not available on the request, fetch it
|
||||||
|
if (userOrgRoleId === undefined) {
|
||||||
|
const userOrgRole = await db.select()
|
||||||
|
.from(userOrgs)
|
||||||
|
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, req.userOrgId)))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (userOrgRole.length === 0) {
|
||||||
|
throw createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization');
|
||||||
|
}
|
||||||
|
|
||||||
|
userOrgRoleId = userOrgRole[0].roleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the user has direct permission for the action in the current org
|
||||||
|
const userActionPermission = await db.select()
|
||||||
|
.from(userActions)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(userActions.userId, userId),
|
||||||
|
eq(userActions.actionId, actionId),
|
||||||
|
eq(userActions.orgId, req.userOrgId)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (userActionPermission.length > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no direct permission, check role-based permission
|
||||||
|
const roleActionPermission = await db.select()
|
||||||
|
.from(roleActions)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(roleActions.actionId, actionId),
|
||||||
|
eq(roleActions.roleId, userOrgRoleId),
|
||||||
|
eq(roleActions.orgId, req.userOrgId)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
return roleActionPermission.length > 0;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error checking user action permission:', error);
|
||||||
|
throw createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error checking action permission');
|
||||||
|
}
|
||||||
|
}
|
40
server/auth/limits.ts
Normal file
40
server/auth/limits.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import { db } from '@server/db';
|
||||||
|
import { limitsTable } from '@server/db/schema';
|
||||||
|
import { and, eq } from 'drizzle-orm';
|
||||||
|
import createHttpError from 'http-errors';
|
||||||
|
import HttpCode from '@server/types/HttpCode';
|
||||||
|
|
||||||
|
interface CheckLimitOptions {
|
||||||
|
orgId: number;
|
||||||
|
limitName: string;
|
||||||
|
currentValue: number;
|
||||||
|
increment?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkOrgLimit({ orgId, limitName, currentValue, increment = 0 }: CheckLimitOptions): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const limit = await db.select()
|
||||||
|
.from(limitsTable)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(limitsTable.orgId, orgId),
|
||||||
|
eq(limitsTable.name, limitName)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (limit.length === 0) {
|
||||||
|
throw createHttpError(HttpCode.NOT_FOUND, `Limit "${limitName}" not found for organization`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const limitValue = limit[0].value;
|
||||||
|
|
||||||
|
// Check if the current value plus the increment is within the limit
|
||||||
|
return (currentValue + increment) <= limitValue;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
throw createHttpError(HttpCode.INTERNAL_SERVER_ERROR, `Error checking limit: ${error.message}`);
|
||||||
|
}
|
||||||
|
throw createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Unknown error occurred while checking limit');
|
||||||
|
}
|
||||||
|
}
|
|
@ -107,7 +107,7 @@ export const userOrgs = sqliteTable("userOrgs", {
|
||||||
orgId: integer("orgId")
|
orgId: integer("orgId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => orgs.orgId),
|
.references(() => orgs.orgId),
|
||||||
role: text("role").notNull(), // e.g., 'admin', 'member', etc.
|
roleId: integer("roleId").notNull().references(() => roles.roleId),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const emailVerificationCodes = sqliteTable("emailVerificationCodes", {
|
export const emailVerificationCodes = sqliteTable("emailVerificationCodes", {
|
||||||
|
@ -130,7 +130,7 @@ export const passwordResetTokens = sqliteTable("passwordResetTokens", {
|
||||||
});
|
});
|
||||||
|
|
||||||
export const actions = sqliteTable("actions", {
|
export const actions = sqliteTable("actions", {
|
||||||
actionId: integer("actionId").primaryKey({ autoIncrement: true }),
|
actionId: text("actionId").primaryKey(),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
description: text("description"),
|
description: text("description"),
|
||||||
});
|
});
|
||||||
|
@ -146,22 +146,28 @@ export const roleActions = sqliteTable("roleActions", {
|
||||||
roleId: integer("roleId")
|
roleId: integer("roleId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => roles.roleId, { onDelete: "cascade" }),
|
.references(() => roles.roleId, { onDelete: "cascade" }),
|
||||||
actionId: integer("actionId")
|
actionId: text("actionId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => actions.actionId, { onDelete: "cascade" }),
|
.references(() => actions.actionId, { onDelete: "cascade" }),
|
||||||
|
orgId: integer("orgId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userActions = sqliteTable("userActions", {
|
export const userActions = sqliteTable("userActions", {
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.id, { onDelete: "cascade" }),
|
.references(() => users.id, { onDelete: "cascade" }),
|
||||||
actionId: integer("actionId")
|
actionId: text("actionId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => actions.actionId, { onDelete: "cascade" }),
|
.references(() => actions.actionId, { onDelete: "cascade" }),
|
||||||
|
orgId: integer("orgId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const roleSites = sqliteTable("roleActions", {
|
export const roleSites = sqliteTable("roleSites", {
|
||||||
roleId: integer("role]Id")
|
roleId: integer("roleId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => roles.roleId, { onDelete: "cascade" }),
|
.references(() => roles.roleId, { onDelete: "cascade" }),
|
||||||
siteId: integer("siteId")
|
siteId: integer("siteId")
|
||||||
|
@ -169,8 +175,8 @@ export const roleSites = sqliteTable("roleActions", {
|
||||||
.references(() => sites.siteId, { onDelete: "cascade" }),
|
.references(() => sites.siteId, { onDelete: "cascade" }),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userSites = sqliteTable("userActions", {
|
export const userSites = sqliteTable("userSites", {
|
||||||
userId: text("user]Id")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.id, { onDelete: "cascade" }),
|
.references(() => users.id, { onDelete: "cascade" }),
|
||||||
siteId: integer("siteId")
|
siteId: integer("siteId")
|
||||||
|
@ -178,24 +184,32 @@ export const userSites = sqliteTable("userActions", {
|
||||||
.references(() => sites.siteId, { onDelete: "cascade" }),
|
.references(() => sites.siteId, { onDelete: "cascade" }),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const roleResources = sqliteTable("roleActions", {
|
export const roleResources = sqliteTable("roleResources", {
|
||||||
roleId: integer("role]Id")
|
roleId: integer("roleId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => roles.roleId, { onDelete: "cascade" }),
|
.references(() => roles.roleId, { onDelete: "cascade" }),
|
||||||
resourceId: integer("resourceId")
|
resourceId: text("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userResources = sqliteTable("userActions", {
|
export const userResources = sqliteTable("userResources", {
|
||||||
userId: text("user]Id")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.id, { onDelete: "cascade" }),
|
.references(() => users.id, { onDelete: "cascade" }),
|
||||||
resourceId: integer("resourceId")
|
resourceId: text("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const limitsTable = sqliteTable("limits", {
|
||||||
|
limitId: integer("limitId").primaryKey({ autoIncrement: true }),
|
||||||
|
orgId: integer("orgId").references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||||
|
name: text("name").notNull(),
|
||||||
|
value: integer("value").notNull(),
|
||||||
|
description: text("description"),
|
||||||
|
});
|
||||||
|
|
||||||
// Define the model types for type inference
|
// Define the model types for type inference
|
||||||
export type Org = InferSelectModel<typeof orgs>;
|
export type Org = InferSelectModel<typeof orgs>;
|
||||||
export type User = InferSelectModel<typeof users>;
|
export type User = InferSelectModel<typeof users>;
|
||||||
|
@ -218,3 +232,4 @@ export type RoleSite = InferSelectModel<typeof roleSites>;
|
||||||
export type UserSite = InferSelectModel<typeof userSites>;
|
export type UserSite = InferSelectModel<typeof userSites>;
|
||||||
export type RoleResource = InferSelectModel<typeof roleResources>;
|
export type RoleResource = InferSelectModel<typeof roleResources>;
|
||||||
export type UserResource = InferSelectModel<typeof userResources>;
|
export type UserResource = InferSelectModel<typeof userResources>;
|
||||||
|
export type Limit = InferSelectModel<typeof limitsTable>;
|
|
@ -85,8 +85,9 @@ declare global {
|
||||||
namespace Express {
|
namespace Express {
|
||||||
interface Request {
|
interface Request {
|
||||||
user?: User;
|
user?: User;
|
||||||
userOrgRole?: string;
|
userOrgRoleId?: number;
|
||||||
userOrgs?: number[];
|
userOrgId?: number;
|
||||||
|
userOrgIds?: number[];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,13 +15,13 @@ export async function getUserOrgs(req: Request, res: Response, next: NextFunctio
|
||||||
try {
|
try {
|
||||||
const userOrganizations = await db.select({
|
const userOrganizations = await db.select({
|
||||||
orgId: userOrgs.orgId,
|
orgId: userOrgs.orgId,
|
||||||
role: userOrgs.role,
|
roleId: userOrgs.roleId,
|
||||||
})
|
})
|
||||||
.from(userOrgs)
|
.from(userOrgs)
|
||||||
.where(eq(userOrgs.userId, userId));
|
.where(eq(userOrgs.userId, userId));
|
||||||
|
|
||||||
req.userOrgs = userOrganizations.map(org => org.orgId);
|
req.userOrgIds = userOrganizations.map(org => org.orgId);
|
||||||
// req.userOrgRoles = userOrganizations.reduce((acc, org) => {
|
// req.userOrgRoleIds = userOrganizations.reduce((acc, org) => {
|
||||||
// acc[org.orgId] = org.role;
|
// acc[org.orgId] = org.role;
|
||||||
// return acc;
|
// return acc;
|
||||||
// }, {} as Record<number, string>);
|
// }, {} as Record<number, string>);
|
||||||
|
|
|
@ -26,7 +26,8 @@ export function verifyOrgAccess(req: Request, res: Response, next: NextFunction)
|
||||||
next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization'));
|
next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization'));
|
||||||
} else {
|
} else {
|
||||||
// User has access, attach the user's role to the request for potential future use
|
// User has access, attach the user's role to the request for potential future use
|
||||||
req.userOrgRole = result[0].role;
|
req.userOrgRoleId = result[0].roleId;
|
||||||
|
req.userOrgId = orgId;
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from 'express';
|
||||||
import { db } from '@server/db';
|
import { db } from '@server/db';
|
||||||
import { resources, userOrgs } from '@server/db/schema';
|
import { resources, userOrgs, userResources, roleResources } from '@server/db/schema';
|
||||||
import { and, eq } from 'drizzle-orm';
|
import { and, eq } from 'drizzle-orm';
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from 'http-errors';
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from '@server/types/HttpCode';
|
||||||
|
@ -13,42 +13,66 @@ export async function verifyResourceAccess(req: Request, res: Response, next: Ne
|
||||||
return next(createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated'));
|
return next(createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get the resource
|
||||||
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);
|
||||||
|
|
||||||
if (resource.length === 0) {
|
if (resource.length === 0) {
|
||||||
return next(
|
return next(createHttpError(HttpCode.NOT_FOUND, `Resource with ID ${resourceId} not found`));
|
||||||
createHttpError(
|
|
||||||
HttpCode.NOT_FOUND,
|
|
||||||
`resource with ID ${resourceId} not found`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!resource[0].orgId) {
|
if (!resource[0].orgId) {
|
||||||
return next(
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, `Resource with ID ${resourceId} does not have an organization ID`));
|
||||||
createHttpError(
|
|
||||||
HttpCode.INTERNAL_SERVER_ERROR,
|
|
||||||
`resource with ID ${resourceId} does not have an organization ID`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
db.select()
|
// Get user's role ID in the organization
|
||||||
|
const userOrgRole = await db.select()
|
||||||
.from(userOrgs)
|
.from(userOrgs)
|
||||||
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, resource[0].orgId)))
|
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, resource[0].orgId)))
|
||||||
.then((result) => {
|
.limit(1);
|
||||||
if (result.length === 0) {
|
|
||||||
next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization'));
|
if (userOrgRole.length === 0) {
|
||||||
} else {
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization'));
|
||||||
// User has access, attach the user's role to the request for potential future use
|
}
|
||||||
req.userOrgRole = result[0].role;
|
|
||||||
next();
|
const userOrgRoleId = userOrgRole[0].roleId;
|
||||||
|
req.userOrgRoleId = userOrgRoleId;
|
||||||
|
req.userOrgId = resource[0].orgId;
|
||||||
|
|
||||||
|
// Check role-based resource access first
|
||||||
|
const roleResourceAccess = await db.select()
|
||||||
|
.from(roleResources)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(roleResources.resourceId, resourceId),
|
||||||
|
eq(roleResources.roleId, userOrgRoleId)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (roleResourceAccess.length > 0) {
|
||||||
|
// User's role has access to the resource
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If role doesn't have access, check user-specific resource access
|
||||||
|
const userResourceAccess = await db.select()
|
||||||
|
.from(userResources)
|
||||||
|
.where(and(eq(userResources.userId, userId), eq(userResources.resourceId, resourceId)))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (userResourceAccess.length > 0) {
|
||||||
|
// User has direct access to the resource
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we reach here, the user doesn't have access to the resource
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this resource'));
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error verifying resource access'));
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error verifying organization access'));
|
|
||||||
});
|
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from 'express';
|
||||||
import { db } from '@server/db';
|
import { db } from '@server/db';
|
||||||
import { sites, userOrgs } from '@server/db/schema';
|
import { sites, userOrgs, userSites, roleSites, roles } from '@server/db/schema';
|
||||||
import { and, eq } from 'drizzle-orm';
|
import { and, eq, or } from 'drizzle-orm';
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from 'http-errors';
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from '@server/types/HttpCode';
|
||||||
|
|
||||||
|
@ -14,45 +14,66 @@ export async function verifySiteAccess(req: Request, res: Response, next: NextFu
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNaN(siteId)) {
|
if (isNaN(siteId)) {
|
||||||
return next(createHttpError(HttpCode.BAD_REQUEST, 'Invalid organization ID'));
|
return next(createHttpError(HttpCode.BAD_REQUEST, 'Invalid site ID'));
|
||||||
}
|
}
|
||||||
|
|
||||||
const site = await db.select()
|
try {
|
||||||
.from(sites)
|
// Get the site
|
||||||
.where(eq(sites.siteId, siteId))
|
const site = await db.select().from(sites).where(eq(sites.siteId, siteId)).limit(1);
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
if (site.length === 0) {
|
if (site.length === 0) {
|
||||||
return next(
|
return next(createHttpError(HttpCode.NOT_FOUND, `Site with ID ${siteId} not found`));
|
||||||
createHttpError(
|
|
||||||
HttpCode.NOT_FOUND,
|
|
||||||
`Site with ID ${siteId} not found`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!site[0].orgId) {
|
if (!site[0].orgId) {
|
||||||
return next(
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, `Site with ID ${siteId} does not have an organization ID`));
|
||||||
createHttpError(
|
|
||||||
HttpCode.INTERNAL_SERVER_ERROR,
|
|
||||||
`Site with ID ${siteId} does not have an organization ID`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
db.select()
|
// Get user's role ID in the organization
|
||||||
|
const userOrgRole = await db.select()
|
||||||
.from(userOrgs)
|
.from(userOrgs)
|
||||||
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, site[0].orgId)))
|
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, site[0].orgId)))
|
||||||
.then((result) => {
|
.limit(1);
|
||||||
if (result.length === 0) {
|
|
||||||
next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization'));
|
if (userOrgRole.length === 0) {
|
||||||
} else {
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization'));
|
||||||
// User has access, attach the user's role to the request for potential future use
|
}
|
||||||
req.userOrgRole = result[0].role;
|
|
||||||
next();
|
const userOrgRoleId = userOrgRole[0].roleId;
|
||||||
|
req.userOrgRoleId = userOrgRoleId;
|
||||||
|
req.userOrgId = site[0].orgId;
|
||||||
|
|
||||||
|
// Check role-based site access first
|
||||||
|
const roleSiteAccess = await db.select()
|
||||||
|
.from(roleSites)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(roleSites.siteId, siteId),
|
||||||
|
eq(roleSites.roleId, userOrgRoleId)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (roleSiteAccess.length > 0) {
|
||||||
|
// User's role has access to the site
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If role doesn't have access, check user-specific site access
|
||||||
|
const userSiteAccess = await db.select()
|
||||||
|
.from(userSites)
|
||||||
|
.where(and(eq(userSites.userId, userId), eq(userSites.siteId, siteId)))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (userSiteAccess.length > 0) {
|
||||||
|
// User has direct access to the site
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we reach here, the user doesn't have access to the site
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this site'));
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error verifying site access'));
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error verifying organization access'));
|
|
||||||
});
|
|
||||||
}
|
}
|
|
@ -73,7 +73,8 @@ export async function verifyTargetAccess(req: Request, res: Response, next: Next
|
||||||
next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization'));
|
next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization'));
|
||||||
} else {
|
} else {
|
||||||
// User has access, attach the user's role to the request for potential future use
|
// User has access, attach the user's role to the request for potential future use
|
||||||
req.userOrgRole = result[0].role;
|
req.userOrgRoleId = result[0].roleId;
|
||||||
|
req.userOrgId = resource[0].orgId!;
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,6 +3,8 @@ import { DrizzleError, eq } from 'drizzle-orm';
|
||||||
import { sites, resources, targets, exitNodes } from '@server/db/schema';
|
import { sites, resources, targets, exitNodes } from '@server/db/schema';
|
||||||
import db from '@server/db';
|
import db from '@server/db';
|
||||||
import logger from '@server/logger';
|
import logger from '@server/logger';
|
||||||
|
import HttpCode from '@server/types/HttpCode';
|
||||||
|
import createHttpError from 'http-errors';
|
||||||
|
|
||||||
export const getConfig = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
export const getConfig = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
|
@ -55,11 +57,7 @@ export const getConfig = async (req: Request, res: Response, next: NextFunction)
|
||||||
res.json(config);
|
res.json(config);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error querying database:', error);
|
logger.error('Error querying database:', error);
|
||||||
if (error instanceof DrizzleError) {
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
res.status(500).json({ error: 'Database query error', message: error.message });
|
|
||||||
} else {
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,9 @@ import { DrizzleError, eq } from 'drizzle-orm';
|
||||||
import { sites, resources, targets, exitNodes } from '@server/db/schema';
|
import { sites, resources, targets, exitNodes } from '@server/db/schema';
|
||||||
import db from '@server/db';
|
import db from '@server/db';
|
||||||
import logger from '@server/logger';
|
import logger from '@server/logger';
|
||||||
|
import createHttpError from 'http-errors';
|
||||||
|
import HttpCode from '@server/types/HttpCode';
|
||||||
|
import response from "@server/utils/response";
|
||||||
|
|
||||||
interface PeerBandwidth {
|
interface PeerBandwidth {
|
||||||
publicKey: string;
|
publicKey: string;
|
||||||
|
@ -10,7 +13,7 @@ interface PeerBandwidth {
|
||||||
bytesOut: number;
|
bytesOut: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const receiveBandwidth = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
export const receiveBandwidth = async (req: Request, res: Response, next: NextFunction): Promise<any> => {
|
||||||
try {
|
try {
|
||||||
const bandwidthData: PeerBandwidth[] = req.body;
|
const bandwidthData: PeerBandwidth[] = req.body;
|
||||||
|
|
||||||
|
@ -43,10 +46,16 @@ export const receiveBandwidth = async (req: Request, res: Response, next: NextFu
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(200).json({ message: 'Bandwidth data updated successfully' });
|
return response(res, {
|
||||||
|
data: {},
|
||||||
|
success: true,
|
||||||
|
error: false,
|
||||||
|
message: "Organization retrieved successfully",
|
||||||
|
status: HttpCode.OK,
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error updating bandwidth data:', error);
|
logger.error('Error updating bandwidth data:', error);
|
||||||
res.status(500).json({ error: 'Internal server error' });
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ import { 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 { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||||
|
import logger from '@server/logger';
|
||||||
|
|
||||||
const createOrgSchema = z.object({
|
const createOrgSchema = z.object({
|
||||||
name: z.string().min(1).max(255),
|
name: z.string().min(1).max(255),
|
||||||
|
@ -25,7 +27,7 @@ export async function createOrg(req: Request, res: Response, next: NextFunction)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const userOrgIds = req.userOrgs;
|
const userOrgIds = req.userOrgIds;
|
||||||
if (userOrgIds && userOrgIds.length > MAX_ORGS) {
|
if (userOrgIds && userOrgIds.length > MAX_ORGS) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
|
@ -35,6 +37,12 @@ export async function createOrg(req: Request, res: Response, next: NextFunction)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the user has permission to list sites
|
||||||
|
const hasPermission = await checkUserActionPermission(ActionsEnum.createOrg, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
|
}
|
||||||
|
|
||||||
const { name, domain } = parsedBody.data;
|
const { name, domain } = parsedBody.data;
|
||||||
|
|
||||||
const newOrg = await db.insert(orgs).values({
|
const newOrg = await db.insert(orgs).values({
|
||||||
|
@ -42,16 +50,15 @@ export async function createOrg(req: Request, res: Response, next: NextFunction)
|
||||||
domain,
|
domain,
|
||||||
}).returning();
|
}).returning();
|
||||||
|
|
||||||
return res.status(HttpCode.CREATED).send(
|
return response(res, {
|
||||||
response(res, {
|
|
||||||
data: newOrg[0],
|
data: newOrg[0],
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
message: "Organization created successfully",
|
message: "Organization created successfully",
|
||||||
status: HttpCode.CREATED,
|
status: HttpCode.CREATED,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ 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 logger from '@server/logger';
|
||||||
|
|
||||||
const deleteOrgSchema = z.object({
|
const deleteOrgSchema = z.object({
|
||||||
orgId: z.string().transform(Number).pipe(z.number().int().positive())
|
orgId: z.string().transform(Number).pipe(z.number().int().positive())
|
||||||
|
@ -25,6 +27,12 @@ export async function deleteOrg(req: Request, res: Response, next: NextFunction)
|
||||||
|
|
||||||
const { orgId } = parsedParams.data;
|
const { orgId } = parsedParams.data;
|
||||||
|
|
||||||
|
// Check if the user has permission to list sites
|
||||||
|
const hasPermission = await checkUserActionPermission(ActionsEnum.deleteOrg, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
|
}
|
||||||
|
|
||||||
const deletedOrg = await db.delete(orgs)
|
const deletedOrg = await db.delete(orgs)
|
||||||
.where(eq(orgs.orgId, orgId))
|
.where(eq(orgs.orgId, orgId))
|
||||||
.returning();
|
.returning();
|
||||||
|
@ -38,16 +46,15 @@ export async function deleteOrg(req: Request, res: Response, next: NextFunction)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(HttpCode.OK).send(
|
return response(res, {
|
||||||
response(res, {
|
|
||||||
data: null,
|
data: null,
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
message: "Organization deleted successfully",
|
message: "Organization deleted successfully",
|
||||||
status: HttpCode.OK,
|
status: HttpCode.OK,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ 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 logger from '@server/logger';
|
||||||
|
|
||||||
const getOrgSchema = z.object({
|
const getOrgSchema = z.object({
|
||||||
orgId: z.string().transform(Number).pipe(z.number().int().positive())
|
orgId: z.string().transform(Number).pipe(z.number().int().positive())
|
||||||
|
@ -25,6 +27,12 @@ export async function getOrg(req: Request, res: Response, next: NextFunction): P
|
||||||
|
|
||||||
const { orgId } = parsedParams.data;
|
const { orgId } = parsedParams.data;
|
||||||
|
|
||||||
|
// Check if the user has permission to list sites
|
||||||
|
const hasPermission = await checkUserActionPermission(ActionsEnum.getOrg, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
|
}
|
||||||
|
|
||||||
const org = await db.select()
|
const org = await db.select()
|
||||||
.from(orgs)
|
.from(orgs)
|
||||||
.where(eq(orgs.orgId, orgId))
|
.where(eq(orgs.orgId, orgId))
|
||||||
|
@ -39,16 +47,15 @@ export async function getOrg(req: Request, res: Response, next: NextFunction): P
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(HttpCode.OK).send(
|
return response(res, {
|
||||||
response(res, {
|
|
||||||
data: org[0],
|
data: org[0],
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
message: "Organization retrieved successfully",
|
message: "Organization retrieved successfully",
|
||||||
status: HttpCode.OK,
|
status: HttpCode.OK,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ 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, inArray } from 'drizzle-orm';
|
import { sql, inArray } from 'drizzle-orm';
|
||||||
|
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||||
|
import logger from '@server/logger';
|
||||||
|
|
||||||
const listOrgsSchema = z.object({
|
const listOrgsSchema = z.object({
|
||||||
limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)),
|
limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)),
|
||||||
|
@ -26,8 +28,14 @@ export async function listOrgs(req: Request, res: Response, next: NextFunction):
|
||||||
|
|
||||||
const { limit, offset } = parsedQuery.data;
|
const { limit, offset } = parsedQuery.data;
|
||||||
|
|
||||||
|
// Check if the user has permission to list sites
|
||||||
|
const hasPermission = await checkUserActionPermission(ActionsEnum.listOrgs, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
|
}
|
||||||
|
|
||||||
// Use the userOrgs passed from the middleware
|
// Use the userOrgs passed from the middleware
|
||||||
const userOrgIds = req.userOrgs;
|
const userOrgIds = req.userOrgIds;
|
||||||
|
|
||||||
if (!userOrgIds || userOrgIds.length === 0) {
|
if (!userOrgIds || userOrgIds.length === 0) {
|
||||||
return res.status(HttpCode.OK).send(
|
return res.status(HttpCode.OK).send(
|
||||||
|
@ -62,11 +70,10 @@ export async function listOrgs(req: Request, res: Response, next: NextFunction):
|
||||||
// // Add the user's role for each organization
|
// // Add the user's role for each organization
|
||||||
// const organizationsWithRoles = organizations.map(org => ({
|
// const organizationsWithRoles = organizations.map(org => ({
|
||||||
// ...org,
|
// ...org,
|
||||||
// userRole: req.userOrgRoles[org.orgId],
|
// userRole: req.userOrgRoleIds[org.orgId],
|
||||||
// }));
|
// }));
|
||||||
|
|
||||||
return res.status(HttpCode.OK).send(
|
return response(res, {
|
||||||
response(res, {
|
|
||||||
data: {
|
data: {
|
||||||
organizations,
|
organizations,
|
||||||
pagination: {
|
pagination: {
|
||||||
|
@ -79,9 +86,9 @@ export async function listOrgs(req: Request, res: Response, next: NextFunction):
|
||||||
error: false,
|
error: false,
|
||||||
message: "Organizations retrieved successfully",
|
message: "Organizations retrieved successfully",
|
||||||
status: HttpCode.OK,
|
status: HttpCode.OK,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,6 +6,8 @@ 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 logger from '@server/logger';
|
||||||
|
|
||||||
const updateOrgParamsSchema = z.object({
|
const updateOrgParamsSchema = z.object({
|
||||||
orgId: z.string().transform(Number).pipe(z.number().int().positive())
|
orgId: z.string().transform(Number).pipe(z.number().int().positive())
|
||||||
|
@ -43,6 +45,13 @@ export async function updateOrg(req: Request, res: Response, next: NextFunction)
|
||||||
const { orgId } = parsedParams.data;
|
const { orgId } = parsedParams.data;
|
||||||
const updateData = parsedBody.data;
|
const updateData = parsedBody.data;
|
||||||
|
|
||||||
|
|
||||||
|
// Check if the user has permission to list sites
|
||||||
|
const hasPermission = await checkUserActionPermission(ActionsEnum.updateOrg, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
|
}
|
||||||
|
|
||||||
const updatedOrg = await db.update(orgs)
|
const updatedOrg = await db.update(orgs)
|
||||||
.set(updateData)
|
.set(updateData)
|
||||||
.where(eq(orgs.orgId, orgId))
|
.where(eq(orgs.orgId, orgId))
|
||||||
|
@ -57,16 +66,15 @@ export async function updateOrg(req: Request, res: Response, next: NextFunction)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(HttpCode.OK).send(
|
return response(res, {
|
||||||
response(res, {
|
|
||||||
data: updatedOrg[0],
|
data: updatedOrg[0],
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
message: "Organization updated successfully",
|
message: "Organization updated successfully",
|
||||||
status: HttpCode.OK,
|
status: HttpCode.OK,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import { resources } 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 logger from '@server/logger';
|
||||||
|
|
||||||
const createResourceParamsSchema = z.object({
|
const createResourceParamsSchema = z.object({
|
||||||
siteId: z.number().int().positive(),
|
siteId: z.number().int().positive(),
|
||||||
|
@ -45,6 +47,12 @@ export async function createResource(req: Request, res: Response, next: NextFunc
|
||||||
|
|
||||||
const { siteId, orgId } = parsedParams.data;
|
const { siteId, orgId } = parsedParams.data;
|
||||||
|
|
||||||
|
// Check if the user has permission to list sites
|
||||||
|
const hasPermission = await checkUserActionPermission(ActionsEnum.createResource, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
|
}
|
||||||
|
|
||||||
// Generate a unique resourceId
|
// Generate a unique resourceId
|
||||||
const resourceId = "subdomain" // TODO: create the subdomain here
|
const resourceId = "subdomain" // TODO: create the subdomain here
|
||||||
|
|
||||||
|
@ -57,16 +65,15 @@ export async function createResource(req: Request, res: Response, next: NextFunc
|
||||||
subdomain,
|
subdomain,
|
||||||
}).returning();
|
}).returning();
|
||||||
|
|
||||||
return res.status(HttpCode.CREATED).send(
|
|
||||||
response(res, {
|
response(res, {
|
||||||
data: newResource[0],
|
data: newResource[0],
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
message: "Resource created successfully",
|
message: "Resource created successfully",
|
||||||
status: HttpCode.CREATED,
|
status: HttpCode.CREATED,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ 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 logger from '@server/logger';
|
||||||
|
|
||||||
// Define Zod schema for request parameters validation
|
// Define Zod schema for request parameters validation
|
||||||
const deleteResourceSchema = z.object({
|
const deleteResourceSchema = z.object({
|
||||||
|
@ -27,6 +29,12 @@ 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
|
||||||
|
const hasPermission = await checkUserActionPermission(ActionsEnum.deleteResource, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
|
}
|
||||||
|
|
||||||
// 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))
|
||||||
|
@ -41,16 +49,15 @@ export async function deleteResource(req: Request, res: Response, next: NextFunc
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(HttpCode.OK).send(
|
return response(res, {
|
||||||
response(res, {
|
|
||||||
data: null,
|
data: null,
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
message: "Resource deleted successfully",
|
message: "Resource deleted successfully",
|
||||||
status: HttpCode.OK,
|
status: HttpCode.OK,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ 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 logger from '@server/logger';
|
||||||
|
|
||||||
// Define Zod schema for request parameters validation
|
// Define Zod schema for request parameters validation
|
||||||
const getResourceSchema = z.object({
|
const getResourceSchema = z.object({
|
||||||
|
@ -27,6 +29,12 @@ 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
|
||||||
|
const hasPermission = await checkUserActionPermission(ActionsEnum.getResource, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
|
}
|
||||||
|
|
||||||
// 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)
|
||||||
|
@ -42,16 +50,15 @@ export async function getResource(req: Request, res: Response, next: NextFunctio
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(HttpCode.OK).send(
|
return response(res, {
|
||||||
response(res, {
|
|
||||||
data: resource[0],
|
data: resource[0],
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
message: "Resource retrieved successfully",
|
message: "Resource retrieved successfully",
|
||||||
status: HttpCode.OK,
|
status: HttpCode.OK,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +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 { resources, sites } from '@server/db/schema';
|
import { resources, sites, userResources, 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 { sql, eq } from 'drizzle-orm';
|
import { sql, eq, and, or, inArray } from 'drizzle-orm';
|
||||||
|
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||||
|
import logger from '@server/logger';
|
||||||
|
|
||||||
const listResourcesParamsSchema = z.object({
|
const listResourcesParamsSchema = z.object({
|
||||||
siteId: z.coerce.number().int().positive().optional(),
|
siteId: z.coerce.number().int().positive().optional(),
|
||||||
|
@ -19,30 +21,49 @@ const listResourcesSchema = z.object({
|
||||||
offset: z.coerce.number().int().nonnegative().default(0),
|
offset: z.coerce.number().int().nonnegative().default(0),
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function listResources(req: Request, res: Response, next: NextFunction): Promise<any> {
|
interface RequestWithOrgAndRole extends Request {
|
||||||
|
userOrgRoleId?: number;
|
||||||
|
orgId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listResources(req: RequestWithOrgAndRole, res: Response, next: NextFunction): Promise<any> {
|
||||||
try {
|
try {
|
||||||
const parsedQuery = listResourcesSchema.safeParse(req.query);
|
const parsedQuery = listResourcesSchema.safeParse(req.query);
|
||||||
if (!parsedQuery.success) {
|
if (!parsedQuery.success) {
|
||||||
return next(
|
return next(createHttpError(HttpCode.BAD_REQUEST, parsedQuery.error.errors.map(e => e.message).join(', ')));
|
||||||
createHttpError(
|
|
||||||
HttpCode.BAD_REQUEST,
|
|
||||||
parsedQuery.error.errors.map(e => e.message).join(', ')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
const { limit, offset } = parsedQuery.data;
|
const { limit, offset } = parsedQuery.data;
|
||||||
|
|
||||||
const parsedParams = listResourcesParamsSchema.safeParse(req.params);
|
const parsedParams = listResourcesParamsSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
return next(
|
return next(createHttpError(HttpCode.BAD_REQUEST, parsedParams.error.errors.map(e => e.message).join(', ')));
|
||||||
createHttpError(
|
|
||||||
HttpCode.BAD_REQUEST,
|
|
||||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
const { siteId, orgId } = parsedParams.data;
|
const { siteId, orgId } = parsedParams.data;
|
||||||
|
|
||||||
|
// Check if the user has permission to list sites
|
||||||
|
const hasPermission = await checkUserActionPermission(ActionsEnum.listResources, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orgId && orgId !== req.orgId) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the list of resources the user has access to
|
||||||
|
const accessibleResources = await db
|
||||||
|
.select({ resourceId: sql<string>`COALESCE(${userResources.resourceId}, ${roleResources.resourceId})` })
|
||||||
|
.from(userResources)
|
||||||
|
.fullJoin(roleResources, eq(userResources.resourceId, roleResources.resourceId))
|
||||||
|
.where(
|
||||||
|
or(
|
||||||
|
eq(userResources.userId, req.user!.id),
|
||||||
|
eq(roleResources.roleId, req.userOrgRoleId!)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const accessibleResourceIds = accessibleResources.map(resource => resource.resourceId);
|
||||||
|
|
||||||
let baseQuery: any = db
|
let baseQuery: any = db
|
||||||
.select({
|
.select({
|
||||||
resourceId: resources.resourceId,
|
resourceId: resources.resourceId,
|
||||||
|
@ -51,24 +72,28 @@ export async function listResources(req: Request, res: Response, next: NextFunct
|
||||||
siteName: sites.name,
|
siteName: sites.name,
|
||||||
})
|
})
|
||||||
.from(resources)
|
.from(resources)
|
||||||
.leftJoin(sites, eq(resources.siteId, sites.siteId));
|
.leftJoin(sites, eq(resources.siteId, sites.siteId))
|
||||||
|
.where(inArray(resources.resourceId, accessibleResourceIds));
|
||||||
|
|
||||||
let countQuery: any = db.select({ count: sql<number>`cast(count(*) as integer)` }).from(resources);
|
let countQuery: any = db
|
||||||
|
.select({ count: sql<number>`cast(count(*) as integer)` })
|
||||||
|
.from(resources)
|
||||||
|
.where(inArray(resources.resourceId, accessibleResourceIds));
|
||||||
|
|
||||||
if (siteId) {
|
if (siteId) {
|
||||||
baseQuery = baseQuery.where(eq(resources.siteId, siteId));
|
baseQuery = baseQuery.where(eq(resources.siteId, siteId));
|
||||||
countQuery = countQuery.where(eq(resources.siteId, siteId));
|
countQuery = countQuery.where(eq(resources.siteId, siteId));
|
||||||
} else if (orgId) {
|
} else {
|
||||||
baseQuery = baseQuery.where(eq(resources.orgId, orgId));
|
// If orgId is provided, it's already checked to match req.orgId
|
||||||
countQuery = countQuery.where(eq(resources.orgId, orgId));
|
baseQuery = baseQuery.where(eq(resources.orgId, req.orgId!));
|
||||||
|
countQuery = countQuery.where(eq(resources.orgId, req.orgId!));
|
||||||
}
|
}
|
||||||
|
|
||||||
const resourcesList = await baseQuery.limit(limit).offset(offset);
|
const resourcesList = await baseQuery.limit(limit).offset(offset);
|
||||||
const totalCountResult = await countQuery;
|
const totalCountResult = await countQuery;
|
||||||
const totalCount = totalCountResult[0].count;
|
const totalCount = totalCountResult[0].count;
|
||||||
|
|
||||||
return res.status(HttpCode.OK).send(
|
return response(res, {
|
||||||
response(res, {
|
|
||||||
data: {
|
data: {
|
||||||
resources: resourcesList,
|
resources: resourcesList,
|
||||||
pagination: {
|
pagination: {
|
||||||
|
@ -81,9 +106,9 @@ export async function listResources(req: Request, res: Response, next: NextFunct
|
||||||
error: false,
|
error: false,
|
||||||
message: "Resources retrieved successfully",
|
message: "Resources retrieved successfully",
|
||||||
status: HttpCode.OK,
|
status: HttpCode.OK,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,6 +6,8 @@ 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 logger from '@server/logger';
|
||||||
|
|
||||||
// Define Zod schema for request parameters validation
|
// Define Zod schema for request parameters validation
|
||||||
const updateResourceParamsSchema = z.object({
|
const updateResourceParamsSchema = z.object({
|
||||||
|
@ -47,6 +49,12 @@ export async function updateResource(req: Request, res: Response, next: NextFunc
|
||||||
const { resourceId } = parsedParams.data;
|
const { resourceId } = parsedParams.data;
|
||||||
const updateData = parsedBody.data;
|
const updateData = parsedBody.data;
|
||||||
|
|
||||||
|
// Check if the user has permission to list sites
|
||||||
|
const hasPermission = await checkUserActionPermission(ActionsEnum.updateResource, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
|
}
|
||||||
|
|
||||||
// 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)
|
||||||
|
@ -62,16 +70,15 @@ export async function updateResource(req: Request, res: Response, next: NextFunc
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(HttpCode.OK).send(
|
return response(res, {
|
||||||
response(res, {
|
|
||||||
data: updatedResource[0],
|
data: updatedResource[0],
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
message: "Resource updated successfully",
|
message: "Resource updated successfully",
|
||||||
status: HttpCode.OK,
|
status: HttpCode.OK,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ 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 fetch from 'node-fetch';
|
import fetch from 'node-fetch';
|
||||||
|
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||||
|
import logger from '@server/logger';
|
||||||
|
|
||||||
const API_BASE_URL = "http://localhost:3000";
|
const API_BASE_URL = "http://localhost:3000";
|
||||||
|
|
||||||
|
@ -49,6 +51,12 @@ 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
|
||||||
|
const hasPermission = await checkUserActionPermission(ActionsEnum.createSite, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
|
}
|
||||||
|
|
||||||
// Create new site in the database
|
// Create new site in the database
|
||||||
const newSite = await db.insert(sites).values({
|
const newSite = await db.insert(sites).values({
|
||||||
orgId,
|
orgId,
|
||||||
|
@ -58,17 +66,16 @@ export async function createSite(req: Request, res: Response, next: NextFunction
|
||||||
subnet,
|
subnet,
|
||||||
}).returning();
|
}).returning();
|
||||||
|
|
||||||
return res.status(HttpCode.CREATED).send(
|
return response(res, {
|
||||||
response(res, {
|
|
||||||
data: newSite[0],
|
data: newSite[0],
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
message: "Site created successfully",
|
message: "Site created successfully",
|
||||||
status: HttpCode.CREATED,
|
status: HttpCode.CREATED,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,10 +95,9 @@ async function addPeer(peer: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const data: any = await response.json();
|
const data: any = await response.json();
|
||||||
console.log('Peer added successfully:', data.status);
|
logger.info('Peer added successfully:', data.status);
|
||||||
return data;
|
return data;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Error adding peer:', error.message);
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ 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 logger from '@server/logger';
|
||||||
|
|
||||||
|
|
||||||
const API_BASE_URL = "http://localhost:3000";
|
const API_BASE_URL = "http://localhost:3000";
|
||||||
|
@ -30,6 +32,12 @@ 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
|
||||||
|
const hasPermission = await checkUserActionPermission(ActionsEnum.deleteSite, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
|
}
|
||||||
|
|
||||||
// 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))
|
||||||
|
@ -44,17 +52,16 @@ export async function deleteSite(req: Request, res: Response, next: NextFunction
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(HttpCode.OK).send(
|
return response(res, {
|
||||||
response(res, {
|
|
||||||
data: null,
|
data: null,
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
message: "Site deleted successfully",
|
message: "Site deleted successfully",
|
||||||
status: HttpCode.OK,
|
status: HttpCode.OK,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ 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 logger from '@server/logger';
|
||||||
|
|
||||||
// Define Zod schema for request parameters validation
|
// Define Zod schema for request parameters validation
|
||||||
const getSiteSchema = z.object({
|
const getSiteSchema = z.object({
|
||||||
|
@ -27,6 +29,12 @@ export async function getSite(req: Request, res: Response, next: NextFunction):
|
||||||
|
|
||||||
const { siteId } = parsedParams.data;
|
const { siteId } = parsedParams.data;
|
||||||
|
|
||||||
|
// Check if the user has permission to list sites
|
||||||
|
const hasPermission = await checkUserActionPermission(ActionsEnum.updateSite, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch the site from the database
|
// Fetch the site from the database
|
||||||
const site = await db.select()
|
const site = await db.select()
|
||||||
.from(sites)
|
.from(sites)
|
||||||
|
@ -42,16 +50,15 @@ export async function getSite(req: Request, res: Response, next: NextFunction):
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(HttpCode.OK).send(
|
return response(res, {
|
||||||
response(res, {
|
|
||||||
data: site[0],
|
data: site[0],
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
message: "Site retrieved successfully",
|
message: "Site retrieved successfully",
|
||||||
status: HttpCode.OK,
|
status: HttpCode.OK,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +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 { sites, orgs, exitNodes } from '@server/db/schema';
|
import { sites, orgs, exitNodes, userSites, 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 { sql, eq } from 'drizzle-orm';
|
import { sql, eq, and, or, inArray } from 'drizzle-orm';
|
||||||
|
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||||
|
import logger from '@server/logger';
|
||||||
|
|
||||||
const listSitesParamsSchema = z.object({
|
const listSitesParamsSchema = z.object({
|
||||||
orgId: z.string().optional().transform(Number).pipe(z.number().int().positive()),
|
orgId: z.string().optional().transform(Number).pipe(z.number().int().positive()),
|
||||||
|
@ -20,27 +22,39 @@ export async function listSites(req: Request, res: Response, next: NextFunction)
|
||||||
try {
|
try {
|
||||||
const parsedQuery = listSitesSchema.safeParse(req.query);
|
const parsedQuery = listSitesSchema.safeParse(req.query);
|
||||||
if (!parsedQuery.success) {
|
if (!parsedQuery.success) {
|
||||||
return next(
|
return next(createHttpError(HttpCode.BAD_REQUEST, parsedQuery.error.errors.map(e => e.message).join(', ')));
|
||||||
createHttpError(
|
|
||||||
HttpCode.BAD_REQUEST,
|
|
||||||
parsedQuery.error.errors.map(e => e.message).join(', ')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { limit, offset } = parsedQuery.data;
|
const { limit, offset } = parsedQuery.data;
|
||||||
|
|
||||||
|
|
||||||
const parsedParams = listSitesParamsSchema.safeParse(req.params);
|
const parsedParams = listSitesParamsSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
return next(
|
return next(createHttpError(HttpCode.BAD_REQUEST, parsedParams.error.errors.map(e => e.message).join(', ')));
|
||||||
createHttpError(
|
}
|
||||||
HttpCode.BAD_REQUEST,
|
const { orgId } = parsedParams.data;
|
||||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
|
||||||
)
|
// Check if the user has permission to list sites
|
||||||
);
|
const hasPermission = await checkUserActionPermission(ActionsEnum.listSites, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
}
|
}
|
||||||
|
|
||||||
const { orgId } = parsedParams.data;
|
if (orgId && orgId !== req.userOrgId) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const accessibleSites = await db
|
||||||
|
.select({ siteId: sql<number>`COALESCE(${userSites.siteId}, ${roleSites.siteId})` })
|
||||||
|
.from(userSites)
|
||||||
|
.fullJoin(roleSites, eq(userSites.siteId, roleSites.siteId))
|
||||||
|
.where(
|
||||||
|
or(
|
||||||
|
eq(userSites.userId, req.user!.id),
|
||||||
|
eq(roleSites.roleId, req.userOrgRoleId!)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const accessibleSiteIds = accessibleSites.map(site => site.siteId);
|
||||||
|
|
||||||
let baseQuery: any = db
|
let baseQuery: any = db
|
||||||
.select({
|
.select({
|
||||||
|
@ -56,9 +70,12 @@ export async function listSites(req: Request, res: Response, next: NextFunction)
|
||||||
})
|
})
|
||||||
.from(sites)
|
.from(sites)
|
||||||
.leftJoin(orgs, eq(sites.orgId, orgs.orgId))
|
.leftJoin(orgs, eq(sites.orgId, orgs.orgId))
|
||||||
.leftJoin(exitNodes, eq(sites.exitNode, exitNodes.exitNodeId));
|
.where(inArray(sites.siteId, accessibleSiteIds));
|
||||||
|
|
||||||
let countQuery: any = db.select({ count: sql<number>`cast(count(*) as integer)` }).from(sites);
|
let countQuery: any = db
|
||||||
|
.select({ count: sql<number>`cast(count(*) as integer)` })
|
||||||
|
.from(sites)
|
||||||
|
.where(inArray(sites.siteId, accessibleSiteIds));
|
||||||
|
|
||||||
if (orgId) {
|
if (orgId) {
|
||||||
baseQuery = baseQuery.where(eq(sites.orgId, orgId));
|
baseQuery = baseQuery.where(eq(sites.orgId, orgId));
|
||||||
|
@ -69,8 +86,7 @@ export async function listSites(req: Request, res: Response, next: NextFunction)
|
||||||
const totalCountResult = await countQuery;
|
const totalCountResult = await countQuery;
|
||||||
const totalCount = totalCountResult[0].count;
|
const totalCount = totalCountResult[0].count;
|
||||||
|
|
||||||
return res.status(HttpCode.OK).send(
|
return response(res, {
|
||||||
response(res, {
|
|
||||||
data: {
|
data: {
|
||||||
sites: sitesList,
|
sites: sitesList,
|
||||||
pagination: {
|
pagination: {
|
||||||
|
@ -83,9 +99,9 @@ export async function listSites(req: Request, res: Response, next: NextFunction)
|
||||||
error: false,
|
error: false,
|
||||||
message: "Sites retrieved successfully",
|
message: "Sites retrieved successfully",
|
||||||
status: HttpCode.OK,
|
status: HttpCode.OK,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,6 +6,8 @@ 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 logger from '@server/logger';
|
||||||
|
|
||||||
// Define Zod schema for request parameters validation
|
// Define Zod schema for request parameters validation
|
||||||
const updateSiteParamsSchema = z.object({
|
const updateSiteParamsSchema = z.object({
|
||||||
|
@ -52,6 +54,12 @@ export async function updateSite(req: Request, res: Response, next: NextFunction
|
||||||
const { siteId } = parsedParams.data;
|
const { siteId } = parsedParams.data;
|
||||||
const updateData = parsedBody.data;
|
const updateData = parsedBody.data;
|
||||||
|
|
||||||
|
// Check if the user has permission to list sites
|
||||||
|
const hasPermission = await checkUserActionPermission(ActionsEnum.updateSite, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
|
}
|
||||||
|
|
||||||
// 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)
|
||||||
|
@ -67,16 +75,15 @@ export async function updateSite(req: Request, res: Response, next: NextFunction
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(HttpCode.OK).send(
|
return response(res, {
|
||||||
response(res, {
|
|
||||||
data: updatedSite[0],
|
data: updatedSite[0],
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
message: "Site updated successfully",
|
message: "Site updated successfully",
|
||||||
status: HttpCode.OK,
|
status: HttpCode.OK,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import { 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 logger from '@server/logger';
|
||||||
|
|
||||||
const createTargetParamsSchema = z.object({
|
const createTargetParamsSchema = z.object({
|
||||||
resourceId: z.string().uuid(),
|
resourceId: z.string().uuid(),
|
||||||
|
@ -44,21 +46,26 @@ 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
|
||||||
|
const hasPermission = await checkUserActionPermission(ActionsEnum.createTarget, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
|
}
|
||||||
|
|
||||||
const newTarget = await db.insert(targets).values({
|
const newTarget = await db.insert(targets).values({
|
||||||
resourceId,
|
resourceId,
|
||||||
...targetData
|
...targetData
|
||||||
}).returning();
|
}).returning();
|
||||||
|
|
||||||
return res.status(HttpCode.CREATED).send(
|
return response(res, {
|
||||||
response(res, {
|
|
||||||
data: newTarget[0],
|
data: newTarget[0],
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
message: "Target created successfully",
|
message: "Target created successfully",
|
||||||
status: HttpCode.CREATED,
|
status: HttpCode.CREATED,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ 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 logger from '@server/logger';
|
||||||
|
|
||||||
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())
|
||||||
|
@ -25,6 +27,12 @@ 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
|
||||||
|
const hasPermission = await checkUserActionPermission(ActionsEnum.deleteTarget, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
|
}
|
||||||
|
|
||||||
const deletedTarget = await db.delete(targets)
|
const deletedTarget = await db.delete(targets)
|
||||||
.where(eq(targets.targetId, targetId))
|
.where(eq(targets.targetId, targetId))
|
||||||
.returning();
|
.returning();
|
||||||
|
@ -38,16 +46,15 @@ export async function deleteTarget(req: Request, res: Response, next: NextFuncti
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(HttpCode.OK).send(
|
return response(res, {
|
||||||
response(res, {
|
|
||||||
data: null,
|
data: null,
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
message: "Target deleted successfully",
|
message: "Target deleted successfully",
|
||||||
status: HttpCode.OK,
|
status: HttpCode.OK,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ 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 logger from '@server/logger';
|
||||||
|
|
||||||
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())
|
||||||
|
@ -25,6 +27,12 @@ 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
|
||||||
|
const hasPermission = await checkUserActionPermission(ActionsEnum.getTarget, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
|
}
|
||||||
|
|
||||||
const target = await db.select()
|
const target = await db.select()
|
||||||
.from(targets)
|
.from(targets)
|
||||||
.where(eq(targets.targetId, targetId))
|
.where(eq(targets.targetId, targetId))
|
||||||
|
@ -39,16 +47,15 @@ export async function getTarget(req: Request, res: Response, next: NextFunction)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(HttpCode.OK).send(
|
return response(res, {
|
||||||
response(res, {
|
|
||||||
data: target[0],
|
data: target[0],
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
message: "Target retrieved successfully",
|
message: "Target retrieved successfully",
|
||||||
status: HttpCode.OK,
|
status: HttpCode.OK,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ 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 logger from '@server/logger';
|
||||||
|
|
||||||
const listTargetsParamsSchema = z.object({
|
const listTargetsParamsSchema = z.object({
|
||||||
resourceId: z.string().optional()
|
resourceId: z.string().optional()
|
||||||
|
@ -42,6 +44,12 @@ export async function listTargets(req: Request, res: Response, next: NextFunctio
|
||||||
|
|
||||||
const { resourceId } = parsedParams.data;
|
const { resourceId } = parsedParams.data;
|
||||||
|
|
||||||
|
// Check if the user has permission to list sites
|
||||||
|
const hasPermission = await checkUserActionPermission(ActionsEnum.listTargets, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
|
}
|
||||||
|
|
||||||
let baseQuery: any = db
|
let baseQuery: any = db
|
||||||
.select({
|
.select({
|
||||||
targetId: targets.targetId,
|
targetId: targets.targetId,
|
||||||
|
@ -66,8 +74,7 @@ export async function listTargets(req: Request, res: Response, next: NextFunctio
|
||||||
const totalCountResult = await countQuery;
|
const totalCountResult = await countQuery;
|
||||||
const totalCount = totalCountResult[0].count;
|
const totalCount = totalCountResult[0].count;
|
||||||
|
|
||||||
return res.status(HttpCode.OK).send(
|
return response(res, {
|
||||||
response(res, {
|
|
||||||
data: {
|
data: {
|
||||||
targets: targetsList,
|
targets: targetsList,
|
||||||
pagination: {
|
pagination: {
|
||||||
|
@ -80,9 +87,9 @@ export async function listTargets(req: Request, res: Response, next: NextFunctio
|
||||||
error: false,
|
error: false,
|
||||||
message: "Targets retrieved successfully",
|
message: "Targets retrieved successfully",
|
||||||
status: HttpCode.OK,
|
status: HttpCode.OK,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,6 +6,8 @@ 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 logger from '@server/logger';
|
||||||
|
|
||||||
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())
|
||||||
|
@ -46,6 +48,12 @@ export async function updateTarget(req: Request, res: Response, next: NextFuncti
|
||||||
const { targetId } = parsedParams.data;
|
const { targetId } = parsedParams.data;
|
||||||
const updateData = parsedBody.data;
|
const updateData = parsedBody.data;
|
||||||
|
|
||||||
|
// Check if the user has permission to list sites
|
||||||
|
const hasPermission = await checkUserActionPermission(ActionsEnum.updateTarget, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
|
}
|
||||||
|
|
||||||
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))
|
||||||
|
@ -60,16 +68,15 @@ export async function updateTarget(req: Request, res: Response, next: NextFuncti
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(HttpCode.OK).send(
|
return response(res, {
|
||||||
response(res, {
|
|
||||||
data: updatedTarget[0],
|
data: updatedTarget[0],
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
message: "Target updated successfully",
|
message: "Target updated successfully",
|
||||||
status: HttpCode.OK,
|
status: HttpCode.OK,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ 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 logger from '@server/logger';
|
||||||
|
|
||||||
const deleteUserSchema = z.object({
|
const deleteUserSchema = z.object({
|
||||||
userId: z.string().uuid()
|
userId: z.string().uuid()
|
||||||
|
@ -25,6 +27,12 @@ export async function deleteUser(req: Request, res: Response, next: NextFunction
|
||||||
|
|
||||||
const { userId } = parsedParams.data;
|
const { userId } = parsedParams.data;
|
||||||
|
|
||||||
|
// Check if the user has permission to list sites
|
||||||
|
const hasPermission = await checkUserActionPermission(ActionsEnum.deleteUser, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
|
}
|
||||||
|
|
||||||
const deletedUser = await db.delete(users)
|
const deletedUser = await db.delete(users)
|
||||||
.where(eq(users.id, userId))
|
.where(eq(users.id, userId))
|
||||||
.returning();
|
.returning();
|
||||||
|
@ -38,16 +46,15 @@ export async function deleteUser(req: Request, res: Response, next: NextFunction
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(HttpCode.OK).send(
|
return response(res, {
|
||||||
response(res, {
|
|
||||||
data: null,
|
data: null,
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
message: "User deleted successfully",
|
message: "User deleted successfully",
|
||||||
status: HttpCode.OK,
|
status: HttpCode.OK,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ 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 logger from '@server/logger';
|
||||||
|
|
||||||
const getUserSchema = z.object({
|
const getUserSchema = z.object({
|
||||||
userId: z.string().uuid()
|
userId: z.string().uuid()
|
||||||
|
@ -25,6 +27,12 @@ export async function getUser(req: Request, res: Response, next: NextFunction):
|
||||||
|
|
||||||
const { userId } = parsedParams.data;
|
const { userId } = parsedParams.data;
|
||||||
|
|
||||||
|
// Check if the user has permission to list sites
|
||||||
|
const hasPermission = await checkUserActionPermission(ActionsEnum.getUser, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
|
}
|
||||||
|
|
||||||
const user = await db.select()
|
const user = await db.select()
|
||||||
.from(users)
|
.from(users)
|
||||||
.where(eq(users.id, userId))
|
.where(eq(users.id, userId))
|
||||||
|
@ -42,16 +50,15 @@ export async function getUser(req: Request, res: Response, next: NextFunction):
|
||||||
// Remove passwordHash from the response
|
// Remove passwordHash from the response
|
||||||
const { passwordHash: _, ...userWithoutPassword } = user[0];
|
const { passwordHash: _, ...userWithoutPassword } = user[0];
|
||||||
|
|
||||||
return res.status(HttpCode.OK).send(
|
return response(res, {
|
||||||
response(res, {
|
|
||||||
data: userWithoutPassword,
|
data: userWithoutPassword,
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
message: "User retrieved successfully",
|
message: "User retrieved successfully",
|
||||||
status: HttpCode.OK,
|
status: HttpCode.OK,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ 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 } from 'drizzle-orm';
|
import { sql } from 'drizzle-orm';
|
||||||
|
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||||
|
import logger from '@server/logger';
|
||||||
|
|
||||||
const listUsersSchema = z.object({
|
const listUsersSchema = z.object({
|
||||||
limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)),
|
limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)),
|
||||||
|
@ -26,6 +28,12 @@ export async function listUsers(req: Request, res: Response, next: NextFunction)
|
||||||
|
|
||||||
const { limit, offset } = parsedQuery.data;
|
const { limit, offset } = parsedQuery.data;
|
||||||
|
|
||||||
|
// Check if the user has permission to list sites
|
||||||
|
const hasPermission = await checkUserActionPermission(ActionsEnum.listUsers, req);
|
||||||
|
if (!hasPermission) {
|
||||||
|
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
|
||||||
|
}
|
||||||
|
|
||||||
const usersList = await db.select()
|
const usersList = await db.select()
|
||||||
.from(users)
|
.from(users)
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
|
@ -39,8 +47,7 @@ export async function listUsers(req: Request, res: Response, next: NextFunction)
|
||||||
// Remove passwordHash from each user object
|
// Remove passwordHash from each user object
|
||||||
const usersWithoutPassword = usersList.map(({ passwordHash, ...userWithoutPassword }) => userWithoutPassword);
|
const usersWithoutPassword = usersList.map(({ passwordHash, ...userWithoutPassword }) => userWithoutPassword);
|
||||||
|
|
||||||
return res.status(HttpCode.OK).send(
|
return response(res, {
|
||||||
response(res, {
|
|
||||||
data: {
|
data: {
|
||||||
users: usersWithoutPassword,
|
users: usersWithoutPassword,
|
||||||
pagination: {
|
pagination: {
|
||||||
|
@ -53,9 +60,9 @@ export async function listUsers(req: Request, res: Response, next: NextFunction)
|
||||||
error: false,
|
error: false,
|
||||||
message: "Users retrieved successfully",
|
message: "Users retrieved successfully",
|
||||||
status: HttpCode.OK,
|
status: HttpCode.OK,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
logger.error(error);
|
||||||
|
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,5 +5,5 @@ import { Session } from "lucia";
|
||||||
export interface AuthenticatedRequest extends Request {
|
export interface AuthenticatedRequest extends Request {
|
||||||
user: User;
|
user: User;
|
||||||
session: Session;
|
session: Session;
|
||||||
userOrgRole?: string;
|
userOrgRoleId?: number;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,7 @@ import LoginForm from "@app/components/LoginForm";
|
||||||
import { verifySession } from "@app/lib/verifySession";
|
import { verifySession } from "@app/lib/verifySession";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
export async function Page({
|
export async function Page() {
|
||||||
searchParams,
|
|
||||||
}: {
|
|
||||||
searchParams: { [key: string]: string | string[] | undefined };
|
|
||||||
}) {
|
|
||||||
const { user } = await verifySession();
|
const { user } = await verifySession();
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
|
@ -15,9 +11,10 @@ export async function Page({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<LoginForm redirect={searchParams.redirect as string} />
|
<LoginForm redirect={"test"} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Page;
|
export default Page;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
:root {
|
:root {
|
||||||
--background: 37 100% 100%;
|
--background: 37 100% 100%;
|
||||||
|
@ -24,6 +25,7 @@
|
||||||
--ring: 37 8% 51%;
|
--ring: 37 8% 51%;
|
||||||
--radius: 0rem;
|
--radius: 0rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--background: 37 50% 10%;
|
--background: 37 50% 10%;
|
||||||
--foreground: 37 5% 100%;
|
--foreground: 37 5% 100%;
|
||||||
|
@ -47,10 +49,12 @@
|
||||||
--radius: 0rem;
|
--radius: 0rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
* {
|
* {
|
||||||
@apply border-border;
|
@apply border-border;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"esnext"
|
||||||
|
],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
@ -14,9 +18,15 @@
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
"baseUrl": "src",
|
"baseUrl": "src",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@server/*": ["../server/*"],
|
"@server/*": [
|
||||||
"@app/*": ["*"],
|
"../server/*"
|
||||||
"@/*": ["./*"]
|
],
|
||||||
|
"@app/*": [
|
||||||
|
"*"
|
||||||
|
],
|
||||||
|
"@/*": [
|
||||||
|
"./*"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
|
@ -24,6 +34,13 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
"include": [
|
||||||
"exclude": ["node_modules"]
|
"next-env.d.ts",
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
".next/types/**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue