From 82130367295b13a130cfcd87af15ed5bf7274b54 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sun, 6 Oct 2024 18:05:20 -0400 Subject: [PATCH] Format files and fix http response --- .eslintrc.json | 7 +- bruno/bruno.json | 14 +- components.json | 2 +- package.json | 2 +- server/auth/actions.ts | 164 +++-- server/auth/limits.ts | 54 +- server/db/names.json | 716 ++++++++++---------- server/db/schema.ts | 6 +- server/routers/auth/getUserOrgs.ts | 40 +- server/routers/auth/verifyOrgAccess.ts | 48 +- server/routers/auth/verifyResourceAccess.ts | 130 ++-- server/routers/auth/verifySiteAccess.ts | 126 ++-- server/routers/auth/verifyTargetAccess.ts | 130 ++-- server/routers/gerbil/getConfig.ts | 8 +- server/routers/gerbil/receiveBandwidth.ts | 93 +-- server/routers/org/createOrg.ts | 94 +-- server/routers/org/deleteOrg.ts | 88 +-- server/routers/org/getOrg.ts | 90 +-- server/routers/org/listOrgs.ts | 146 ++-- server/routers/org/updateOrg.ts | 120 ++-- server/routers/resource/createResource.ts | 120 ++-- server/routers/resource/deleteResource.ts | 92 +-- server/routers/resource/getResource.ts | 94 +-- server/routers/resource/listResources.ts | 176 ++--- server/routers/resource/updateResource.ts | 124 ++-- server/routers/site/createSite.ts | 151 ++--- server/routers/site/deleteSite.ts | 120 ++-- server/routers/site/getSite.ts | 94 +-- server/routers/site/listSites.ts | 174 ++--- server/routers/site/updateSite.ts | 134 ++-- server/routers/target/createTarget.ts | 106 +-- server/routers/target/deleteTarget.ts | 88 +-- server/routers/target/getTarget.ts | 90 +-- server/routers/target/listTargets.ts | 150 ++-- server/routers/target/updateTarget.ts | 124 ++-- server/routers/user/deleteUser.ts | 88 +-- server/routers/user/getUser.ts | 96 +-- server/routers/user/listUsers.ts | 104 +-- src/app/auth/login/page.tsx | 2 +- src/app/globals.css | 6 +- src/components/ui/alert.tsx | 70 +- src/components/ui/button.tsx | 76 +-- src/components/ui/card.tsx | 88 +-- src/components/ui/form.tsx | 214 +++--- src/components/ui/input.tsx | 26 +- src/components/ui/label.tsx | 16 +- src/lib/utils.ts | 2 +- tailwind.config.ts | 98 +-- tsconfig.json | 31 +- 49 files changed, 2428 insertions(+), 2404 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 98ef693e..e8f71847 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,3 +1,6 @@ { - "extends": ["next/core-web-vitals", "next/typescript"] -} + "extends": [ + "next/core-web-vitals", + "next/typescript" + ] +} \ No newline at end of file diff --git a/bruno/bruno.json b/bruno/bruno.json index c28a54bb..ba373216 100644 --- a/bruno/bruno.json +++ b/bruno/bruno.json @@ -1,9 +1,9 @@ { - "version": "1", - "name": "Pangolin", - "type": "collection", - "ignore": [ - "node_modules", - ".git" - ] + "version": "1", + "name": "Pangolin", + "type": "collection", + "ignore": [ + "node_modules", + ".git" + ] } \ No newline at end of file diff --git a/components.json b/components.json index 13f7efef..97d8c8c0 100644 --- a/components.json +++ b/components.json @@ -17,4 +17,4 @@ "lib": "@/lib", "hooks": "@/hooks" } -} +} \ No newline at end of file diff --git a/package.json b/package.json index f4080bbe..a06f640e 100644 --- a/package.json +++ b/package.json @@ -76,4 +76,4 @@ "tsx": "4.19.1", "typescript": "^5" } -} +} \ No newline at end of file diff --git a/server/auth/actions.ts b/server/auth/actions.ts index 01008722..02f13820 100644 --- a/server/auth/actions.ts +++ b/server/auth/actions.ts @@ -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 { - const userId = req.user?.id; +export async function checkUserActionPermission(actionId: string, req: Request): Promise { + 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'); + } } \ No newline at end of file diff --git a/server/auth/limits.ts b/server/auth/limits.ts index 4eb06490..c6e4dc53 100644 --- a/server/auth/limits.ts +++ b/server/auth/limits.ts @@ -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 { - 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'); - } } \ No newline at end of file diff --git a/server/db/names.json b/server/db/names.json index 8ffeff12..4a154c5c 100644 --- a/server/db/names.json +++ b/server/db/names.json @@ -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" ] \ No newline at end of file diff --git a/server/db/schema.ts b/server/db/schema.ts index c9408d2a..3e627ce8 100644 --- a/server/db/schema.ts +++ b/server/db/schema.ts @@ -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") diff --git a/server/routers/auth/getUserOrgs.ts b/server/routers/auth/getUserOrgs.ts index 1753d0b7..224a33b2 100644 --- a/server/routers/auth/getUserOrgs.ts +++ b/server/routers/auth/getUserOrgs.ts @@ -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); + req.userOrgIds = userOrganizations.map(org => org.orgId); + // req.userOrgRoleIds = userOrganizations.reduce((acc, org) => { + // acc[org.orgId] = org.role; + // return acc; + // }, {} as Record); - 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')); + } } \ No newline at end of file diff --git a/server/routers/auth/verifyOrgAccess.ts b/server/routers/auth/verifyOrgAccess.ts index e24e765e..45792072 100644 --- a/server/routers/auth/verifyOrgAccess.ts +++ b/server/routers/auth/verifyOrgAccess.ts @@ -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')); + }); } diff --git a/server/routers/auth/verifyResourceAccess.ts b/server/routers/auth/verifyResourceAccess.ts index f5518323..16f6b97b 100644 --- a/server/routers/auth/verifyResourceAccess.ts +++ b/server/routers/auth/verifyResourceAccess.ts @@ -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')); - } } \ No newline at end of file diff --git a/server/routers/auth/verifySiteAccess.ts b/server/routers/auth/verifySiteAccess.ts index 6b8e16cd..c2b3f083 100644 --- a/server/routers/auth/verifySiteAccess.ts +++ b/server/routers/auth/verifySiteAccess.ts @@ -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')); - } } \ No newline at end of file diff --git a/server/routers/auth/verifyTargetAccess.ts b/server/routers/auth/verifyTargetAccess.ts index fa070e22..eb4db4b6 100644 --- a/server/routers/auth/verifyTargetAccess.ts +++ b/server/routers/auth/verifyTargetAccess.ts @@ -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')); + }); } \ No newline at end of file diff --git a/server/routers/gerbil/getConfig.ts b/server/routers/gerbil/getConfig.ts index 9cb14449..da760d2b 100644 --- a/server/routers/gerbil/getConfig.ts +++ b/server/routers/gerbil/getConfig.ts @@ -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 => { 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...")); } }; diff --git a/server/routers/gerbil/receiveBandwidth.ts b/server/routers/gerbil/receiveBandwidth.ts index ff17c53f..f63d3f22 100644 --- a/server/routers/gerbil/receiveBandwidth.ts +++ b/server/routers/gerbil/receiveBandwidth.ts @@ -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 => { - try { - const bandwidthData: PeerBandwidth[] = req.body; +export const receiveBandwidth = async (req: Request, res: Response, next: NextFunction): Promise => { + 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`; } diff --git a/server/routers/org/createOrg.ts b/server/routers/org/createOrg.ts index 5b22d284..fafe197e 100644 --- a/server/routers/org/createOrg.ts +++ b/server/routers/org/createOrg.ts @@ -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 { - 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); - } } diff --git a/server/routers/org/deleteOrg.ts b/server/routers/org/deleteOrg.ts index 59287e6a..e83196df 100644 --- a/server/routers/org/deleteOrg.ts +++ b/server/routers/org/deleteOrg.ts @@ -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 { - 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); - } } diff --git a/server/routers/org/getOrg.ts b/server/routers/org/getOrg.ts index 179190d8..d9686a9f 100644 --- a/server/routers/org/getOrg.ts +++ b/server/routers/org/getOrg.ts @@ -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 { - 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); - } } diff --git a/server/routers/org/listOrgs.ts b/server/routers/org/listOrgs.ts index 8cd0131b..820ed7ac 100644 --- a/server/routers/org/listOrgs.ts +++ b/server/routers/org/listOrgs.ts @@ -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 { - 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`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`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); - } } \ No newline at end of file diff --git a/server/routers/org/updateOrg.ts b/server/routers/org/updateOrg.ts index d21b79bf..666d1925 100644 --- a/server/routers/org/updateOrg.ts +++ b/server/routers/org/updateOrg.ts @@ -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 { - 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); - } } diff --git a/server/routers/resource/createResource.ts b/server/routers/resource/createResource.ts index 2b723682..5242327f 100644 --- a/server/routers/resource/createResource.ts +++ b/server/routers/resource/createResource.ts @@ -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 { - 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); - } } diff --git a/server/routers/resource/deleteResource.ts b/server/routers/resource/deleteResource.ts index 5c6a7dc3..27f72b86 100644 --- a/server/routers/resource/deleteResource.ts +++ b/server/routers/resource/deleteResource.ts @@ -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 { - 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); - } } diff --git a/server/routers/resource/getResource.ts b/server/routers/resource/getResource.ts index 4feac131..de37cd37 100644 --- a/server/routers/resource/getResource.ts +++ b/server/routers/resource/getResource.ts @@ -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 { - 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); - } } diff --git a/server/routers/resource/listResources.ts b/server/routers/resource/listResources.ts index 52cfa487..533aff4c 100644 --- a/server/routers/resource/listResources.ts +++ b/server/routers/resource/listResources.ts @@ -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 { - 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`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`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`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`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); - } } \ No newline at end of file diff --git a/server/routers/resource/updateResource.ts b/server/routers/resource/updateResource.ts index 02d6bb12..be7e72bd 100644 --- a/server/routers/resource/updateResource.ts +++ b/server/routers/resource/updateResource.ts @@ -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 { - 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); - } } diff --git a/server/routers/site/createSite.ts b/server/routers/site/createSite.ts index 69e5bc51..38ad97f0 100644 --- a/server/routers/site/createSite.ts +++ b/server/routers/site/createSite.ts @@ -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 { - 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; - } } diff --git a/server/routers/site/deleteSite.ts b/server/routers/site/deleteSite.ts index 413671fa..bad0d1ce 100644 --- a/server/routers/site/deleteSite.ts +++ b/server/routers/site/deleteSite.ts @@ -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 { - 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; - } } diff --git a/server/routers/site/getSite.ts b/server/routers/site/getSite.ts index 968aa7ff..2b14a765 100644 --- a/server/routers/site/getSite.ts +++ b/server/routers/site/getSite.ts @@ -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 { - 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); - } } diff --git a/server/routers/site/listSites.ts b/server/routers/site/listSites.ts index 010548ca..63a7eeed 100644 --- a/server/routers/site/listSites.ts +++ b/server/routers/site/listSites.ts @@ -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 { - 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`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`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`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`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); - } } \ No newline at end of file diff --git a/server/routers/site/updateSite.ts b/server/routers/site/updateSite.ts index 38cce4b4..23ba2b21 100644 --- a/server/routers/site/updateSite.ts +++ b/server/routers/site/updateSite.ts @@ -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 { - 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); - } } diff --git a/server/routers/target/createTarget.ts b/server/routers/target/createTarget.ts index 53215c2a..fcfe20db 100644 --- a/server/routers/target/createTarget.ts +++ b/server/routers/target/createTarget.ts @@ -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 { - 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); - } } diff --git a/server/routers/target/deleteTarget.ts b/server/routers/target/deleteTarget.ts index 46a7212f..5d5132e3 100644 --- a/server/routers/target/deleteTarget.ts +++ b/server/routers/target/deleteTarget.ts @@ -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 { - 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); - } } diff --git a/server/routers/target/getTarget.ts b/server/routers/target/getTarget.ts index be0025a3..49fd0952 100644 --- a/server/routers/target/getTarget.ts +++ b/server/routers/target/getTarget.ts @@ -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 { - 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); - } } diff --git a/server/routers/target/listTargets.ts b/server/routers/target/listTargets.ts index 0dd2de52..375abe8c 100644 --- a/server/routers/target/listTargets.ts +++ b/server/routers/target/listTargets.ts @@ -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 { - 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`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`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")); - } } \ No newline at end of file diff --git a/server/routers/target/updateTarget.ts b/server/routers/target/updateTarget.ts index ad9a41d9..01401c08 100644 --- a/server/routers/target/updateTarget.ts +++ b/server/routers/target/updateTarget.ts @@ -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 { - 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); - } } diff --git a/server/routers/user/deleteUser.ts b/server/routers/user/deleteUser.ts index b835199c..d74d6424 100644 --- a/server/routers/user/deleteUser.ts +++ b/server/routers/user/deleteUser.ts @@ -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 { - 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); - } } diff --git a/server/routers/user/getUser.ts b/server/routers/user/getUser.ts index 70a3cffc..cd949be5 100644 --- a/server/routers/user/getUser.ts +++ b/server/routers/user/getUser.ts @@ -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 { - 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); - } } diff --git a/server/routers/user/listUsers.ts b/server/routers/user/listUsers.ts index 8f19186e..3a365788 100644 --- a/server/routers/user/listUsers.ts +++ b/server/routers/user/listUsers.ts @@ -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 { - 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`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`cast(count(*) as integer)` }) - .from(users); - const totalCount = totalCountResult[0].count; - - // Remove passwordHash from each user object - const usersWithoutPassword = usersList.map(({ passwordHash, ...userWithoutPassword }) => userWithoutPassword); - - return res.status(HttpCode.OK).send( - response(res, { - data: { - users: usersWithoutPassword, - pagination: { - total: totalCount, - limit, - offset, - }, - }, - success: true, - error: false, - message: "Users retrieved successfully", - status: HttpCode.OK, - }) - ); - } catch (error) { - next(error); - } } \ No newline at end of file diff --git a/src/app/auth/login/page.tsx b/src/app/auth/login/page.tsx index 8f27333c..c97ad7af 100644 --- a/src/app/auth/login/page.tsx +++ b/src/app/auth/login/page.tsx @@ -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", ); }); } diff --git a/src/app/globals.css b/src/app/globals.css index bd82533c..67d85552 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -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; } -} +} \ No newline at end of file diff --git a/src/components/ui/alert.tsx b/src/components/ui/alert.tsx index 41fa7e05..81e4f01a 100644 --- a/src/components/ui/alert.tsx +++ b/src/components/ui/alert.tsx @@ -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 & VariantProps + HTMLDivElement, + React.HTMLAttributes & VariantProps >(({ className, variant, ...props }, ref) => ( -
+
)) Alert.displayName = "Alert" const AlertTitle = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes + HTMLParagraphElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -
+
)) AlertTitle.displayName = "AlertTitle" const AlertDescription = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes + HTMLParagraphElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -
+
)) AlertDescription.displayName = "AlertDescription" diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 0ba42773..28b2fa4c 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -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, + extends React.ButtonHTMLAttributes, VariantProps { - asChild?: boolean + asChild?: boolean } const Button = React.forwardRef( - ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : "button" - return ( - - ) - } + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button" + return ( + + ) + } ) Button.displayName = "Button" diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx index afa13ecf..5b8e64f7 100644 --- a/src/components/ui/card.tsx +++ b/src/components/ui/card.tsx @@ -3,76 +3,76 @@ import * as React from "react" import { cn } from "@/lib/utils" const Card = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes + HTMLDivElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -
+
)) Card.displayName = "Card" const CardHeader = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes + HTMLDivElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -
+
)) CardHeader.displayName = "CardHeader" const CardTitle = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes + HTMLParagraphElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -

+

)) CardTitle.displayName = "CardTitle" const CardDescription = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes + HTMLParagraphElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -

+

)) CardDescription.displayName = "CardDescription" const CardContent = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes + HTMLDivElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -

+
)) CardContent.displayName = "CardContent" const CardFooter = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes + HTMLDivElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -
+
)) CardFooter.displayName = "CardFooter" diff --git a/src/components/ui/form.tsx b/src/components/ui/form.tsx index ce264aef..25b9ea17 100644 --- a/src/components/ui/form.tsx +++ b/src/components/ui/form.tsx @@ -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 = FieldPath + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath > = { - name: TName + name: TName } const FormFieldContext = React.createContext( - {} as FormFieldContextValue + {} as FormFieldContextValue ) const FormField = < - TFieldValues extends FieldValues = FieldValues, - TName extends FieldPath = FieldPath + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath >({ - ...props + ...props }: ControllerProps) => { - return ( - - - - ) + return ( + + + + ) } 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 ") - } + if (!fieldContext) { + throw new Error("useFormField should be used within ") + } - 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( - {} as FormItemContextValue + {} as FormItemContextValue ) const FormItem = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes + HTMLDivElement, + React.HTMLAttributes >(({ className, ...props }, ref) => { - const id = React.useId() + const id = React.useId() - return ( - -
- - ) + return ( + +
+ + ) }) FormItem.displayName = "FormItem" const FormLabel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => { - const { error, formItemId } = useFormField() + const { error, formItemId } = useFormField() - return ( -