mirror of
https://github.com/fosrl/pangolin.git
synced 2025-06-20 20:35:43 +02:00
Format files and fix http response
This commit is contained in:
parent
797f72e1d0
commit
8213036729
49 changed files with 2428 additions and 2404 deletions
|
@ -1,3 +1,6 @@
|
|||
{
|
||||
"extends": ["next/core-web-vitals", "next/typescript"]
|
||||
}
|
||||
"extends": [
|
||||
"next/core-web-vitals",
|
||||
"next/typescript"
|
||||
]
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"version": "1",
|
||||
"name": "Pangolin",
|
||||
"type": "collection",
|
||||
"ignore": [
|
||||
"node_modules",
|
||||
".git"
|
||||
]
|
||||
"version": "1",
|
||||
"name": "Pangolin",
|
||||
"type": "collection",
|
||||
"ignore": [
|
||||
"node_modules",
|
||||
".git"
|
||||
]
|
||||
}
|
|
@ -17,4 +17,4 @@
|
|||
"lib": "@/lib",
|
||||
"hooks": "@/hooks"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -76,4 +76,4 @@
|
|||
"tsx": "4.19.1",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,98 +5,92 @@ import { and, eq } from 'drizzle-orm';
|
|||
import createHttpError from 'http-errors';
|
||||
import HttpCode from '@server/types/HttpCode';
|
||||
|
||||
export const ActionsEnum = {
|
||||
|
||||
createOrg: 1,
|
||||
deleteOrg: 2,
|
||||
getOrg: 3,
|
||||
listOrgs: 4,
|
||||
updateOrg: 5,
|
||||
|
||||
createSite: 6,
|
||||
deleteSite: 7,
|
||||
getSite: 8,
|
||||
listSites: 9,
|
||||
updateSite: 10,
|
||||
|
||||
createResource: 11,
|
||||
deleteResource: 12,
|
||||
getResource: 13,
|
||||
listResources: 14,
|
||||
updateResource: 15,
|
||||
|
||||
createTarget: 16,
|
||||
deleteTarget: 17,
|
||||
getTarget: 18,
|
||||
listTargets: 19,
|
||||
updateTarget: 20,
|
||||
|
||||
getUser: 21,
|
||||
deleteUser: 22,
|
||||
listUsers: 23
|
||||
|
||||
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: number, req: Request): Promise<boolean> {
|
||||
const userId = req.user?.id;
|
||||
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;
|
||||
if (!userId) {
|
||||
throw createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated');
|
||||
}
|
||||
|
||||
// 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 (!req.userOrgId) {
|
||||
throw createHttpError(HttpCode.BAD_REQUEST, 'Organization ID is required');
|
||||
}
|
||||
|
||||
// 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);
|
||||
try {
|
||||
let userOrgRoleId = req.userOrgRoleId;
|
||||
|
||||
return roleActionPermission.length > 0;
|
||||
// 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);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error checking user action permission:', error);
|
||||
throw createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error checking action permission');
|
||||
}
|
||||
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');
|
||||
}
|
||||
}
|
|
@ -5,36 +5,36 @@ import createHttpError from 'http-errors';
|
|||
import HttpCode from '@server/types/HttpCode';
|
||||
|
||||
interface CheckLimitOptions {
|
||||
orgId: number;
|
||||
limitName: string;
|
||||
currentValue: number;
|
||||
increment?: number;
|
||||
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);
|
||||
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`);
|
||||
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');
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
|
@ -1,360 +1,360 @@
|
|||
[
|
||||
"Cape Fox",
|
||||
"Short-Beaked Echidna",
|
||||
"Platypus",
|
||||
"Arctic Ground Squirrel",
|
||||
"Black-Tailed Prairie Dog",
|
||||
"Franklin's Ground Squirrel",
|
||||
"Golden-Mantled Ground Squirrel",
|
||||
"Groundhog",
|
||||
"Yellow-Bellied Marmot",
|
||||
"Eastern Mole",
|
||||
"Pink Fairy Armadillo",
|
||||
"Star-Nosed Mole",
|
||||
"Smooth-Coated Otter",
|
||||
"Degu",
|
||||
"Meadow Vole",
|
||||
"Campbell's Dwarf Hamster",
|
||||
"Fat Sand Rat",
|
||||
"Striped Ground Squirrel",
|
||||
"Syrian Hamster",
|
||||
"Common Wombat",
|
||||
"Greater Bilby",
|
||||
"Marsupial Mole",
|
||||
"Numbat",
|
||||
"Southern Hairy-Nosed Wombat",
|
||||
"American Badger",
|
||||
"Little Blue Penguin",
|
||||
"Giant Armadillo",
|
||||
"Eastern Long-Beaked Echidna",
|
||||
"Screaming Hairy Armadillo",
|
||||
"Chinese Hamster",
|
||||
"Roborovski Hamster",
|
||||
"Djungarian Hamster",
|
||||
"Indian Desert Jird",
|
||||
"Great Gerbil",
|
||||
"Plains Rat",
|
||||
"Big-Headed Mole-Rat",
|
||||
"Cape Ground Squirrel",
|
||||
"Colorado Chipmunk",
|
||||
"Alpine Chipmunk",
|
||||
"Cliff Chipmunk",
|
||||
"Hoary Marmot",
|
||||
"Himalayan Marmot",
|
||||
"Olympic Marmot",
|
||||
"San Joaquin Antelope Squirrel",
|
||||
"Gunnison's Prairie Dog",
|
||||
"California Ground Squirrel",
|
||||
"White-Tailed Prairie Dog",
|
||||
"Spotted Ground Squirrel",
|
||||
"Uinta Ground Squirrel",
|
||||
"Columbian Ground Squirrel",
|
||||
"Richardson's Ground Squirrel",
|
||||
"European Ground Squirrel",
|
||||
"Speckled Ground Squirrel",
|
||||
"Broad-Footed Mole",
|
||||
"European Mole",
|
||||
"Sunda Pangolin",
|
||||
"Desert Rosy Boa",
|
||||
"Desert Tortoise",
|
||||
"Brahminy Blind Snake",
|
||||
"Eastern Hognose Snake",
|
||||
"Saharan Horned Viper",
|
||||
"Gopher Snake",
|
||||
"Scarlet Kingsnake",
|
||||
"Eastern Pine Snake",
|
||||
"Eastern Coral Snake",
|
||||
"Naked Mole-Rat",
|
||||
"Mud Snake",
|
||||
"Barbados Threadsnake",
|
||||
"Arabian Sand Boa",
|
||||
"Japanese Badger",
|
||||
"Rainbow Snake",
|
||||
"Red-Eyed Crocodile Skink",
|
||||
"Texas Coral Snake",
|
||||
"Glossy Snake",
|
||||
"Oriental Wolf Snake",
|
||||
"Hog Badger",
|
||||
"Mongolian Gerbil",
|
||||
"Damaraland Mole-Rat",
|
||||
"Steppe Polecat",
|
||||
"Woma Python",
|
||||
"Southern Hognose Snake",
|
||||
"Asian Badger",
|
||||
"Giant Girdled Lizard",
|
||||
"Common Vole",
|
||||
"Bank Vole",
|
||||
"Chinese Ferret-Badger",
|
||||
"Desert Grassland Whiptail Lizard",
|
||||
"Rough Earth Snake",
|
||||
"Thirteen-Lined Ground Squirrel",
|
||||
"Southern Three-Banded Armadillo",
|
||||
"Slowworm",
|
||||
"Siberian Chipmunk",
|
||||
"Round-Tailed Ground Squirrel",
|
||||
"Pygmy Rabbit",
|
||||
"Pied Kingfisher",
|
||||
"Northern Short-Tailed Shrew ",
|
||||
"Northern Pika",
|
||||
"Nine-Banded Armadillo",
|
||||
"Nile Monitor",
|
||||
"Lowland Streaked Tenrec",
|
||||
"Lowland Paca",
|
||||
"Long-Nosed Bandicoot",
|
||||
"Long-Eared Jerboa",
|
||||
"Idaho Ground Squirrel",
|
||||
"Ground Pangolin",
|
||||
"Great Plains Rat Snake",
|
||||
"Gopher Tortoise",
|
||||
"Giant Pangolin",
|
||||
"European Hedgehog",
|
||||
"European Hamster",
|
||||
"Common Box Turtle",
|
||||
"Brown Rat",
|
||||
"Bog Turtle",
|
||||
"Bengal Fox",
|
||||
"American Alligator",
|
||||
"Aardvark",
|
||||
"Olm",
|
||||
"Tiger salamander",
|
||||
"Chinese giant salamander",
|
||||
"Spotted salamander",
|
||||
"Blue-spotted salamander",
|
||||
"Eastern worm snake",
|
||||
"Deinagkistrodon",
|
||||
"Northern crested newt",
|
||||
"Barred tiger salamander",
|
||||
"Rainbow bee-eater",
|
||||
"Sunbeam Snake",
|
||||
"Sandfish Skink",
|
||||
"Mexican Mole Lizard",
|
||||
"Tarbagan marmot",
|
||||
"Black-Headed Python",
|
||||
"Vancouver Island Marmot",
|
||||
"Bothrochilus",
|
||||
"Western Box Turtle",
|
||||
"Long-toed salamander",
|
||||
"Fat-Tailed Gerbil",
|
||||
"Mexican Prairie Dog",
|
||||
"Marbled salamander",
|
||||
"Bandy-Bandy",
|
||||
"Smooth Earth Snake",
|
||||
"Boodie",
|
||||
"Zebra-Tailed Lizard",
|
||||
"White-headed langur",
|
||||
"Javan Ferret-Badger",
|
||||
"Southwestern Blackhead Snake",
|
||||
"Malagasy Giant Rat",
|
||||
"Big Hairy Armadillo",
|
||||
"Camas pocket gopher",
|
||||
"Woodland vole",
|
||||
"Lesser Egyptian jerboa",
|
||||
"Little Brown Skink",
|
||||
"Plains pocket gopher",
|
||||
"Alaska marmot",
|
||||
"Gray marmot",
|
||||
"Louisiana waterthrush",
|
||||
"Ord's kangaroo rat",
|
||||
"North American least shrew",
|
||||
"Western rosella",
|
||||
"Northwestern salamander",
|
||||
"Acrochordus granulatus",
|
||||
"Kowari",
|
||||
"Anilius",
|
||||
"Gastrophryne carolinensis",
|
||||
"Yellow mud turtle",
|
||||
"Plateau pika",
|
||||
"Steppe lemming",
|
||||
"American shrew mole",
|
||||
"Calabar python",
|
||||
"Dermophis mexicanus",
|
||||
"Rufous rat-kangaroo",
|
||||
"Hairy-tailed mole",
|
||||
"Mexican burrowing toad",
|
||||
"Seven-banded armadillo",
|
||||
"Scaphiopus holbrookii",
|
||||
"Asiatic brush-tailed porcupine",
|
||||
"Bolson tortoise",
|
||||
"Common midwife toad",
|
||||
"Ambystoma talpoideum",
|
||||
"Crucifix toad",
|
||||
"Red Hills salamander",
|
||||
"Uperodon taprobanicus",
|
||||
"Plains spadefoot toad",
|
||||
"Spea hammondii",
|
||||
"Puerto Rican crested toad",
|
||||
"Physalaemus nattereri",
|
||||
"Yosemite toad",
|
||||
"Frosted flatwoods salamander",
|
||||
"Striped newt",
|
||||
"Streamside salamander",
|
||||
"Southern red-backed salamander",
|
||||
"Spencer's burrowing frog",
|
||||
"Ringed salamander",
|
||||
"Kaloula baleata",
|
||||
"Uperodon systoma",
|
||||
"Ichthyophis beddomei",
|
||||
"Uperodon globulosus",
|
||||
"Herpele squalostoma",
|
||||
"Ichthyophis mindanaoensis",
|
||||
"Sandhill frog",
|
||||
"Strecker's chorus frog",
|
||||
"Uraeotyphlus oxyurus",
|
||||
"Caecilia nigricans",
|
||||
"Uraeotyphlus menoni",
|
||||
"Savannah forest tree frog",
|
||||
"Uraeotyphlus interruptus",
|
||||
"Rose's rain frog",
|
||||
"Dermophis parviceps",
|
||||
"Leptopelis gramineus",
|
||||
"Rhombophryne coudreaui",
|
||||
"Elachistocleis pearsei",
|
||||
"Hylodes heyeri",
|
||||
"Carphophis vermis",
|
||||
"Anniella pulchra",
|
||||
"Lampropeltis calligaster rhombomaculata",
|
||||
"Xerotyphlops vermicularis",
|
||||
"Iberian worm lizard",
|
||||
"Lytorhynchus diadema",
|
||||
"Micrurus frontalis",
|
||||
"Euprepiophis conspicillata",
|
||||
"Amphisbaena fuliginosa",
|
||||
"Greater earless lizard",
|
||||
"Afrotyphlops schlegelii",
|
||||
"Texas lined snake",
|
||||
"Atractaspis branchi",
|
||||
"Calamaria gervaisii",
|
||||
"Brachyurophis fasciolatus",
|
||||
"Brongersma's worm snake",
|
||||
"Letheobia simonii",
|
||||
"Grypotyphlops acutus",
|
||||
"Acontias breviceps",
|
||||
"Reticulate worm snake",
|
||||
"Trinidad worm snake",
|
||||
"Amphisbaena microcephala",
|
||||
"Lerista labialis",
|
||||
"Flathead worm snake",
|
||||
"Mertens's worm lizard",
|
||||
"Elegant worm snake",
|
||||
"Iranian worm snake",
|
||||
"Pernambuco worm snake",
|
||||
"Crest-tailed mulgara",
|
||||
"Southern long-nosed armadillo",
|
||||
"Greater fairy armadillo",
|
||||
"Steppe pika",
|
||||
"Black-capped marmot",
|
||||
"Armored rat",
|
||||
"Giant mole-rat",
|
||||
"Montane vole",
|
||||
"Oldfield mouse",
|
||||
"Southeastern pocket gopher",
|
||||
"Long-tailed vole",
|
||||
"Greater naked-tailed armadillo",
|
||||
"Common mole-rat",
|
||||
"Philippine porcupine",
|
||||
"Milne-Edwards's sifaka",
|
||||
"Townsend's mole",
|
||||
"Giant golden mole",
|
||||
"Daurian pika",
|
||||
"Cape golden mole",
|
||||
"Yellow-faced pocket gopher",
|
||||
"Indian gerbil",
|
||||
"Plains viscacha rat",
|
||||
"Red tree vole",
|
||||
"Middle East blind mole-rat",
|
||||
"Mountain paca",
|
||||
"Pallas's pika",
|
||||
"Bicolored shrew",
|
||||
"Cape mole-rat",
|
||||
"Cascade golden-mantled ground squirrel",
|
||||
"Unstriped ground squirrel",
|
||||
"Townsend's vole",
|
||||
"Yellow ground squirrel",
|
||||
"Desert pocket gopher",
|
||||
"Bunny rat",
|
||||
"Washington ground squirrel",
|
||||
"Mole-like rice tenrec",
|
||||
"Greater mole-rat",
|
||||
"Hottentot golden mole",
|
||||
"Plains pocket mouse",
|
||||
"Cheesman's gerbil",
|
||||
"Judean Mountains blind mole-rat",
|
||||
"Chisel-toothed kangaroo rat",
|
||||
"Rough-haired golden mole",
|
||||
"Southeastern shrew",
|
||||
"California pocket mouse",
|
||||
"Coruro",
|
||||
"Merriam's shrew",
|
||||
"Long-tailed mole",
|
||||
"Orange leaf-nosed bat",
|
||||
"South African pouched mouse",
|
||||
"Selous's mongoose",
|
||||
"Ash-grey mouse",
|
||||
"Russet ground squirrel",
|
||||
"Gulf Coast kangaroo rat",
|
||||
"Olive-backed pocket mouse",
|
||||
"Northeast African mole-rat",
|
||||
"San Diego pocket mouse",
|
||||
"Nelson's pocket mouse",
|
||||
"Geoffroy's horseshoe bat",
|
||||
"Narrow-faced kangaroo rat",
|
||||
"Chilean rock rat",
|
||||
"R\u00fcppell's horseshoe bat",
|
||||
"Long-tailed pocket mouse",
|
||||
"Aztec mouse",
|
||||
"Western mouse",
|
||||
"Felten's myotis",
|
||||
"Akodon azarae",
|
||||
"Talas tuco-tuco",
|
||||
"Upper Galilee Mountains blind mole-rat",
|
||||
"Pearson's tuco-tuco",
|
||||
"Mount Carmel blind mole-rat",
|
||||
"Plethobasus cyphyus",
|
||||
"Long-Nosed Snake",
|
||||
"Russian Desman",
|
||||
"Texas Blind Snake",
|
||||
"Florida Box Turtle",
|
||||
"Lesser Bandicoot Rat",
|
||||
"Bush Rat",
|
||||
"Six-Lined Racerunner",
|
||||
"Eastern Bearded Dragon",
|
||||
"Lesser Antillean Iguana",
|
||||
"Eastern Mud Turtle",
|
||||
"Slender Glass Lizard",
|
||||
"Scarlet Snake",
|
||||
"Natal Multimammate Mouse",
|
||||
"Mountain Beaver",
|
||||
"Bobak Marmot",
|
||||
"Kirtland's Snake",
|
||||
"Pine Woods Snake",
|
||||
"Western Whiptail",
|
||||
"Boxelder bug",
|
||||
"Porcellio scaber",
|
||||
"German cockroach",
|
||||
"Forficula auricularia",
|
||||
"Anisolabis maritima",
|
||||
"Trigoniulus corallinus",
|
||||
"Sinea diadema",
|
||||
"Black imported fire ant",
|
||||
"Scutigera coleoptrata",
|
||||
"Mastigoproctus giganteus",
|
||||
"Dermacentor andersoni",
|
||||
"Deathstalker",
|
||||
"Larinioides cornutus",
|
||||
"Cheiracanthium inclusum",
|
||||
"Latrodectus hesperus",
|
||||
"Scytodes thoracica",
|
||||
"Atypus affinis",
|
||||
"Illacme plenipes",
|
||||
"Ommatoiulus moreleti",
|
||||
"Narceus americanus",
|
||||
"Madagascar hissing cockroach",
|
||||
"Labidura riparia",
|
||||
"Forficula smyrnensis",
|
||||
"Argentine ant",
|
||||
"Texas leafcutter ant",
|
||||
"Brachypelma klaasi",
|
||||
"Western Blind Snake",
|
||||
"Desert Box Turtle",
|
||||
"African Striped Weasel"
|
||||
"Cape Fox",
|
||||
"Short-Beaked Echidna",
|
||||
"Platypus",
|
||||
"Arctic Ground Squirrel",
|
||||
"Black-Tailed Prairie Dog",
|
||||
"Franklin's Ground Squirrel",
|
||||
"Golden-Mantled Ground Squirrel",
|
||||
"Groundhog",
|
||||
"Yellow-Bellied Marmot",
|
||||
"Eastern Mole",
|
||||
"Pink Fairy Armadillo",
|
||||
"Star-Nosed Mole",
|
||||
"Smooth-Coated Otter",
|
||||
"Degu",
|
||||
"Meadow Vole",
|
||||
"Campbell's Dwarf Hamster",
|
||||
"Fat Sand Rat",
|
||||
"Striped Ground Squirrel",
|
||||
"Syrian Hamster",
|
||||
"Common Wombat",
|
||||
"Greater Bilby",
|
||||
"Marsupial Mole",
|
||||
"Numbat",
|
||||
"Southern Hairy-Nosed Wombat",
|
||||
"American Badger",
|
||||
"Little Blue Penguin",
|
||||
"Giant Armadillo",
|
||||
"Eastern Long-Beaked Echidna",
|
||||
"Screaming Hairy Armadillo",
|
||||
"Chinese Hamster",
|
||||
"Roborovski Hamster",
|
||||
"Djungarian Hamster",
|
||||
"Indian Desert Jird",
|
||||
"Great Gerbil",
|
||||
"Plains Rat",
|
||||
"Big-Headed Mole-Rat",
|
||||
"Cape Ground Squirrel",
|
||||
"Colorado Chipmunk",
|
||||
"Alpine Chipmunk",
|
||||
"Cliff Chipmunk",
|
||||
"Hoary Marmot",
|
||||
"Himalayan Marmot",
|
||||
"Olympic Marmot",
|
||||
"San Joaquin Antelope Squirrel",
|
||||
"Gunnison's Prairie Dog",
|
||||
"California Ground Squirrel",
|
||||
"White-Tailed Prairie Dog",
|
||||
"Spotted Ground Squirrel",
|
||||
"Uinta Ground Squirrel",
|
||||
"Columbian Ground Squirrel",
|
||||
"Richardson's Ground Squirrel",
|
||||
"European Ground Squirrel",
|
||||
"Speckled Ground Squirrel",
|
||||
"Broad-Footed Mole",
|
||||
"European Mole",
|
||||
"Sunda Pangolin",
|
||||
"Desert Rosy Boa",
|
||||
"Desert Tortoise",
|
||||
"Brahminy Blind Snake",
|
||||
"Eastern Hognose Snake",
|
||||
"Saharan Horned Viper",
|
||||
"Gopher Snake",
|
||||
"Scarlet Kingsnake",
|
||||
"Eastern Pine Snake",
|
||||
"Eastern Coral Snake",
|
||||
"Naked Mole-Rat",
|
||||
"Mud Snake",
|
||||
"Barbados Threadsnake",
|
||||
"Arabian Sand Boa",
|
||||
"Japanese Badger",
|
||||
"Rainbow Snake",
|
||||
"Red-Eyed Crocodile Skink",
|
||||
"Texas Coral Snake",
|
||||
"Glossy Snake",
|
||||
"Oriental Wolf Snake",
|
||||
"Hog Badger",
|
||||
"Mongolian Gerbil",
|
||||
"Damaraland Mole-Rat",
|
||||
"Steppe Polecat",
|
||||
"Woma Python",
|
||||
"Southern Hognose Snake",
|
||||
"Asian Badger",
|
||||
"Giant Girdled Lizard",
|
||||
"Common Vole",
|
||||
"Bank Vole",
|
||||
"Chinese Ferret-Badger",
|
||||
"Desert Grassland Whiptail Lizard",
|
||||
"Rough Earth Snake",
|
||||
"Thirteen-Lined Ground Squirrel",
|
||||
"Southern Three-Banded Armadillo",
|
||||
"Slowworm",
|
||||
"Siberian Chipmunk",
|
||||
"Round-Tailed Ground Squirrel",
|
||||
"Pygmy Rabbit",
|
||||
"Pied Kingfisher",
|
||||
"Northern Short-Tailed Shrew ",
|
||||
"Northern Pika",
|
||||
"Nine-Banded Armadillo",
|
||||
"Nile Monitor",
|
||||
"Lowland Streaked Tenrec",
|
||||
"Lowland Paca",
|
||||
"Long-Nosed Bandicoot",
|
||||
"Long-Eared Jerboa",
|
||||
"Idaho Ground Squirrel",
|
||||
"Ground Pangolin",
|
||||
"Great Plains Rat Snake",
|
||||
"Gopher Tortoise",
|
||||
"Giant Pangolin",
|
||||
"European Hedgehog",
|
||||
"European Hamster",
|
||||
"Common Box Turtle",
|
||||
"Brown Rat",
|
||||
"Bog Turtle",
|
||||
"Bengal Fox",
|
||||
"American Alligator",
|
||||
"Aardvark",
|
||||
"Olm",
|
||||
"Tiger salamander",
|
||||
"Chinese giant salamander",
|
||||
"Spotted salamander",
|
||||
"Blue-spotted salamander",
|
||||
"Eastern worm snake",
|
||||
"Deinagkistrodon",
|
||||
"Northern crested newt",
|
||||
"Barred tiger salamander",
|
||||
"Rainbow bee-eater",
|
||||
"Sunbeam Snake",
|
||||
"Sandfish Skink",
|
||||
"Mexican Mole Lizard",
|
||||
"Tarbagan marmot",
|
||||
"Black-Headed Python",
|
||||
"Vancouver Island Marmot",
|
||||
"Bothrochilus",
|
||||
"Western Box Turtle",
|
||||
"Long-toed salamander",
|
||||
"Fat-Tailed Gerbil",
|
||||
"Mexican Prairie Dog",
|
||||
"Marbled salamander",
|
||||
"Bandy-Bandy",
|
||||
"Smooth Earth Snake",
|
||||
"Boodie",
|
||||
"Zebra-Tailed Lizard",
|
||||
"White-headed langur",
|
||||
"Javan Ferret-Badger",
|
||||
"Southwestern Blackhead Snake",
|
||||
"Malagasy Giant Rat",
|
||||
"Big Hairy Armadillo",
|
||||
"Camas pocket gopher",
|
||||
"Woodland vole",
|
||||
"Lesser Egyptian jerboa",
|
||||
"Little Brown Skink",
|
||||
"Plains pocket gopher",
|
||||
"Alaska marmot",
|
||||
"Gray marmot",
|
||||
"Louisiana waterthrush",
|
||||
"Ord's kangaroo rat",
|
||||
"North American least shrew",
|
||||
"Western rosella",
|
||||
"Northwestern salamander",
|
||||
"Acrochordus granulatus",
|
||||
"Kowari",
|
||||
"Anilius",
|
||||
"Gastrophryne carolinensis",
|
||||
"Yellow mud turtle",
|
||||
"Plateau pika",
|
||||
"Steppe lemming",
|
||||
"American shrew mole",
|
||||
"Calabar python",
|
||||
"Dermophis mexicanus",
|
||||
"Rufous rat-kangaroo",
|
||||
"Hairy-tailed mole",
|
||||
"Mexican burrowing toad",
|
||||
"Seven-banded armadillo",
|
||||
"Scaphiopus holbrookii",
|
||||
"Asiatic brush-tailed porcupine",
|
||||
"Bolson tortoise",
|
||||
"Common midwife toad",
|
||||
"Ambystoma talpoideum",
|
||||
"Crucifix toad",
|
||||
"Red Hills salamander",
|
||||
"Uperodon taprobanicus",
|
||||
"Plains spadefoot toad",
|
||||
"Spea hammondii",
|
||||
"Puerto Rican crested toad",
|
||||
"Physalaemus nattereri",
|
||||
"Yosemite toad",
|
||||
"Frosted flatwoods salamander",
|
||||
"Striped newt",
|
||||
"Streamside salamander",
|
||||
"Southern red-backed salamander",
|
||||
"Spencer's burrowing frog",
|
||||
"Ringed salamander",
|
||||
"Kaloula baleata",
|
||||
"Uperodon systoma",
|
||||
"Ichthyophis beddomei",
|
||||
"Uperodon globulosus",
|
||||
"Herpele squalostoma",
|
||||
"Ichthyophis mindanaoensis",
|
||||
"Sandhill frog",
|
||||
"Strecker's chorus frog",
|
||||
"Uraeotyphlus oxyurus",
|
||||
"Caecilia nigricans",
|
||||
"Uraeotyphlus menoni",
|
||||
"Savannah forest tree frog",
|
||||
"Uraeotyphlus interruptus",
|
||||
"Rose's rain frog",
|
||||
"Dermophis parviceps",
|
||||
"Leptopelis gramineus",
|
||||
"Rhombophryne coudreaui",
|
||||
"Elachistocleis pearsei",
|
||||
"Hylodes heyeri",
|
||||
"Carphophis vermis",
|
||||
"Anniella pulchra",
|
||||
"Lampropeltis calligaster rhombomaculata",
|
||||
"Xerotyphlops vermicularis",
|
||||
"Iberian worm lizard",
|
||||
"Lytorhynchus diadema",
|
||||
"Micrurus frontalis",
|
||||
"Euprepiophis conspicillata",
|
||||
"Amphisbaena fuliginosa",
|
||||
"Greater earless lizard",
|
||||
"Afrotyphlops schlegelii",
|
||||
"Texas lined snake",
|
||||
"Atractaspis branchi",
|
||||
"Calamaria gervaisii",
|
||||
"Brachyurophis fasciolatus",
|
||||
"Brongersma's worm snake",
|
||||
"Letheobia simonii",
|
||||
"Grypotyphlops acutus",
|
||||
"Acontias breviceps",
|
||||
"Reticulate worm snake",
|
||||
"Trinidad worm snake",
|
||||
"Amphisbaena microcephala",
|
||||
"Lerista labialis",
|
||||
"Flathead worm snake",
|
||||
"Mertens's worm lizard",
|
||||
"Elegant worm snake",
|
||||
"Iranian worm snake",
|
||||
"Pernambuco worm snake",
|
||||
"Crest-tailed mulgara",
|
||||
"Southern long-nosed armadillo",
|
||||
"Greater fairy armadillo",
|
||||
"Steppe pika",
|
||||
"Black-capped marmot",
|
||||
"Armored rat",
|
||||
"Giant mole-rat",
|
||||
"Montane vole",
|
||||
"Oldfield mouse",
|
||||
"Southeastern pocket gopher",
|
||||
"Long-tailed vole",
|
||||
"Greater naked-tailed armadillo",
|
||||
"Common mole-rat",
|
||||
"Philippine porcupine",
|
||||
"Milne-Edwards's sifaka",
|
||||
"Townsend's mole",
|
||||
"Giant golden mole",
|
||||
"Daurian pika",
|
||||
"Cape golden mole",
|
||||
"Yellow-faced pocket gopher",
|
||||
"Indian gerbil",
|
||||
"Plains viscacha rat",
|
||||
"Red tree vole",
|
||||
"Middle East blind mole-rat",
|
||||
"Mountain paca",
|
||||
"Pallas's pika",
|
||||
"Bicolored shrew",
|
||||
"Cape mole-rat",
|
||||
"Cascade golden-mantled ground squirrel",
|
||||
"Unstriped ground squirrel",
|
||||
"Townsend's vole",
|
||||
"Yellow ground squirrel",
|
||||
"Desert pocket gopher",
|
||||
"Bunny rat",
|
||||
"Washington ground squirrel",
|
||||
"Mole-like rice tenrec",
|
||||
"Greater mole-rat",
|
||||
"Hottentot golden mole",
|
||||
"Plains pocket mouse",
|
||||
"Cheesman's gerbil",
|
||||
"Judean Mountains blind mole-rat",
|
||||
"Chisel-toothed kangaroo rat",
|
||||
"Rough-haired golden mole",
|
||||
"Southeastern shrew",
|
||||
"California pocket mouse",
|
||||
"Coruro",
|
||||
"Merriam's shrew",
|
||||
"Long-tailed mole",
|
||||
"Orange leaf-nosed bat",
|
||||
"South African pouched mouse",
|
||||
"Selous's mongoose",
|
||||
"Ash-grey mouse",
|
||||
"Russet ground squirrel",
|
||||
"Gulf Coast kangaroo rat",
|
||||
"Olive-backed pocket mouse",
|
||||
"Northeast African mole-rat",
|
||||
"San Diego pocket mouse",
|
||||
"Nelson's pocket mouse",
|
||||
"Geoffroy's horseshoe bat",
|
||||
"Narrow-faced kangaroo rat",
|
||||
"Chilean rock rat",
|
||||
"R\u00fcppell's horseshoe bat",
|
||||
"Long-tailed pocket mouse",
|
||||
"Aztec mouse",
|
||||
"Western mouse",
|
||||
"Felten's myotis",
|
||||
"Akodon azarae",
|
||||
"Talas tuco-tuco",
|
||||
"Upper Galilee Mountains blind mole-rat",
|
||||
"Pearson's tuco-tuco",
|
||||
"Mount Carmel blind mole-rat",
|
||||
"Plethobasus cyphyus",
|
||||
"Long-Nosed Snake",
|
||||
"Russian Desman",
|
||||
"Texas Blind Snake",
|
||||
"Florida Box Turtle",
|
||||
"Lesser Bandicoot Rat",
|
||||
"Bush Rat",
|
||||
"Six-Lined Racerunner",
|
||||
"Eastern Bearded Dragon",
|
||||
"Lesser Antillean Iguana",
|
||||
"Eastern Mud Turtle",
|
||||
"Slender Glass Lizard",
|
||||
"Scarlet Snake",
|
||||
"Natal Multimammate Mouse",
|
||||
"Mountain Beaver",
|
||||
"Bobak Marmot",
|
||||
"Kirtland's Snake",
|
||||
"Pine Woods Snake",
|
||||
"Western Whiptail",
|
||||
"Boxelder bug",
|
||||
"Porcellio scaber",
|
||||
"German cockroach",
|
||||
"Forficula auricularia",
|
||||
"Anisolabis maritima",
|
||||
"Trigoniulus corallinus",
|
||||
"Sinea diadema",
|
||||
"Black imported fire ant",
|
||||
"Scutigera coleoptrata",
|
||||
"Mastigoproctus giganteus",
|
||||
"Dermacentor andersoni",
|
||||
"Deathstalker",
|
||||
"Larinioides cornutus",
|
||||
"Cheiracanthium inclusum",
|
||||
"Latrodectus hesperus",
|
||||
"Scytodes thoracica",
|
||||
"Atypus affinis",
|
||||
"Illacme plenipes",
|
||||
"Ommatoiulus moreleti",
|
||||
"Narceus americanus",
|
||||
"Madagascar hissing cockroach",
|
||||
"Labidura riparia",
|
||||
"Forficula smyrnensis",
|
||||
"Argentine ant",
|
||||
"Texas leafcutter ant",
|
||||
"Brachypelma klaasi",
|
||||
"Western Blind Snake",
|
||||
"Desert Box Turtle",
|
||||
"African Striped Weasel"
|
||||
]
|
|
@ -130,7 +130,7 @@ export const passwordResetTokens = sqliteTable("passwordResetTokens", {
|
|||
});
|
||||
|
||||
export const actions = sqliteTable("actions", {
|
||||
actionId: integer("actionId").primaryKey({ autoIncrement: true }),
|
||||
actionId: text("actionId").primaryKey(),
|
||||
name: text("name").notNull(),
|
||||
description: text("description"),
|
||||
});
|
||||
|
@ -146,7 +146,7 @@ export const roleActions = sqliteTable("roleActions", {
|
|||
roleId: integer("roleId")
|
||||
.notNull()
|
||||
.references(() => roles.roleId, { onDelete: "cascade" }),
|
||||
actionId: integer("actionId")
|
||||
actionId: text("actionId")
|
||||
.notNull()
|
||||
.references(() => actions.actionId, { onDelete: "cascade" }),
|
||||
orgId: integer("orgId")
|
||||
|
@ -158,7 +158,7 @@ export const userActions = sqliteTable("userActions", {
|
|||
userId: text("userId")
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
actionId: integer("actionId")
|
||||
actionId: text("actionId")
|
||||
.notNull()
|
||||
.references(() => actions.actionId, { onDelete: "cascade" }),
|
||||
orgId: integer("orgId")
|
||||
|
|
|
@ -6,28 +6,28 @@ import createHttpError from 'http-errors';
|
|||
import HttpCode from '@server/types/HttpCode';
|
||||
|
||||
export async function getUserOrgs(req: Request, res: Response, next: NextFunction) {
|
||||
const userId = req.user?.id; // Assuming you have user information in the request
|
||||
const userId = req.user?.id; // Assuming you have user information in the request
|
||||
|
||||
if (!userId) {
|
||||
return next(createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated'));
|
||||
}
|
||||
if (!userId) {
|
||||
return next(createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated'));
|
||||
}
|
||||
|
||||
try {
|
||||
const userOrganizations = await db.select({
|
||||
orgId: userOrgs.orgId,
|
||||
role: userOrgs.role,
|
||||
})
|
||||
.from(userOrgs)
|
||||
.where(eq(userOrgs.userId, userId));
|
||||
try {
|
||||
const userOrganizations = await db.select({
|
||||
orgId: userOrgs.orgId,
|
||||
roleId: userOrgs.roleId,
|
||||
})
|
||||
.from(userOrgs)
|
||||
.where(eq(userOrgs.userId, userId));
|
||||
|
||||
req.userOrgs = userOrganizations.map(org => org.orgId);
|
||||
// req.userOrgRoleIds = userOrganizations.reduce((acc, org) => {
|
||||
// acc[org.orgId] = org.role;
|
||||
// return acc;
|
||||
// }, {} as Record<number, string>);
|
||||
req.userOrgIds = userOrganizations.map(org => org.orgId);
|
||||
// req.userOrgRoleIds = userOrganizations.reduce((acc, org) => {
|
||||
// acc[org.orgId] = org.role;
|
||||
// return acc;
|
||||
// }, {} as Record<number, string>);
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error retrieving user organizations'));
|
||||
}
|
||||
next();
|
||||
} catch (error) {
|
||||
next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error retrieving user organizations'));
|
||||
}
|
||||
}
|
|
@ -7,31 +7,31 @@ import HttpCode from '@server/types/HttpCode';
|
|||
import { AuthenticatedRequest } from '@server/types/Auth';
|
||||
|
||||
export function verifyOrgAccess(req: Request, res: Response, next: NextFunction) {
|
||||
const userId = req.user!.id; // Assuming you have user information in the request
|
||||
const orgId = parseInt(req.params.orgId);
|
||||
const userId = req.user!.id; // Assuming you have user information in the request
|
||||
const orgId = parseInt(req.params.orgId);
|
||||
|
||||
if (!userId) {
|
||||
return next(createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated'));
|
||||
}
|
||||
if (!userId) {
|
||||
return next(createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated'));
|
||||
}
|
||||
|
||||
if (isNaN(orgId)) {
|
||||
return next(createHttpError(HttpCode.BAD_REQUEST, 'Invalid organization ID'));
|
||||
}
|
||||
if (isNaN(orgId)) {
|
||||
return next(createHttpError(HttpCode.BAD_REQUEST, 'Invalid organization ID'));
|
||||
}
|
||||
|
||||
db.select()
|
||||
.from(userOrgs)
|
||||
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, orgId)))
|
||||
.then((result) => {
|
||||
if (result.length === 0) {
|
||||
next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization'));
|
||||
} else {
|
||||
// User has access, attach the user's role to the request for potential future use
|
||||
req.userOrgRoleId = result[0].roleId;
|
||||
req.userOrgId = orgId;
|
||||
next();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error verifying organization access'));
|
||||
});
|
||||
db.select()
|
||||
.from(userOrgs)
|
||||
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, orgId)))
|
||||
.then((result) => {
|
||||
if (result.length === 0) {
|
||||
next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization'));
|
||||
} else {
|
||||
// User has access, attach the user's role to the request for potential future use
|
||||
req.userOrgRoleId = result[0].roleId;
|
||||
req.userOrgId = orgId;
|
||||
next();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error verifying organization access'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -6,73 +6,73 @@ import createHttpError from 'http-errors';
|
|||
import HttpCode from '@server/types/HttpCode';
|
||||
|
||||
export async function verifyResourceAccess(req: Request, res: Response, next: NextFunction) {
|
||||
const userId = req.user!.id; // Assuming you have user information in the request
|
||||
const resourceId = req.params.resourceId;
|
||||
const userId = req.user!.id; // Assuming you have user information in the request
|
||||
const resourceId = req.params.resourceId;
|
||||
|
||||
if (!userId) {
|
||||
return next(createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated'));
|
||||
}
|
||||
|
||||
try {
|
||||
// Get the resource
|
||||
const resource = await db.select()
|
||||
.from(resources)
|
||||
.where(eq(resources.resourceId, resourceId))
|
||||
.limit(1);
|
||||
|
||||
if (resource.length === 0) {
|
||||
return next(createHttpError(HttpCode.NOT_FOUND, `Resource with ID ${resourceId} not found`));
|
||||
if (!userId) {
|
||||
return next(createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated'));
|
||||
}
|
||||
|
||||
if (!resource[0].orgId) {
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, `Resource with ID ${resourceId} does not have an organization ID`));
|
||||
try {
|
||||
// Get the resource
|
||||
const resource = await db.select()
|
||||
.from(resources)
|
||||
.where(eq(resources.resourceId, resourceId))
|
||||
.limit(1);
|
||||
|
||||
if (resource.length === 0) {
|
||||
return next(createHttpError(HttpCode.NOT_FOUND, `Resource with ID ${resourceId} not found`));
|
||||
}
|
||||
|
||||
if (!resource[0].orgId) {
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, `Resource with ID ${resourceId} does not have an organization ID`));
|
||||
}
|
||||
|
||||
// Get user's role ID in the organization
|
||||
const userOrgRole = await db.select()
|
||||
.from(userOrgs)
|
||||
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, resource[0].orgId)))
|
||||
.limit(1);
|
||||
|
||||
if (userOrgRole.length === 0) {
|
||||
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization'));
|
||||
}
|
||||
|
||||
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'));
|
||||
}
|
||||
|
||||
// Get user's role ID in the organization
|
||||
const userOrgRole = await db.select()
|
||||
.from(userOrgs)
|
||||
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, resource[0].orgId)))
|
||||
.limit(1);
|
||||
|
||||
if (userOrgRole.length === 0) {
|
||||
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization'));
|
||||
}
|
||||
|
||||
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'));
|
||||
}
|
||||
}
|
|
@ -6,74 +6,74 @@ import createHttpError from 'http-errors';
|
|||
import HttpCode from '@server/types/HttpCode';
|
||||
|
||||
export async function verifySiteAccess(req: Request, res: Response, next: NextFunction) {
|
||||
const userId = req.user!.id; // Assuming you have user information in the request
|
||||
const siteId = parseInt(req.params.siteId);
|
||||
const userId = req.user!.id; // Assuming you have user information in the request
|
||||
const siteId = parseInt(req.params.siteId);
|
||||
|
||||
if (!userId) {
|
||||
return next(createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated'));
|
||||
}
|
||||
|
||||
if (isNaN(siteId)) {
|
||||
return next(createHttpError(HttpCode.BAD_REQUEST, 'Invalid site ID'));
|
||||
}
|
||||
|
||||
try {
|
||||
// Get the site
|
||||
const site = await db.select().from(sites).where(eq(sites.siteId, siteId)).limit(1);
|
||||
|
||||
if (site.length === 0) {
|
||||
return next(createHttpError(HttpCode.NOT_FOUND, `Site with ID ${siteId} not found`));
|
||||
if (!userId) {
|
||||
return next(createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated'));
|
||||
}
|
||||
|
||||
if (!site[0].orgId) {
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, `Site with ID ${siteId} does not have an organization ID`));
|
||||
if (isNaN(siteId)) {
|
||||
return next(createHttpError(HttpCode.BAD_REQUEST, 'Invalid site ID'));
|
||||
}
|
||||
|
||||
// Get user's role ID in the organization
|
||||
const userOrgRole = await db.select()
|
||||
.from(userOrgs)
|
||||
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, site[0].orgId)))
|
||||
.limit(1);
|
||||
try {
|
||||
// Get the site
|
||||
const site = await db.select().from(sites).where(eq(sites.siteId, siteId)).limit(1);
|
||||
|
||||
if (userOrgRole.length === 0) {
|
||||
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization'));
|
||||
if (site.length === 0) {
|
||||
return next(createHttpError(HttpCode.NOT_FOUND, `Site with ID ${siteId} not found`));
|
||||
}
|
||||
|
||||
if (!site[0].orgId) {
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, `Site with ID ${siteId} does not have an organization ID`));
|
||||
}
|
||||
|
||||
// Get user's role ID in the organization
|
||||
const userOrgRole = await db.select()
|
||||
.from(userOrgs)
|
||||
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, site[0].orgId)))
|
||||
.limit(1);
|
||||
|
||||
if (userOrgRole.length === 0) {
|
||||
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization'));
|
||||
}
|
||||
|
||||
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'));
|
||||
}
|
||||
|
||||
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'));
|
||||
}
|
||||
}
|
|
@ -6,79 +6,79 @@ import createHttpError from 'http-errors';
|
|||
import HttpCode from '@server/types/HttpCode';
|
||||
|
||||
export async function verifyTargetAccess(req: Request, res: Response, next: NextFunction) {
|
||||
const userId = req.user!.id; // Assuming you have user information in the request
|
||||
const targetId = parseInt(req.params.targetId);
|
||||
const userId = req.user!.id; // Assuming you have user information in the request
|
||||
const targetId = parseInt(req.params.targetId);
|
||||
|
||||
if (!userId) {
|
||||
return next(createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated'));
|
||||
}
|
||||
if (!userId) {
|
||||
return next(createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated'));
|
||||
}
|
||||
|
||||
if (isNaN(targetId)) {
|
||||
return next(createHttpError(HttpCode.BAD_REQUEST, 'Invalid organization ID'));
|
||||
}
|
||||
if (isNaN(targetId)) {
|
||||
return next(createHttpError(HttpCode.BAD_REQUEST, 'Invalid organization ID'));
|
||||
}
|
||||
|
||||
const target = await db.select()
|
||||
.from(targets)
|
||||
.where(eq(targets.targetId, targetId))
|
||||
.limit(1);
|
||||
const target = await db.select()
|
||||
.from(targets)
|
||||
.where(eq(targets.targetId, targetId))
|
||||
.limit(1);
|
||||
|
||||
if (target.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`target with ID ${targetId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
if (target.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`target with ID ${targetId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const resourceId = target[0].resourceId;
|
||||
const resourceId = target[0].resourceId;
|
||||
|
||||
if (resourceId) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.INTERNAL_SERVER_ERROR,
|
||||
`target with ID ${targetId} does not have a resource ID`
|
||||
)
|
||||
);
|
||||
}
|
||||
if (resourceId) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.INTERNAL_SERVER_ERROR,
|
||||
`target with ID ${targetId} does not have a resource ID`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const resource = await db.select()
|
||||
.from(resources)
|
||||
.where(eq(resources.resourceId, resourceId!))
|
||||
.limit(1);
|
||||
const resource = await db.select()
|
||||
.from(resources)
|
||||
.where(eq(resources.resourceId, resourceId!))
|
||||
.limit(1);
|
||||
|
||||
if (resource.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`resource with ID ${resourceId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
if (resource.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`resource with ID ${resourceId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (!resource[0].orgId) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.INTERNAL_SERVER_ERROR,
|
||||
`resource with ID ${resourceId} does not have an organization ID`
|
||||
)
|
||||
);
|
||||
}
|
||||
if (!resource[0].orgId) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.INTERNAL_SERVER_ERROR,
|
||||
`resource with ID ${resourceId} does not have an organization ID`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
db.select()
|
||||
.from(userOrgs)
|
||||
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, resource[0].orgId)))
|
||||
.then((result) => {
|
||||
if (result.length === 0) {
|
||||
next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization'));
|
||||
} else {
|
||||
// User has access, attach the user's role to the request for potential future use
|
||||
req.userOrgRoleId = result[0].roleId;
|
||||
req.userOrgId = resource[0].orgId!;
|
||||
next();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error verifying organization access'));
|
||||
});
|
||||
db.select()
|
||||
.from(userOrgs)
|
||||
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, resource[0].orgId)))
|
||||
.then((result) => {
|
||||
if (result.length === 0) {
|
||||
next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization'));
|
||||
} else {
|
||||
// User has access, attach the user's role to the request for potential future use
|
||||
req.userOrgRoleId = result[0].roleId;
|
||||
req.userOrgId = resource[0].orgId!;
|
||||
next();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error verifying organization access'));
|
||||
});
|
||||
}
|
|
@ -3,6 +3,8 @@ import { DrizzleError, eq } from 'drizzle-orm';
|
|||
import { sites, resources, targets, exitNodes } from '@server/db/schema';
|
||||
import db from '@server/db';
|
||||
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> => {
|
||||
try {
|
||||
|
@ -55,11 +57,7 @@ export const getConfig = async (req: Request, res: Response, next: NextFunction)
|
|||
res.json(config);
|
||||
} catch (error) {
|
||||
logger.error('Error querying database:', error);
|
||||
if (error instanceof DrizzleError) {
|
||||
res.status(500).json({ error: 'Database query error', message: error.message });
|
||||
} else {
|
||||
next(error);
|
||||
}
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -3,55 +3,64 @@ import { DrizzleError, eq } from 'drizzle-orm';
|
|||
import { sites, resources, targets, exitNodes } from '@server/db/schema';
|
||||
import db from '@server/db';
|
||||
import logger from '@server/logger';
|
||||
import createHttpError from 'http-errors';
|
||||
import HttpCode from '@server/types/HttpCode';
|
||||
import response from "@server/utils/response";
|
||||
|
||||
interface PeerBandwidth {
|
||||
publicKey: string;
|
||||
bytesIn: number;
|
||||
bytesOut: number;
|
||||
publicKey: string;
|
||||
bytesIn: number;
|
||||
bytesOut: number;
|
||||
}
|
||||
|
||||
export const receiveBandwidth = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
||||
try {
|
||||
const bandwidthData: PeerBandwidth[] = req.body;
|
||||
export const receiveBandwidth = async (req: Request, res: Response, next: NextFunction): Promise<any> => {
|
||||
try {
|
||||
const bandwidthData: PeerBandwidth[] = req.body;
|
||||
|
||||
if (!Array.isArray(bandwidthData)) {
|
||||
throw new Error('Invalid bandwidth data');
|
||||
if (!Array.isArray(bandwidthData)) {
|
||||
throw new Error('Invalid bandwidth data');
|
||||
}
|
||||
|
||||
for (const peer of bandwidthData) {
|
||||
const { publicKey, bytesIn, bytesOut } = peer;
|
||||
|
||||
// Find the site by public key
|
||||
const site = await db.query.sites.findFirst({
|
||||
where: eq(sites.pubKey, publicKey),
|
||||
});
|
||||
|
||||
if (!site) {
|
||||
logger.warn(`Site not found for public key: ${publicKey}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update the site's bandwidth usage
|
||||
await db.update(sites)
|
||||
.set({
|
||||
megabytesIn: (site.megabytesIn || 0) + bytesIn,
|
||||
megabytesOut: (site.megabytesOut || 0) + bytesOut,
|
||||
})
|
||||
.where(eq(sites.siteId, site.siteId));
|
||||
|
||||
logger.debug(`Updated bandwidth for site: ${site.siteId}: megabytesIn: ${(site.megabytesIn || 0) + bytesIn}, megabytesOut: ${(site.megabytesOut || 0) + bytesOut}`);
|
||||
|
||||
}
|
||||
|
||||
return response(res, {
|
||||
data: {},
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Organization retrieved successfully",
|
||||
status: HttpCode.OK,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Error updating bandwidth data:', error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
|
||||
for (const peer of bandwidthData) {
|
||||
const { publicKey, bytesIn, bytesOut } = peer;
|
||||
|
||||
// Find the site by public key
|
||||
const site = await db.query.sites.findFirst({
|
||||
where: eq(sites.pubKey, publicKey),
|
||||
});
|
||||
|
||||
if (!site) {
|
||||
logger.warn(`Site not found for public key: ${publicKey}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update the site's bandwidth usage
|
||||
await db.update(sites)
|
||||
.set({
|
||||
megabytesIn: (site.megabytesIn || 0) + bytesIn,
|
||||
megabytesOut: (site.megabytesOut || 0) + bytesOut,
|
||||
})
|
||||
.where(eq(sites.siteId, site.siteId));
|
||||
|
||||
logger.debug(`Updated bandwidth for site: ${site.siteId}: megabytesIn: ${(site.megabytesIn || 0) + bytesIn}, megabytesOut: ${(site.megabytesOut || 0) + bytesOut}`);
|
||||
|
||||
}
|
||||
|
||||
res.status(200).json({ message: 'Bandwidth data updated successfully' });
|
||||
} catch (error) {
|
||||
logger.error('Error updating bandwidth data:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
};
|
||||
|
||||
function calculateSubnet(index: number): string {
|
||||
const baseIp = 10 << 24;
|
||||
const subnetSize = 16;
|
||||
return `${(baseIp | (index * subnetSize)).toString()}/28`;
|
||||
const baseIp = 10 << 24;
|
||||
const subnetSize = 16;
|
||||
return `${(baseIp | (index * subnetSize)).toString()}/28`;
|
||||
}
|
||||
|
|
|
@ -6,59 +6,59 @@ import response from "@server/utils/response";
|
|||
import HttpCode from '@server/types/HttpCode';
|
||||
import createHttpError from 'http-errors';
|
||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||
import logger from '@server/logger';
|
||||
|
||||
const createOrgSchema = z.object({
|
||||
name: z.string().min(1).max(255),
|
||||
domain: z.string().min(1).max(255),
|
||||
name: z.string().min(1).max(255),
|
||||
domain: z.string().min(1).max(255),
|
||||
});
|
||||
|
||||
const MAX_ORGS = 5;
|
||||
|
||||
export async function createOrg(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
const parsedBody = createOrgSchema.safeParse(req.body);
|
||||
if (!parsedBody.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedBody.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
try {
|
||||
const parsedBody = createOrgSchema.safeParse(req.body);
|
||||
if (!parsedBody.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedBody.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const userOrgIds = req.userOrgIds;
|
||||
if (userOrgIds && userOrgIds.length > MAX_ORGS) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.FORBIDDEN,
|
||||
`Maximum number of organizations reached.`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// 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 newOrg = await db.insert(orgs).values({
|
||||
name,
|
||||
domain,
|
||||
}).returning();
|
||||
|
||||
return response(res, {
|
||||
data: newOrg[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Organization created successfully",
|
||||
status: HttpCode.CREATED,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
|
||||
const userOrgIds = req.userOrgIds;
|
||||
if (userOrgIds && userOrgIds.length > MAX_ORGS) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.FORBIDDEN,
|
||||
`Maximum number of organizations reached.`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// 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 newOrg = await db.insert(orgs).values({
|
||||
name,
|
||||
domain,
|
||||
}).returning();
|
||||
|
||||
return res.status(HttpCode.CREATED).send(
|
||||
response(res, {
|
||||
data: newOrg[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Organization created successfully",
|
||||
status: HttpCode.CREATED,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,54 +7,54 @@ import response from "@server/utils/response";
|
|||
import HttpCode from '@server/types/HttpCode';
|
||||
import createHttpError from 'http-errors';
|
||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||
import logger from '@server/logger';
|
||||
|
||||
const deleteOrgSchema = z.object({
|
||||
orgId: z.string().transform(Number).pipe(z.number().int().positive())
|
||||
orgId: z.string().transform(Number).pipe(z.number().int().positive())
|
||||
});
|
||||
|
||||
export async function deleteOrg(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
const parsedParams = deleteOrgSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
try {
|
||||
const parsedParams = deleteOrgSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
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)
|
||||
.where(eq(orgs.orgId, orgId))
|
||||
.returning();
|
||||
|
||||
if (deletedOrg.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Organization with ID ${orgId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return response(res, {
|
||||
data: null,
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Organization deleted successfully",
|
||||
status: HttpCode.OK,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
|
||||
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)
|
||||
.where(eq(orgs.orgId, orgId))
|
||||
.returning();
|
||||
|
||||
if (deletedOrg.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Organization with ID ${orgId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return res.status(HttpCode.OK).send(
|
||||
response(res, {
|
||||
data: null,
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Organization deleted successfully",
|
||||
status: HttpCode.OK,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,55 +7,55 @@ import response from "@server/utils/response";
|
|||
import HttpCode from '@server/types/HttpCode';
|
||||
import createHttpError from 'http-errors';
|
||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||
import logger from '@server/logger';
|
||||
|
||||
const getOrgSchema = z.object({
|
||||
orgId: z.string().transform(Number).pipe(z.number().int().positive())
|
||||
orgId: z.string().transform(Number).pipe(z.number().int().positive())
|
||||
});
|
||||
|
||||
export async function getOrg(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
const parsedParams = getOrgSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
try {
|
||||
const parsedParams = getOrgSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
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()
|
||||
.from(orgs)
|
||||
.where(eq(orgs.orgId, orgId))
|
||||
.limit(1);
|
||||
|
||||
if (org.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Organization with ID ${orgId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return response(res, {
|
||||
data: org[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Organization retrieved successfully",
|
||||
status: HttpCode.OK,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
|
||||
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()
|
||||
.from(orgs)
|
||||
.where(eq(orgs.orgId, orgId))
|
||||
.limit(1);
|
||||
|
||||
if (org.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Organization with ID ${orgId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return res.status(HttpCode.OK).send(
|
||||
response(res, {
|
||||
data: org[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Organization retrieved successfully",
|
||||
status: HttpCode.OK,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,88 +7,88 @@ import HttpCode from '@server/types/HttpCode';
|
|||
import createHttpError from 'http-errors';
|
||||
import { sql, inArray } from 'drizzle-orm';
|
||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||
import logger from '@server/logger';
|
||||
|
||||
const listOrgsSchema = z.object({
|
||||
limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)),
|
||||
offset: z.string().optional().transform(Number).pipe(z.number().int().nonnegative().default(0)),
|
||||
limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)),
|
||||
offset: z.string().optional().transform(Number).pipe(z.number().int().nonnegative().default(0)),
|
||||
});
|
||||
|
||||
export async function listOrgs(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
const parsedQuery = listOrgsSchema.safeParse(req.query);
|
||||
if (!parsedQuery.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedQuery.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
try {
|
||||
const parsedQuery = listOrgsSchema.safeParse(req.query);
|
||||
if (!parsedQuery.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedQuery.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const { limit, offset } = parsedQuery.data;
|
||||
const { 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'));
|
||||
}
|
||||
// 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
|
||||
const userOrgIds = req.userOrgIds;
|
||||
// Use the userOrgs passed from the middleware
|
||||
const userOrgIds = req.userOrgIds;
|
||||
|
||||
if (!userOrgIds || userOrgIds.length === 0) {
|
||||
return res.status(HttpCode.OK).send(
|
||||
response(res, {
|
||||
data: {
|
||||
organizations: [],
|
||||
pagination: {
|
||||
total: 0,
|
||||
limit,
|
||||
offset,
|
||||
if (!userOrgIds || userOrgIds.length === 0) {
|
||||
return res.status(HttpCode.OK).send(
|
||||
response(res, {
|
||||
data: {
|
||||
organizations: [],
|
||||
pagination: {
|
||||
total: 0,
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
},
|
||||
success: true,
|
||||
error: false,
|
||||
message: "No organizations found for the user",
|
||||
status: HttpCode.OK,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const organizations = await db.select()
|
||||
.from(orgs)
|
||||
.where(inArray(orgs.orgId, userOrgIds))
|
||||
.limit(limit)
|
||||
.offset(offset);
|
||||
|
||||
const totalCountResult = await db.select({ count: sql<number>`cast(count(*) as integer)` })
|
||||
.from(orgs)
|
||||
.where(inArray(orgs.orgId, userOrgIds));
|
||||
const totalCount = totalCountResult[0].count;
|
||||
|
||||
// // Add the user's role for each organization
|
||||
// const organizationsWithRoles = organizations.map(org => ({
|
||||
// ...org,
|
||||
// userRole: req.userOrgRoleIds[org.orgId],
|
||||
// }));
|
||||
|
||||
return response(res, {
|
||||
data: {
|
||||
organizations,
|
||||
pagination: {
|
||||
total: totalCount,
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
},
|
||||
},
|
||||
success: true,
|
||||
error: false,
|
||||
message: "No organizations found for the user",
|
||||
status: HttpCode.OK,
|
||||
})
|
||||
);
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Organizations retrieved successfully",
|
||||
status: HttpCode.OK,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
|
||||
const organizations = await db.select()
|
||||
.from(orgs)
|
||||
.where(inArray(orgs.orgId, userOrgIds))
|
||||
.limit(limit)
|
||||
.offset(offset);
|
||||
|
||||
const totalCountResult = await db.select({ count: sql<number>`cast(count(*) as integer)` })
|
||||
.from(orgs)
|
||||
.where(inArray(orgs.orgId, userOrgIds));
|
||||
const totalCount = totalCountResult[0].count;
|
||||
|
||||
// // Add the user's role for each organization
|
||||
// const organizationsWithRoles = organizations.map(org => ({
|
||||
// ...org,
|
||||
// userRole: req.userOrgRoleIds[org.orgId],
|
||||
// }));
|
||||
|
||||
return res.status(HttpCode.OK).send(
|
||||
response(res, {
|
||||
data: {
|
||||
organizations,
|
||||
pagination: {
|
||||
total: totalCount,
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
},
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Organizations retrieved successfully",
|
||||
status: HttpCode.OK,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
|
@ -7,74 +7,74 @@ import response from "@server/utils/response";
|
|||
import HttpCode from '@server/types/HttpCode';
|
||||
import createHttpError from 'http-errors';
|
||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||
import logger from '@server/logger';
|
||||
|
||||
const updateOrgParamsSchema = z.object({
|
||||
orgId: z.string().transform(Number).pipe(z.number().int().positive())
|
||||
orgId: z.string().transform(Number).pipe(z.number().int().positive())
|
||||
});
|
||||
|
||||
const updateOrgBodySchema = z.object({
|
||||
name: z.string().min(1).max(255).optional(),
|
||||
domain: z.string().min(1).max(255).optional(),
|
||||
name: z.string().min(1).max(255).optional(),
|
||||
domain: z.string().min(1).max(255).optional(),
|
||||
}).refine(data => Object.keys(data).length > 0, {
|
||||
message: "At least one field must be provided for update"
|
||||
message: "At least one field must be provided for update"
|
||||
});
|
||||
|
||||
export async function updateOrg(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
const parsedParams = updateOrgParamsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
try {
|
||||
const parsedParams = updateOrgParamsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const parsedBody = updateOrgBodySchema.safeParse(req.body);
|
||||
if (!parsedBody.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedBody.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const { orgId } = parsedParams.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)
|
||||
.set(updateData)
|
||||
.where(eq(orgs.orgId, orgId))
|
||||
.returning();
|
||||
|
||||
if (updatedOrg.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Organization with ID ${orgId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return response(res, {
|
||||
data: updatedOrg[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Organization updated successfully",
|
||||
status: HttpCode.OK,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
|
||||
const parsedBody = updateOrgBodySchema.safeParse(req.body);
|
||||
if (!parsedBody.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedBody.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const { orgId } = parsedParams.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)
|
||||
.set(updateData)
|
||||
.where(eq(orgs.orgId, orgId))
|
||||
.returning();
|
||||
|
||||
if (updatedOrg.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Organization with ID ${orgId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return res.status(HttpCode.OK).send(
|
||||
response(res, {
|
||||
data: updatedOrg[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Organization updated successfully",
|
||||
status: HttpCode.OK,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,74 +6,74 @@ import response from "@server/utils/response";
|
|||
import HttpCode from '@server/types/HttpCode';
|
||||
import createHttpError from 'http-errors';
|
||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||
import logger from '@server/logger';
|
||||
|
||||
const createResourceParamsSchema = z.object({
|
||||
siteId: z.number().int().positive(),
|
||||
orgId: z.number().int().positive(),
|
||||
siteId: z.number().int().positive(),
|
||||
orgId: z.number().int().positive(),
|
||||
});
|
||||
|
||||
// Define Zod schema for request body validation
|
||||
const createResourceSchema = z.object({
|
||||
name: z.string().min(1).max(255),
|
||||
subdomain: z.string().min(1).max(255).optional(),
|
||||
name: z.string().min(1).max(255),
|
||||
subdomain: z.string().min(1).max(255).optional(),
|
||||
});
|
||||
|
||||
export async function createResource(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
// Validate request body
|
||||
const parsedBody = createResourceSchema.safeParse(req.body);
|
||||
if (!parsedBody.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedBody.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
try {
|
||||
// Validate request body
|
||||
const parsedBody = createResourceSchema.safeParse(req.body);
|
||||
if (!parsedBody.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedBody.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const { name, subdomain } = parsedBody.data;
|
||||
|
||||
// Validate request params
|
||||
const parsedParams = createResourceParamsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
const resourceId = "subdomain" // TODO: create the subdomain here
|
||||
|
||||
// Create new resource in the database
|
||||
const newResource = await db.insert(resources).values({
|
||||
resourceId,
|
||||
siteId,
|
||||
orgId,
|
||||
name,
|
||||
subdomain,
|
||||
}).returning();
|
||||
|
||||
response(res, {
|
||||
data: newResource[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Resource created successfully",
|
||||
status: HttpCode.CREATED,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
|
||||
const { name, subdomain } = parsedBody.data;
|
||||
|
||||
// Validate request params
|
||||
const parsedParams = createResourceParamsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
const resourceId = "subdomain" // TODO: create the subdomain here
|
||||
|
||||
// Create new resource in the database
|
||||
const newResource = await db.insert(resources).values({
|
||||
resourceId,
|
||||
siteId,
|
||||
orgId,
|
||||
name,
|
||||
subdomain,
|
||||
}).returning();
|
||||
|
||||
return res.status(HttpCode.CREATED).send(
|
||||
response(res, {
|
||||
data: newResource[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Resource created successfully",
|
||||
status: HttpCode.CREATED,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,57 +7,57 @@ import response from "@server/utils/response";
|
|||
import HttpCode from '@server/types/HttpCode';
|
||||
import createHttpError from 'http-errors';
|
||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||
import logger from '@server/logger';
|
||||
|
||||
// Define Zod schema for request parameters validation
|
||||
const deleteResourceSchema = z.object({
|
||||
resourceId: z.string().uuid()
|
||||
resourceId: z.string().uuid()
|
||||
});
|
||||
|
||||
export async function deleteResource(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
// Validate request parameters
|
||||
const parsedParams = deleteResourceSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
try {
|
||||
// Validate request parameters
|
||||
const parsedParams = deleteResourceSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
const deletedResource = await db.delete(resources)
|
||||
.where(eq(resources.resourceId, resourceId))
|
||||
.returning();
|
||||
|
||||
if (deletedResource.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Resource with ID ${resourceId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return response(res, {
|
||||
data: null,
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Resource deleted successfully",
|
||||
status: HttpCode.OK,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
|
||||
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
|
||||
const deletedResource = await db.delete(resources)
|
||||
.where(eq(resources.resourceId, resourceId))
|
||||
.returning();
|
||||
|
||||
if (deletedResource.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Resource with ID ${resourceId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return res.status(HttpCode.OK).send(
|
||||
response(res, {
|
||||
data: null,
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Resource deleted successfully",
|
||||
status: HttpCode.OK,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,58 +7,58 @@ import response from "@server/utils/response";
|
|||
import HttpCode from '@server/types/HttpCode';
|
||||
import createHttpError from 'http-errors';
|
||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||
import logger from '@server/logger';
|
||||
|
||||
// Define Zod schema for request parameters validation
|
||||
const getResourceSchema = z.object({
|
||||
resourceId: z.string().uuid()
|
||||
resourceId: z.string().uuid()
|
||||
});
|
||||
|
||||
export async function getResource(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
// Validate request parameters
|
||||
const parsedParams = getResourceSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
try {
|
||||
// Validate request parameters
|
||||
const parsedParams = getResourceSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
const resource = await db.select()
|
||||
.from(resources)
|
||||
.where(eq(resources.resourceId, resourceId))
|
||||
.limit(1);
|
||||
|
||||
if (resource.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Resource with ID ${resourceId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return response(res, {
|
||||
data: resource[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Resource retrieved successfully",
|
||||
status: HttpCode.OK,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
|
||||
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
|
||||
const resource = await db.select()
|
||||
.from(resources)
|
||||
.where(eq(resources.resourceId, resourceId))
|
||||
.limit(1);
|
||||
|
||||
if (resource.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Resource with ID ${resourceId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return res.status(HttpCode.OK).send(
|
||||
response(res, {
|
||||
data: resource[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Resource retrieved successfully",
|
||||
status: HttpCode.OK,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import HttpCode from '@server/types/HttpCode';
|
|||
import createHttpError from 'http-errors';
|
||||
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({
|
||||
siteId: z.coerce.number().int().positive().optional(),
|
||||
|
@ -16,99 +17,98 @@ const listResourcesParamsSchema = z.object({
|
|||
});
|
||||
|
||||
const listResourcesSchema = z.object({
|
||||
limit: z.coerce.number().int().positive().default(10),
|
||||
offset: z.coerce.number().int().nonnegative().default(0),
|
||||
limit: z.coerce.number().int().positive().default(10),
|
||||
offset: z.coerce.number().int().nonnegative().default(0),
|
||||
});
|
||||
|
||||
interface RequestWithOrgAndRole extends Request {
|
||||
userOrgRoleId?: number;
|
||||
orgId?: number;
|
||||
userOrgRoleId?: number;
|
||||
orgId?: number;
|
||||
}
|
||||
|
||||
export async function listResources(req: RequestWithOrgAndRole, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
const parsedQuery = listResourcesSchema.safeParse(req.query);
|
||||
if (!parsedQuery.success) {
|
||||
return next(createHttpError(HttpCode.BAD_REQUEST, parsedQuery.error.errors.map(e => e.message).join(', ')));
|
||||
try {
|
||||
const parsedQuery = listResourcesSchema.safeParse(req.query);
|
||||
if (!parsedQuery.success) {
|
||||
return next(createHttpError(HttpCode.BAD_REQUEST, parsedQuery.error.errors.map(e => e.message).join(', ')));
|
||||
}
|
||||
const { limit, offset } = parsedQuery.data;
|
||||
|
||||
const parsedParams = listResourcesParamsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(createHttpError(HttpCode.BAD_REQUEST, parsedParams.error.errors.map(e => e.message).join(', ')));
|
||||
}
|
||||
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
|
||||
.select({
|
||||
resourceId: resources.resourceId,
|
||||
name: resources.name,
|
||||
subdomain: resources.subdomain,
|
||||
siteName: sites.name,
|
||||
})
|
||||
.from(resources)
|
||||
.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)
|
||||
.where(inArray(resources.resourceId, accessibleResourceIds));
|
||||
|
||||
if (siteId) {
|
||||
baseQuery = baseQuery.where(eq(resources.siteId, siteId));
|
||||
countQuery = countQuery.where(eq(resources.siteId, siteId));
|
||||
} else {
|
||||
// If orgId is provided, it's already checked to match req.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 totalCountResult = await countQuery;
|
||||
const totalCount = totalCountResult[0].count;
|
||||
|
||||
return response(res, {
|
||||
data: {
|
||||
resources: resourcesList,
|
||||
pagination: {
|
||||
total: totalCount,
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
},
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Resources retrieved successfully",
|
||||
status: HttpCode.OK,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
const { limit, offset } = parsedQuery.data;
|
||||
|
||||
const parsedParams = listResourcesParamsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(createHttpError(HttpCode.BAD_REQUEST, parsedParams.error.errors.map(e => e.message).join(', ')));
|
||||
}
|
||||
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
|
||||
.select({
|
||||
resourceId: resources.resourceId,
|
||||
name: resources.name,
|
||||
subdomain: resources.subdomain,
|
||||
siteName: sites.name,
|
||||
})
|
||||
.from(resources)
|
||||
.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)
|
||||
.where(inArray(resources.resourceId, accessibleResourceIds));
|
||||
|
||||
if (siteId) {
|
||||
baseQuery = baseQuery.where(eq(resources.siteId, siteId));
|
||||
countQuery = countQuery.where(eq(resources.siteId, siteId));
|
||||
} else {
|
||||
// If orgId is provided, it's already checked to match req.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 totalCountResult = await countQuery;
|
||||
const totalCount = totalCountResult[0].count;
|
||||
|
||||
return res.status(HttpCode.OK).send(
|
||||
response(res, {
|
||||
data: {
|
||||
resources: resourcesList,
|
||||
pagination: {
|
||||
total: totalCount,
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
},
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Resources retrieved successfully",
|
||||
status: HttpCode.OK,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
|
@ -7,78 +7,78 @@ import response from "@server/utils/response";
|
|||
import HttpCode from '@server/types/HttpCode';
|
||||
import createHttpError from 'http-errors';
|
||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||
import logger from '@server/logger';
|
||||
|
||||
// Define Zod schema for request parameters validation
|
||||
const updateResourceParamsSchema = z.object({
|
||||
resourceId: z.string().uuid()
|
||||
resourceId: z.string().uuid()
|
||||
});
|
||||
|
||||
// Define Zod schema for request body validation
|
||||
const updateResourceBodySchema = z.object({
|
||||
name: z.string().min(1).max(255).optional(),
|
||||
subdomain: z.string().min(1).max(255).optional(),
|
||||
name: z.string().min(1).max(255).optional(),
|
||||
subdomain: z.string().min(1).max(255).optional(),
|
||||
}).refine(data => Object.keys(data).length > 0, {
|
||||
message: "At least one field must be provided for update"
|
||||
message: "At least one field must be provided for update"
|
||||
});
|
||||
|
||||
export async function updateResource(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
// Validate request parameters
|
||||
const parsedParams = updateResourceParamsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
try {
|
||||
// Validate request parameters
|
||||
const parsedParams = updateResourceParamsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Validate request body
|
||||
const parsedBody = updateResourceBodySchema.safeParse(req.body);
|
||||
if (!parsedBody.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedBody.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const { resourceId } = parsedParams.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
|
||||
const updatedResource = await db.update(resources)
|
||||
.set(updateData)
|
||||
.where(eq(resources.resourceId, resourceId))
|
||||
.returning();
|
||||
|
||||
if (updatedResource.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Resource with ID ${resourceId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return response(res, {
|
||||
data: updatedResource[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Resource updated successfully",
|
||||
status: HttpCode.OK,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
|
||||
// Validate request body
|
||||
const parsedBody = updateResourceBodySchema.safeParse(req.body);
|
||||
if (!parsedBody.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedBody.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const { resourceId } = parsedParams.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
|
||||
const updatedResource = await db.update(resources)
|
||||
.set(updateData)
|
||||
.where(eq(resources.resourceId, resourceId))
|
||||
.returning();
|
||||
|
||||
if (updatedResource.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Resource with ID ${resourceId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return res.status(HttpCode.OK).send(
|
||||
response(res, {
|
||||
data: updatedResource[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Resource updated successfully",
|
||||
status: HttpCode.OK,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,99 +7,98 @@ import HttpCode from '@server/types/HttpCode';
|
|||
import createHttpError from 'http-errors';
|
||||
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 createSiteParamsSchema = z.object({
|
||||
const createSiteParamsSchema = z.object({
|
||||
orgId: z.number().int().positive(),
|
||||
});
|
||||
|
||||
// Define Zod schema for request body validation
|
||||
const createSiteSchema = z.object({
|
||||
name: z.string().min(1).max(255),
|
||||
subdomain: z.string().min(1).max(255).optional(),
|
||||
pubKey: z.string().optional(),
|
||||
subnet: z.string().optional(),
|
||||
name: z.string().min(1).max(255),
|
||||
subdomain: z.string().min(1).max(255).optional(),
|
||||
pubKey: z.string().optional(),
|
||||
subnet: z.string().optional(),
|
||||
});
|
||||
|
||||
export async function createSite(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
// Validate request body
|
||||
const parsedBody = createSiteSchema.safeParse(req.body);
|
||||
if (!parsedBody.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedBody.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
try {
|
||||
// Validate request body
|
||||
const parsedBody = createSiteSchema.safeParse(req.body);
|
||||
if (!parsedBody.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedBody.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const { name, subdomain, pubKey, subnet } = parsedBody.data;
|
||||
|
||||
// Validate request params
|
||||
const parsedParams = createSiteParamsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
const newSite = await db.insert(sites).values({
|
||||
orgId,
|
||||
name,
|
||||
subdomain,
|
||||
pubKey,
|
||||
subnet,
|
||||
}).returning();
|
||||
|
||||
return response(res, {
|
||||
data: newSite[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Site created successfully",
|
||||
status: HttpCode.CREATED,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
|
||||
const { name, subdomain, pubKey, subnet } = parsedBody.data;
|
||||
|
||||
// Validate request params
|
||||
const parsedParams = createSiteParamsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
const newSite = await db.insert(sites).values({
|
||||
orgId,
|
||||
name,
|
||||
subdomain,
|
||||
pubKey,
|
||||
subnet,
|
||||
}).returning();
|
||||
|
||||
return res.status(HttpCode.CREATED).send(
|
||||
response(res, {
|
||||
data: newSite[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Site created successfully",
|
||||
status: HttpCode.CREATED,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function addPeer(peer: string) {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/peer`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(peer),
|
||||
});
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/peer`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(peer),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data: any = await response.json();
|
||||
logger.info('Peer added successfully:', data.status);
|
||||
return data;
|
||||
} catch (error: any) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
const data: any = await response.json();
|
||||
console.log('Peer added successfully:', data.status);
|
||||
return data;
|
||||
} catch (error: any) {
|
||||
console.error('Error adding peer:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,80 +7,80 @@ import response from "@server/utils/response";
|
|||
import HttpCode from '@server/types/HttpCode';
|
||||
import createHttpError from 'http-errors';
|
||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||
import logger from '@server/logger';
|
||||
|
||||
|
||||
const API_BASE_URL = "http://localhost:3000";
|
||||
|
||||
// Define Zod schema for request parameters validation
|
||||
const deleteSiteSchema = z.object({
|
||||
siteId: z.string().transform(Number).pipe(z.number().int().positive())
|
||||
siteId: z.string().transform(Number).pipe(z.number().int().positive())
|
||||
});
|
||||
|
||||
export async function deleteSite(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
// Validate request parameters
|
||||
const parsedParams = deleteSiteSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
try {
|
||||
// Validate request parameters
|
||||
const parsedParams = deleteSiteSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
const deletedSite = await db.delete(sites)
|
||||
.where(eq(sites.siteId, siteId))
|
||||
.returning();
|
||||
|
||||
if (deletedSite.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Site with ID ${siteId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return response(res, {
|
||||
data: null,
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Site deleted successfully",
|
||||
status: HttpCode.OK,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
|
||||
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
|
||||
const deletedSite = await db.delete(sites)
|
||||
.where(eq(sites.siteId, siteId))
|
||||
.returning();
|
||||
|
||||
if (deletedSite.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Site with ID ${siteId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return res.status(HttpCode.OK).send(
|
||||
response(res, {
|
||||
data: null,
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Site deleted successfully",
|
||||
status: HttpCode.OK,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function removePeer(publicKey: string) {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/peer?public_key=${encodeURIComponent(publicKey)}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/peer?public_key=${encodeURIComponent(publicKey)}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log('Peer removed successfully:', data.status);
|
||||
return data;
|
||||
} catch (error: any) {
|
||||
console.error('Error removing peer:', error.message);
|
||||
throw error;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log('Peer removed successfully:', data.status);
|
||||
return data;
|
||||
} catch (error: any) {
|
||||
console.error('Error removing peer:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,58 +7,58 @@ import response from "@server/utils/response";
|
|||
import HttpCode from '@server/types/HttpCode';
|
||||
import createHttpError from 'http-errors';
|
||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||
import logger from '@server/logger';
|
||||
|
||||
// Define Zod schema for request parameters validation
|
||||
const getSiteSchema = z.object({
|
||||
siteId: z.string().transform(Number).pipe(z.number().int().positive())
|
||||
siteId: z.string().transform(Number).pipe(z.number().int().positive())
|
||||
});
|
||||
|
||||
export async function getSite(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
// Validate request parameters
|
||||
const parsedParams = getSiteSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
try {
|
||||
// Validate request parameters
|
||||
const parsedParams = getSiteSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
const site = await db.select()
|
||||
.from(sites)
|
||||
.where(eq(sites.siteId, siteId))
|
||||
.limit(1);
|
||||
|
||||
if (site.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Site with ID ${siteId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return response(res, {
|
||||
data: site[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Site retrieved successfully",
|
||||
status: HttpCode.OK,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
|
||||
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
|
||||
const site = await db.select()
|
||||
.from(sites)
|
||||
.where(eq(sites.siteId, siteId))
|
||||
.limit(1);
|
||||
|
||||
if (site.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Site with ID ${siteId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return res.status(HttpCode.OK).send(
|
||||
response(res, {
|
||||
data: site[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Site retrieved successfully",
|
||||
status: HttpCode.OK,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,101 +7,101 @@ import HttpCode from '@server/types/HttpCode';
|
|||
import createHttpError from 'http-errors';
|
||||
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({
|
||||
orgId: z.string().optional().transform(Number).pipe(z.number().int().positive()),
|
||||
});
|
||||
|
||||
const listSitesSchema = z.object({
|
||||
limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)),
|
||||
offset: z.string().optional().transform(Number).pipe(z.number().int().nonnegative().default(0)),
|
||||
limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)),
|
||||
offset: z.string().optional().transform(Number).pipe(z.number().int().nonnegative().default(0)),
|
||||
});
|
||||
|
||||
export async function listSites(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
const parsedQuery = listSitesSchema.safeParse(req.query);
|
||||
if (!parsedQuery.success) {
|
||||
return next(createHttpError(HttpCode.BAD_REQUEST, parsedQuery.error.errors.map(e => e.message).join(', ')));
|
||||
try {
|
||||
const parsedQuery = listSitesSchema.safeParse(req.query);
|
||||
if (!parsedQuery.success) {
|
||||
return next(createHttpError(HttpCode.BAD_REQUEST, parsedQuery.error.errors.map(e => e.message).join(', ')));
|
||||
}
|
||||
const { limit, offset } = parsedQuery.data;
|
||||
|
||||
|
||||
const parsedParams = listSitesParamsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(createHttpError(HttpCode.BAD_REQUEST, parsedParams.error.errors.map(e => e.message).join(', ')));
|
||||
}
|
||||
const { orgId } = parsedParams.data;
|
||||
|
||||
// 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'));
|
||||
}
|
||||
|
||||
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
|
||||
.select({
|
||||
siteId: sites.siteId,
|
||||
name: sites.name,
|
||||
subdomain: sites.subdomain,
|
||||
pubKey: sites.pubKey,
|
||||
subnet: sites.subnet,
|
||||
megabytesIn: sites.megabytesIn,
|
||||
megabytesOut: sites.megabytesOut,
|
||||
orgName: orgs.name,
|
||||
exitNodeName: exitNodes.name,
|
||||
})
|
||||
.from(sites)
|
||||
.leftJoin(orgs, eq(sites.orgId, orgs.orgId))
|
||||
.where(inArray(sites.siteId, accessibleSiteIds));
|
||||
|
||||
let countQuery: any = db
|
||||
.select({ count: sql<number>`cast(count(*) as integer)` })
|
||||
.from(sites)
|
||||
.where(inArray(sites.siteId, accessibleSiteIds));
|
||||
|
||||
if (orgId) {
|
||||
baseQuery = baseQuery.where(eq(sites.orgId, orgId));
|
||||
countQuery = countQuery.where(eq(sites.orgId, orgId));
|
||||
}
|
||||
|
||||
const sitesList = await baseQuery.limit(limit).offset(offset);
|
||||
const totalCountResult = await countQuery;
|
||||
const totalCount = totalCountResult[0].count;
|
||||
|
||||
return response(res, {
|
||||
data: {
|
||||
sites: sitesList,
|
||||
pagination: {
|
||||
total: totalCount,
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
},
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Sites retrieved successfully",
|
||||
status: HttpCode.OK,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
const { limit, offset } = parsedQuery.data;
|
||||
|
||||
|
||||
const parsedParams = listSitesParamsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(createHttpError(HttpCode.BAD_REQUEST, parsedParams.error.errors.map(e => e.message).join(', ')));
|
||||
}
|
||||
const { orgId } = parsedParams.data;
|
||||
|
||||
// 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'));
|
||||
}
|
||||
|
||||
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
|
||||
.select({
|
||||
siteId: sites.siteId,
|
||||
name: sites.name,
|
||||
subdomain: sites.subdomain,
|
||||
pubKey: sites.pubKey,
|
||||
subnet: sites.subnet,
|
||||
megabytesIn: sites.megabytesIn,
|
||||
megabytesOut: sites.megabytesOut,
|
||||
orgName: orgs.name,
|
||||
exitNodeName: exitNodes.name,
|
||||
})
|
||||
.from(sites)
|
||||
.leftJoin(orgs, eq(sites.orgId, orgs.orgId))
|
||||
.where(inArray(sites.siteId, accessibleSiteIds));
|
||||
|
||||
let countQuery: any = db
|
||||
.select({ count: sql<number>`cast(count(*) as integer)` })
|
||||
.from(sites)
|
||||
.where(inArray(sites.siteId, accessibleSiteIds));
|
||||
|
||||
if (orgId) {
|
||||
baseQuery = baseQuery.where(eq(sites.orgId, orgId));
|
||||
countQuery = countQuery.where(eq(sites.orgId, orgId));
|
||||
}
|
||||
|
||||
const sitesList = await baseQuery.limit(limit).offset(offset);
|
||||
const totalCountResult = await countQuery;
|
||||
const totalCount = totalCountResult[0].count;
|
||||
|
||||
return res.status(HttpCode.OK).send(
|
||||
response(res, {
|
||||
data: {
|
||||
sites: sitesList,
|
||||
pagination: {
|
||||
total: totalCount,
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
},
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Sites retrieved successfully",
|
||||
status: HttpCode.OK,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
|
@ -7,83 +7,83 @@ import response from "@server/utils/response";
|
|||
import HttpCode from '@server/types/HttpCode';
|
||||
import createHttpError from 'http-errors';
|
||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||
import logger from '@server/logger';
|
||||
|
||||
// Define Zod schema for request parameters validation
|
||||
const updateSiteParamsSchema = z.object({
|
||||
siteId: z.string().transform(Number).pipe(z.number().int().positive())
|
||||
siteId: z.string().transform(Number).pipe(z.number().int().positive())
|
||||
});
|
||||
|
||||
// Define Zod schema for request body validation
|
||||
const updateSiteBodySchema = z.object({
|
||||
name: z.string().min(1).max(255).optional(),
|
||||
subdomain: z.string().min(1).max(255).optional(),
|
||||
pubKey: z.string().optional(),
|
||||
subnet: z.string().optional(),
|
||||
exitNode: z.number().int().positive().optional(),
|
||||
megabytesIn: z.number().int().nonnegative().optional(),
|
||||
megabytesOut: z.number().int().nonnegative().optional(),
|
||||
name: z.string().min(1).max(255).optional(),
|
||||
subdomain: z.string().min(1).max(255).optional(),
|
||||
pubKey: z.string().optional(),
|
||||
subnet: z.string().optional(),
|
||||
exitNode: z.number().int().positive().optional(),
|
||||
megabytesIn: z.number().int().nonnegative().optional(),
|
||||
megabytesOut: z.number().int().nonnegative().optional(),
|
||||
}).refine(data => Object.keys(data).length > 0, {
|
||||
message: "At least one field must be provided for update"
|
||||
message: "At least one field must be provided for update"
|
||||
});
|
||||
|
||||
export async function updateSite(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
// Validate request parameters
|
||||
const parsedParams = updateSiteParamsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
try {
|
||||
// Validate request parameters
|
||||
const parsedParams = updateSiteParamsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Validate request body
|
||||
const parsedBody = updateSiteBodySchema.safeParse(req.body);
|
||||
if (!parsedBody.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedBody.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const { siteId } = parsedParams.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
|
||||
const updatedSite = await db.update(sites)
|
||||
.set(updateData)
|
||||
.where(eq(sites.siteId, siteId))
|
||||
.returning();
|
||||
|
||||
if (updatedSite.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Site with ID ${siteId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return response(res, {
|
||||
data: updatedSite[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Site updated successfully",
|
||||
status: HttpCode.OK,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
|
||||
// Validate request body
|
||||
const parsedBody = updateSiteBodySchema.safeParse(req.body);
|
||||
if (!parsedBody.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedBody.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const { siteId } = parsedParams.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
|
||||
const updatedSite = await db.update(sites)
|
||||
.set(updateData)
|
||||
.where(eq(sites.siteId, siteId))
|
||||
.returning();
|
||||
|
||||
if (updatedSite.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Site with ID ${siteId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return res.status(HttpCode.OK).send(
|
||||
response(res, {
|
||||
data: updatedSite[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Site updated successfully",
|
||||
status: HttpCode.OK,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,66 +6,66 @@ import response from "@server/utils/response";
|
|||
import HttpCode from '@server/types/HttpCode';
|
||||
import createHttpError from 'http-errors';
|
||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||
import logger from '@server/logger';
|
||||
|
||||
const createTargetParamsSchema = z.object({
|
||||
resourceId: z.string().uuid(),
|
||||
resourceId: z.string().uuid(),
|
||||
});
|
||||
|
||||
const createTargetSchema = z.object({
|
||||
ip: z.string().ip(),
|
||||
method: z.string().min(1).max(10),
|
||||
port: z.number().int().min(1).max(65535),
|
||||
protocol: z.string().optional(),
|
||||
enabled: z.boolean().default(true),
|
||||
ip: z.string().ip(),
|
||||
method: z.string().min(1).max(10),
|
||||
port: z.number().int().min(1).max(65535),
|
||||
protocol: z.string().optional(),
|
||||
enabled: z.boolean().default(true),
|
||||
});
|
||||
|
||||
export async function createTarget(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
const parsedBody = createTargetSchema.safeParse(req.body);
|
||||
if (!parsedBody.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedBody.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
try {
|
||||
const parsedBody = createTargetSchema.safeParse(req.body);
|
||||
if (!parsedBody.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedBody.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const targetData = parsedBody.data;
|
||||
|
||||
const parsedParams = createTargetParamsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
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({
|
||||
resourceId,
|
||||
...targetData
|
||||
}).returning();
|
||||
|
||||
return response(res, {
|
||||
data: newTarget[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Target created successfully",
|
||||
status: HttpCode.CREATED,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
|
||||
const targetData = parsedBody.data;
|
||||
|
||||
const parsedParams = createTargetParamsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
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({
|
||||
resourceId,
|
||||
...targetData
|
||||
}).returning();
|
||||
|
||||
return res.status(HttpCode.CREATED).send(
|
||||
response(res, {
|
||||
data: newTarget[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Target created successfully",
|
||||
status: HttpCode.CREATED,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,54 +7,54 @@ import response from "@server/utils/response";
|
|||
import HttpCode from '@server/types/HttpCode';
|
||||
import createHttpError from 'http-errors';
|
||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||
import logger from '@server/logger';
|
||||
|
||||
const deleteTargetSchema = z.object({
|
||||
targetId: z.string().transform(Number).pipe(z.number().int().positive())
|
||||
targetId: z.string().transform(Number).pipe(z.number().int().positive())
|
||||
});
|
||||
|
||||
export async function deleteTarget(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
const parsedParams = deleteTargetSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
try {
|
||||
const parsedParams = deleteTargetSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
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)
|
||||
.where(eq(targets.targetId, targetId))
|
||||
.returning();
|
||||
|
||||
if (deletedTarget.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Target with ID ${targetId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return response(res, {
|
||||
data: null,
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Target deleted successfully",
|
||||
status: HttpCode.OK,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
|
||||
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)
|
||||
.where(eq(targets.targetId, targetId))
|
||||
.returning();
|
||||
|
||||
if (deletedTarget.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Target with ID ${targetId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return res.status(HttpCode.OK).send(
|
||||
response(res, {
|
||||
data: null,
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Target deleted successfully",
|
||||
status: HttpCode.OK,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,55 +7,55 @@ import response from "@server/utils/response";
|
|||
import HttpCode from '@server/types/HttpCode';
|
||||
import createHttpError from 'http-errors';
|
||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||
import logger from '@server/logger';
|
||||
|
||||
const getTargetSchema = z.object({
|
||||
targetId: z.string().transform(Number).pipe(z.number().int().positive())
|
||||
targetId: z.string().transform(Number).pipe(z.number().int().positive())
|
||||
});
|
||||
|
||||
export async function getTarget(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
const parsedParams = getTargetSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
try {
|
||||
const parsedParams = getTargetSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
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()
|
||||
.from(targets)
|
||||
.where(eq(targets.targetId, targetId))
|
||||
.limit(1);
|
||||
|
||||
if (target.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Target with ID ${targetId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return response(res, {
|
||||
data: target[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Target retrieved successfully",
|
||||
status: HttpCode.OK,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
|
||||
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()
|
||||
.from(targets)
|
||||
.where(eq(targets.targetId, targetId))
|
||||
.limit(1);
|
||||
|
||||
if (target.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Target with ID ${targetId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return res.status(HttpCode.OK).send(
|
||||
response(res, {
|
||||
data: target[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Target retrieved successfully",
|
||||
status: HttpCode.OK,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,89 +7,89 @@ import HttpCode from '@server/types/HttpCode';
|
|||
import createHttpError from 'http-errors';
|
||||
import { sql, eq } from 'drizzle-orm';
|
||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||
import logger from '@server/logger';
|
||||
|
||||
const listTargetsParamsSchema = z.object({
|
||||
resourceId: z.string().optional()
|
||||
});
|
||||
|
||||
const listTargetsSchema = z.object({
|
||||
limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)),
|
||||
offset: z.string().optional().transform(Number).pipe(z.number().int().nonnegative().default(0)),
|
||||
limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)),
|
||||
offset: z.string().optional().transform(Number).pipe(z.number().int().nonnegative().default(0)),
|
||||
});
|
||||
|
||||
export async function listTargets(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
const parsedQuery = listTargetsSchema.safeParse(req.query);
|
||||
if (!parsedQuery.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedQuery.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
try {
|
||||
const parsedQuery = listTargetsSchema.safeParse(req.query);
|
||||
if (!parsedQuery.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedQuery.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const { limit, offset } = parsedQuery.data;
|
||||
|
||||
const parsedParams = listTargetsParamsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
.select({
|
||||
targetId: targets.targetId,
|
||||
ip: targets.ip,
|
||||
method: targets.method,
|
||||
port: targets.port,
|
||||
protocol: targets.protocol,
|
||||
enabled: targets.enabled,
|
||||
resourceName: resources.name,
|
||||
})
|
||||
.from(targets)
|
||||
.leftJoin(resources, eq(targets.resourceId, resources.resourceId));
|
||||
|
||||
let countQuery: any = db.select({ count: sql<number>`cast(count(*) as integer)` }).from(targets);
|
||||
|
||||
if (resourceId) {
|
||||
baseQuery = baseQuery.where(eq(targets.resourceId, resourceId));
|
||||
countQuery = countQuery.where(eq(targets.resourceId, resourceId));
|
||||
}
|
||||
|
||||
const targetsList = await baseQuery.limit(limit).offset(offset);
|
||||
const totalCountResult = await countQuery;
|
||||
const totalCount = totalCountResult[0].count;
|
||||
|
||||
return response(res, {
|
||||
data: {
|
||||
targets: targetsList,
|
||||
pagination: {
|
||||
total: totalCount,
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
},
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Targets retrieved successfully",
|
||||
status: HttpCode.OK,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
|
||||
const { limit, offset } = parsedQuery.data;
|
||||
|
||||
const parsedParams = listTargetsParamsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
.select({
|
||||
targetId: targets.targetId,
|
||||
ip: targets.ip,
|
||||
method: targets.method,
|
||||
port: targets.port,
|
||||
protocol: targets.protocol,
|
||||
enabled: targets.enabled,
|
||||
resourceName: resources.name,
|
||||
})
|
||||
.from(targets)
|
||||
.leftJoin(resources, eq(targets.resourceId, resources.resourceId));
|
||||
|
||||
let countQuery: any = db.select({ count: sql<number>`cast(count(*) as integer)` }).from(targets);
|
||||
|
||||
if (resourceId) {
|
||||
baseQuery = baseQuery.where(eq(targets.resourceId, resourceId));
|
||||
countQuery = countQuery.where(eq(targets.resourceId, resourceId));
|
||||
}
|
||||
|
||||
const targetsList = await baseQuery.limit(limit).offset(offset);
|
||||
const totalCountResult = await countQuery;
|
||||
const totalCount = totalCountResult[0].count;
|
||||
|
||||
return response(res, {
|
||||
data: {
|
||||
targets: targetsList,
|
||||
pagination: {
|
||||
total: totalCount,
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
},
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Targets retrieved successfully",
|
||||
status: HttpCode.OK,
|
||||
})
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "sadfdf"));
|
||||
}
|
||||
}
|
|
@ -7,76 +7,76 @@ import response from "@server/utils/response";
|
|||
import HttpCode from '@server/types/HttpCode';
|
||||
import createHttpError from 'http-errors';
|
||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||
import logger from '@server/logger';
|
||||
|
||||
const updateTargetParamsSchema = z.object({
|
||||
targetId: z.string().transform(Number).pipe(z.number().int().positive())
|
||||
targetId: z.string().transform(Number).pipe(z.number().int().positive())
|
||||
});
|
||||
|
||||
const updateTargetBodySchema = z.object({
|
||||
ip: z.string().ip().optional(),
|
||||
method: z.string().min(1).max(10).optional(),
|
||||
port: z.number().int().min(1).max(65535).optional(),
|
||||
protocol: z.string().optional(),
|
||||
enabled: z.boolean().optional(),
|
||||
ip: z.string().ip().optional(),
|
||||
method: z.string().min(1).max(10).optional(),
|
||||
port: z.number().int().min(1).max(65535).optional(),
|
||||
protocol: z.string().optional(),
|
||||
enabled: z.boolean().optional(),
|
||||
}).refine(data => Object.keys(data).length > 0, {
|
||||
message: "At least one field must be provided for update"
|
||||
message: "At least one field must be provided for update"
|
||||
});
|
||||
|
||||
export async function updateTarget(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
const parsedParams = updateTargetParamsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
try {
|
||||
const parsedParams = updateTargetParamsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const parsedBody = updateTargetBodySchema.safeParse(req.body);
|
||||
if (!parsedBody.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedBody.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const { targetId } = parsedParams.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)
|
||||
.set(updateData)
|
||||
.where(eq(targets.targetId, targetId))
|
||||
.returning();
|
||||
|
||||
if (updatedTarget.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Target with ID ${targetId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return response(res, {
|
||||
data: updatedTarget[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Target updated successfully",
|
||||
status: HttpCode.OK,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
|
||||
const parsedBody = updateTargetBodySchema.safeParse(req.body);
|
||||
if (!parsedBody.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedBody.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const { targetId } = parsedParams.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)
|
||||
.set(updateData)
|
||||
.where(eq(targets.targetId, targetId))
|
||||
.returning();
|
||||
|
||||
if (updatedTarget.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Target with ID ${targetId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return res.status(HttpCode.OK).send(
|
||||
response(res, {
|
||||
data: updatedTarget[0],
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Target updated successfully",
|
||||
status: HttpCode.OK,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,54 +7,54 @@ import response from "@server/utils/response";
|
|||
import HttpCode from '@server/types/HttpCode';
|
||||
import createHttpError from 'http-errors';
|
||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||
import logger from '@server/logger';
|
||||
|
||||
const deleteUserSchema = z.object({
|
||||
userId: z.string().uuid()
|
||||
userId: z.string().uuid()
|
||||
});
|
||||
|
||||
export async function deleteUser(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
const parsedParams = deleteUserSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
try {
|
||||
const parsedParams = deleteUserSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
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)
|
||||
.where(eq(users.id, userId))
|
||||
.returning();
|
||||
|
||||
if (deletedUser.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`User with ID ${userId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return response(res, {
|
||||
data: null,
|
||||
success: true,
|
||||
error: false,
|
||||
message: "User deleted successfully",
|
||||
status: HttpCode.OK,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
|
||||
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)
|
||||
.where(eq(users.id, userId))
|
||||
.returning();
|
||||
|
||||
if (deletedUser.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`User with ID ${userId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return res.status(HttpCode.OK).send(
|
||||
response(res, {
|
||||
data: null,
|
||||
success: true,
|
||||
error: false,
|
||||
message: "User deleted successfully",
|
||||
status: HttpCode.OK,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,58 +7,58 @@ import response from "@server/utils/response";
|
|||
import HttpCode from '@server/types/HttpCode';
|
||||
import createHttpError from 'http-errors';
|
||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||
import logger from '@server/logger';
|
||||
|
||||
const getUserSchema = z.object({
|
||||
userId: z.string().uuid()
|
||||
userId: z.string().uuid()
|
||||
});
|
||||
|
||||
export async function getUser(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
const parsedParams = getUserSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
try {
|
||||
const parsedParams = getUserSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedParams.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
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()
|
||||
.from(users)
|
||||
.where(eq(users.id, userId))
|
||||
.limit(1);
|
||||
|
||||
if (user.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`User with ID ${userId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Remove passwordHash from the response
|
||||
const { passwordHash: _, ...userWithoutPassword } = user[0];
|
||||
|
||||
return response(res, {
|
||||
data: userWithoutPassword,
|
||||
success: true,
|
||||
error: false,
|
||||
message: "User retrieved successfully",
|
||||
status: HttpCode.OK,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
|
||||
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()
|
||||
.from(users)
|
||||
.where(eq(users.id, userId))
|
||||
.limit(1);
|
||||
|
||||
if (user.length === 0) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`User with ID ${userId} not found`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Remove passwordHash from the response
|
||||
const { passwordHash: _, ...userWithoutPassword } = user[0];
|
||||
|
||||
return res.status(HttpCode.OK).send(
|
||||
response(res, {
|
||||
data: userWithoutPassword,
|
||||
success: true,
|
||||
error: false,
|
||||
message: "User retrieved successfully",
|
||||
status: HttpCode.OK,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,62 +7,62 @@ import HttpCode from '@server/types/HttpCode';
|
|||
import createHttpError from 'http-errors';
|
||||
import { sql } from 'drizzle-orm';
|
||||
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
|
||||
import logger from '@server/logger';
|
||||
|
||||
const listUsersSchema = z.object({
|
||||
limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)),
|
||||
offset: z.string().optional().transform(Number).pipe(z.number().int().nonnegative().default(0)),
|
||||
limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)),
|
||||
offset: z.string().optional().transform(Number).pipe(z.number().int().nonnegative().default(0)),
|
||||
});
|
||||
|
||||
export async function listUsers(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
const parsedQuery = listUsersSchema.safeParse(req.query);
|
||||
if (!parsedQuery.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedQuery.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
try {
|
||||
const parsedQuery = listUsersSchema.safeParse(req.query);
|
||||
if (!parsedQuery.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
parsedQuery.error.errors.map(e => e.message).join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const { limit, offset } = parsedQuery.data;
|
||||
|
||||
// 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()
|
||||
.from(users)
|
||||
.limit(limit)
|
||||
.offset(offset);
|
||||
|
||||
const totalCountResult = await db
|
||||
.select({ count: sql<number>`cast(count(*) as integer)` })
|
||||
.from(users);
|
||||
const totalCount = totalCountResult[0].count;
|
||||
|
||||
// Remove passwordHash from each user object
|
||||
const usersWithoutPassword = usersList.map(({ passwordHash, ...userWithoutPassword }) => userWithoutPassword);
|
||||
|
||||
return response(res, {
|
||||
data: {
|
||||
users: usersWithoutPassword,
|
||||
pagination: {
|
||||
total: totalCount,
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
},
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Users retrieved successfully",
|
||||
status: HttpCode.OK,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
|
||||
}
|
||||
|
||||
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()
|
||||
.from(users)
|
||||
.limit(limit)
|
||||
.offset(offset);
|
||||
|
||||
const totalCountResult = await db
|
||||
.select({ count: sql<number>`cast(count(*) as integer)` })
|
||||
.from(users);
|
||||
const totalCount = totalCountResult[0].count;
|
||||
|
||||
// Remove passwordHash from each user object
|
||||
const usersWithoutPassword = usersList.map(({ passwordHash, ...userWithoutPassword }) => userWithoutPassword);
|
||||
|
||||
return res.status(HttpCode.OK).send(
|
||||
response(res, {
|
||||
data: {
|
||||
users: usersWithoutPassword,
|
||||
pagination: {
|
||||
total: totalCount,
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
},
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Users retrieved successfully",
|
||||
status: HttpCode.OK,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
|
@ -55,7 +55,7 @@ export default function LoginForm() {
|
|||
.catch((e) => {
|
||||
setError(
|
||||
e.response?.data?.message ||
|
||||
"An error occurred while logging in",
|
||||
"An error occurred while logging in",
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 37 100% 100%;
|
||||
|
@ -24,6 +25,7 @@
|
|||
--ring: 37 8% 51%;
|
||||
--radius: 0rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 37 50% 10%;
|
||||
--foreground: 37 5% 100%;
|
||||
|
@ -47,11 +49,13 @@
|
|||
--radius: 0rem;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,55 +4,55 @@ import { cva, type VariantProps } from "class-variance-authority"
|
|||
import { cn } from "@/lib/utils"
|
||||
|
||||
const alertVariants = cva(
|
||||
"relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-background text-foreground",
|
||||
destructive:
|
||||
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
"relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-background text-foreground",
|
||||
destructive:
|
||||
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const Alert = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
|
||||
>(({ className, variant, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
role="alert"
|
||||
className={cn(alertVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
<div
|
||||
ref={ref}
|
||||
role="alert"
|
||||
className={cn(alertVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
Alert.displayName = "Alert"
|
||||
|
||||
const AlertTitle = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLHeadingElement>
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLHeadingElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<h5
|
||||
ref={ref}
|
||||
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
||||
{...props}
|
||||
/>
|
||||
<h5
|
||||
ref={ref}
|
||||
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertTitle.displayName = "AlertTitle"
|
||||
|
||||
const AlertDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("text-sm [&_p]:leading-relaxed", className)}
|
||||
{...props}
|
||||
/>
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("text-sm [&_p]:leading-relaxed", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertDescription.displayName = "AlertDescription"
|
||||
|
||||
|
|
|
@ -5,51 +5,51 @@ import { cva, type VariantProps } from "class-variance-authority"
|
|||
import { cn } from "@/lib/utils"
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||
outline:
|
||||
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-4 py-2",
|
||||
sm: "h-9 rounded-md px-3",
|
||||
lg: "h-11 rounded-md px-8",
|
||||
icon: "h-10 w-10",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||
outline:
|
||||
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-4 py-2",
|
||||
sm: "h-9 rounded-md px-3",
|
||||
lg: "h-11 rounded-md px-8",
|
||||
icon: "h-10 w-10",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
export interface ButtonProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {
|
||||
asChild?: boolean
|
||||
asChild?: boolean
|
||||
}
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : "button"
|
||||
return (
|
||||
<Comp
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : "button"
|
||||
return (
|
||||
<Comp
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
)
|
||||
Button.displayName = "Button"
|
||||
|
||||
|
|
|
@ -3,76 +3,76 @@ import * as React from "react"
|
|||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Card = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"rounded-lg border bg-card text-card-foreground shadow-sm",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"rounded-lg border bg-card text-card-foreground shadow-sm",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
Card.displayName = "Card"
|
||||
|
||||
const CardHeader = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
||||
{...props}
|
||||
/>
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
CardHeader.displayName = "CardHeader"
|
||||
|
||||
const CardTitle = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLHeadingElement>
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLHeadingElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<h3
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"text-2xl font-semibold leading-none tracking-tight",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
<h3
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"text-2xl font-semibold leading-none tracking-tight",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
CardTitle.displayName = "CardTitle"
|
||||
|
||||
const CardDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<p
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
<p
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
CardDescription.displayName = "CardDescription"
|
||||
|
||||
const CardContent = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
||||
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
||||
))
|
||||
CardContent.displayName = "CardContent"
|
||||
|
||||
const CardFooter = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("flex items-center p-6 pt-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("flex items-center p-6 pt-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
CardFooter.displayName = "CardFooter"
|
||||
|
||||
|
|
|
@ -4,12 +4,12 @@ import * as React from "react"
|
|||
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import {
|
||||
Controller,
|
||||
ControllerProps,
|
||||
FieldPath,
|
||||
FieldValues,
|
||||
FormProvider,
|
||||
useFormContext,
|
||||
Controller,
|
||||
ControllerProps,
|
||||
FieldPath,
|
||||
FieldValues,
|
||||
FormProvider,
|
||||
useFormContext,
|
||||
} from "react-hook-form"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
@ -18,161 +18,161 @@ import { Label } from "@/components/ui/label"
|
|||
const Form = FormProvider
|
||||
|
||||
type FormFieldContextValue<
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
||||
> = {
|
||||
name: TName
|
||||
name: TName
|
||||
}
|
||||
|
||||
const FormFieldContext = React.createContext<FormFieldContextValue>(
|
||||
{} as FormFieldContextValue
|
||||
{} as FormFieldContextValue
|
||||
)
|
||||
|
||||
const FormField = <
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
||||
>({
|
||||
...props
|
||||
...props
|
||||
}: ControllerProps<TFieldValues, TName>) => {
|
||||
return (
|
||||
<FormFieldContext.Provider value={{ name: props.name }}>
|
||||
<Controller {...props} />
|
||||
</FormFieldContext.Provider>
|
||||
)
|
||||
return (
|
||||
<FormFieldContext.Provider value={{ name: props.name }}>
|
||||
<Controller {...props} />
|
||||
</FormFieldContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
const useFormField = () => {
|
||||
const fieldContext = React.useContext(FormFieldContext)
|
||||
const itemContext = React.useContext(FormItemContext)
|
||||
const { getFieldState, formState } = useFormContext()
|
||||
const fieldContext = React.useContext(FormFieldContext)
|
||||
const itemContext = React.useContext(FormItemContext)
|
||||
const { getFieldState, formState } = useFormContext()
|
||||
|
||||
const fieldState = getFieldState(fieldContext.name, formState)
|
||||
const fieldState = getFieldState(fieldContext.name, formState)
|
||||
|
||||
if (!fieldContext) {
|
||||
throw new Error("useFormField should be used within <FormField>")
|
||||
}
|
||||
if (!fieldContext) {
|
||||
throw new Error("useFormField should be used within <FormField>")
|
||||
}
|
||||
|
||||
const { id } = itemContext
|
||||
const { id } = itemContext
|
||||
|
||||
return {
|
||||
id,
|
||||
name: fieldContext.name,
|
||||
formItemId: `${id}-form-item`,
|
||||
formDescriptionId: `${id}-form-item-description`,
|
||||
formMessageId: `${id}-form-item-message`,
|
||||
...fieldState,
|
||||
}
|
||||
return {
|
||||
id,
|
||||
name: fieldContext.name,
|
||||
formItemId: `${id}-form-item`,
|
||||
formDescriptionId: `${id}-form-item-description`,
|
||||
formMessageId: `${id}-form-item-message`,
|
||||
...fieldState,
|
||||
}
|
||||
}
|
||||
|
||||
type FormItemContextValue = {
|
||||
id: string
|
||||
id: string
|
||||
}
|
||||
|
||||
const FormItemContext = React.createContext<FormItemContextValue>(
|
||||
{} as FormItemContextValue
|
||||
{} as FormItemContextValue
|
||||
)
|
||||
|
||||
const FormItem = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const id = React.useId()
|
||||
const id = React.useId()
|
||||
|
||||
return (
|
||||
<FormItemContext.Provider value={{ id }}>
|
||||
<div ref={ref} className={cn("space-y-2", className)} {...props} />
|
||||
</FormItemContext.Provider>
|
||||
)
|
||||
return (
|
||||
<FormItemContext.Provider value={{ id }}>
|
||||
<div ref={ref} className={cn("space-y-2", className)} {...props} />
|
||||
</FormItemContext.Provider>
|
||||
)
|
||||
})
|
||||
FormItem.displayName = "FormItem"
|
||||
|
||||
const FormLabel = React.forwardRef<
|
||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { error, formItemId } = useFormField()
|
||||
const { error, formItemId } = useFormField()
|
||||
|
||||
return (
|
||||
<Label
|
||||
ref={ref}
|
||||
className={cn(error && "text-destructive", className)}
|
||||
htmlFor={formItemId}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
return (
|
||||
<Label
|
||||
ref={ref}
|
||||
className={cn(error && "text-destructive", className)}
|
||||
htmlFor={formItemId}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
FormLabel.displayName = "FormLabel"
|
||||
|
||||
const FormControl = React.forwardRef<
|
||||
React.ElementRef<typeof Slot>,
|
||||
React.ComponentPropsWithoutRef<typeof Slot>
|
||||
React.ElementRef<typeof Slot>,
|
||||
React.ComponentPropsWithoutRef<typeof Slot>
|
||||
>(({ ...props }, ref) => {
|
||||
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
|
||||
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
|
||||
|
||||
return (
|
||||
<Slot
|
||||
ref={ref}
|
||||
id={formItemId}
|
||||
aria-describedby={
|
||||
!error
|
||||
? `${formDescriptionId}`
|
||||
: `${formDescriptionId} ${formMessageId}`
|
||||
}
|
||||
aria-invalid={!!error}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
return (
|
||||
<Slot
|
||||
ref={ref}
|
||||
id={formItemId}
|
||||
aria-describedby={
|
||||
!error
|
||||
? `${formDescriptionId}`
|
||||
: `${formDescriptionId} ${formMessageId}`
|
||||
}
|
||||
aria-invalid={!!error}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
FormControl.displayName = "FormControl"
|
||||
|
||||
const FormDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { formDescriptionId } = useFormField()
|
||||
const { formDescriptionId } = useFormField()
|
||||
|
||||
return (
|
||||
<p
|
||||
ref={ref}
|
||||
id={formDescriptionId}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
return (
|
||||
<p
|
||||
ref={ref}
|
||||
id={formDescriptionId}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
FormDescription.displayName = "FormDescription"
|
||||
|
||||
const FormMessage = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, children, ...props }, ref) => {
|
||||
const { error, formMessageId } = useFormField()
|
||||
const body = error ? String(error?.message) : children
|
||||
const { error, formMessageId } = useFormField()
|
||||
const body = error ? String(error?.message) : children
|
||||
|
||||
if (!body) {
|
||||
return null
|
||||
}
|
||||
if (!body) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<p
|
||||
ref={ref}
|
||||
id={formMessageId}
|
||||
className={cn("text-sm font-medium text-destructive", className)}
|
||||
{...props}
|
||||
>
|
||||
{body}
|
||||
</p>
|
||||
)
|
||||
return (
|
||||
<p
|
||||
ref={ref}
|
||||
id={formMessageId}
|
||||
className={cn("text-sm font-medium text-destructive", className)}
|
||||
{...props}
|
||||
>
|
||||
{body}
|
||||
</p>
|
||||
)
|
||||
})
|
||||
FormMessage.displayName = "FormMessage"
|
||||
|
||||
export {
|
||||
useFormField,
|
||||
Form,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormMessage,
|
||||
FormField,
|
||||
useFormField,
|
||||
Form,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormMessage,
|
||||
FormField,
|
||||
}
|
||||
|
|
|
@ -5,19 +5,19 @@ import { cn } from "@/lib/utils"
|
|||
export type InputProps = React.InputHTMLAttributes<HTMLInputElement>;
|
||||
|
||||
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
({ className, type, ...props }, ref) => {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
({ className, type, ...props }, ref) => {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
)
|
||||
Input.displayName = "Input"
|
||||
|
||||
|
|
|
@ -7,19 +7,19 @@ import { cva, type VariantProps } from "class-variance-authority"
|
|||
import { cn } from "@/lib/utils"
|
||||
|
||||
const labelVariants = cva(
|
||||
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
)
|
||||
|
||||
const Label = React.forwardRef<
|
||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
||||
VariantProps<typeof labelVariants>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<LabelPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(labelVariants(), className)}
|
||||
{...props}
|
||||
/>
|
||||
<LabelPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(labelVariants(), className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
Label.displayName = LabelPrimitive.Root.displayName
|
||||
|
||||
|
|
|
@ -2,5 +2,5 @@ import { clsx, type ClassValue } from "clsx"
|
|||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
|
|
|
@ -8,55 +8,55 @@ const config: Config = {
|
|||
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
background: 'hsl(var(--background))',
|
||||
foreground: 'hsl(var(--foreground))',
|
||||
card: {
|
||||
DEFAULT: 'hsl(var(--card))',
|
||||
foreground: 'hsl(var(--card-foreground))'
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: 'hsl(var(--popover))',
|
||||
foreground: 'hsl(var(--popover-foreground))'
|
||||
},
|
||||
primary: {
|
||||
DEFAULT: 'hsl(var(--primary))',
|
||||
foreground: 'hsl(var(--primary-foreground))'
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: 'hsl(var(--secondary))',
|
||||
foreground: 'hsl(var(--secondary-foreground))'
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: 'hsl(var(--muted))',
|
||||
foreground: 'hsl(var(--muted-foreground))'
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: 'hsl(var(--accent))',
|
||||
foreground: 'hsl(var(--accent-foreground))'
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: 'hsl(var(--destructive))',
|
||||
foreground: 'hsl(var(--destructive-foreground))'
|
||||
},
|
||||
border: 'hsl(var(--border))',
|
||||
input: 'hsl(var(--input))',
|
||||
ring: 'hsl(var(--ring))',
|
||||
chart: {
|
||||
'1': 'hsl(var(--chart-1))',
|
||||
'2': 'hsl(var(--chart-2))',
|
||||
'3': 'hsl(var(--chart-3))',
|
||||
'4': 'hsl(var(--chart-4))',
|
||||
'5': 'hsl(var(--chart-5))'
|
||||
}
|
||||
},
|
||||
borderRadius: {
|
||||
lg: 'var(--radius)',
|
||||
md: 'calc(var(--radius) - 2px)',
|
||||
sm: 'calc(var(--radius) - 4px)'
|
||||
}
|
||||
}
|
||||
extend: {
|
||||
colors: {
|
||||
background: 'hsl(var(--background))',
|
||||
foreground: 'hsl(var(--foreground))',
|
||||
card: {
|
||||
DEFAULT: 'hsl(var(--card))',
|
||||
foreground: 'hsl(var(--card-foreground))'
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: 'hsl(var(--popover))',
|
||||
foreground: 'hsl(var(--popover-foreground))'
|
||||
},
|
||||
primary: {
|
||||
DEFAULT: 'hsl(var(--primary))',
|
||||
foreground: 'hsl(var(--primary-foreground))'
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: 'hsl(var(--secondary))',
|
||||
foreground: 'hsl(var(--secondary-foreground))'
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: 'hsl(var(--muted))',
|
||||
foreground: 'hsl(var(--muted-foreground))'
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: 'hsl(var(--accent))',
|
||||
foreground: 'hsl(var(--accent-foreground))'
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: 'hsl(var(--destructive))',
|
||||
foreground: 'hsl(var(--destructive-foreground))'
|
||||
},
|
||||
border: 'hsl(var(--border))',
|
||||
input: 'hsl(var(--input))',
|
||||
ring: 'hsl(var(--ring))',
|
||||
chart: {
|
||||
'1': 'hsl(var(--chart-1))',
|
||||
'2': 'hsl(var(--chart-2))',
|
||||
'3': 'hsl(var(--chart-3))',
|
||||
'4': 'hsl(var(--chart-4))',
|
||||
'5': 'hsl(var(--chart-5))'
|
||||
}
|
||||
},
|
||||
borderRadius: {
|
||||
lg: 'var(--radius)',
|
||||
md: 'calc(var(--radius) - 2px)',
|
||||
sm: 'calc(var(--radius) - 4px)'
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [require("tailwindcss-animate")],
|
||||
};
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
|
@ -14,9 +18,15 @@
|
|||
"incremental": true,
|
||||
"baseUrl": "src",
|
||||
"paths": {
|
||||
"@server/*": ["../server/*"],
|
||||
"@app/*": ["*"],
|
||||
"@/*": ["./*"]
|
||||
"@server/*": [
|
||||
"../server/*"
|
||||
],
|
||||
"@app/*": [
|
||||
"*"
|
||||
],
|
||||
"@/*": [
|
||||
"./*"
|
||||
]
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
|
@ -24,6 +34,13 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue