Format files and fix http response

This commit is contained in:
Owen Schwartz 2024-10-06 18:05:20 -04:00
parent 797f72e1d0
commit 8213036729
No known key found for this signature in database
GPG key ID: 8271FDFFD9E0CCBD
49 changed files with 2428 additions and 2404 deletions

View file

@ -1,3 +1,6 @@
{
"extends": ["next/core-web-vitals", "next/typescript"]
}
"extends": [
"next/core-web-vitals",
"next/typescript"
]
}

View file

@ -1,9 +1,9 @@
{
"version": "1",
"name": "Pangolin",
"type": "collection",
"ignore": [
"node_modules",
".git"
]
"version": "1",
"name": "Pangolin",
"type": "collection",
"ignore": [
"node_modules",
".git"
]
}

View file

@ -17,4 +17,4 @@
"lib": "@/lib",
"hooks": "@/hooks"
}
}
}

View file

@ -76,4 +76,4 @@
"tsx": "4.19.1",
"typescript": "^5"
}
}
}

View file

@ -5,98 +5,92 @@ import { and, eq } from 'drizzle-orm';
import createHttpError from 'http-errors';
import HttpCode from '@server/types/HttpCode';
export const ActionsEnum = {
createOrg: 1,
deleteOrg: 2,
getOrg: 3,
listOrgs: 4,
updateOrg: 5,
createSite: 6,
deleteSite: 7,
getSite: 8,
listSites: 9,
updateSite: 10,
createResource: 11,
deleteResource: 12,
getResource: 13,
listResources: 14,
updateResource: 15,
createTarget: 16,
deleteTarget: 17,
getTarget: 18,
listTargets: 19,
updateTarget: 20,
getUser: 21,
deleteUser: 22,
listUsers: 23
export enum ActionsEnum {
createOrg = "createOrg",
deleteOrg = "deleteOrg",
getOrg = "getOrg",
listOrgs = "listOrgs",
updateOrg = "updateOrg",
createSite = "createSite",
deleteSite = "deleteSite",
getSite = "getSite",
listSites = "listSites",
updateSite = "updateSite",
createResource = "createResource",
deleteResource = "deleteResource",
getResource = "getResource",
listResources = "listResources",
updateResource = "updateResource",
createTarget = "createTarget",
deleteTarget = "deleteTarget",
getTarget = "getTarget",
listTargets = "listTargets",
updateTarget = "updateTarget",
getUser = "getUser",
deleteUser = "deleteUser",
listUsers = "listUsers"
}
export async function checkUserActionPermission(actionId: number, req: Request): Promise<boolean> {
const userId = req.user?.id;
export async function checkUserActionPermission(actionId: string, req: Request): Promise<boolean> {
const userId = req.user?.id;
if (!userId) {
throw createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated');
}
if (!req.userOrgId) {
throw createHttpError(HttpCode.BAD_REQUEST, 'Organization ID is required');
}
try {
let userOrgRoleId = req.userOrgRoleId;
// If userOrgRoleId is not available on the request, fetch it
if (userOrgRoleId === undefined) {
const userOrgRole = await db.select()
.from(userOrgs)
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, req.userOrgId)))
.limit(1);
if (userOrgRole.length === 0) {
throw createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization');
}
userOrgRoleId = userOrgRole[0].roleId;
if (!userId) {
throw createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated');
}
// Check if the user has direct permission for the action in the current org
const userActionPermission = await db.select()
.from(userActions)
.where(
and(
eq(userActions.userId, userId),
eq(userActions.actionId, actionId),
eq(userActions.orgId, req.userOrgId)
)
)
.limit(1);
if (userActionPermission.length > 0) {
return true;
if (!req.userOrgId) {
throw createHttpError(HttpCode.BAD_REQUEST, 'Organization ID is required');
}
// If no direct permission, check role-based permission
const roleActionPermission = await db.select()
.from(roleActions)
.where(
and(
eq(roleActions.actionId, actionId),
eq(roleActions.roleId, userOrgRoleId),
eq(roleActions.orgId, req.userOrgId)
)
)
.limit(1);
try {
let userOrgRoleId = req.userOrgRoleId;
return roleActionPermission.length > 0;
// If userOrgRoleId is not available on the request, fetch it
if (userOrgRoleId === undefined) {
const userOrgRole = await db.select()
.from(userOrgs)
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, req.userOrgId)))
.limit(1);
} catch (error) {
console.error('Error checking user action permission:', error);
throw createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error checking action permission');
}
if (userOrgRole.length === 0) {
throw createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization');
}
userOrgRoleId = userOrgRole[0].roleId;
}
// Check if the user has direct permission for the action in the current org
const userActionPermission = await db.select()
.from(userActions)
.where(
and(
eq(userActions.userId, userId),
eq(userActions.actionId, actionId),
eq(userActions.orgId, req.userOrgId)
)
)
.limit(1);
if (userActionPermission.length > 0) {
return true;
}
// If no direct permission, check role-based permission
const roleActionPermission = await db.select()
.from(roleActions)
.where(
and(
eq(roleActions.actionId, actionId),
eq(roleActions.roleId, userOrgRoleId),
eq(roleActions.orgId, req.userOrgId)
)
)
.limit(1);
return roleActionPermission.length > 0;
} catch (error) {
console.error('Error checking user action permission:', error);
throw createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error checking action permission');
}
}

View file

@ -5,36 +5,36 @@ import createHttpError from 'http-errors';
import HttpCode from '@server/types/HttpCode';
interface CheckLimitOptions {
orgId: number;
limitName: string;
currentValue: number;
increment?: number;
orgId: number;
limitName: string;
currentValue: number;
increment?: number;
}
export async function checkOrgLimit({ orgId, limitName, currentValue, increment = 0 }: CheckLimitOptions): Promise<boolean> {
try {
const limit = await db.select()
.from(limitsTable)
.where(
and(
eq(limitsTable.orgId, orgId),
eq(limitsTable.name, limitName)
)
)
.limit(1);
try {
const limit = await db.select()
.from(limitsTable)
.where(
and(
eq(limitsTable.orgId, orgId),
eq(limitsTable.name, limitName)
)
)
.limit(1);
if (limit.length === 0) {
throw createHttpError(HttpCode.NOT_FOUND, `Limit "${limitName}" not found for organization`);
if (limit.length === 0) {
throw createHttpError(HttpCode.NOT_FOUND, `Limit "${limitName}" not found for organization`);
}
const limitValue = limit[0].value;
// Check if the current value plus the increment is within the limit
return (currentValue + increment) <= limitValue;
} catch (error) {
if (error instanceof Error) {
throw createHttpError(HttpCode.INTERNAL_SERVER_ERROR, `Error checking limit: ${error.message}`);
}
throw createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Unknown error occurred while checking limit');
}
const limitValue = limit[0].value;
// Check if the current value plus the increment is within the limit
return (currentValue + increment) <= limitValue;
} catch (error) {
if (error instanceof Error) {
throw createHttpError(HttpCode.INTERNAL_SERVER_ERROR, `Error checking limit: ${error.message}`);
}
throw createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Unknown error occurred while checking limit');
}
}

View file

@ -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"
]

View file

@ -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")

View file

@ -6,28 +6,28 @@ import createHttpError from 'http-errors';
import HttpCode from '@server/types/HttpCode';
export async function getUserOrgs(req: Request, res: Response, next: NextFunction) {
const userId = req.user?.id; // Assuming you have user information in the request
const userId = req.user?.id; // Assuming you have user information in the request
if (!userId) {
return next(createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated'));
}
if (!userId) {
return next(createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated'));
}
try {
const userOrganizations = await db.select({
orgId: userOrgs.orgId,
role: userOrgs.role,
})
.from(userOrgs)
.where(eq(userOrgs.userId, userId));
try {
const userOrganizations = await db.select({
orgId: userOrgs.orgId,
roleId: userOrgs.roleId,
})
.from(userOrgs)
.where(eq(userOrgs.userId, userId));
req.userOrgs = userOrganizations.map(org => org.orgId);
// req.userOrgRoleIds = userOrganizations.reduce((acc, org) => {
// acc[org.orgId] = org.role;
// return acc;
// }, {} as Record<number, string>);
req.userOrgIds = userOrganizations.map(org => org.orgId);
// req.userOrgRoleIds = userOrganizations.reduce((acc, org) => {
// acc[org.orgId] = org.role;
// return acc;
// }, {} as Record<number, string>);
next();
} catch (error) {
next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error retrieving user organizations'));
}
next();
} catch (error) {
next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error retrieving user organizations'));
}
}

View file

@ -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'));
});
}

View file

@ -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'));
}
}

View file

@ -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'));
}
}

View file

@ -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'));
});
}

View file

@ -3,6 +3,8 @@ import { DrizzleError, eq } from 'drizzle-orm';
import { sites, resources, targets, exitNodes } from '@server/db/schema';
import db from '@server/db';
import logger from '@server/logger';
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
export const getConfig = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
try {
@ -55,11 +57,7 @@ export const getConfig = async (req: Request, res: Response, next: NextFunction)
res.json(config);
} catch (error) {
logger.error('Error querying database:', error);
if (error instanceof DrizzleError) {
res.status(500).json({ error: 'Database query error', message: error.message });
} else {
next(error);
}
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
};

View file

@ -3,55 +3,64 @@ import { DrizzleError, eq } from 'drizzle-orm';
import { sites, resources, targets, exitNodes } from '@server/db/schema';
import db from '@server/db';
import logger from '@server/logger';
import createHttpError from 'http-errors';
import HttpCode from '@server/types/HttpCode';
import response from "@server/utils/response";
interface PeerBandwidth {
publicKey: string;
bytesIn: number;
bytesOut: number;
publicKey: string;
bytesIn: number;
bytesOut: number;
}
export const receiveBandwidth = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
try {
const bandwidthData: PeerBandwidth[] = req.body;
export const receiveBandwidth = async (req: Request, res: Response, next: NextFunction): Promise<any> => {
try {
const bandwidthData: PeerBandwidth[] = req.body;
if (!Array.isArray(bandwidthData)) {
throw new Error('Invalid bandwidth data');
if (!Array.isArray(bandwidthData)) {
throw new Error('Invalid bandwidth data');
}
for (const peer of bandwidthData) {
const { publicKey, bytesIn, bytesOut } = peer;
// Find the site by public key
const site = await db.query.sites.findFirst({
where: eq(sites.pubKey, publicKey),
});
if (!site) {
logger.warn(`Site not found for public key: ${publicKey}`);
continue;
}
// Update the site's bandwidth usage
await db.update(sites)
.set({
megabytesIn: (site.megabytesIn || 0) + bytesIn,
megabytesOut: (site.megabytesOut || 0) + bytesOut,
})
.where(eq(sites.siteId, site.siteId));
logger.debug(`Updated bandwidth for site: ${site.siteId}: megabytesIn: ${(site.megabytesIn || 0) + bytesIn}, megabytesOut: ${(site.megabytesOut || 0) + bytesOut}`);
}
return response(res, {
data: {},
success: true,
error: false,
message: "Organization retrieved successfully",
status: HttpCode.OK,
});
} catch (error) {
logger.error('Error updating bandwidth data:', error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
for (const peer of bandwidthData) {
const { publicKey, bytesIn, bytesOut } = peer;
// Find the site by public key
const site = await db.query.sites.findFirst({
where: eq(sites.pubKey, publicKey),
});
if (!site) {
logger.warn(`Site not found for public key: ${publicKey}`);
continue;
}
// Update the site's bandwidth usage
await db.update(sites)
.set({
megabytesIn: (site.megabytesIn || 0) + bytesIn,
megabytesOut: (site.megabytesOut || 0) + bytesOut,
})
.where(eq(sites.siteId, site.siteId));
logger.debug(`Updated bandwidth for site: ${site.siteId}: megabytesIn: ${(site.megabytesIn || 0) + bytesIn}, megabytesOut: ${(site.megabytesOut || 0) + bytesOut}`);
}
res.status(200).json({ message: 'Bandwidth data updated successfully' });
} catch (error) {
logger.error('Error updating bandwidth data:', error);
res.status(500).json({ error: 'Internal server error' });
}
};
function calculateSubnet(index: number): string {
const baseIp = 10 << 24;
const subnetSize = 16;
return `${(baseIp | (index * subnetSize)).toString()}/28`;
const baseIp = 10 << 24;
const subnetSize = 16;
return `${(baseIp | (index * subnetSize)).toString()}/28`;
}

View file

@ -6,59 +6,59 @@ import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
const createOrgSchema = z.object({
name: z.string().min(1).max(255),
domain: z.string().min(1).max(255),
name: z.string().min(1).max(255),
domain: z.string().min(1).max(255),
});
const MAX_ORGS = 5;
export async function createOrg(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
const parsedBody = createOrgSchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
)
);
try {
const parsedBody = createOrgSchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
)
);
}
const userOrgIds = req.userOrgIds;
if (userOrgIds && userOrgIds.length > MAX_ORGS) {
return next(
createHttpError(
HttpCode.FORBIDDEN,
`Maximum number of organizations reached.`
)
);
}
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.createOrg, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
const { name, domain } = parsedBody.data;
const newOrg = await db.insert(orgs).values({
name,
domain,
}).returning();
return response(res, {
data: newOrg[0],
success: true,
error: false,
message: "Organization created successfully",
status: HttpCode.CREATED,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
const userOrgIds = req.userOrgIds;
if (userOrgIds && userOrgIds.length > MAX_ORGS) {
return next(
createHttpError(
HttpCode.FORBIDDEN,
`Maximum number of organizations reached.`
)
);
}
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.createOrg, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
const { name, domain } = parsedBody.data;
const newOrg = await db.insert(orgs).values({
name,
domain,
}).returning();
return res.status(HttpCode.CREATED).send(
response(res, {
data: newOrg[0],
success: true,
error: false,
message: "Organization created successfully",
status: HttpCode.CREATED,
})
);
} catch (error) {
next(error);
}
}

View file

@ -7,54 +7,54 @@ import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
const deleteOrgSchema = z.object({
orgId: z.string().transform(Number).pipe(z.number().int().positive())
orgId: z.string().transform(Number).pipe(z.number().int().positive())
});
export async function deleteOrg(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
const parsedParams = deleteOrgSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
try {
const parsedParams = deleteOrgSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
const { orgId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.deleteOrg, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
const deletedOrg = await db.delete(orgs)
.where(eq(orgs.orgId, orgId))
.returning();
if (deletedOrg.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Organization with ID ${orgId} not found`
)
);
}
return response(res, {
data: null,
success: true,
error: false,
message: "Organization deleted successfully",
status: HttpCode.OK,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
const { orgId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.deleteOrg, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
const deletedOrg = await db.delete(orgs)
.where(eq(orgs.orgId, orgId))
.returning();
if (deletedOrg.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Organization with ID ${orgId} not found`
)
);
}
return res.status(HttpCode.OK).send(
response(res, {
data: null,
success: true,
error: false,
message: "Organization deleted successfully",
status: HttpCode.OK,
})
);
} catch (error) {
next(error);
}
}

View file

@ -7,55 +7,55 @@ import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
const getOrgSchema = z.object({
orgId: z.string().transform(Number).pipe(z.number().int().positive())
orgId: z.string().transform(Number).pipe(z.number().int().positive())
});
export async function getOrg(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
const parsedParams = getOrgSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
try {
const parsedParams = getOrgSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
const { orgId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.getOrg, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
const org = await db.select()
.from(orgs)
.where(eq(orgs.orgId, orgId))
.limit(1);
if (org.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Organization with ID ${orgId} not found`
)
);
}
return response(res, {
data: org[0],
success: true,
error: false,
message: "Organization retrieved successfully",
status: HttpCode.OK,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
const { orgId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.getOrg, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
const org = await db.select()
.from(orgs)
.where(eq(orgs.orgId, orgId))
.limit(1);
if (org.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Organization with ID ${orgId} not found`
)
);
}
return res.status(HttpCode.OK).send(
response(res, {
data: org[0],
success: true,
error: false,
message: "Organization retrieved successfully",
status: HttpCode.OK,
})
);
} catch (error) {
next(error);
}
}

View file

@ -7,88 +7,88 @@ import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { sql, inArray } from 'drizzle-orm';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
const listOrgsSchema = z.object({
limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)),
offset: z.string().optional().transform(Number).pipe(z.number().int().nonnegative().default(0)),
limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)),
offset: z.string().optional().transform(Number).pipe(z.number().int().nonnegative().default(0)),
});
export async function listOrgs(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
const parsedQuery = listOrgsSchema.safeParse(req.query);
if (!parsedQuery.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedQuery.error.errors.map(e => e.message).join(', ')
)
);
}
try {
const parsedQuery = listOrgsSchema.safeParse(req.query);
if (!parsedQuery.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedQuery.error.errors.map(e => e.message).join(', ')
)
);
}
const { limit, offset } = parsedQuery.data;
const { limit, offset } = parsedQuery.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.listOrgs, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.listOrgs, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
// Use the userOrgs passed from the middleware
const userOrgIds = req.userOrgIds;
// Use the userOrgs passed from the middleware
const userOrgIds = req.userOrgIds;
if (!userOrgIds || userOrgIds.length === 0) {
return res.status(HttpCode.OK).send(
response(res, {
data: {
organizations: [],
pagination: {
total: 0,
limit,
offset,
if (!userOrgIds || userOrgIds.length === 0) {
return res.status(HttpCode.OK).send(
response(res, {
data: {
organizations: [],
pagination: {
total: 0,
limit,
offset,
},
},
success: true,
error: false,
message: "No organizations found for the user",
status: HttpCode.OK,
})
);
}
const organizations = await db.select()
.from(orgs)
.where(inArray(orgs.orgId, userOrgIds))
.limit(limit)
.offset(offset);
const totalCountResult = await db.select({ count: sql<number>`cast(count(*) as integer)` })
.from(orgs)
.where(inArray(orgs.orgId, userOrgIds));
const totalCount = totalCountResult[0].count;
// // Add the user's role for each organization
// const organizationsWithRoles = organizations.map(org => ({
// ...org,
// userRole: req.userOrgRoleIds[org.orgId],
// }));
return response(res, {
data: {
organizations,
pagination: {
total: totalCount,
limit,
offset,
},
},
},
success: true,
error: false,
message: "No organizations found for the user",
status: HttpCode.OK,
})
);
success: true,
error: false,
message: "Organizations retrieved successfully",
status: HttpCode.OK,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
const organizations = await db.select()
.from(orgs)
.where(inArray(orgs.orgId, userOrgIds))
.limit(limit)
.offset(offset);
const totalCountResult = await db.select({ count: sql<number>`cast(count(*) as integer)` })
.from(orgs)
.where(inArray(orgs.orgId, userOrgIds));
const totalCount = totalCountResult[0].count;
// // Add the user's role for each organization
// const organizationsWithRoles = organizations.map(org => ({
// ...org,
// userRole: req.userOrgRoleIds[org.orgId],
// }));
return res.status(HttpCode.OK).send(
response(res, {
data: {
organizations,
pagination: {
total: totalCount,
limit,
offset,
},
},
success: true,
error: false,
message: "Organizations retrieved successfully",
status: HttpCode.OK,
})
);
} catch (error) {
next(error);
}
}

View file

@ -7,74 +7,74 @@ import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
const updateOrgParamsSchema = z.object({
orgId: z.string().transform(Number).pipe(z.number().int().positive())
orgId: z.string().transform(Number).pipe(z.number().int().positive())
});
const updateOrgBodySchema = z.object({
name: z.string().min(1).max(255).optional(),
domain: z.string().min(1).max(255).optional(),
name: z.string().min(1).max(255).optional(),
domain: z.string().min(1).max(255).optional(),
}).refine(data => Object.keys(data).length > 0, {
message: "At least one field must be provided for update"
message: "At least one field must be provided for update"
});
export async function updateOrg(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
const parsedParams = updateOrgParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
try {
const parsedParams = updateOrgParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
const parsedBody = updateOrgBodySchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
)
);
}
const { orgId } = parsedParams.data;
const updateData = parsedBody.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.updateOrg, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
const updatedOrg = await db.update(orgs)
.set(updateData)
.where(eq(orgs.orgId, orgId))
.returning();
if (updatedOrg.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Organization with ID ${orgId} not found`
)
);
}
return response(res, {
data: updatedOrg[0],
success: true,
error: false,
message: "Organization updated successfully",
status: HttpCode.OK,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
const parsedBody = updateOrgBodySchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
)
);
}
const { orgId } = parsedParams.data;
const updateData = parsedBody.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.updateOrg, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
const updatedOrg = await db.update(orgs)
.set(updateData)
.where(eq(orgs.orgId, orgId))
.returning();
if (updatedOrg.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Organization with ID ${orgId} not found`
)
);
}
return res.status(HttpCode.OK).send(
response(res, {
data: updatedOrg[0],
success: true,
error: false,
message: "Organization updated successfully",
status: HttpCode.OK,
})
);
} catch (error) {
next(error);
}
}

View file

@ -6,74 +6,74 @@ import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
const createResourceParamsSchema = z.object({
siteId: z.number().int().positive(),
orgId: z.number().int().positive(),
siteId: z.number().int().positive(),
orgId: z.number().int().positive(),
});
// Define Zod schema for request body validation
const createResourceSchema = z.object({
name: z.string().min(1).max(255),
subdomain: z.string().min(1).max(255).optional(),
name: z.string().min(1).max(255),
subdomain: z.string().min(1).max(255).optional(),
});
export async function createResource(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
// Validate request body
const parsedBody = createResourceSchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
)
);
try {
// Validate request body
const parsedBody = createResourceSchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
)
);
}
const { name, subdomain } = parsedBody.data;
// Validate request params
const parsedParams = createResourceParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
const { siteId, orgId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.createResource, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
// Generate a unique resourceId
const resourceId = "subdomain" // TODO: create the subdomain here
// Create new resource in the database
const newResource = await db.insert(resources).values({
resourceId,
siteId,
orgId,
name,
subdomain,
}).returning();
response(res, {
data: newResource[0],
success: true,
error: false,
message: "Resource created successfully",
status: HttpCode.CREATED,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
const { name, subdomain } = parsedBody.data;
// Validate request params
const parsedParams = createResourceParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
const { siteId, orgId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.createResource, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
// Generate a unique resourceId
const resourceId = "subdomain" // TODO: create the subdomain here
// Create new resource in the database
const newResource = await db.insert(resources).values({
resourceId,
siteId,
orgId,
name,
subdomain,
}).returning();
return res.status(HttpCode.CREATED).send(
response(res, {
data: newResource[0],
success: true,
error: false,
message: "Resource created successfully",
status: HttpCode.CREATED,
})
);
} catch (error) {
next(error);
}
}

View file

@ -7,57 +7,57 @@ import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
// Define Zod schema for request parameters validation
const deleteResourceSchema = z.object({
resourceId: z.string().uuid()
resourceId: z.string().uuid()
});
export async function deleteResource(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
// Validate request parameters
const parsedParams = deleteResourceSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
try {
// Validate request parameters
const parsedParams = deleteResourceSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
const { resourceId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.deleteResource, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
// Delete the resource from the database
const deletedResource = await db.delete(resources)
.where(eq(resources.resourceId, resourceId))
.returning();
if (deletedResource.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Resource with ID ${resourceId} not found`
)
);
}
return response(res, {
data: null,
success: true,
error: false,
message: "Resource deleted successfully",
status: HttpCode.OK,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
const { resourceId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.deleteResource, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
// Delete the resource from the database
const deletedResource = await db.delete(resources)
.where(eq(resources.resourceId, resourceId))
.returning();
if (deletedResource.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Resource with ID ${resourceId} not found`
)
);
}
return res.status(HttpCode.OK).send(
response(res, {
data: null,
success: true,
error: false,
message: "Resource deleted successfully",
status: HttpCode.OK,
})
);
} catch (error) {
next(error);
}
}

View file

@ -7,58 +7,58 @@ import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
// Define Zod schema for request parameters validation
const getResourceSchema = z.object({
resourceId: z.string().uuid()
resourceId: z.string().uuid()
});
export async function getResource(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
// Validate request parameters
const parsedParams = getResourceSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
try {
// Validate request parameters
const parsedParams = getResourceSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
const { resourceId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.getResource, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
// Fetch the resource from the database
const resource = await db.select()
.from(resources)
.where(eq(resources.resourceId, resourceId))
.limit(1);
if (resource.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Resource with ID ${resourceId} not found`
)
);
}
return response(res, {
data: resource[0],
success: true,
error: false,
message: "Resource retrieved successfully",
status: HttpCode.OK,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
const { resourceId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.getResource, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
// Fetch the resource from the database
const resource = await db.select()
.from(resources)
.where(eq(resources.resourceId, resourceId))
.limit(1);
if (resource.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Resource with ID ${resourceId} not found`
)
);
}
return res.status(HttpCode.OK).send(
response(res, {
data: resource[0],
success: true,
error: false,
message: "Resource retrieved successfully",
status: HttpCode.OK,
})
);
} catch (error) {
next(error);
}
}

View file

@ -7,6 +7,7 @@ import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { sql, eq, and, or, inArray } from 'drizzle-orm';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
const listResourcesParamsSchema = z.object({
siteId: z.coerce.number().int().positive().optional(),
@ -16,99 +17,98 @@ const listResourcesParamsSchema = z.object({
});
const listResourcesSchema = z.object({
limit: z.coerce.number().int().positive().default(10),
offset: z.coerce.number().int().nonnegative().default(0),
limit: z.coerce.number().int().positive().default(10),
offset: z.coerce.number().int().nonnegative().default(0),
});
interface RequestWithOrgAndRole extends Request {
userOrgRoleId?: number;
orgId?: number;
userOrgRoleId?: number;
orgId?: number;
}
export async function listResources(req: RequestWithOrgAndRole, res: Response, next: NextFunction): Promise<any> {
try {
const parsedQuery = listResourcesSchema.safeParse(req.query);
if (!parsedQuery.success) {
return next(createHttpError(HttpCode.BAD_REQUEST, parsedQuery.error.errors.map(e => e.message).join(', ')));
try {
const parsedQuery = listResourcesSchema.safeParse(req.query);
if (!parsedQuery.success) {
return next(createHttpError(HttpCode.BAD_REQUEST, parsedQuery.error.errors.map(e => e.message).join(', ')));
}
const { limit, offset } = parsedQuery.data;
const parsedParams = listResourcesParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(createHttpError(HttpCode.BAD_REQUEST, parsedParams.error.errors.map(e => e.message).join(', ')));
}
const { siteId, orgId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.listResources, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
if (orgId && orgId !== req.orgId) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization'));
}
// Get the list of resources the user has access to
const accessibleResources = await db
.select({ resourceId: sql<string>`COALESCE(${userResources.resourceId}, ${roleResources.resourceId})` })
.from(userResources)
.fullJoin(roleResources, eq(userResources.resourceId, roleResources.resourceId))
.where(
or(
eq(userResources.userId, req.user!.id),
eq(roleResources.roleId, req.userOrgRoleId!)
)
);
const accessibleResourceIds = accessibleResources.map(resource => resource.resourceId);
let baseQuery: any = db
.select({
resourceId: resources.resourceId,
name: resources.name,
subdomain: resources.subdomain,
siteName: sites.name,
})
.from(resources)
.leftJoin(sites, eq(resources.siteId, sites.siteId))
.where(inArray(resources.resourceId, accessibleResourceIds));
let countQuery: any = db
.select({ count: sql<number>`cast(count(*) as integer)` })
.from(resources)
.where(inArray(resources.resourceId, accessibleResourceIds));
if (siteId) {
baseQuery = baseQuery.where(eq(resources.siteId, siteId));
countQuery = countQuery.where(eq(resources.siteId, siteId));
} else {
// If orgId is provided, it's already checked to match req.orgId
baseQuery = baseQuery.where(eq(resources.orgId, req.orgId!));
countQuery = countQuery.where(eq(resources.orgId, req.orgId!));
}
const resourcesList = await baseQuery.limit(limit).offset(offset);
const totalCountResult = await countQuery;
const totalCount = totalCountResult[0].count;
return response(res, {
data: {
resources: resourcesList,
pagination: {
total: totalCount,
limit,
offset,
},
},
success: true,
error: false,
message: "Resources retrieved successfully",
status: HttpCode.OK,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
const { limit, offset } = parsedQuery.data;
const parsedParams = listResourcesParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(createHttpError(HttpCode.BAD_REQUEST, parsedParams.error.errors.map(e => e.message).join(', ')));
}
const { siteId, orgId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.listResources, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
if (orgId && orgId !== req.orgId) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization'));
}
// Get the list of resources the user has access to
const accessibleResources = await db
.select({ resourceId: sql<string>`COALESCE(${userResources.resourceId}, ${roleResources.resourceId})` })
.from(userResources)
.fullJoin(roleResources, eq(userResources.resourceId, roleResources.resourceId))
.where(
or(
eq(userResources.userId, req.user!.id),
eq(roleResources.roleId, req.userOrgRoleId!)
)
);
const accessibleResourceIds = accessibleResources.map(resource => resource.resourceId);
let baseQuery: any = db
.select({
resourceId: resources.resourceId,
name: resources.name,
subdomain: resources.subdomain,
siteName: sites.name,
})
.from(resources)
.leftJoin(sites, eq(resources.siteId, sites.siteId))
.where(inArray(resources.resourceId, accessibleResourceIds));
let countQuery: any = db
.select({ count: sql<number>`cast(count(*) as integer)` })
.from(resources)
.where(inArray(resources.resourceId, accessibleResourceIds));
if (siteId) {
baseQuery = baseQuery.where(eq(resources.siteId, siteId));
countQuery = countQuery.where(eq(resources.siteId, siteId));
} else {
// If orgId is provided, it's already checked to match req.orgId
baseQuery = baseQuery.where(eq(resources.orgId, req.orgId!));
countQuery = countQuery.where(eq(resources.orgId, req.orgId!));
}
const resourcesList = await baseQuery.limit(limit).offset(offset);
const totalCountResult = await countQuery;
const totalCount = totalCountResult[0].count;
return res.status(HttpCode.OK).send(
response(res, {
data: {
resources: resourcesList,
pagination: {
total: totalCount,
limit,
offset,
},
},
success: true,
error: false,
message: "Resources retrieved successfully",
status: HttpCode.OK,
})
);
} catch (error) {
next(error);
}
}

View file

@ -7,78 +7,78 @@ import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
// Define Zod schema for request parameters validation
const updateResourceParamsSchema = z.object({
resourceId: z.string().uuid()
resourceId: z.string().uuid()
});
// Define Zod schema for request body validation
const updateResourceBodySchema = z.object({
name: z.string().min(1).max(255).optional(),
subdomain: z.string().min(1).max(255).optional(),
name: z.string().min(1).max(255).optional(),
subdomain: z.string().min(1).max(255).optional(),
}).refine(data => Object.keys(data).length > 0, {
message: "At least one field must be provided for update"
message: "At least one field must be provided for update"
});
export async function updateResource(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
// Validate request parameters
const parsedParams = updateResourceParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
try {
// Validate request parameters
const parsedParams = updateResourceParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
// Validate request body
const parsedBody = updateResourceBodySchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
)
);
}
const { resourceId } = parsedParams.data;
const updateData = parsedBody.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.updateResource, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
// Update the resource in the database
const updatedResource = await db.update(resources)
.set(updateData)
.where(eq(resources.resourceId, resourceId))
.returning();
if (updatedResource.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Resource with ID ${resourceId} not found`
)
);
}
return response(res, {
data: updatedResource[0],
success: true,
error: false,
message: "Resource updated successfully",
status: HttpCode.OK,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
// Validate request body
const parsedBody = updateResourceBodySchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
)
);
}
const { resourceId } = parsedParams.data;
const updateData = parsedBody.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.updateResource, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
// Update the resource in the database
const updatedResource = await db.update(resources)
.set(updateData)
.where(eq(resources.resourceId, resourceId))
.returning();
if (updatedResource.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Resource with ID ${resourceId} not found`
)
);
}
return res.status(HttpCode.OK).send(
response(res, {
data: updatedResource[0],
success: true,
error: false,
message: "Resource updated successfully",
status: HttpCode.OK,
})
);
} catch (error) {
next(error);
}
}

View file

@ -7,99 +7,98 @@ import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import fetch from 'node-fetch';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
const API_BASE_URL = "http://localhost:3000";
const createSiteParamsSchema = z.object({
const createSiteParamsSchema = z.object({
orgId: z.number().int().positive(),
});
// Define Zod schema for request body validation
const createSiteSchema = z.object({
name: z.string().min(1).max(255),
subdomain: z.string().min(1).max(255).optional(),
pubKey: z.string().optional(),
subnet: z.string().optional(),
name: z.string().min(1).max(255),
subdomain: z.string().min(1).max(255).optional(),
pubKey: z.string().optional(),
subnet: z.string().optional(),
});
export async function createSite(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
// Validate request body
const parsedBody = createSiteSchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
)
);
try {
// Validate request body
const parsedBody = createSiteSchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
)
);
}
const { name, subdomain, pubKey, subnet } = parsedBody.data;
// Validate request params
const parsedParams = createSiteParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
const { orgId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.createSite, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
// Create new site in the database
const newSite = await db.insert(sites).values({
orgId,
name,
subdomain,
pubKey,
subnet,
}).returning();
return response(res, {
data: newSite[0],
success: true,
error: false,
message: "Site created successfully",
status: HttpCode.CREATED,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
const { name, subdomain, pubKey, subnet } = parsedBody.data;
// Validate request params
const parsedParams = createSiteParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
const { orgId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.createSite, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
// Create new site in the database
const newSite = await db.insert(sites).values({
orgId,
name,
subdomain,
pubKey,
subnet,
}).returning();
return res.status(HttpCode.CREATED).send(
response(res, {
data: newSite[0],
success: true,
error: false,
message: "Site created successfully",
status: HttpCode.CREATED,
})
);
} catch (error) {
next(error);
}
}
async function addPeer(peer: string) {
try {
const response = await fetch(`${API_BASE_URL}/peer`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(peer),
});
try {
const response = await fetch(`${API_BASE_URL}/peer`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(peer),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data: any = await response.json();
logger.info('Peer added successfully:', data.status);
return data;
} catch (error: any) {
throw error;
}
const data: any = await response.json();
console.log('Peer added successfully:', data.status);
return data;
} catch (error: any) {
console.error('Error adding peer:', error.message);
throw error;
}
}

View file

@ -7,80 +7,80 @@ import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
const API_BASE_URL = "http://localhost:3000";
// Define Zod schema for request parameters validation
const deleteSiteSchema = z.object({
siteId: z.string().transform(Number).pipe(z.number().int().positive())
siteId: z.string().transform(Number).pipe(z.number().int().positive())
});
export async function deleteSite(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
// Validate request parameters
const parsedParams = deleteSiteSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
try {
// Validate request parameters
const parsedParams = deleteSiteSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
const { siteId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.deleteSite, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
// Delete the site from the database
const deletedSite = await db.delete(sites)
.where(eq(sites.siteId, siteId))
.returning();
if (deletedSite.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Site with ID ${siteId} not found`
)
);
}
return response(res, {
data: null,
success: true,
error: false,
message: "Site deleted successfully",
status: HttpCode.OK,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
const { siteId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.deleteSite, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
// Delete the site from the database
const deletedSite = await db.delete(sites)
.where(eq(sites.siteId, siteId))
.returning();
if (deletedSite.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Site with ID ${siteId} not found`
)
);
}
return res.status(HttpCode.OK).send(
response(res, {
data: null,
success: true,
error: false,
message: "Site deleted successfully",
status: HttpCode.OK,
})
);
} catch (error) {
next(error);
}
}
async function removePeer(publicKey: string) {
try {
const response = await fetch(`${API_BASE_URL}/peer?public_key=${encodeURIComponent(publicKey)}`, {
method: 'DELETE',
});
try {
const response = await fetch(`${API_BASE_URL}/peer?public_key=${encodeURIComponent(publicKey)}`, {
method: 'DELETE',
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log('Peer removed successfully:', data.status);
return data;
} catch (error: any) {
console.error('Error removing peer:', error.message);
throw error;
}
const data = await response.json();
console.log('Peer removed successfully:', data.status);
return data;
} catch (error: any) {
console.error('Error removing peer:', error.message);
throw error;
}
}

View file

@ -7,58 +7,58 @@ import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
// Define Zod schema for request parameters validation
const getSiteSchema = z.object({
siteId: z.string().transform(Number).pipe(z.number().int().positive())
siteId: z.string().transform(Number).pipe(z.number().int().positive())
});
export async function getSite(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
// Validate request parameters
const parsedParams = getSiteSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
try {
// Validate request parameters
const parsedParams = getSiteSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
const { siteId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.updateSite, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
// Fetch the site from the database
const site = await db.select()
.from(sites)
.where(eq(sites.siteId, siteId))
.limit(1);
if (site.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Site with ID ${siteId} not found`
)
);
}
return response(res, {
data: site[0],
success: true,
error: false,
message: "Site retrieved successfully",
status: HttpCode.OK,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
const { siteId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.updateSite, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
// Fetch the site from the database
const site = await db.select()
.from(sites)
.where(eq(sites.siteId, siteId))
.limit(1);
if (site.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Site with ID ${siteId} not found`
)
);
}
return res.status(HttpCode.OK).send(
response(res, {
data: site[0],
success: true,
error: false,
message: "Site retrieved successfully",
status: HttpCode.OK,
})
);
} catch (error) {
next(error);
}
}

View file

@ -7,101 +7,101 @@ import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { sql, eq, and, or, inArray } from 'drizzle-orm';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
const listSitesParamsSchema = z.object({
orgId: z.string().optional().transform(Number).pipe(z.number().int().positive()),
});
const listSitesSchema = z.object({
limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)),
offset: z.string().optional().transform(Number).pipe(z.number().int().nonnegative().default(0)),
limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)),
offset: z.string().optional().transform(Number).pipe(z.number().int().nonnegative().default(0)),
});
export async function listSites(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
const parsedQuery = listSitesSchema.safeParse(req.query);
if (!parsedQuery.success) {
return next(createHttpError(HttpCode.BAD_REQUEST, parsedQuery.error.errors.map(e => e.message).join(', ')));
try {
const parsedQuery = listSitesSchema.safeParse(req.query);
if (!parsedQuery.success) {
return next(createHttpError(HttpCode.BAD_REQUEST, parsedQuery.error.errors.map(e => e.message).join(', ')));
}
const { limit, offset } = parsedQuery.data;
const parsedParams = listSitesParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(createHttpError(HttpCode.BAD_REQUEST, parsedParams.error.errors.map(e => e.message).join(', ')));
}
const { orgId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.listSites, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
if (orgId && orgId !== req.userOrgId) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization'));
}
const accessibleSites = await db
.select({ siteId: sql<number>`COALESCE(${userSites.siteId}, ${roleSites.siteId})` })
.from(userSites)
.fullJoin(roleSites, eq(userSites.siteId, roleSites.siteId))
.where(
or(
eq(userSites.userId, req.user!.id),
eq(roleSites.roleId, req.userOrgRoleId!)
)
);
const accessibleSiteIds = accessibleSites.map(site => site.siteId);
let baseQuery: any = db
.select({
siteId: sites.siteId,
name: sites.name,
subdomain: sites.subdomain,
pubKey: sites.pubKey,
subnet: sites.subnet,
megabytesIn: sites.megabytesIn,
megabytesOut: sites.megabytesOut,
orgName: orgs.name,
exitNodeName: exitNodes.name,
})
.from(sites)
.leftJoin(orgs, eq(sites.orgId, orgs.orgId))
.where(inArray(sites.siteId, accessibleSiteIds));
let countQuery: any = db
.select({ count: sql<number>`cast(count(*) as integer)` })
.from(sites)
.where(inArray(sites.siteId, accessibleSiteIds));
if (orgId) {
baseQuery = baseQuery.where(eq(sites.orgId, orgId));
countQuery = countQuery.where(eq(sites.orgId, orgId));
}
const sitesList = await baseQuery.limit(limit).offset(offset);
const totalCountResult = await countQuery;
const totalCount = totalCountResult[0].count;
return response(res, {
data: {
sites: sitesList,
pagination: {
total: totalCount,
limit,
offset,
},
},
success: true,
error: false,
message: "Sites retrieved successfully",
status: HttpCode.OK,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
const { limit, offset } = parsedQuery.data;
const parsedParams = listSitesParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(createHttpError(HttpCode.BAD_REQUEST, parsedParams.error.errors.map(e => e.message).join(', ')));
}
const { orgId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.listSites, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
if (orgId && orgId !== req.userOrgId) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization'));
}
const accessibleSites = await db
.select({ siteId: sql<number>`COALESCE(${userSites.siteId}, ${roleSites.siteId})` })
.from(userSites)
.fullJoin(roleSites, eq(userSites.siteId, roleSites.siteId))
.where(
or(
eq(userSites.userId, req.user!.id),
eq(roleSites.roleId, req.userOrgRoleId!)
)
);
const accessibleSiteIds = accessibleSites.map(site => site.siteId);
let baseQuery: any = db
.select({
siteId: sites.siteId,
name: sites.name,
subdomain: sites.subdomain,
pubKey: sites.pubKey,
subnet: sites.subnet,
megabytesIn: sites.megabytesIn,
megabytesOut: sites.megabytesOut,
orgName: orgs.name,
exitNodeName: exitNodes.name,
})
.from(sites)
.leftJoin(orgs, eq(sites.orgId, orgs.orgId))
.where(inArray(sites.siteId, accessibleSiteIds));
let countQuery: any = db
.select({ count: sql<number>`cast(count(*) as integer)` })
.from(sites)
.where(inArray(sites.siteId, accessibleSiteIds));
if (orgId) {
baseQuery = baseQuery.where(eq(sites.orgId, orgId));
countQuery = countQuery.where(eq(sites.orgId, orgId));
}
const sitesList = await baseQuery.limit(limit).offset(offset);
const totalCountResult = await countQuery;
const totalCount = totalCountResult[0].count;
return res.status(HttpCode.OK).send(
response(res, {
data: {
sites: sitesList,
pagination: {
total: totalCount,
limit,
offset,
},
},
success: true,
error: false,
message: "Sites retrieved successfully",
status: HttpCode.OK,
})
);
} catch (error) {
next(error);
}
}

View file

@ -7,83 +7,83 @@ import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
// Define Zod schema for request parameters validation
const updateSiteParamsSchema = z.object({
siteId: z.string().transform(Number).pipe(z.number().int().positive())
siteId: z.string().transform(Number).pipe(z.number().int().positive())
});
// Define Zod schema for request body validation
const updateSiteBodySchema = z.object({
name: z.string().min(1).max(255).optional(),
subdomain: z.string().min(1).max(255).optional(),
pubKey: z.string().optional(),
subnet: z.string().optional(),
exitNode: z.number().int().positive().optional(),
megabytesIn: z.number().int().nonnegative().optional(),
megabytesOut: z.number().int().nonnegative().optional(),
name: z.string().min(1).max(255).optional(),
subdomain: z.string().min(1).max(255).optional(),
pubKey: z.string().optional(),
subnet: z.string().optional(),
exitNode: z.number().int().positive().optional(),
megabytesIn: z.number().int().nonnegative().optional(),
megabytesOut: z.number().int().nonnegative().optional(),
}).refine(data => Object.keys(data).length > 0, {
message: "At least one field must be provided for update"
message: "At least one field must be provided for update"
});
export async function updateSite(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
// Validate request parameters
const parsedParams = updateSiteParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
try {
// Validate request parameters
const parsedParams = updateSiteParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
// Validate request body
const parsedBody = updateSiteBodySchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
)
);
}
const { siteId } = parsedParams.data;
const updateData = parsedBody.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.updateSite, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
// Update the site in the database
const updatedSite = await db.update(sites)
.set(updateData)
.where(eq(sites.siteId, siteId))
.returning();
if (updatedSite.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Site with ID ${siteId} not found`
)
);
}
return response(res, {
data: updatedSite[0],
success: true,
error: false,
message: "Site updated successfully",
status: HttpCode.OK,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
// Validate request body
const parsedBody = updateSiteBodySchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
)
);
}
const { siteId } = parsedParams.data;
const updateData = parsedBody.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.updateSite, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
// Update the site in the database
const updatedSite = await db.update(sites)
.set(updateData)
.where(eq(sites.siteId, siteId))
.returning();
if (updatedSite.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Site with ID ${siteId} not found`
)
);
}
return res.status(HttpCode.OK).send(
response(res, {
data: updatedSite[0],
success: true,
error: false,
message: "Site updated successfully",
status: HttpCode.OK,
})
);
} catch (error) {
next(error);
}
}

View file

@ -6,66 +6,66 @@ import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
const createTargetParamsSchema = z.object({
resourceId: z.string().uuid(),
resourceId: z.string().uuid(),
});
const createTargetSchema = z.object({
ip: z.string().ip(),
method: z.string().min(1).max(10),
port: z.number().int().min(1).max(65535),
protocol: z.string().optional(),
enabled: z.boolean().default(true),
ip: z.string().ip(),
method: z.string().min(1).max(10),
port: z.number().int().min(1).max(65535),
protocol: z.string().optional(),
enabled: z.boolean().default(true),
});
export async function createTarget(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
const parsedBody = createTargetSchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
)
);
try {
const parsedBody = createTargetSchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
)
);
}
const targetData = parsedBody.data;
const parsedParams = createTargetParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
const { resourceId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.createTarget, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
const newTarget = await db.insert(targets).values({
resourceId,
...targetData
}).returning();
return response(res, {
data: newTarget[0],
success: true,
error: false,
message: "Target created successfully",
status: HttpCode.CREATED,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
const targetData = parsedBody.data;
const parsedParams = createTargetParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
const { resourceId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.createTarget, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
const newTarget = await db.insert(targets).values({
resourceId,
...targetData
}).returning();
return res.status(HttpCode.CREATED).send(
response(res, {
data: newTarget[0],
success: true,
error: false,
message: "Target created successfully",
status: HttpCode.CREATED,
})
);
} catch (error) {
next(error);
}
}

View file

@ -7,54 +7,54 @@ import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
const deleteTargetSchema = z.object({
targetId: z.string().transform(Number).pipe(z.number().int().positive())
targetId: z.string().transform(Number).pipe(z.number().int().positive())
});
export async function deleteTarget(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
const parsedParams = deleteTargetSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
try {
const parsedParams = deleteTargetSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
const { targetId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.deleteTarget, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
const deletedTarget = await db.delete(targets)
.where(eq(targets.targetId, targetId))
.returning();
if (deletedTarget.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Target with ID ${targetId} not found`
)
);
}
return response(res, {
data: null,
success: true,
error: false,
message: "Target deleted successfully",
status: HttpCode.OK,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
const { targetId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.deleteTarget, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
const deletedTarget = await db.delete(targets)
.where(eq(targets.targetId, targetId))
.returning();
if (deletedTarget.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Target with ID ${targetId} not found`
)
);
}
return res.status(HttpCode.OK).send(
response(res, {
data: null,
success: true,
error: false,
message: "Target deleted successfully",
status: HttpCode.OK,
})
);
} catch (error) {
next(error);
}
}

View file

@ -7,55 +7,55 @@ import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
const getTargetSchema = z.object({
targetId: z.string().transform(Number).pipe(z.number().int().positive())
targetId: z.string().transform(Number).pipe(z.number().int().positive())
});
export async function getTarget(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
const parsedParams = getTargetSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
try {
const parsedParams = getTargetSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
const { targetId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.getTarget, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
const target = await db.select()
.from(targets)
.where(eq(targets.targetId, targetId))
.limit(1);
if (target.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Target with ID ${targetId} not found`
)
);
}
return response(res, {
data: target[0],
success: true,
error: false,
message: "Target retrieved successfully",
status: HttpCode.OK,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
const { targetId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.getTarget, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
const target = await db.select()
.from(targets)
.where(eq(targets.targetId, targetId))
.limit(1);
if (target.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Target with ID ${targetId} not found`
)
);
}
return res.status(HttpCode.OK).send(
response(res, {
data: target[0],
success: true,
error: false,
message: "Target retrieved successfully",
status: HttpCode.OK,
})
);
} catch (error) {
next(error);
}
}

View file

@ -7,89 +7,89 @@ import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { sql, eq } from 'drizzle-orm';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
const listTargetsParamsSchema = z.object({
resourceId: z.string().optional()
});
const listTargetsSchema = z.object({
limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)),
offset: z.string().optional().transform(Number).pipe(z.number().int().nonnegative().default(0)),
limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)),
offset: z.string().optional().transform(Number).pipe(z.number().int().nonnegative().default(0)),
});
export async function listTargets(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
const parsedQuery = listTargetsSchema.safeParse(req.query);
if (!parsedQuery.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedQuery.error.errors.map(e => e.message).join(', ')
)
);
try {
const parsedQuery = listTargetsSchema.safeParse(req.query);
if (!parsedQuery.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedQuery.error.errors.map(e => e.message).join(', ')
)
);
}
const { limit, offset } = parsedQuery.data;
const parsedParams = listTargetsParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
const { resourceId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.listTargets, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
let baseQuery: any = db
.select({
targetId: targets.targetId,
ip: targets.ip,
method: targets.method,
port: targets.port,
protocol: targets.protocol,
enabled: targets.enabled,
resourceName: resources.name,
})
.from(targets)
.leftJoin(resources, eq(targets.resourceId, resources.resourceId));
let countQuery: any = db.select({ count: sql<number>`cast(count(*) as integer)` }).from(targets);
if (resourceId) {
baseQuery = baseQuery.where(eq(targets.resourceId, resourceId));
countQuery = countQuery.where(eq(targets.resourceId, resourceId));
}
const targetsList = await baseQuery.limit(limit).offset(offset);
const totalCountResult = await countQuery;
const totalCount = totalCountResult[0].count;
return response(res, {
data: {
targets: targetsList,
pagination: {
total: totalCount,
limit,
offset,
},
},
success: true,
error: false,
message: "Targets retrieved successfully",
status: HttpCode.OK,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
const { limit, offset } = parsedQuery.data;
const parsedParams = listTargetsParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
const { resourceId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.listTargets, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
let baseQuery: any = db
.select({
targetId: targets.targetId,
ip: targets.ip,
method: targets.method,
port: targets.port,
protocol: targets.protocol,
enabled: targets.enabled,
resourceName: resources.name,
})
.from(targets)
.leftJoin(resources, eq(targets.resourceId, resources.resourceId));
let countQuery: any = db.select({ count: sql<number>`cast(count(*) as integer)` }).from(targets);
if (resourceId) {
baseQuery = baseQuery.where(eq(targets.resourceId, resourceId));
countQuery = countQuery.where(eq(targets.resourceId, resourceId));
}
const targetsList = await baseQuery.limit(limit).offset(offset);
const totalCountResult = await countQuery;
const totalCount = totalCountResult[0].count;
return response(res, {
data: {
targets: targetsList,
pagination: {
total: totalCount,
limit,
offset,
},
},
success: true,
error: false,
message: "Targets retrieved successfully",
status: HttpCode.OK,
})
} catch (error) {
console.log(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "sadfdf"));
}
}

View file

@ -7,76 +7,76 @@ import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
const updateTargetParamsSchema = z.object({
targetId: z.string().transform(Number).pipe(z.number().int().positive())
targetId: z.string().transform(Number).pipe(z.number().int().positive())
});
const updateTargetBodySchema = z.object({
ip: z.string().ip().optional(),
method: z.string().min(1).max(10).optional(),
port: z.number().int().min(1).max(65535).optional(),
protocol: z.string().optional(),
enabled: z.boolean().optional(),
ip: z.string().ip().optional(),
method: z.string().min(1).max(10).optional(),
port: z.number().int().min(1).max(65535).optional(),
protocol: z.string().optional(),
enabled: z.boolean().optional(),
}).refine(data => Object.keys(data).length > 0, {
message: "At least one field must be provided for update"
message: "At least one field must be provided for update"
});
export async function updateTarget(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
const parsedParams = updateTargetParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
try {
const parsedParams = updateTargetParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
const parsedBody = updateTargetBodySchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
)
);
}
const { targetId } = parsedParams.data;
const updateData = parsedBody.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.updateTarget, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
const updatedTarget = await db.update(targets)
.set(updateData)
.where(eq(targets.targetId, targetId))
.returning();
if (updatedTarget.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Target with ID ${targetId} not found`
)
);
}
return response(res, {
data: updatedTarget[0],
success: true,
error: false,
message: "Target updated successfully",
status: HttpCode.OK,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
const parsedBody = updateTargetBodySchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedBody.error.errors.map(e => e.message).join(', ')
)
);
}
const { targetId } = parsedParams.data;
const updateData = parsedBody.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.updateTarget, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
const updatedTarget = await db.update(targets)
.set(updateData)
.where(eq(targets.targetId, targetId))
.returning();
if (updatedTarget.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Target with ID ${targetId} not found`
)
);
}
return res.status(HttpCode.OK).send(
response(res, {
data: updatedTarget[0],
success: true,
error: false,
message: "Target updated successfully",
status: HttpCode.OK,
})
);
} catch (error) {
next(error);
}
}

View file

@ -7,54 +7,54 @@ import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
const deleteUserSchema = z.object({
userId: z.string().uuid()
userId: z.string().uuid()
});
export async function deleteUser(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
const parsedParams = deleteUserSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
try {
const parsedParams = deleteUserSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
const { userId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.deleteUser, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
const deletedUser = await db.delete(users)
.where(eq(users.id, userId))
.returning();
if (deletedUser.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`User with ID ${userId} not found`
)
);
}
return response(res, {
data: null,
success: true,
error: false,
message: "User deleted successfully",
status: HttpCode.OK,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
const { userId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.deleteUser, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
const deletedUser = await db.delete(users)
.where(eq(users.id, userId))
.returning();
if (deletedUser.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`User with ID ${userId} not found`
)
);
}
return res.status(HttpCode.OK).send(
response(res, {
data: null,
success: true,
error: false,
message: "User deleted successfully",
status: HttpCode.OK,
})
);
} catch (error) {
next(error);
}
}

View file

@ -7,58 +7,58 @@ import response from "@server/utils/response";
import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
const getUserSchema = z.object({
userId: z.string().uuid()
userId: z.string().uuid()
});
export async function getUser(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
const parsedParams = getUserSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
try {
const parsedParams = getUserSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map(e => e.message).join(', ')
)
);
}
const { userId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.getUser, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
const user = await db.select()
.from(users)
.where(eq(users.id, userId))
.limit(1);
if (user.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`User with ID ${userId} not found`
)
);
}
// Remove passwordHash from the response
const { passwordHash: _, ...userWithoutPassword } = user[0];
return response(res, {
data: userWithoutPassword,
success: true,
error: false,
message: "User retrieved successfully",
status: HttpCode.OK,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
const { userId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.getUser, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
const user = await db.select()
.from(users)
.where(eq(users.id, userId))
.limit(1);
if (user.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`User with ID ${userId} not found`
)
);
}
// Remove passwordHash from the response
const { passwordHash: _, ...userWithoutPassword } = user[0];
return res.status(HttpCode.OK).send(
response(res, {
data: userWithoutPassword,
success: true,
error: false,
message: "User retrieved successfully",
status: HttpCode.OK,
})
);
} catch (error) {
next(error);
}
}

View file

@ -7,62 +7,62 @@ import HttpCode from '@server/types/HttpCode';
import createHttpError from 'http-errors';
import { sql } from 'drizzle-orm';
import { ActionsEnum, checkUserActionPermission } from '@server/auth/actions';
import logger from '@server/logger';
const listUsersSchema = z.object({
limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)),
offset: z.string().optional().transform(Number).pipe(z.number().int().nonnegative().default(0)),
limit: z.string().optional().transform(Number).pipe(z.number().int().positive().default(10)),
offset: z.string().optional().transform(Number).pipe(z.number().int().nonnegative().default(0)),
});
export async function listUsers(req: Request, res: Response, next: NextFunction): Promise<any> {
try {
const parsedQuery = listUsersSchema.safeParse(req.query);
if (!parsedQuery.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedQuery.error.errors.map(e => e.message).join(', ')
)
);
try {
const parsedQuery = listUsersSchema.safeParse(req.query);
if (!parsedQuery.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedQuery.error.errors.map(e => e.message).join(', ')
)
);
}
const { limit, offset } = parsedQuery.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.listUsers, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
const usersList = await db.select()
.from(users)
.limit(limit)
.offset(offset);
const totalCountResult = await db
.select({ count: sql<number>`cast(count(*) as integer)` })
.from(users);
const totalCount = totalCountResult[0].count;
// Remove passwordHash from each user object
const usersWithoutPassword = usersList.map(({ passwordHash, ...userWithoutPassword }) => userWithoutPassword);
return response(res, {
data: {
users: usersWithoutPassword,
pagination: {
total: totalCount,
limit,
offset,
},
},
success: true,
error: false,
message: "Users retrieved successfully",
status: HttpCode.OK,
});
} catch (error) {
logger.error(error);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred..."));
}
const { limit, offset } = parsedQuery.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(ActionsEnum.listUsers, req);
if (!hasPermission) {
return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to list sites'));
}
const usersList = await db.select()
.from(users)
.limit(limit)
.offset(offset);
const totalCountResult = await db
.select({ count: sql<number>`cast(count(*) as integer)` })
.from(users);
const totalCount = totalCountResult[0].count;
// Remove passwordHash from each user object
const usersWithoutPassword = usersList.map(({ passwordHash, ...userWithoutPassword }) => userWithoutPassword);
return res.status(HttpCode.OK).send(
response(res, {
data: {
users: usersWithoutPassword,
pagination: {
total: totalCount,
limit,
offset,
},
},
success: true,
error: false,
message: "Users retrieved successfully",
status: HttpCode.OK,
})
);
} catch (error) {
next(error);
}
}

View file

@ -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",
);
});
}

View file

@ -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;
}
}
}

View file

@ -4,55 +4,55 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const alertVariants = cva(
"relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
{
variants: {
variant: {
default: "bg-background text-foreground",
destructive:
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
},
},
defaultVariants: {
variant: "default",
},
}
"relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
{
variants: {
variant: {
default: "bg-background text-foreground",
destructive:
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
},
},
defaultVariants: {
variant: "default",
},
}
)
const Alert = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
>(({ className, variant, ...props }, ref) => (
<div
ref={ref}
role="alert"
className={cn(alertVariants({ variant }), className)}
{...props}
/>
<div
ref={ref}
role="alert"
className={cn(alertVariants({ variant }), className)}
{...props}
/>
))
Alert.displayName = "Alert"
const AlertTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h5
ref={ref}
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
{...props}
/>
<h5
ref={ref}
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
{...props}
/>
))
AlertTitle.displayName = "AlertTitle"
const AlertDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("text-sm [&_p]:leading-relaxed", className)}
{...props}
/>
<div
ref={ref}
className={cn("text-sm [&_p]:leading-relaxed", className)}
{...props}
/>
))
AlertDescription.displayName = "AlertDescription"

View file

@ -5,51 +5,51 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"

View file

@ -3,76 +3,76 @@ import * as React from "react"
import { cn } from "@/lib/utils"
const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
"rounded-lg border bg-card text-card-foreground shadow-sm",
className
)}
{...props}
/>
<div
ref={ref}
className={cn(
"rounded-lg border bg-card text-card-foreground shadow-sm",
className
)}
{...props}
/>
))
Card.displayName = "Card"
const CardHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
<div
ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
))
CardHeader.displayName = "CardHeader"
const CardTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h3
ref={ref}
className={cn(
"text-2xl font-semibold leading-none tracking-tight",
className
)}
{...props}
/>
<h3
ref={ref}
className={cn(
"text-2xl font-semibold leading-none tracking-tight",
className
)}
{...props}
/>
))
CardTitle.displayName = "CardTitle"
const CardDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<p
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
<p
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
CardDescription.displayName = "CardDescription"
const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
))
CardContent.displayName = "CardContent"
const CardFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
<div
ref={ref}
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
))
CardFooter.displayName = "CardFooter"

View file

@ -4,12 +4,12 @@ import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { Slot } from "@radix-ui/react-slot"
import {
Controller,
ControllerProps,
FieldPath,
FieldValues,
FormProvider,
useFormContext,
Controller,
ControllerProps,
FieldPath,
FieldValues,
FormProvider,
useFormContext,
} from "react-hook-form"
import { cn } from "@/lib/utils"
@ -18,161 +18,161 @@ import { Label } from "@/components/ui/label"
const Form = FormProvider
type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
> = {
name: TName
name: TName
}
const FormFieldContext = React.createContext<FormFieldContextValue>(
{} as FormFieldContextValue
{} as FormFieldContextValue
)
const FormField = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
>({
...props
...props
}: ControllerProps<TFieldValues, TName>) => {
return (
<FormFieldContext.Provider value={{ name: props.name }}>
<Controller {...props} />
</FormFieldContext.Provider>
)
return (
<FormFieldContext.Provider value={{ name: props.name }}>
<Controller {...props} />
</FormFieldContext.Provider>
)
}
const useFormField = () => {
const fieldContext = React.useContext(FormFieldContext)
const itemContext = React.useContext(FormItemContext)
const { getFieldState, formState } = useFormContext()
const fieldContext = React.useContext(FormFieldContext)
const itemContext = React.useContext(FormItemContext)
const { getFieldState, formState } = useFormContext()
const fieldState = getFieldState(fieldContext.name, formState)
const fieldState = getFieldState(fieldContext.name, formState)
if (!fieldContext) {
throw new Error("useFormField should be used within <FormField>")
}
if (!fieldContext) {
throw new Error("useFormField should be used within <FormField>")
}
const { id } = itemContext
const { id } = itemContext
return {
id,
name: fieldContext.name,
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState,
}
return {
id,
name: fieldContext.name,
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState,
}
}
type FormItemContextValue = {
id: string
id: string
}
const FormItemContext = React.createContext<FormItemContextValue>(
{} as FormItemContextValue
{} as FormItemContextValue
)
const FormItem = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
const id = React.useId()
const id = React.useId()
return (
<FormItemContext.Provider value={{ id }}>
<div ref={ref} className={cn("space-y-2", className)} {...props} />
</FormItemContext.Provider>
)
return (
<FormItemContext.Provider value={{ id }}>
<div ref={ref} className={cn("space-y-2", className)} {...props} />
</FormItemContext.Provider>
)
})
FormItem.displayName = "FormItem"
const FormLabel = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
>(({ className, ...props }, ref) => {
const { error, formItemId } = useFormField()
const { error, formItemId } = useFormField()
return (
<Label
ref={ref}
className={cn(error && "text-destructive", className)}
htmlFor={formItemId}
{...props}
/>
)
return (
<Label
ref={ref}
className={cn(error && "text-destructive", className)}
htmlFor={formItemId}
{...props}
/>
)
})
FormLabel.displayName = "FormLabel"
const FormControl = React.forwardRef<
React.ElementRef<typeof Slot>,
React.ComponentPropsWithoutRef<typeof Slot>
React.ElementRef<typeof Slot>,
React.ComponentPropsWithoutRef<typeof Slot>
>(({ ...props }, ref) => {
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
return (
<Slot
ref={ref}
id={formItemId}
aria-describedby={
!error
? `${formDescriptionId}`
: `${formDescriptionId} ${formMessageId}`
}
aria-invalid={!!error}
{...props}
/>
)
return (
<Slot
ref={ref}
id={formItemId}
aria-describedby={
!error
? `${formDescriptionId}`
: `${formDescriptionId} ${formMessageId}`
}
aria-invalid={!!error}
{...props}
/>
)
})
FormControl.displayName = "FormControl"
const FormDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => {
const { formDescriptionId } = useFormField()
const { formDescriptionId } = useFormField()
return (
<p
ref={ref}
id={formDescriptionId}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
)
return (
<p
ref={ref}
id={formDescriptionId}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
)
})
FormDescription.displayName = "FormDescription"
const FormMessage = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField()
const body = error ? String(error?.message) : children
const { error, formMessageId } = useFormField()
const body = error ? String(error?.message) : children
if (!body) {
return null
}
if (!body) {
return null
}
return (
<p
ref={ref}
id={formMessageId}
className={cn("text-sm font-medium text-destructive", className)}
{...props}
>
{body}
</p>
)
return (
<p
ref={ref}
id={formMessageId}
className={cn("text-sm font-medium text-destructive", className)}
{...props}
>
{body}
</p>
)
})
FormMessage.displayName = "FormMessage"
export {
useFormField,
Form,
FormItem,
FormLabel,
FormControl,
FormDescription,
FormMessage,
FormField,
useFormField,
Form,
FormItem,
FormLabel,
FormControl,
FormDescription,
FormMessage,
FormField,
}

View file

@ -5,19 +5,19 @@ import { cn } from "@/lib/utils"
export type InputProps = React.InputHTMLAttributes<HTMLInputElement>;
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
}
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
}
)
Input.displayName = "Input"

View file

@ -7,19 +7,19 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
)
const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
<LabelPrimitive.Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
))
Label.displayName = LabelPrimitive.Root.displayName

View file

@ -2,5 +2,5 @@ import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
return twMerge(clsx(inputs))
}

View file

@ -8,55 +8,55 @@ const config: Config = {
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
colors: {
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))'
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))'
},
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))'
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))'
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))'
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))'
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))'
},
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
chart: {
'1': 'hsl(var(--chart-1))',
'2': 'hsl(var(--chart-2))',
'3': 'hsl(var(--chart-3))',
'4': 'hsl(var(--chart-4))',
'5': 'hsl(var(--chart-5))'
}
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)'
}
}
extend: {
colors: {
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))'
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))'
},
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))'
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))'
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))'
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))'
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))'
},
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
chart: {
'1': 'hsl(var(--chart-1))',
'2': 'hsl(var(--chart-2))',
'3': 'hsl(var(--chart-3))',
'4': 'hsl(var(--chart-4))',
'5': 'hsl(var(--chart-5))'
}
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)'
}
}
},
plugins: [require("tailwindcss-animate")],
};

View file

@ -1,6 +1,10 @@
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@ -14,9 +18,15 @@
"incremental": true,
"baseUrl": "src",
"paths": {
"@server/*": ["../server/*"],
"@app/*": ["*"],
"@/*": ["./*"]
"@server/*": [
"../server/*"
],
"@app/*": [
"*"
],
"@/*": [
"./*"
]
},
"plugins": [
{
@ -24,6 +34,13 @@
}
]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}