소스 검색

fix issues from test deploy

Milo Schwartz 6 달 전
부모
커밋
ce5df3b0b9
92개의 변경된 파일1419개의 추가작업 그리고 1028개의 파일을 삭제
  1. 4 3
      config.example.yml
  2. 3 3
      server/apiServer.ts
  3. 0 0
      server/auth/sendEmailVerificationCode.ts
  4. 22 14
      server/config.ts
  5. 5 3
      server/routers/accessToken/deleteAccessToken.ts
  6. 57 30
      server/routers/accessToken/generateAccessToken.ts
  7. 9 2
      server/routers/accessToken/listAccessTokens.ts
  8. 3 1
      server/routers/auth/changePassword.ts
  9. 3 1
      server/routers/auth/checkResourceSession.ts
  10. 3 1
      server/routers/auth/disable2fa.ts
  11. 2 1
      server/routers/auth/login.ts
  12. 7 10
      server/routers/auth/logout.ts
  13. 5 3
      server/routers/auth/requestEmailVerificationCode.ts
  14. 2 1
      server/routers/auth/requestPasswordReset.ts
  15. 18 14
      server/routers/auth/requestTotpSecret.ts
  16. 24 20
      server/routers/auth/resetPassword.ts
  17. 3 1
      server/routers/auth/signup.ts
  18. 10 6
      server/routers/auth/verifyEmail.ts
  19. 10 6
      server/routers/auth/verifyTotp.ts
  20. 30 33
      server/routers/badger/verifySession.ts
  21. 7 2
      server/routers/external.ts
  22. 7 5
      server/routers/org/checkId.ts
  23. 6 4
      server/routers/org/deleteOrg.ts
  24. 7 5
      server/routers/org/getOrg.ts
  25. 8 6
      server/routers/org/updateOrg.ts
  26. 23 20
      server/routers/resource/authWithAccessToken.ts
  27. 18 12
      server/routers/resource/authWithPassword.ts
  28. 15 11
      server/routers/resource/authWithPincode.ts
  29. 16 12
      server/routers/resource/authWithWhitelist.ts
  30. 27 10
      server/routers/resource/createResource.ts
  31. 13 8
      server/routers/resource/deleteResource.ts
  32. 11 4
      server/routers/resource/getResource.ts
  33. 19 15
      server/routers/resource/getResourceAuthInfo.ts
  34. 8 3
      server/routers/resource/getResourceWhitelist.ts
  35. 11 6
      server/routers/resource/listResourceRoles.ts
  36. 11 6
      server/routers/resource/listResourceUsers.ts
  37. 30 32
      server/routers/resource/listResources.ts
  38. 6 4
      server/routers/resource/setResourcePassword.ts
  39. 2 0
      server/routers/resource/setResourcePincode.ts
  40. 14 7
      server/routers/resource/setResourceRoles.ts
  41. 14 7
      server/routers/resource/setResourceUsers.ts
  42. 13 6
      server/routers/resource/setResourceWhitelist.ts
  43. 24 22
      server/routers/resource/updateResource.ts
  44. 12 8
      server/routers/role/addRoleAction.ts
  45. 13 9
      server/routers/role/addRoleSite.ts
  46. 11 9
      server/routers/role/createRole.ts
  47. 11 7
      server/routers/role/deleteRole.ts
  48. 6 4
      server/routers/role/getRole.ts
  49. 7 5
      server/routers/role/listRoleActions.ts
  50. 7 5
      server/routers/role/listRoleResources.ts
  51. 7 5
      server/routers/role/listRoleSites.ts
  52. 10 8
      server/routers/role/listRoles.ts
  53. 11 7
      server/routers/role/removeRoleAction.ts
  54. 14 7
      server/routers/role/removeRoleResource.ts
  55. 11 7
      server/routers/role/removeRoleSite.ts
  56. 8 6
      server/routers/role/updateRole.ts
  57. 19 26
      server/routers/site/createSite.ts
  58. 7 5
      server/routers/site/deleteSite.ts
  59. 15 13
      server/routers/site/getSite.ts
  60. 7 5
      server/routers/site/listSiteRoles.ts
  61. 27 26
      server/routers/site/listSites.ts
  62. 8 6
      server/routers/site/updateSite.ts
  63. 19 11
      server/routers/target/createTarget.ts
  64. 10 8
      server/routers/target/deleteTarget.ts
  65. 6 4
      server/routers/target/getTarget.ts
  66. 13 8
      server/routers/target/listTargets.ts
  67. 12 10
      server/routers/target/updateTarget.ts
  68. 9 7
      server/routers/user/acceptInvite.ts
  69. 9 7
      server/routers/user/addUserAction.ts
  70. 7 5
      server/routers/user/addUserRole.ts
  71. 9 7
      server/routers/user/addUserSite.ts
  72. 8 6
      server/routers/user/getOrgUser.ts
  73. 53 45
      server/routers/user/inviteUser.ts
  74. 25 21
      server/routers/user/listUsers.ts
  75. 12 8
      server/routers/user/removeUserAction.ts
  76. 7 5
      server/routers/user/removeUserOrg.ts
  77. 10 5
      server/routers/user/removeUserResource.ts
  78. 11 7
      server/routers/user/removeUserSite.ts
  79. 1 1
      src/app/[orgId]/settings/access/roles/components/CreateRoleForm.tsx
  80. 14 17
      src/app/[orgId]/settings/access/roles/components/RolesTable.tsx
  81. 56 19
      src/app/[orgId]/settings/access/users/components/InviteUserForm.tsx
  82. 23 24
      src/app/[orgId]/settings/access/users/components/UsersTable.tsx
  83. 66 57
      src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx
  84. 21 24
      src/app/[orgId]/settings/resources/components/ResourcesTable.tsx
  85. 14 4
      src/app/[orgId]/settings/share-links/components/CreateShareLinkForm.tsx
  86. 76 68
      src/app/[orgId]/settings/share-links/components/ShareLinksTable.tsx
  87. 3 3
      src/app/[orgId]/settings/share-links/page.tsx
  88. 50 24
      src/app/[orgId]/settings/sites/components/CreateSiteForm.tsx
  89. 34 33
      src/app/[orgId]/settings/sites/components/SitesTable.tsx
  90. 84 0
      src/app/auth/resource/[resourceId]/components/AccessToken.tsx
  91. 0 32
      src/app/auth/resource/[resourceId]/components/AccessTokenInvalid.tsx
  92. 16 30
      src/app/auth/resource/[resourceId]/page.tsx

+ 4 - 3
config.example.yml

@@ -25,9 +25,10 @@ gerbil:
     block_size: 16
     block_size: 16
     subnet_group: 10.0.0.0/8
     subnet_group: 10.0.0.0/8
 
 
-rate_limit:
-    window_minutes: 1
-    max_requests: 100
+rate_limits:
+    global:
+        window_minutes: 1
+        max_requests: 100
 
 
 email:
 email:
     smtp_host: host.hoster.net
     smtp_host: host.hoster.net

+ 3 - 3
server/apiServer.ts

@@ -38,9 +38,9 @@ export function createApiServer() {
     if (!dev) {
     if (!dev) {
         apiServer.use(
         apiServer.use(
             rateLimitMiddleware({
             rateLimitMiddleware({
-                windowMin: config.rate_limit.window_minutes,
-                max: config.rate_limit.max_requests,
-                type: "IP_ONLY",
+                windowMin: config.rate_limits.global.window_minutes,
+                max: config.rate_limits.global.max_requests,
+                type: "IP_AND_PATH",
             }),
             }),
         );
         );
     }
     }

+ 0 - 0
server/routers/auth/sendEmailVerificationCode.ts → server/auth/sendEmailVerificationCode.ts


+ 22 - 14
server/config.ts

@@ -16,7 +16,7 @@ const environmentSchema = z.object({
     app: z.object({
     app: z.object({
         base_url: z.string().url(),
         base_url: z.string().url(),
         log_level: z.enum(["debug", "info", "warn", "error"]),
         log_level: z.enum(["debug", "info", "warn", "error"]),
-        save_logs: z.boolean(),
+        save_logs: z.boolean()
     }),
     }),
     server: z.object({
     server: z.object({
         external_port: portSchema,
         external_port: portSchema,
@@ -26,24 +26,32 @@ const environmentSchema = z.object({
         secure_cookies: z.boolean(),
         secure_cookies: z.boolean(),
         signup_secret: z.string().optional(),
         signup_secret: z.string().optional(),
         session_cookie_name: z.string(),
         session_cookie_name: z.string(),
-        resource_session_cookie_name: z.string(),
+        resource_session_cookie_name: z.string()
     }),
     }),
     traefik: z.object({
     traefik: z.object({
         http_entrypoint: z.string(),
         http_entrypoint: z.string(),
         https_entrypoint: z.string().optional(),
         https_entrypoint: z.string().optional(),
         cert_resolver: z.string().optional(),
         cert_resolver: z.string().optional(),
-        prefer_wildcard_cert: z.boolean().optional(),
+        prefer_wildcard_cert: z.boolean().optional()
     }),
     }),
     gerbil: z.object({
     gerbil: z.object({
         start_port: portSchema,
         start_port: portSchema,
         base_endpoint: z.string(),
         base_endpoint: z.string(),
         use_subdomain: z.boolean(),
         use_subdomain: z.boolean(),
         subnet_group: z.string(),
         subnet_group: z.string(),
-        block_size: z.number().positive().gt(0),
+        block_size: z.number().positive().gt(0)
     }),
     }),
-    rate_limit: z.object({
-        window_minutes: z.number().positive().gt(0),
-        max_requests: z.number().positive().gt(0),
+    rate_limits: z.object({
+        global: z.object({
+            window_minutes: z.number().positive().gt(0),
+            max_requests: z.number().positive().gt(0)
+        }),
+        auth: z
+            .object({
+                window_minutes: z.number().positive().gt(0),
+                max_requests: z.number().positive().gt(0)
+            })
+            .optional()
     }),
     }),
     email: z
     email: z
         .object({
         .object({
@@ -51,7 +59,7 @@ const environmentSchema = z.object({
             smtp_port: portSchema.optional(),
             smtp_port: portSchema.optional(),
             smtp_user: z.string().optional(),
             smtp_user: z.string().optional(),
             smtp_pass: z.string().optional(),
             smtp_pass: z.string().optional(),
-            no_reply: z.string().email().optional(),
+            no_reply: z.string().email().optional()
         })
         })
         .optional(),
         .optional(),
     flags: z
     flags: z
@@ -59,9 +67,9 @@ const environmentSchema = z.object({
             allow_org_subdomain_changing: z.boolean().optional(),
             allow_org_subdomain_changing: z.boolean().optional(),
             require_email_verification: z.boolean().optional(),
             require_email_verification: z.boolean().optional(),
             disable_signup_without_invite: z.boolean().optional(),
             disable_signup_without_invite: z.boolean().optional(),
-            require_signup_secret: z.boolean().optional(),
+            require_signup_secret: z.boolean().optional()
         })
         })
-        .optional(),
+        .optional()
 });
 });
 
 
 const loadConfig = (configPath: string) => {
 const loadConfig = (configPath: string) => {
@@ -72,7 +80,7 @@ const loadConfig = (configPath: string) => {
     } catch (error) {
     } catch (error) {
         if (error instanceof Error) {
         if (error instanceof Error) {
             throw new Error(
             throw new Error(
-                `Error loading configuration file: ${error.message}`,
+                `Error loading configuration file: ${error.message}`
             );
             );
         }
         }
         throw error;
         throw error;
@@ -94,21 +102,21 @@ if (!environment) {
         try {
         try {
             const exampleConfigContent = fs.readFileSync(
             const exampleConfigContent = fs.readFileSync(
                 exampleConfigPath,
                 exampleConfigPath,
-                "utf8",
+                "utf8"
             );
             );
             fs.writeFileSync(configFilePath1, exampleConfigContent, "utf8");
             fs.writeFileSync(configFilePath1, exampleConfigContent, "utf8");
             environment = loadConfig(configFilePath1);
             environment = loadConfig(configFilePath1);
         } catch (error) {
         } catch (error) {
             if (error instanceof Error) {
             if (error instanceof Error) {
                 throw new Error(
                 throw new Error(
-                    `Error creating configuration file from example: ${error.message}`,
+                    `Error creating configuration file from example: ${error.message}`
                 );
                 );
             }
             }
             throw error;
             throw error;
         }
         }
     } else {
     } else {
         throw new Error(
         throw new Error(
-            "No configuration file found and no example configuration available",
+            "No configuration file found and no example configuration available"
         );
         );
     }
     }
 }
 }

+ 5 - 3
server/routers/accessToken/deleteAccessToken.ts

@@ -9,9 +9,11 @@ import { resourceAccessToken } from "@server/db/schema";
 import { and, eq } from "drizzle-orm";
 import { and, eq } from "drizzle-orm";
 import db from "@server/db";
 import db from "@server/db";
 
 
-const deleteAccessTokenParamsSchema = z.object({
-    accessTokenId: z.string()
-});
+const deleteAccessTokenParamsSchema = z
+    .object({
+        accessTokenId: z.string()
+    })
+    .strict();
 
 
 export async function deleteAccessToken(
 export async function deleteAccessToken(
     req: Request,
     req: Request,

+ 57 - 30
server/routers/accessToken/generateAccessToken.ts

@@ -5,7 +5,11 @@ import {
     SESSION_COOKIE_EXPIRES
     SESSION_COOKIE_EXPIRES
 } from "@server/auth";
 } from "@server/auth";
 import db from "@server/db";
 import db from "@server/db";
-import { ResourceAccessToken, resourceAccessToken, resources } from "@server/db/schema";
+import {
+    ResourceAccessToken,
+    resourceAccessToken,
+    resources
+} from "@server/db/schema";
 import HttpCode from "@server/types/HttpCode";
 import HttpCode from "@server/types/HttpCode";
 import response from "@server/utils/response";
 import response from "@server/utils/response";
 import { eq } from "drizzle-orm";
 import { eq } from "drizzle-orm";
@@ -16,17 +20,27 @@ import { fromError } from "zod-validation-error";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { createDate, TimeSpan } from "oslo";
 import { createDate, TimeSpan } from "oslo";
 
 
-export const generateAccessTokenBodySchema = z.object({
-    validForSeconds: z.number().int().positive().optional(), // seconds
-    title: z.string().optional(),
-    description: z.string().optional()
-});
-
-export const generateAccssTokenParamsSchema = z.object({
-    resourceId: z.string().transform(Number).pipe(z.number().int().positive())
-});
-
-export type GenerateAccessTokenResponse = ResourceAccessToken;
+export const generateAccessTokenBodySchema = z
+    .object({
+        validForSeconds: z.number().int().positive().optional(), // seconds
+        title: z.string().optional(),
+        description: z.string().optional()
+    })
+    .strict();
+
+export const generateAccssTokenParamsSchema = z
+    .object({
+        resourceId: z
+            .string()
+            .transform(Number)
+            .pipe(z.number().int().positive())
+    })
+    .strict();
+
+export type GenerateAccessTokenResponse = Omit<
+    ResourceAccessToken,
+    "tokenHash"
+> & { accessToken: string };
 
 
 export async function generateAccessToken(
 export async function generateAccessToken(
     req: Request,
     req: Request,
@@ -77,25 +91,38 @@ export async function generateAccessToken(
 
 
         const token = generateIdFromEntropySize(25);
         const token = generateIdFromEntropySize(25);
 
 
-        // const tokenHash = await hash(token, {
-        //     memoryCost: 19456,
-        //     timeCost: 2,
-        //     outputLen: 32,
-        //     parallelism: 1
-        // });
+        const tokenHash = await hash(token, {
+            memoryCost: 19456,
+            timeCost: 2,
+            outputLen: 32,
+            parallelism: 1
+        });
 
 
         const id = generateId(15);
         const id = generateId(15);
-        const [result] = await db.insert(resourceAccessToken).values({
-            accessTokenId: id,
-            orgId: resource.orgId,
-            resourceId,
-            tokenHash: token,
-            expiresAt: expiresAt || null,
-            sessionLength: sessionLength,
-            title: title || null,
-            description: description || null,
-            createdAt: new Date().getTime()
-        }).returning();
+        const [result] = await db
+            .insert(resourceAccessToken)
+            .values({
+                accessTokenId: id,
+                orgId: resource.orgId,
+                resourceId,
+                tokenHash,
+                expiresAt: expiresAt || null,
+                sessionLength: sessionLength,
+                title: title || null,
+                description: description || null,
+                createdAt: new Date().getTime()
+            })
+            .returning({
+                accessTokenId: resourceAccessToken.accessTokenId,
+                orgId: resourceAccessToken.orgId,
+                resourceId: resourceAccessToken.resourceId,
+                expiresAt: resourceAccessToken.expiresAt,
+                sessionLength: resourceAccessToken.sessionLength,
+                title: resourceAccessToken.title,
+                description: resourceAccessToken.description,
+                createdAt: resourceAccessToken.createdAt
+            })
+            .execute();
 
 
         if (!result) {
         if (!result) {
             return next(
             return next(
@@ -107,7 +134,7 @@ export async function generateAccessToken(
         }
         }
 
 
         return response<GenerateAccessTokenResponse>(res, {
         return response<GenerateAccessTokenResponse>(res, {
-            data: result,
+            data: { ...result, accessToken: token },
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Resource access token generated successfully",
             message: "Resource access token generated successfully",

+ 9 - 2
server/routers/accessToken/listAccessTokens.ts

@@ -23,6 +23,7 @@ const listAccessTokensParamsSchema = z
             .pipe(z.number().int().positive().optional()),
             .pipe(z.number().int().positive().optional()),
         orgId: z.string().optional()
         orgId: z.string().optional()
     })
     })
+    .strict()
     .refine((data) => !!data.resourceId !== !!data.orgId, {
     .refine((data) => !!data.resourceId !== !!data.orgId, {
         message: "Either resourceId or orgId must be provided, but not both"
         message: "Either resourceId or orgId must be provided, but not both"
     });
     });
@@ -65,7 +66,10 @@ function queryAccessTokens(
         return db
         return db
             .select(cols)
             .select(cols)
             .from(resourceAccessToken)
             .from(resourceAccessToken)
-            .leftJoin(resources, eq(resourceAccessToken.resourceId, resources.resourceId))
+            .leftJoin(
+                resources,
+                eq(resourceAccessToken.resourceId, resources.resourceId)
+            )
             .where(
             .where(
                 and(
                 and(
                     inArray(
                     inArray(
@@ -83,7 +87,10 @@ function queryAccessTokens(
         return db
         return db
             .select(cols)
             .select(cols)
             .from(resourceAccessToken)
             .from(resourceAccessToken)
-            .leftJoin(resources, eq(resourceAccessToken.resourceId, resources.resourceId))
+            .leftJoin(
+                resources,
+                eq(resourceAccessToken.resourceId, resources.resourceId)
+            )
             .where(
             .where(
                 and(
                 and(
                     inArray(
                     inArray(

+ 3 - 1
server/routers/auth/changePassword.ts

@@ -11,12 +11,13 @@ import { response } from "@server/utils";
 import { hashPassword, verifyPassword } from "@server/auth/password";
 import { hashPassword, verifyPassword } from "@server/auth/password";
 import { verifyTotpCode } from "@server/auth/2fa";
 import { verifyTotpCode } from "@server/auth/2fa";
 import { passwordSchema } from "@server/auth/passwordSchema";
 import { passwordSchema } from "@server/auth/passwordSchema";
+import logger from "@server/logger";
 
 
 export const changePasswordBody = z.object({
 export const changePasswordBody = z.object({
     oldPassword: z.string(),
     oldPassword: z.string(),
     newPassword: passwordSchema,
     newPassword: passwordSchema,
     code: z.string().optional(),
     code: z.string().optional(),
-});
+}).strict();
 
 
 export type ChangePasswordBody = z.infer<typeof changePasswordBody>;
 export type ChangePasswordBody = z.infer<typeof changePasswordBody>;
 
 
@@ -108,6 +109,7 @@ export async function changePassword(
             status: HttpCode.OK,
             status: HttpCode.OK,
         });
         });
     } catch (error) {
     } catch (error) {
+        logger.error(error);
         return next(
         return next(
             createHttpError(
             createHttpError(
                 HttpCode.INTERNAL_SERVER_ERROR,
                 HttpCode.INTERNAL_SERVER_ERROR,

+ 3 - 1
server/routers/auth/checkResourceSession.ts

@@ -5,11 +5,12 @@ import { fromError } from "zod-validation-error";
 import HttpCode from "@server/types/HttpCode";
 import HttpCode from "@server/types/HttpCode";
 import { response } from "@server/utils";
 import { response } from "@server/utils";
 import { validateResourceSessionToken } from "@server/auth/resource";
 import { validateResourceSessionToken } from "@server/auth/resource";
+import logger from "@server/logger";
 
 
 export const params = z.object({
 export const params = z.object({
     token: z.string(),
     token: z.string(),
     resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
     resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+}).strict();
 
 
 export type CheckResourceSessionParams = z.infer<typeof params>;
 export type CheckResourceSessionParams = z.infer<typeof params>;
 
 
@@ -54,6 +55,7 @@ export async function checkResourceSession(
             status: HttpCode.OK,
             status: HttpCode.OK,
         });
         });
     } catch (e) {
     } catch (e) {
+        logger.error(e);
         return next(
         return next(
             createHttpError(
             createHttpError(
                 HttpCode.INTERNAL_SERVER_ERROR,
                 HttpCode.INTERNAL_SERVER_ERROR,

+ 3 - 1
server/routers/auth/disable2fa.ts

@@ -10,11 +10,12 @@ import { eq } from "drizzle-orm";
 import { response } from "@server/utils";
 import { response } from "@server/utils";
 import { verifyPassword } from "@server/auth/password";
 import { verifyPassword } from "@server/auth/password";
 import { verifyTotpCode } from "@server/auth/2fa";
 import { verifyTotpCode } from "@server/auth/2fa";
+import logger from "@server/logger";
 
 
 export const disable2faBody = z.object({
 export const disable2faBody = z.object({
     password: z.string(),
     password: z.string(),
     code: z.string().optional(),
     code: z.string().optional(),
-});
+}).strict();
 
 
 export type Disable2faBody = z.infer<typeof disable2faBody>;
 export type Disable2faBody = z.infer<typeof disable2faBody>;
 
 
@@ -100,6 +101,7 @@ export async function disable2fa(
             status: HttpCode.OK,
             status: HttpCode.OK,
         });
         });
     } catch (error) {
     } catch (error) {
+        logger.error(error);
         return next(
         return next(
             createHttpError(
             createHttpError(
                 HttpCode.INTERNAL_SERVER_ERROR,
                 HttpCode.INTERNAL_SERVER_ERROR,

+ 2 - 1
server/routers/auth/login.ts

@@ -22,7 +22,7 @@ export const loginBodySchema = z.object({
     email: z.string().email(),
     email: z.string().email(),
     password: z.string(),
     password: z.string(),
     code: z.string().optional(),
     code: z.string().optional(),
-});
+}).strict();
 
 
 export type LoginBody = z.infer<typeof loginBodySchema>;
 export type LoginBody = z.infer<typeof loginBodySchema>;
 
 
@@ -151,6 +151,7 @@ export async function login(
             status: HttpCode.OK,
             status: HttpCode.OK,
         });
         });
     } catch (e) {
     } catch (e) {
+        logger.error(e);
         return next(
         return next(
             createHttpError(
             createHttpError(
                 HttpCode.INTERNAL_SERVER_ERROR,
                 HttpCode.INTERNAL_SERVER_ERROR,

+ 7 - 10
server/routers/auth/logout.ts

@@ -6,13 +6,13 @@ import logger from "@server/logger";
 import {
 import {
     createBlankSessionTokenCookie,
     createBlankSessionTokenCookie,
     invalidateSession,
     invalidateSession,
-    SESSION_COOKIE_NAME,
+    SESSION_COOKIE_NAME
 } from "@server/auth";
 } from "@server/auth";
 
 
 export async function logout(
 export async function logout(
     req: Request,
     req: Request,
     res: Response,
     res: Response,
-    next: NextFunction,
+    next: NextFunction
 ): Promise<any> {
 ): Promise<any> {
     const sessionId = req.cookies[SESSION_COOKIE_NAME];
     const sessionId = req.cookies[SESSION_COOKIE_NAME];
 
 
@@ -20,8 +20,8 @@ export async function logout(
         return next(
         return next(
             createHttpError(
             createHttpError(
                 HttpCode.BAD_REQUEST,
                 HttpCode.BAD_REQUEST,
-                "You must be logged in to sign out",
-            ),
+                "You must be logged in to sign out"
+            )
         );
         );
     }
     }
 
 
@@ -34,15 +34,12 @@ export async function logout(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Logged out successfully",
             message: "Logged out successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
-        logger.error("Failed to log out", error);
+        logger.error(error);
         return next(
         return next(
-            createHttpError(
-                HttpCode.INTERNAL_SERVER_ERROR,
-                "Failed to log out",
-            ),
+            createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "Failed to log out")
         );
         );
     }
     }
 }
 }

+ 5 - 3
server/routers/auth/requestEmailVerificationCode.ts

@@ -3,8 +3,9 @@ import createHttpError from "http-errors";
 import HttpCode from "@server/types/HttpCode";
 import HttpCode from "@server/types/HttpCode";
 import { response } from "@server/utils";
 import { response } from "@server/utils";
 import { User } from "@server/db/schema";
 import { User } from "@server/db/schema";
-import { sendEmailVerificationCode } from "./sendEmailVerificationCode";
+import { sendEmailVerificationCode } from "../../auth/sendEmailVerificationCode";
 import config from "@server/config";
 import config from "@server/config";
+import logger from "@server/logger";
 
 
 export type RequestEmailVerificationCodeResponse = {
 export type RequestEmailVerificationCodeResponse = {
     codeSent: boolean;
     codeSent: boolean;
@@ -40,14 +41,15 @@ export async function requestEmailVerificationCode(
 
 
         return response<RequestEmailVerificationCodeResponse>(res, {
         return response<RequestEmailVerificationCodeResponse>(res, {
             data: {
             data: {
-                codeSent: true,
+                codeSent: true
             },
             },
             status: HttpCode.OK,
             status: HttpCode.OK,
             success: true,
             success: true,
             error: false,
             error: false,
-            message: `Email verification code sent to ${user.email}`,
+            message: `Email verification code sent to ${user.email}`
         });
         });
     } catch (error) {
     } catch (error) {
+        logger.error(error);
         return next(
         return next(
             createHttpError(
             createHttpError(
                 HttpCode.INTERNAL_SERVER_ERROR,
                 HttpCode.INTERNAL_SERVER_ERROR,

+ 2 - 1
server/routers/auth/requestPasswordReset.ts

@@ -16,7 +16,7 @@ import { TimeSpan } from "oslo";
 
 
 export const requestPasswordResetBody = z.object({
 export const requestPasswordResetBody = z.object({
     email: z.string().email(),
     email: z.string().email(),
-});
+}).strict();
 
 
 export type RequestPasswordResetBody = z.infer<typeof requestPasswordResetBody>;
 export type RequestPasswordResetBody = z.infer<typeof requestPasswordResetBody>;
 
 
@@ -87,6 +87,7 @@ export async function requestPasswordReset(
             status: HttpCode.OK,
             status: HttpCode.OK,
         });
         });
     } catch (e) {
     } catch (e) {
+        logger.error(e);
         return next(
         return next(
             createHttpError(
             createHttpError(
                 HttpCode.INTERNAL_SERVER_ERROR,
                 HttpCode.INTERNAL_SERVER_ERROR,

+ 18 - 14
server/routers/auth/requestTotpSecret.ts

@@ -12,10 +12,13 @@ import { eq } from "drizzle-orm";
 import { verify } from "@node-rs/argon2";
 import { verify } from "@node-rs/argon2";
 import { createTOTPKeyURI } from "oslo/otp";
 import { createTOTPKeyURI } from "oslo/otp";
 import config from "@server/config";
 import config from "@server/config";
+import logger from "@server/logger";
 
 
-export const requestTotpSecretBody = z.object({
-    password: z.string(),
-});
+export const requestTotpSecretBody = z
+    .object({
+        password: z.string()
+    })
+    .strict();
 
 
 export type RequestTotpSecretBody = z.infer<typeof requestTotpSecretBody>;
 export type RequestTotpSecretBody = z.infer<typeof requestTotpSecretBody>;
 
 
@@ -26,7 +29,7 @@ export type RequestTotpSecretResponse = {
 export async function requestTotpSecret(
 export async function requestTotpSecret(
     req: Request,
     req: Request,
     res: Response,
     res: Response,
-    next: NextFunction,
+    next: NextFunction
 ): Promise<any> {
 ): Promise<any> {
     const parsedBody = requestTotpSecretBody.safeParse(req.body);
     const parsedBody = requestTotpSecretBody.safeParse(req.body);
 
 
@@ -34,8 +37,8 @@ export async function requestTotpSecret(
         return next(
         return next(
             createHttpError(
             createHttpError(
                 HttpCode.BAD_REQUEST,
                 HttpCode.BAD_REQUEST,
-                fromError(parsedBody.error).toString(),
-            ),
+                fromError(parsedBody.error).toString()
+            )
         );
         );
     }
     }
 
 
@@ -48,7 +51,7 @@ export async function requestTotpSecret(
             memoryCost: 19456,
             memoryCost: 19456,
             timeCost: 2,
             timeCost: 2,
             outputLen: 32,
             outputLen: 32,
-            parallelism: 1,
+            parallelism: 1
         });
         });
         if (!validPassword) {
         if (!validPassword) {
             return next(unauthorized());
             return next(unauthorized());
@@ -58,8 +61,8 @@ export async function requestTotpSecret(
             return next(
             return next(
                 createHttpError(
                 createHttpError(
                     HttpCode.BAD_REQUEST,
                     HttpCode.BAD_REQUEST,
-                    "User has already enabled two-factor authentication",
-                ),
+                    "User has already enabled two-factor authentication"
+                )
             );
             );
         }
         }
 
 
@@ -70,25 +73,26 @@ export async function requestTotpSecret(
         await db
         await db
             .update(users)
             .update(users)
             .set({
             .set({
-                twoFactorSecret: secret,
+                twoFactorSecret: secret
             })
             })
             .where(eq(users.userId, user.userId));
             .where(eq(users.userId, user.userId));
 
 
         return response<RequestTotpSecretResponse>(res, {
         return response<RequestTotpSecretResponse>(res, {
             data: {
             data: {
-                secret: uri,
+                secret: uri
             },
             },
             success: true,
             success: true,
             error: false,
             error: false,
             message: "TOTP secret generated successfully",
             message: "TOTP secret generated successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
+        logger.error(error);
         return next(
         return next(
             createHttpError(
             createHttpError(
                 HttpCode.INTERNAL_SERVER_ERROR,
                 HttpCode.INTERNAL_SERVER_ERROR,
-                "Failed to generate TOTP secret",
-            ),
+                "Failed to generate TOTP secret"
+            )
         );
         );
     }
     }
 }
 }

+ 24 - 20
server/routers/auth/resetPassword.ts

@@ -14,12 +14,15 @@ import { passwordSchema } from "@server/auth/passwordSchema";
 import { encodeHex } from "oslo/encoding";
 import { encodeHex } from "oslo/encoding";
 import { isWithinExpirationDate } from "oslo";
 import { isWithinExpirationDate } from "oslo";
 import { invalidateAllSessions } from "@server/auth";
 import { invalidateAllSessions } from "@server/auth";
+import logger from "@server/logger";
 
 
-export const resetPasswordBody = z.object({
-    token: z.string(),
-    newPassword: passwordSchema,
-    code: z.string().optional(),
-});
+export const resetPasswordBody = z
+    .object({
+        token: z.string(),
+        newPassword: passwordSchema,
+        code: z.string().optional()
+    })
+    .strict();
 
 
 export type ResetPasswordBody = z.infer<typeof resetPasswordBody>;
 export type ResetPasswordBody = z.infer<typeof resetPasswordBody>;
 
 
@@ -30,7 +33,7 @@ export type ResetPasswordResponse = {
 export async function resetPassword(
 export async function resetPassword(
     req: Request,
     req: Request,
     res: Response,
     res: Response,
-    next: NextFunction,
+    next: NextFunction
 ): Promise<any> {
 ): Promise<any> {
     const parsedBody = resetPasswordBody.safeParse(req.body);
     const parsedBody = resetPasswordBody.safeParse(req.body);
 
 
@@ -38,8 +41,8 @@ export async function resetPassword(
         return next(
         return next(
             createHttpError(
             createHttpError(
                 HttpCode.BAD_REQUEST,
                 HttpCode.BAD_REQUEST,
-                fromError(parsedBody.error).toString(),
-            ),
+                fromError(parsedBody.error).toString()
+            )
         );
         );
     }
     }
 
 
@@ -47,7 +50,7 @@ export async function resetPassword(
 
 
     try {
     try {
         const tokenHash = encodeHex(
         const tokenHash = encodeHex(
-            await sha256(new TextEncoder().encode(token)),
+            await sha256(new TextEncoder().encode(token))
         );
         );
 
 
         const resetRequest = await db
         const resetRequest = await db
@@ -63,8 +66,8 @@ export async function resetPassword(
             return next(
             return next(
                 createHttpError(
                 createHttpError(
                     HttpCode.BAD_REQUEST,
                     HttpCode.BAD_REQUEST,
-                    "Invalid or expired password reset token",
-                ),
+                    "Invalid or expired password reset token"
+                )
             );
             );
         }
         }
 
 
@@ -77,8 +80,8 @@ export async function resetPassword(
             return next(
             return next(
                 createHttpError(
                 createHttpError(
                     HttpCode.INTERNAL_SERVER_ERROR,
                     HttpCode.INTERNAL_SERVER_ERROR,
-                    "User not found",
-                ),
+                    "User not found"
+                )
             );
             );
         }
         }
 
 
@@ -89,22 +92,22 @@ export async function resetPassword(
                     success: true,
                     success: true,
                     error: false,
                     error: false,
                     message: "Two-factor authentication required",
                     message: "Two-factor authentication required",
-                    status: HttpCode.ACCEPTED,
+                    status: HttpCode.ACCEPTED
                 });
                 });
             }
             }
 
 
             const validOTP = await verifyTotpCode(
             const validOTP = await verifyTotpCode(
                 code!,
                 code!,
                 user[0].twoFactorSecret!,
                 user[0].twoFactorSecret!,
-                user[0].userId,
+                user[0].userId
             );
             );
 
 
             if (!validOTP) {
             if (!validOTP) {
                 return next(
                 return next(
                     createHttpError(
                     createHttpError(
                         HttpCode.BAD_REQUEST,
                         HttpCode.BAD_REQUEST,
-                        "Invalid two-factor authentication code",
-                    ),
+                        "Invalid two-factor authentication code"
+                    )
                 );
                 );
             }
             }
         }
         }
@@ -129,14 +132,15 @@ export async function resetPassword(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Password reset successfully",
             message: "Password reset successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (e) {
     } catch (e) {
+        logger.error(e);
         return next(
         return next(
             createHttpError(
             createHttpError(
                 HttpCode.INTERNAL_SERVER_ERROR,
                 HttpCode.INTERNAL_SERVER_ERROR,
-                "Failed to reset password",
-            ),
+                "Failed to reset password"
+            )
         );
         );
     }
     }
 }
 }

+ 3 - 1
server/routers/auth/signup.ts

@@ -8,7 +8,7 @@ import { fromError } from "zod-validation-error";
 import createHttpError from "http-errors";
 import createHttpError from "http-errors";
 import response from "@server/utils/response";
 import response from "@server/utils/response";
 import { SqliteError } from "better-sqlite3";
 import { SqliteError } from "better-sqlite3";
-import { sendEmailVerificationCode } from "./sendEmailVerificationCode";
+import { sendEmailVerificationCode } from "../../auth/sendEmailVerificationCode";
 import { passwordSchema } from "@server/auth/passwordSchema";
 import { passwordSchema } from "@server/auth/passwordSchema";
 import { eq } from "drizzle-orm";
 import { eq } from "drizzle-orm";
 import moment from "moment";
 import moment from "moment";
@@ -20,6 +20,7 @@ import {
 } from "@server/auth";
 } from "@server/auth";
 import { ActionsEnum } from "@server/auth/actions";
 import { ActionsEnum } from "@server/auth/actions";
 import config from "@server/config";
 import config from "@server/config";
+import logger from "@server/logger";
 
 
 export const signupBodySchema = z.object({
 export const signupBodySchema = z.object({
     email: z.string().email(),
     email: z.string().email(),
@@ -153,6 +154,7 @@ export async function signup(
                 )
                 )
             );
             );
         } else {
         } else {
+            logger.error(e);
             return next(
             return next(
                 createHttpError(
                 createHttpError(
                     HttpCode.INTERNAL_SERVER_ERROR,
                     HttpCode.INTERNAL_SERVER_ERROR,

+ 10 - 6
server/routers/auth/verifyEmail.ts

@@ -9,10 +9,13 @@ import { User, emailVerificationCodes, users } from "@server/db/schema";
 import { eq } from "drizzle-orm";
 import { eq } from "drizzle-orm";
 import { isWithinExpirationDate } from "oslo";
 import { isWithinExpirationDate } from "oslo";
 import config from "@server/config";
 import config from "@server/config";
+import logger from "@server/logger";
 
 
-export const verifyEmailBody = z.object({
-    code: z.string(),
-});
+export const verifyEmailBody = z
+    .object({
+        code: z.string()
+    })
+    .strict();
 
 
 export type VerifyEmailBody = z.infer<typeof verifyEmailBody>;
 export type VerifyEmailBody = z.infer<typeof verifyEmailBody>;
 
 
@@ -66,7 +69,7 @@ export async function verifyEmail(
             await db
             await db
                 .update(users)
                 .update(users)
                 .set({
                 .set({
-                    emailVerified: true,
+                    emailVerified: true
                 })
                 })
                 .where(eq(users.userId, user.userId));
                 .where(eq(users.userId, user.userId));
         } else {
         } else {
@@ -84,10 +87,11 @@ export async function verifyEmail(
             message: "Email verified",
             message: "Email verified",
             status: HttpCode.OK,
             status: HttpCode.OK,
             data: {
             data: {
-                valid,
-            },
+                valid
+            }
         });
         });
     } catch (error) {
     } catch (error) {
+        logger.error(error);
         return next(
         return next(
             createHttpError(
             createHttpError(
                 HttpCode.INTERNAL_SERVER_ERROR,
                 HttpCode.INTERNAL_SERVER_ERROR,

+ 10 - 6
server/routers/auth/verifyTotp.ts

@@ -10,10 +10,13 @@ import { eq } from "drizzle-orm";
 import { alphabet, generateRandomString } from "oslo/crypto";
 import { alphabet, generateRandomString } from "oslo/crypto";
 import { hashPassword } from "@server/auth/password";
 import { hashPassword } from "@server/auth/password";
 import { verifyTotpCode } from "@server/auth/2fa";
 import { verifyTotpCode } from "@server/auth/2fa";
+import logger from "@server/logger";
 
 
-export const verifyTotpBody = z.object({
-    code: z.string(),
-});
+export const verifyTotpBody = z
+    .object({
+        code: z.string()
+    })
+    .strict();
 
 
 export type VerifyTotpBody = z.infer<typeof verifyTotpBody>;
 export type VerifyTotpBody = z.infer<typeof verifyTotpBody>;
 
 
@@ -82,7 +85,7 @@ export async function verifyTotp(
 
 
                 await db.insert(twoFactorBackupCodes).values({
                 await db.insert(twoFactorBackupCodes).values({
                     userId: user.userId,
                     userId: user.userId,
-                    codeHash: hash,
+                    codeHash: hash
                 });
                 });
             }
             }
         }
         }
@@ -92,16 +95,17 @@ export async function verifyTotp(
         return response<VerifyTotpResponse>(res, {
         return response<VerifyTotpResponse>(res, {
             data: {
             data: {
                 valid,
                 valid,
-                ...(valid && codes ? { backupCodes: codes } : {}),
+                ...(valid && codes ? { backupCodes: codes } : {})
             },
             },
             success: true,
             success: true,
             error: false,
             error: false,
             message: valid
             message: valid
                 ? "Code is valid. Two-factor is now enabled"
                 ? "Code is valid. Two-factor is now enabled"
                 : "Code is invalid",
                 : "Code is invalid",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
+        logger.error(error);
         return next(
         return next(
             createHttpError(
             createHttpError(
                 HttpCode.INTERNAL_SERVER_ERROR,
                 HttpCode.INTERNAL_SERVER_ERROR,

+ 30 - 33
server/routers/badger/verifySession.ts

@@ -139,39 +139,36 @@ export async function verifyResourceSession(
             );
             );
 
 
             if (resourceSession) {
             if (resourceSession) {
-                return allowed(res);
-
-                // Might not be needed
-                // if (pincode && resourceSession.pincodeId) {
-                //     logger.debug(
-                //         "Resource allowed because pincode session is valid"
-                //     );
-                //     return allowed(res);
-                // }
-                //
-                // if (password && resourceSession.passwordId) {
-                //     logger.debug(
-                //         "Resource allowed because password session is valid"
-                //     );
-                //     return allowed(res);
-                // }
-                //
-                // if (
-                //     resource.emailWhitelistEnabled &&
-                //     resourceSession.whitelistId
-                // ) {
-                //     logger.debug(
-                //         "Resource allowed because whitelist session is valid"
-                //     );
-                //     return allowed(res);
-                // }
-                //
-                // if (resourceSession.accessTokenId) {
-                //     logger.debug(
-                //         "Resource allowed because access token session is valid"
-                //     );
-                //     return allowed(res);
-                // }
+                if (pincode && resourceSession.pincodeId) {
+                    logger.debug(
+                        "Resource allowed because pincode session is valid"
+                    );
+                    return allowed(res);
+                }
+
+                if (password && resourceSession.passwordId) {
+                    logger.debug(
+                        "Resource allowed because password session is valid"
+                    );
+                    return allowed(res);
+                }
+
+                if (
+                    resource.emailWhitelistEnabled &&
+                    resourceSession.whitelistId
+                ) {
+                    logger.debug(
+                        "Resource allowed because whitelist session is valid"
+                    );
+                    return allowed(res);
+                }
+
+                if (resourceSession.accessTokenId) {
+                    logger.debug(
+                        "Resource allowed because access token session is valid"
+                    );
+                    return allowed(res);
+                }
             }
             }
         }
         }
 
 

+ 7 - 2
server/routers/external.ts

@@ -1,4 +1,5 @@
 import { Router } from "express";
 import { Router } from "express";
+import config from "@server/config";
 import * as site from "./site";
 import * as site from "./site";
 import * as org from "./org";
 import * as org from "./org";
 import * as resource from "./resource";
 import * as resource from "./resource";
@@ -419,8 +420,12 @@ export const authRouter = Router();
 unauthenticated.use("/auth", authRouter);
 unauthenticated.use("/auth", authRouter);
 authRouter.use(
 authRouter.use(
     rateLimitMiddleware({
     rateLimitMiddleware({
-        windowMin: 10,
-        max: 75,
+        windowMin:
+            config.rate_limits.auth?.window_minutes ||
+            config.rate_limits.global.window_minutes,
+        max:
+            config.rate_limits.auth?.max_requests ||
+            config.rate_limits.global.max_requests,
         type: "IP_AND_PATH"
         type: "IP_AND_PATH"
     })
     })
 );
 );

+ 7 - 5
server/routers/org/checkId.ts

@@ -9,9 +9,11 @@ import createHttpError from "http-errors";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const getOrgSchema = z.object({
-    orgId: z.string(),
-});
+const getOrgSchema = z
+    .object({
+        orgId: z.string()
+    })
+    .strict();
 
 
 export async function checkId(
 export async function checkId(
     req: Request,
     req: Request,
@@ -43,7 +45,7 @@ export async function checkId(
                 success: true,
                 success: true,
                 error: false,
                 error: false,
                 message: "Organization ID already exists",
                 message: "Organization ID already exists",
-                status: HttpCode.OK,
+                status: HttpCode.OK
             });
             });
         }
         }
 
 
@@ -52,7 +54,7 @@ export async function checkId(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Organization ID is available",
             message: "Organization ID is available",
-            status: HttpCode.NOT_FOUND,
+            status: HttpCode.NOT_FOUND
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 6 - 4
server/routers/org/deleteOrg.ts

@@ -10,9 +10,11 @@ import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const deleteOrgSchema = z.object({
-    orgId: z.string(),
-});
+const deleteOrgSchema = z
+    .object({
+        orgId: z.string()
+    })
+    .strict();
 
 
 export async function deleteOrg(
 export async function deleteOrg(
     req: Request,
     req: Request,
@@ -65,7 +67,7 @@ export async function deleteOrg(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Organization deleted successfully",
             message: "Organization deleted successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 7 - 5
server/routers/org/getOrg.ts

@@ -8,9 +8,11 @@ import HttpCode from "@server/types/HttpCode";
 import createHttpError from "http-errors";
 import createHttpError from "http-errors";
 import logger from "@server/logger";
 import logger from "@server/logger";
 
 
-const getOrgSchema = z.object({
-    orgId: z.string(),
-});
+const getOrgSchema = z
+    .object({
+        orgId: z.string()
+    })
+    .strict();
 
 
 export type GetOrgResponse = {
 export type GetOrgResponse = {
     org: Org;
     org: Org;
@@ -51,12 +53,12 @@ export async function getOrg(
 
 
         return response<GetOrgResponse>(res, {
         return response<GetOrgResponse>(res, {
             data: {
             data: {
-                org: org[0],
+                org: org[0]
             },
             },
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Organization retrieved successfully",
             message: "Organization retrieved successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 8 - 6
server/routers/org/updateOrg.ts

@@ -9,18 +9,20 @@ import createHttpError from "http-errors";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const updateOrgParamsSchema = z.object({
-    orgId: z.string(),
-});
+const updateOrgParamsSchema = z
+    .object({
+        orgId: z.string()
+    })
+    .strict();
 
 
 const updateOrgBodySchema = z
 const updateOrgBodySchema = z
     .object({
     .object({
         name: z.string().min(1).max(255).optional(),
         name: z.string().min(1).max(255).optional(),
-        domain: z.string().min(1).max(255).optional(),
+        domain: z.string().min(1).max(255).optional()
     })
     })
     .strict()
     .strict()
     .refine((data) => Object.keys(data).length > 0, {
     .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(
 export async function updateOrg(
@@ -72,7 +74,7 @@ export async function updateOrg(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Organization updated successfully",
             message: "Organization updated successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 23 - 20
server/routers/resource/authWithAccessToken.ts

@@ -17,13 +17,21 @@ import logger from "@server/logger";
 import { verify } from "@node-rs/argon2";
 import { verify } from "@node-rs/argon2";
 import { isWithinExpirationDate } from "oslo";
 import { isWithinExpirationDate } from "oslo";
 
 
-const authWithAccessTokenBodySchema = z.object({
-    accessToken: z.string()
-});
-
-const authWithAccessTokenParamsSchema = z.object({
-    resourceId: z.string().transform(Number).pipe(z.number().int().positive())
-});
+const authWithAccessTokenBodySchema = z
+    .object({
+        accessToken: z.string(),
+        accessTokenId: z.string()
+    })
+    .strict();
+
+const authWithAccessTokenParamsSchema = z
+    .object({
+        resourceId: z
+            .string()
+            .transform(Number)
+            .pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export type AuthWithAccessTokenResponse = {
 export type AuthWithAccessTokenResponse = {
     session?: string;
     session?: string;
@@ -57,9 +65,7 @@ export async function authWithAccessToken(
     }
     }
 
 
     const { resourceId } = parsedParams.data;
     const { resourceId } = parsedParams.data;
-    const { accessToken: at } = parsedBody.data;
-
-    const [accessTokenId, accessToken] = at.split(".");
+    const { accessToken, accessTokenId } = parsedBody.data;
 
 
     try {
     try {
         const [result] = await db
         const [result] = await db
@@ -86,7 +92,7 @@ export async function authWithAccessToken(
                     HttpCode.UNAUTHORIZED,
                     HttpCode.UNAUTHORIZED,
                     createHttpError(
                     createHttpError(
                         HttpCode.BAD_REQUEST,
                         HttpCode.BAD_REQUEST,
-                        "Email is not whitelisted"
+                        "Access token does not exist for resource"
                     )
                     )
                 )
                 )
             );
             );
@@ -98,15 +104,12 @@ export async function authWithAccessToken(
             );
             );
         }
         }
 
 
-        // const validCode = await verify(tokenItem.tokenHash, accessToken, {
-        //     memoryCost: 19456,
-        //     timeCost: 2,
-        //     outputLen: 32,
-        //     parallelism: 1
-        // });
-        logger.debug(`${accessToken} ${tokenItem.tokenHash}`)
-        const validCode = accessToken === tokenItem.tokenHash;
-
+        const validCode = await verify(tokenItem.tokenHash, accessToken, {
+            memoryCost: 19456,
+            timeCost: 2,
+            outputLen: 32,
+            parallelism: 1
+        });
         if (!validCode) {
         if (!validCode) {
             return next(
             return next(
                 createHttpError(HttpCode.UNAUTHORIZED, "Invalid access token")
                 createHttpError(HttpCode.UNAUTHORIZED, "Invalid access token")

+ 18 - 12
server/routers/resource/authWithPassword.ts

@@ -14,14 +14,22 @@ import {
     serializeResourceSessionCookie
     serializeResourceSessionCookie
 } from "@server/auth/resource";
 } from "@server/auth/resource";
 import config from "@server/config";
 import config from "@server/config";
-
-export const authWithPasswordBodySchema = z.object({
-    password: z.string()
-});
-
-export const authWithPasswordParamsSchema = z.object({
-    resourceId: z.string().transform(Number).pipe(z.number().int().positive())
-});
+import logger from "@server/logger";
+
+export const authWithPasswordBodySchema = z
+    .object({
+        password: z.string()
+    })
+    .strict();
+
+export const authWithPasswordParamsSchema = z
+    .object({
+        resourceId: z
+            .string()
+            .transform(Number)
+            .pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export type AuthWithPasswordResponse = {
 export type AuthWithPasswordResponse = {
     session?: string;
     session?: string;
@@ -120,10 +128,7 @@ export async function authWithPassword(
             passwordId: definedPassword.passwordId
             passwordId: definedPassword.passwordId
         });
         });
         const cookieName = `${config.server.resource_session_cookie_name}_${resource.resourceId}`;
         const cookieName = `${config.server.resource_session_cookie_name}_${resource.resourceId}`;
-        const cookie = serializeResourceSessionCookie(
-            cookieName,
-            token,
-        );
+        const cookie = serializeResourceSessionCookie(cookieName, token);
         res.appendHeader("Set-Cookie", cookie);
         res.appendHeader("Set-Cookie", cookie);
 
 
         return response<AuthWithPasswordResponse>(res, {
         return response<AuthWithPasswordResponse>(res, {
@@ -136,6 +141,7 @@ export async function authWithPassword(
             status: HttpCode.OK
             status: HttpCode.OK
         });
         });
     } catch (e) {
     } catch (e) {
+        logger.error(e);
         return next(
         return next(
             createHttpError(
             createHttpError(
                 HttpCode.INTERNAL_SERVER_ERROR,
                 HttpCode.INTERNAL_SERVER_ERROR,

+ 15 - 11
server/routers/resource/authWithPincode.ts

@@ -24,13 +24,20 @@ import config from "@server/config";
 import { AuthWithPasswordResponse } from "./authWithPassword";
 import { AuthWithPasswordResponse } from "./authWithPassword";
 import { isValidOtp, sendResourceOtpEmail } from "@server/auth/resourceOtp";
 import { isValidOtp, sendResourceOtpEmail } from "@server/auth/resourceOtp";
 
 
-export const authWithPincodeBodySchema = z.object({
-    pincode: z.string()
-});
-
-export const authWithPincodeParamsSchema = z.object({
-    resourceId: z.string().transform(Number).pipe(z.number().int().positive())
-});
+export const authWithPincodeBodySchema = z
+    .object({
+        pincode: z.string()
+    })
+    .strict();
+
+export const authWithPincodeParamsSchema = z
+    .object({
+        resourceId: z
+            .string()
+            .transform(Number)
+            .pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export type AuthWithPincodeResponse = {
 export type AuthWithPincodeResponse = {
     session?: string;
     session?: string;
@@ -128,10 +135,7 @@ export async function authWithPincode(
             pincodeId: definedPincode.pincodeId
             pincodeId: definedPincode.pincodeId
         });
         });
         const cookieName = `${config.server.resource_session_cookie_name}_${resource.resourceId}`;
         const cookieName = `${config.server.resource_session_cookie_name}_${resource.resourceId}`;
-        const cookie = serializeResourceSessionCookie(
-            cookieName,
-            token,
-        );
+        const cookie = serializeResourceSessionCookie(cookieName, token);
         res.appendHeader("Set-Cookie", cookie);
         res.appendHeader("Set-Cookie", cookie);
 
 
         return response<AuthWithPincodeResponse>(res, {
         return response<AuthWithPincodeResponse>(res, {

+ 16 - 12
server/routers/resource/authWithWhitelist.ts

@@ -22,14 +22,21 @@ import config from "@server/config";
 import { isValidOtp, sendResourceOtpEmail } from "@server/auth/resourceOtp";
 import { isValidOtp, sendResourceOtpEmail } from "@server/auth/resourceOtp";
 import logger from "@server/logger";
 import logger from "@server/logger";
 
 
-const authWithWhitelistBodySchema = z.object({
-    email: z.string().email(),
-    otp: z.string().optional()
-});
-
-const authWithWhitelistParamsSchema = z.object({
-    resourceId: z.string().transform(Number).pipe(z.number().int().positive())
-});
+const authWithWhitelistBodySchema = z
+    .object({
+        email: z.string().email(),
+        otp: z.string().optional()
+    })
+    .strict();
+
+const authWithWhitelistParamsSchema = z
+    .object({
+        resourceId: z
+            .string()
+            .transform(Number)
+            .pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export type AuthWithWhitelistResponse = {
 export type AuthWithWhitelistResponse = {
     otpSent?: boolean;
     otpSent?: boolean;
@@ -171,10 +178,7 @@ export async function authWithWhitelist(
             whitelistId: whitelistedEmail.whitelistId
             whitelistId: whitelistedEmail.whitelistId
         });
         });
         const cookieName = `${config.server.resource_session_cookie_name}_${resource.resourceId}`;
         const cookieName = `${config.server.resource_session_cookie_name}_${resource.resourceId}`;
-        const cookie = serializeResourceSessionCookie(
-            cookieName,
-            token,
-        );
+        const cookie = serializeResourceSessionCookie(cookieName, token);
         res.appendHeader("Set-Cookie", cookie);
         res.appendHeader("Set-Cookie", cookie);
 
 
         return response<AuthWithWhitelistResponse>(res, {
         return response<AuthWithWhitelistResponse>(res, {

+ 27 - 10
server/routers/resource/createResource.ts

@@ -1,3 +1,4 @@
+import { SqliteError } from "better-sqlite3";
 import { Request, Response, NextFunction } from "express";
 import { Request, Response, NextFunction } from "express";
 import { z } from "zod";
 import { z } from "zod";
 import { db } from "@server/db";
 import { db } from "@server/db";
@@ -7,7 +8,7 @@ import {
     resources,
     resources,
     roleResources,
     roleResources,
     roles,
     roles,
-    userResources,
+    userResources
 } from "@server/db/schema";
 } from "@server/db/schema";
 import response from "@server/utils/response";
 import response from "@server/utils/response";
 import HttpCode from "@server/types/HttpCode";
 import HttpCode from "@server/types/HttpCode";
@@ -16,16 +17,19 @@ import { eq, and } from "drizzle-orm";
 import stoi from "@server/utils/stoi";
 import stoi from "@server/utils/stoi";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 import { subdomainSchema } from "@server/schemas/subdomainSchema";
 import { subdomainSchema } from "@server/schemas/subdomainSchema";
+import logger from "@server/logger";
 
 
-const createResourceParamsSchema = z.object({
-    siteId: z.string().transform(stoi).pipe(z.number().int().positive()),
-    orgId: z.string(),
-});
+const createResourceParamsSchema = z
+    .object({
+        siteId: z.string().transform(stoi).pipe(z.number().int().positive()),
+        orgId: z.string()
+    })
+    .strict();
 
 
 const createResourceSchema = z
 const createResourceSchema = z
     .object({
     .object({
         name: z.string().min(1).max(255),
         name: z.string().min(1).max(255),
-        subdomain: subdomainSchema,
+        subdomain: subdomainSchema
     })
     })
     .strict();
     .strict();
 
 
@@ -94,7 +98,7 @@ export async function createResource(
                 orgId,
                 orgId,
                 name,
                 name,
                 subdomain,
                 subdomain,
-                ssl: true,
+                ssl: true
             })
             })
             .returning();
             .returning();
 
 
@@ -112,14 +116,14 @@ export async function createResource(
 
 
         await db.insert(roleResources).values({
         await db.insert(roleResources).values({
             roleId: adminRole[0].roleId,
             roleId: adminRole[0].roleId,
-            resourceId: newResource[0].resourceId,
+            resourceId: newResource[0].resourceId
         });
         });
 
 
         if (req.userOrgRoleId != adminRole[0].roleId) {
         if (req.userOrgRoleId != adminRole[0].roleId) {
             // make sure the user can access the resource
             // make sure the user can access the resource
             await db.insert(userResources).values({
             await db.insert(userResources).values({
                 userId: req.user?.userId!,
                 userId: req.user?.userId!,
-                resourceId: newResource[0].resourceId,
+                resourceId: newResource[0].resourceId
             });
             });
         }
         }
 
 
@@ -128,9 +132,22 @@ export async function createResource(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Resource created successfully",
             message: "Resource created successfully",
-            status: HttpCode.CREATED,
+            status: HttpCode.CREATED
         });
         });
     } catch (error) {
     } catch (error) {
+        if (
+            error instanceof SqliteError &&
+            error.code === "SQLITE_CONSTRAINT_UNIQUE"
+        ) {
+            return next(
+                createHttpError(
+                    HttpCode.CONFLICT,
+                    "Resource with that subdomain already exists"
+                )
+            );
+        }
+
+        logger.error(error);
         return next(
         return next(
             createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
             createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
         );
         );

+ 13 - 8
server/routers/resource/deleteResource.ts

@@ -12,9 +12,14 @@ import { addPeer } from "../gerbil/peers";
 import { removeTargets } from "../newt/targets";
 import { removeTargets } from "../newt/targets";
 
 
 // Define Zod schema for request parameters validation
 // Define Zod schema for request parameters validation
-const deleteResourceSchema = z.object({
-    resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const deleteResourceSchema = z
+    .object({
+        resourceId: z
+            .string()
+            .transform(Number)
+            .pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export async function deleteResource(
 export async function deleteResource(
     req: Request,
     req: Request,
@@ -67,20 +72,20 @@ export async function deleteResource(
                 )
                 )
             );
             );
         }
         }
-        
+
         if (site.pubKey) {
         if (site.pubKey) {
             if (site.type == "wireguard") {
             if (site.type == "wireguard") {
                 // TODO: is this all inefficient?
                 // TODO: is this all inefficient?
                 // Fetch resources for this site
                 // Fetch resources for this site
                 const resourcesRes = await db.query.resources.findMany({
                 const resourcesRes = await db.query.resources.findMany({
-                    where: eq(resources.siteId, site.siteId),
+                    where: eq(resources.siteId, site.siteId)
                 });
                 });
 
 
                 // Fetch targets for all resources of this site
                 // Fetch targets for all resources of this site
                 const targetIps = await Promise.all(
                 const targetIps = await Promise.all(
                     resourcesRes.map(async (resource) => {
                     resourcesRes.map(async (resource) => {
                         const targetsRes = await db.query.targets.findMany({
                         const targetsRes = await db.query.targets.findMany({
-                            where: eq(targets.resourceId, resource.resourceId),
+                            where: eq(targets.resourceId, resource.resourceId)
                         });
                         });
                         return targetsRes.map((target) => `${target.ip}/32`);
                         return targetsRes.map((target) => `${target.ip}/32`);
                     })
                     })
@@ -88,7 +93,7 @@ export async function deleteResource(
 
 
                 await addPeer(site.exitNodeId!, {
                 await addPeer(site.exitNodeId!, {
                     publicKey: site.pubKey,
                     publicKey: site.pubKey,
-                    allowedIps: targetIps.flat(),
+                    allowedIps: targetIps.flat()
                 });
                 });
             } else if (site.type == "newt") {
             } else if (site.type == "newt") {
                 // get the newt on the site by querying the newt table for siteId
                 // get the newt on the site by querying the newt table for siteId
@@ -107,7 +112,7 @@ export async function deleteResource(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Resource deleted successfully",
             message: "Resource deleted successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 11 - 4
server/routers/resource/getResource.ts

@@ -7,10 +7,16 @@ import response from "@server/utils/response";
 import HttpCode from "@server/types/HttpCode";
 import HttpCode from "@server/types/HttpCode";
 import createHttpError from "http-errors";
 import createHttpError from "http-errors";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
+import logger from "@server/logger";
 
 
-const getResourceSchema = z.object({
-    resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const getResourceSchema = z
+    .object({
+        resourceId: z
+            .string()
+            .transform(Number)
+            .pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export type GetResourceResponse = Resource;
 export type GetResourceResponse = Resource;
 
 
@@ -52,9 +58,10 @@ export async function getResource(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Resource retrieved successfully",
             message: "Resource retrieved successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
+        logger.error(error);
         return next(
         return next(
             createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
             createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
         );
         );

+ 19 - 15
server/routers/resource/getResourceAuthInfo.ts

@@ -4,17 +4,23 @@ import { db } from "@server/db";
 import {
 import {
     resourcePassword,
     resourcePassword,
     resourcePincode,
     resourcePincode,
-    resources,
+    resources
 } from "@server/db/schema";
 } from "@server/db/schema";
 import { eq } from "drizzle-orm";
 import { eq } from "drizzle-orm";
 import response from "@server/utils/response";
 import response from "@server/utils/response";
 import HttpCode from "@server/types/HttpCode";
 import HttpCode from "@server/types/HttpCode";
 import createHttpError from "http-errors";
 import createHttpError from "http-errors";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
+import logger from "@server/logger";
 
 
-const getResourceAuthInfoSchema = z.object({
-    resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const getResourceAuthInfoSchema = z
+    .object({
+        resourceId: z
+            .string()
+            .transform(Number)
+            .pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export type GetResourceAuthInfoResponse = {
 export type GetResourceAuthInfoResponse = {
     resourceId: number;
     resourceId: number;
@@ -30,7 +36,7 @@ export type GetResourceAuthInfoResponse = {
 export async function getResourceAuthInfo(
 export async function getResourceAuthInfo(
     req: Request,
     req: Request,
     res: Response,
     res: Response,
-    next: NextFunction,
+    next: NextFunction
 ): Promise<any> {
 ): Promise<any> {
     try {
     try {
         const parsedParams = getResourceAuthInfoSchema.safeParse(req.params);
         const parsedParams = getResourceAuthInfoSchema.safeParse(req.params);
@@ -38,8 +44,8 @@ export async function getResourceAuthInfo(
             return next(
             return next(
                 createHttpError(
                 createHttpError(
                     HttpCode.BAD_REQUEST,
                     HttpCode.BAD_REQUEST,
-                    fromError(parsedParams.error).toString(),
-                ),
+                    fromError(parsedParams.error).toString()
+                )
             );
             );
         }
         }
 
 
@@ -50,11 +56,11 @@ export async function getResourceAuthInfo(
             .from(resources)
             .from(resources)
             .leftJoin(
             .leftJoin(
                 resourcePincode,
                 resourcePincode,
-                eq(resourcePincode.resourceId, resources.resourceId),
+                eq(resourcePincode.resourceId, resources.resourceId)
             )
             )
             .leftJoin(
             .leftJoin(
                 resourcePassword,
                 resourcePassword,
-                eq(resourcePassword.resourceId, resources.resourceId),
+                eq(resourcePassword.resourceId, resources.resourceId)
             )
             )
             .where(eq(resources.resourceId, resourceId))
             .where(eq(resources.resourceId, resourceId))
             .limit(1);
             .limit(1);
@@ -67,7 +73,7 @@ export async function getResourceAuthInfo(
 
 
         if (!resource) {
         if (!resource) {
             return next(
             return next(
-                createHttpError(HttpCode.NOT_FOUND, "Resource not found"),
+                createHttpError(HttpCode.NOT_FOUND, "Resource not found")
             );
             );
         }
         }
 
 
@@ -85,14 +91,12 @@ export async function getResourceAuthInfo(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Resource auth info retrieved successfully",
             message: "Resource auth info retrieved successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
+        logger.error(error);
         return next(
         return next(
-            createHttpError(
-                HttpCode.INTERNAL_SERVER_ERROR,
-                "An error occurred",
-            ),
+            createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
         );
         );
     }
     }
 }
 }

+ 8 - 3
server/routers/resource/getResourceWhitelist.ts

@@ -9,9 +9,14 @@ import createHttpError from "http-errors";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const getResourceWhitelistSchema = z.object({
-    resourceId: z.string().transform(Number).pipe(z.number().int().positive())
-});
+const getResourceWhitelistSchema = z
+    .object({
+        resourceId: z
+            .string()
+            .transform(Number)
+            .pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 async function queryWhitelist(resourceId: number) {
 async function queryWhitelist(resourceId: number) {
     return await db
     return await db

+ 11 - 6
server/routers/resource/listResourceRoles.ts

@@ -9,9 +9,14 @@ import createHttpError from "http-errors";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const listResourceRolesSchema = z.object({
-    resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const listResourceRolesSchema = z
+    .object({
+        resourceId: z
+            .string()
+            .transform(Number)
+            .pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 async function query(resourceId: number) {
 async function query(resourceId: number) {
     return await db
     return await db
@@ -19,7 +24,7 @@ async function query(resourceId: number) {
             roleId: roles.roleId,
             roleId: roles.roleId,
             name: roles.name,
             name: roles.name,
             description: roles.description,
             description: roles.description,
-            isAdmin: roles.isAdmin,
+            isAdmin: roles.isAdmin
         })
         })
         .from(roleResources)
         .from(roleResources)
         .innerJoin(roles, eq(roleResources.roleId, roles.roleId))
         .innerJoin(roles, eq(roleResources.roleId, roles.roleId))
@@ -52,12 +57,12 @@ export async function listResourceRoles(
 
 
         return response<ListResourceRolesResponse>(res, {
         return response<ListResourceRolesResponse>(res, {
             data: {
             data: {
-                roles: resourceRolesList,
+                roles: resourceRolesList
             },
             },
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Resource roles retrieved successfully",
             message: "Resource roles retrieved successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 11 - 6
server/routers/resource/listResourceUsers.ts

@@ -9,15 +9,20 @@ import createHttpError from "http-errors";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const listResourceUsersSchema = z.object({
-    resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const listResourceUsersSchema = z
+    .object({
+        resourceId: z
+            .string()
+            .transform(Number)
+            .pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 async function queryUsers(resourceId: number) {
 async function queryUsers(resourceId: number) {
     return await db
     return await db
         .select({
         .select({
             userId: userResources.userId,
             userId: userResources.userId,
-            email: users.email,
+            email: users.email
         })
         })
         .from(userResources)
         .from(userResources)
         .innerJoin(users, eq(userResources.userId, users.userId))
         .innerJoin(users, eq(userResources.userId, users.userId))
@@ -50,12 +55,12 @@ export async function listResourceUsers(
 
 
         return response<ListResourceUsersResponse>(res, {
         return response<ListResourceUsersResponse>(res, {
             data: {
             data: {
-                users: resourceUsersList,
+                users: resourceUsersList
             },
             },
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Resource users retrieved successfully",
             message: "Resource users retrieved successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 30 - 32
server/routers/resource/listResources.ts

@@ -7,7 +7,7 @@ import {
     userResources,
     userResources,
     roleResources,
     roleResources,
     resourcePassword,
     resourcePassword,
-    resourcePincode,
+    resourcePincode
 } from "@server/db/schema";
 } from "@server/db/schema";
 import response from "@server/utils/response";
 import response from "@server/utils/response";
 import HttpCode from "@server/types/HttpCode";
 import HttpCode from "@server/types/HttpCode";
@@ -23,10 +23,11 @@ const listResourcesParamsSchema = z
             .optional()
             .optional()
             .transform(stoi)
             .transform(stoi)
             .pipe(z.number().int().positive().optional()),
             .pipe(z.number().int().positive().optional()),
-        orgId: z.string().optional(),
+        orgId: z.string().optional()
     })
     })
+    .strict()
     .refine((data) => !!data.siteId !== !!data.orgId, {
     .refine((data) => !!data.siteId !== !!data.orgId, {
-        message: "Either siteId or orgId must be provided, but not both",
+        message: "Either siteId or orgId must be provided, but not both"
     });
     });
 
 
 const listResourcesSchema = z.object({
 const listResourcesSchema = z.object({
@@ -42,13 +43,13 @@ const listResourcesSchema = z.object({
         .optional()
         .optional()
         .default("0")
         .default("0")
         .transform(Number)
         .transform(Number)
-        .pipe(z.number().int().nonnegative()),
+        .pipe(z.number().int().nonnegative())
 });
 });
 
 
 function queryResources(
 function queryResources(
     accessibleResourceIds: number[],
     accessibleResourceIds: number[],
     siteId?: number,
     siteId?: number,
-    orgId?: string,
+    orgId?: string
 ) {
 ) {
     if (siteId) {
     if (siteId) {
         return db
         return db
@@ -68,17 +69,17 @@ function queryResources(
             .leftJoin(sites, eq(resources.siteId, sites.siteId))
             .leftJoin(sites, eq(resources.siteId, sites.siteId))
             .leftJoin(
             .leftJoin(
                 resourcePassword,
                 resourcePassword,
-                eq(resourcePassword.resourceId, resources.resourceId),
+                eq(resourcePassword.resourceId, resources.resourceId)
             )
             )
             .leftJoin(
             .leftJoin(
                 resourcePincode,
                 resourcePincode,
-                eq(resourcePincode.resourceId, resources.resourceId),
+                eq(resourcePincode.resourceId, resources.resourceId)
             )
             )
             .where(
             .where(
                 and(
                 and(
                     inArray(resources.resourceId, accessibleResourceIds),
                     inArray(resources.resourceId, accessibleResourceIds),
-                    eq(resources.siteId, siteId),
-                ),
+                    eq(resources.siteId, siteId)
+                )
             );
             );
     } else if (orgId) {
     } else if (orgId) {
         return db
         return db
@@ -98,17 +99,17 @@ function queryResources(
             .leftJoin(sites, eq(resources.siteId, sites.siteId))
             .leftJoin(sites, eq(resources.siteId, sites.siteId))
             .leftJoin(
             .leftJoin(
                 resourcePassword,
                 resourcePassword,
-                eq(resourcePassword.resourceId, resources.resourceId),
+                eq(resourcePassword.resourceId, resources.resourceId)
             )
             )
             .leftJoin(
             .leftJoin(
                 resourcePincode,
                 resourcePincode,
-                eq(resourcePincode.resourceId, resources.resourceId),
+                eq(resourcePincode.resourceId, resources.resourceId)
             )
             )
             .where(
             .where(
                 and(
                 and(
                     inArray(resources.resourceId, accessibleResourceIds),
                     inArray(resources.resourceId, accessibleResourceIds),
-                    eq(resources.orgId, orgId),
-                ),
+                    eq(resources.orgId, orgId)
+                )
             );
             );
     }
     }
 }
 }
@@ -121,7 +122,7 @@ export type ListResourcesResponse = {
 export async function listResources(
 export async function listResources(
     req: Request,
     req: Request,
     res: Response,
     res: Response,
-    next: NextFunction,
+    next: NextFunction
 ): Promise<any> {
 ): Promise<any> {
     try {
     try {
         const parsedQuery = listResourcesSchema.safeParse(req.query);
         const parsedQuery = listResourcesSchema.safeParse(req.query);
@@ -129,8 +130,8 @@ export async function listResources(
             return next(
             return next(
                 createHttpError(
                 createHttpError(
                     HttpCode.BAD_REQUEST,
                     HttpCode.BAD_REQUEST,
-                    parsedQuery.error.errors.map((e) => e.message).join(", "),
-                ),
+                    parsedQuery.error.errors.map((e) => e.message).join(", ")
+                )
             );
             );
         }
         }
         const { limit, offset } = parsedQuery.data;
         const { limit, offset } = parsedQuery.data;
@@ -140,8 +141,8 @@ export async function listResources(
             return next(
             return next(
                 createHttpError(
                 createHttpError(
                     HttpCode.BAD_REQUEST,
                     HttpCode.BAD_REQUEST,
-                    parsedParams.error.errors.map((e) => e.message).join(", "),
-                ),
+                    parsedParams.error.errors.map((e) => e.message).join(", ")
+                )
             );
             );
         }
         }
         const { siteId, orgId } = parsedParams.data;
         const { siteId, orgId } = parsedParams.data;
@@ -150,29 +151,29 @@ export async function listResources(
             return next(
             return next(
                 createHttpError(
                 createHttpError(
                     HttpCode.FORBIDDEN,
                     HttpCode.FORBIDDEN,
-                    "User does not have access to this organization",
-                ),
+                    "User does not have access to this organization"
+                )
             );
             );
         }
         }
 
 
         const accessibleResources = await db
         const accessibleResources = await db
             .select({
             .select({
-                resourceId: sql<number>`COALESCE(${userResources.resourceId}, ${roleResources.resourceId})`,
+                resourceId: sql<number>`COALESCE(${userResources.resourceId}, ${roleResources.resourceId})`
             })
             })
             .from(userResources)
             .from(userResources)
             .fullJoin(
             .fullJoin(
                 roleResources,
                 roleResources,
-                eq(userResources.resourceId, roleResources.resourceId),
+                eq(userResources.resourceId, roleResources.resourceId)
             )
             )
             .where(
             .where(
                 or(
                 or(
                     eq(userResources.userId, req.user!.userId),
                     eq(userResources.userId, req.user!.userId),
-                    eq(roleResources.roleId, req.userOrgRoleId!),
-                ),
+                    eq(roleResources.roleId, req.userOrgRoleId!)
+                )
             );
             );
 
 
         const accessibleResourceIds = accessibleResources.map(
         const accessibleResourceIds = accessibleResources.map(
-            (resource) => resource.resourceId,
+            (resource) => resource.resourceId
         );
         );
 
 
         let countQuery: any = db
         let countQuery: any = db
@@ -192,21 +193,18 @@ export async function listResources(
                 pagination: {
                 pagination: {
                     total: totalCount,
                     total: totalCount,
                     limit,
                     limit,
-                    offset,
-                },
+                    offset
+                }
             },
             },
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Resources retrieved successfully",
             message: "Resources retrieved successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);
         return next(
         return next(
-            createHttpError(
-                HttpCode.INTERNAL_SERVER_ERROR,
-                "An error occurred",
-            ),
+            createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
         );
         );
     }
     }
 }
 }

+ 6 - 4
server/routers/resource/setResourcePassword.ts

@@ -8,14 +8,15 @@ import createHttpError from "http-errors";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 import { hash } from "@node-rs/argon2";
 import { hash } from "@node-rs/argon2";
 import { response } from "@server/utils";
 import { response } from "@server/utils";
+import logger from "@server/logger";
 
 
 const setResourceAuthMethodsParamsSchema = z.object({
 const setResourceAuthMethodsParamsSchema = z.object({
-    resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
+    resourceId: z.string().transform(Number).pipe(z.number().int().positive())
 });
 });
 
 
 const setResourceAuthMethodsBodySchema = z
 const setResourceAuthMethodsBodySchema = z
     .object({
     .object({
-        password: z.string().min(4).max(100).nullable(),
+        password: z.string().min(4).max(100).nullable()
     })
     })
     .strict();
     .strict();
 
 
@@ -60,7 +61,7 @@ export async function setResourcePassword(
                     memoryCost: 19456,
                     memoryCost: 19456,
                     timeCost: 2,
                     timeCost: 2,
                     outputLen: 32,
                     outputLen: 32,
-                    parallelism: 1,
+                    parallelism: 1
                 });
                 });
 
 
                 await trx
                 await trx
@@ -74,9 +75,10 @@ export async function setResourcePassword(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Resource password set successfully",
             message: "Resource password set successfully",
-            status: HttpCode.CREATED,
+            status: HttpCode.CREATED
         });
         });
     } catch (error) {
     } catch (error) {
+        logger.error(error);
         return next(
         return next(
             createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
             createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
         );
         );

+ 2 - 0
server/routers/resource/setResourcePincode.ts

@@ -9,6 +9,7 @@ import { fromError } from "zod-validation-error";
 import { hash } from "@node-rs/argon2";
 import { hash } from "@node-rs/argon2";
 import { response } from "@server/utils";
 import { response } from "@server/utils";
 import stoi from "@server/utils/stoi";
 import stoi from "@server/utils/stoi";
+import logger from "@server/logger";
 
 
 const setResourceAuthMethodsParamsSchema = z.object({
 const setResourceAuthMethodsParamsSchema = z.object({
     resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
     resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
@@ -81,6 +82,7 @@ export async function setResourcePincode(
             status: HttpCode.CREATED,
             status: HttpCode.CREATED,
         });
         });
     } catch (error) {
     } catch (error) {
+        logger.error(error);
         return next(
         return next(
             createHttpError(
             createHttpError(
                 HttpCode.INTERNAL_SERVER_ERROR,
                 HttpCode.INTERNAL_SERVER_ERROR,

+ 14 - 7
server/routers/resource/setResourceRoles.ts

@@ -9,13 +9,20 @@ import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 import { eq, and, ne } from "drizzle-orm";
 import { eq, and, ne } from "drizzle-orm";
 
 
-const setResourceRolesBodySchema = z.object({
-    roleIds: z.array(z.number().int().positive()),
-});
+const setResourceRolesBodySchema = z
+    .object({
+        roleIds: z.array(z.number().int().positive())
+    })
+    .strict();
 
 
-const setResourceRolesParamsSchema = z.object({
-    resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const setResourceRolesParamsSchema = z
+    .object({
+        resourceId: z
+            .string()
+            .transform(Number)
+            .pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export async function setResourceRoles(
 export async function setResourceRoles(
     req: Request,
     req: Request,
@@ -99,7 +106,7 @@ export async function setResourceRoles(
                 success: true,
                 success: true,
                 error: false,
                 error: false,
                 message: "Roles set for resource successfully",
                 message: "Roles set for resource successfully",
-                status: HttpCode.CREATED,
+                status: HttpCode.CREATED
             });
             });
         });
         });
     } catch (error) {
     } catch (error) {

+ 14 - 7
server/routers/resource/setResourceUsers.ts

@@ -9,13 +9,20 @@ import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 import { eq } from "drizzle-orm";
 import { eq } from "drizzle-orm";
 
 
-const setUserResourcesBodySchema = z.object({
-    userIds: z.array(z.string()),
-});
+const setUserResourcesBodySchema = z
+    .object({
+        userIds: z.array(z.string())
+    })
+    .strict();
 
 
-const setUserResourcesParamsSchema = z.object({
-    resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const setUserResourcesParamsSchema = z
+    .object({
+        resourceId: z
+            .string()
+            .transform(Number)
+            .pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export async function setResourceUsers(
 export async function setResourceUsers(
     req: Request,
     req: Request,
@@ -66,7 +73,7 @@ export async function setResourceUsers(
                 success: true,
                 success: true,
                 error: false,
                 error: false,
                 message: "Users set for resource successfully",
                 message: "Users set for resource successfully",
-                status: HttpCode.CREATED,
+                status: HttpCode.CREATED
             });
             });
         });
         });
     } catch (error) {
     } catch (error) {

+ 13 - 6
server/routers/resource/setResourceWhitelist.ts

@@ -9,13 +9,20 @@ import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 import { and, eq } from "drizzle-orm";
 import { and, eq } from "drizzle-orm";
 
 
-const setResourceWhitelistBodySchema = z.object({
-    emails: z.array(z.string().email()).max(50)
-});
+const setResourceWhitelistBodySchema = z
+    .object({
+        emails: z.array(z.string().email()).max(50)
+    })
+    .strict();
 
 
-const setResourceWhitelistParamsSchema = z.object({
-    resourceId: z.string().transform(Number).pipe(z.number().int().positive())
-});
+const setResourceWhitelistParamsSchema = z
+    .object({
+        resourceId: z
+            .string()
+            .transform(Number)
+            .pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export async function setResourceWhitelist(
 export async function setResourceWhitelist(
     req: Request,
     req: Request,

+ 24 - 22
server/routers/resource/updateResource.ts

@@ -10,9 +10,14 @@ import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 import { subdomainSchema } from "@server/schemas/subdomainSchema";
 import { subdomainSchema } from "@server/schemas/subdomainSchema";
 
 
-const updateResourceParamsSchema = z.object({
-    resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const updateResourceParamsSchema = z
+    .object({
+        resourceId: z
+            .string()
+            .transform(Number)
+            .pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 const updateResourceBodySchema = z
 const updateResourceBodySchema = z
     .object({
     .object({
@@ -21,18 +26,18 @@ const updateResourceBodySchema = z
         ssl: z.boolean().optional(),
         ssl: z.boolean().optional(),
         sso: z.boolean().optional(),
         sso: z.boolean().optional(),
         blockAccess: z.boolean().optional(),
         blockAccess: z.boolean().optional(),
-        emailWhitelistEnabled: z.boolean().optional(),
+        emailWhitelistEnabled: z.boolean().optional()
         // siteId: z.number(),
         // siteId: z.number(),
     })
     })
     .strict()
     .strict()
     .refine((data) => Object.keys(data).length > 0, {
     .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(
 export async function updateResource(
     req: Request,
     req: Request,
     res: Response,
     res: Response,
-    next: NextFunction,
+    next: NextFunction
 ): Promise<any> {
 ): Promise<any> {
     try {
     try {
         const parsedParams = updateResourceParamsSchema.safeParse(req.params);
         const parsedParams = updateResourceParamsSchema.safeParse(req.params);
@@ -40,8 +45,8 @@ export async function updateResource(
             return next(
             return next(
                 createHttpError(
                 createHttpError(
                     HttpCode.BAD_REQUEST,
                     HttpCode.BAD_REQUEST,
-                    fromError(parsedParams.error).toString(),
-                ),
+                    fromError(parsedParams.error).toString()
+                )
             );
             );
         }
         }
 
 
@@ -50,8 +55,8 @@ export async function updateResource(
             return next(
             return next(
                 createHttpError(
                 createHttpError(
                     HttpCode.BAD_REQUEST,
                     HttpCode.BAD_REQUEST,
-                    fromError(parsedBody.error).toString(),
-                ),
+                    fromError(parsedBody.error).toString()
+                )
             );
             );
         }
         }
 
 
@@ -68,8 +73,8 @@ export async function updateResource(
             return next(
             return next(
                 createHttpError(
                 createHttpError(
                     HttpCode.NOT_FOUND,
                     HttpCode.NOT_FOUND,
-                    `Resource with ID ${resourceId} not found`,
-                ),
+                    `Resource with ID ${resourceId} not found`
+                )
             );
             );
         }
         }
 
 
@@ -77,8 +82,8 @@ export async function updateResource(
             return next(
             return next(
                 createHttpError(
                 createHttpError(
                     HttpCode.BAD_REQUEST,
                     HttpCode.BAD_REQUEST,
-                    "Resource does not have a domain",
-                ),
+                    "Resource does not have a domain"
+                )
             );
             );
         }
         }
 
 
@@ -88,7 +93,7 @@ export async function updateResource(
 
 
         const updatePayload = {
         const updatePayload = {
             ...updateData,
             ...updateData,
-            ...(fullDomain && { fullDomain }),
+            ...(fullDomain && { fullDomain })
         };
         };
 
 
         const updatedResource = await db
         const updatedResource = await db
@@ -101,8 +106,8 @@ export async function updateResource(
             return next(
             return next(
                 createHttpError(
                 createHttpError(
                     HttpCode.NOT_FOUND,
                     HttpCode.NOT_FOUND,
-                    `Resource with ID ${resourceId} not found`,
-                ),
+                    `Resource with ID ${resourceId} not found`
+                )
             );
             );
         }
         }
 
 
@@ -111,15 +116,12 @@ export async function updateResource(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Resource updated successfully",
             message: "Resource updated successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);
         return next(
         return next(
-            createHttpError(
-                HttpCode.INTERNAL_SERVER_ERROR,
-                "An error occurred",
-            ),
+            createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
         );
         );
     }
     }
 }
 }

+ 12 - 8
server/routers/role/addRoleAction.ts

@@ -9,13 +9,17 @@ import logger from "@server/logger";
 import { eq } from "drizzle-orm";
 import { eq } from "drizzle-orm";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const addRoleActionParamSchema = z.object({
-    roleId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const addRoleActionParamSchema = z
+    .object({
+        roleId: z.string().transform(Number).pipe(z.number().int().positive())
+    })
+    .strict();
 
 
-const addRoleActionSchema = z.object({
-    actionId: z.string(),
-});
+const addRoleActionSchema = z
+    .object({
+        actionId: z.string()
+    })
+    .strict();
 
 
 export async function addRoleAction(
 export async function addRoleAction(
     req: Request,
     req: Request,
@@ -66,7 +70,7 @@ export async function addRoleAction(
             .values({
             .values({
                 roleId,
                 roleId,
                 actionId,
                 actionId,
-                orgId: role[0].orgId!,
+                orgId: role[0].orgId!
             })
             })
             .returning();
             .returning();
 
 
@@ -75,7 +79,7 @@ export async function addRoleAction(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Action added to role successfully",
             message: "Action added to role successfully",
-            status: HttpCode.CREATED,
+            status: HttpCode.CREATED
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 13 - 9
server/routers/role/addRoleSite.ts

@@ -9,13 +9,17 @@ import logger from "@server/logger";
 import { eq } from "drizzle-orm";
 import { eq } from "drizzle-orm";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const addRoleSiteParamsSchema = z.object({
-    roleId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const addRoleSiteParamsSchema = z
+    .object({
+        roleId: z.string().transform(Number).pipe(z.number().int().positive())
+    })
+    .strict();
 
 
-const addRoleSiteSchema = z.object({
-    siteId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const addRoleSiteSchema = z
+    .object({
+        siteId: z.string().transform(Number).pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export async function addRoleSite(
 export async function addRoleSite(
     req: Request,
     req: Request,
@@ -51,7 +55,7 @@ export async function addRoleSite(
             .insert(roleSites)
             .insert(roleSites)
             .values({
             .values({
                 roleId,
                 roleId,
-                siteId,
+                siteId
             })
             })
             .returning();
             .returning();
 
 
@@ -63,7 +67,7 @@ export async function addRoleSite(
         for (const resource of siteResources) {
         for (const resource of siteResources) {
             await db.insert(roleResources).values({
             await db.insert(roleResources).values({
                 roleId,
                 roleId,
-                resourceId: resource.resourceId,
+                resourceId: resource.resourceId
             });
             });
         }
         }
 
 
@@ -72,7 +76,7 @@ export async function addRoleSite(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Site added to role successfully",
             message: "Site added to role successfully",
-            status: HttpCode.CREATED,
+            status: HttpCode.CREATED
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 11 - 9
server/routers/role/createRole.ts

@@ -10,21 +10,23 @@ import { fromError } from "zod-validation-error";
 import { ActionsEnum } from "@server/auth/actions";
 import { ActionsEnum } from "@server/auth/actions";
 import { eq, and } from "drizzle-orm";
 import { eq, and } from "drizzle-orm";
 
 
-const createRoleParamsSchema = z.object({
-    orgId: z.string(),
-});
+const createRoleParamsSchema = z
+    .object({
+        orgId: z.string()
+    })
+    .strict();
 
 
 const createRoleSchema = z
 const createRoleSchema = z
     .object({
     .object({
         name: z.string().min(1).max(255),
         name: z.string().min(1).max(255),
-        description: z.string().optional(),
+        description: z.string().optional()
     })
     })
     .strict();
     .strict();
 
 
 export const defaultRoleAllowedActions: ActionsEnum[] = [
 export const defaultRoleAllowedActions: ActionsEnum[] = [
     ActionsEnum.getOrg,
     ActionsEnum.getOrg,
     ActionsEnum.getResource,
     ActionsEnum.getResource,
-    ActionsEnum.listResources,
+    ActionsEnum.listResources
 ];
 ];
 
 
 export type CreateRoleBody = z.infer<typeof createRoleSchema>;
 export type CreateRoleBody = z.infer<typeof createRoleSchema>;
@@ -64,7 +66,7 @@ export async function createRole(
         const allRoles = await db
         const allRoles = await db
             .select({
             .select({
                 roleId: roles.roleId,
                 roleId: roles.roleId,
-                name: roles.name,
+                name: roles.name
             })
             })
             .from(roles)
             .from(roles)
             .leftJoin(orgs, eq(roles.orgId, orgs.orgId))
             .leftJoin(orgs, eq(roles.orgId, orgs.orgId))
@@ -84,7 +86,7 @@ export async function createRole(
             .insert(roles)
             .insert(roles)
             .values({
             .values({
                 ...roleData,
                 ...roleData,
-                orgId,
+                orgId
             })
             })
             .returning();
             .returning();
 
 
@@ -94,7 +96,7 @@ export async function createRole(
                 defaultRoleAllowedActions.map((action) => ({
                 defaultRoleAllowedActions.map((action) => ({
                     roleId: newRole[0].roleId,
                     roleId: newRole[0].roleId,
                     actionId: action,
                     actionId: action,
-                    orgId,
+                    orgId
                 }))
                 }))
             )
             )
             .execute();
             .execute();
@@ -104,7 +106,7 @@ export async function createRole(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Role created successfully",
             message: "Role created successfully",
-            status: HttpCode.CREATED,
+            status: HttpCode.CREATED
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 11 - 7
server/routers/role/deleteRole.ts

@@ -9,13 +9,17 @@ import createHttpError from "http-errors";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const deleteRoleSchema = z.object({
-    roleId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const deleteRoleSchema = z
+    .object({
+        roleId: z.string().transform(Number).pipe(z.number().int().positive())
+    })
+    .strict();
 
 
-const deelteRoleBodySchema = z.object({
-    roleId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const deelteRoleBodySchema = z
+    .object({
+        roleId: z.string().transform(Number).pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export async function deleteRole(
 export async function deleteRole(
     req: Request,
     req: Request,
@@ -108,7 +112,7 @@ export async function deleteRole(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Role deleted successfully",
             message: "Role deleted successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 6 - 4
server/routers/role/getRole.ts

@@ -9,9 +9,11 @@ import createHttpError from "http-errors";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const getRoleSchema = z.object({
-    roleId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const getRoleSchema = z
+    .object({
+        roleId: z.string().transform(Number).pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export async function getRole(
 export async function getRole(
     req: Request,
     req: Request,
@@ -51,7 +53,7 @@ export async function getRole(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Role retrieved successfully",
             message: "Role retrieved successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 7 - 5
server/routers/role/listRoleActions.ts

@@ -9,9 +9,11 @@ import createHttpError from "http-errors";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const listRoleActionsSchema = z.object({
-    roleId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const listRoleActionsSchema = z
+    .object({
+        roleId: z.string().transform(Number).pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export async function listRoleActions(
 export async function listRoleActions(
     req: Request,
     req: Request,
@@ -35,7 +37,7 @@ export async function listRoleActions(
             .select({
             .select({
                 actionId: actions.actionId,
                 actionId: actions.actionId,
                 name: actions.name,
                 name: actions.name,
-                description: actions.description,
+                description: actions.description
             })
             })
             .from(roleActions)
             .from(roleActions)
             .innerJoin(actions, eq(roleActions.actionId, actions.actionId))
             .innerJoin(actions, eq(roleActions.actionId, actions.actionId))
@@ -48,7 +50,7 @@ export async function listRoleActions(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Role actions retrieved successfully",
             message: "Role actions retrieved successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 7 - 5
server/routers/role/listRoleResources.ts

@@ -9,9 +9,11 @@ import createHttpError from "http-errors";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const listRoleResourcesSchema = z.object({
-    roleId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const listRoleResourcesSchema = z
+    .object({
+        roleId: z.string().transform(Number).pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export async function listRoleResources(
 export async function listRoleResources(
     req: Request,
     req: Request,
@@ -35,7 +37,7 @@ export async function listRoleResources(
             .select({
             .select({
                 resourceId: resources.resourceId,
                 resourceId: resources.resourceId,
                 name: resources.name,
                 name: resources.name,
-                subdomain: resources.subdomain,
+                subdomain: resources.subdomain
             })
             })
             .from(roleResources)
             .from(roleResources)
             .innerJoin(
             .innerJoin(
@@ -51,7 +53,7 @@ export async function listRoleResources(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Role resources retrieved successfully",
             message: "Role resources retrieved successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 7 - 5
server/routers/role/listRoleSites.ts

@@ -9,9 +9,11 @@ import createHttpError from "http-errors";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const listRoleSitesSchema = z.object({
-    roleId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const listRoleSitesSchema = z
+    .object({
+        roleId: z.string().transform(Number).pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export async function listRoleSites(
 export async function listRoleSites(
     req: Request,
     req: Request,
@@ -34,7 +36,7 @@ export async function listRoleSites(
         const roleSitesList = await db
         const roleSitesList = await db
             .select({
             .select({
                 siteId: sites.siteId,
                 siteId: sites.siteId,
-                name: sites.name,
+                name: sites.name
             })
             })
             .from(roleSites)
             .from(roleSites)
             .innerJoin(sites, eq(roleSites.siteId, sites.siteId))
             .innerJoin(sites, eq(roleSites.siteId, sites.siteId))
@@ -47,7 +49,7 @@ export async function listRoleSites(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Role sites retrieved successfully",
             message: "Role sites retrieved successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 10 - 8
server/routers/role/listRoles.ts

@@ -10,9 +10,11 @@ import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 import stoi from "@server/utils/stoi";
 import stoi from "@server/utils/stoi";
 
 
-const listRolesParamsSchema = z.object({
-    orgId: z.string(),
-});
+const listRolesParamsSchema = z
+    .object({
+        orgId: z.string()
+    })
+    .strict();
 
 
 const listRolesSchema = z.object({
 const listRolesSchema = z.object({
     limit: z
     limit: z
@@ -26,7 +28,7 @@ const listRolesSchema = z.object({
         .optional()
         .optional()
         .default("0")
         .default("0")
         .transform(Number)
         .transform(Number)
-        .pipe(z.number().int().nonnegative()),
+        .pipe(z.number().int().nonnegative())
 });
 });
 
 
 async function queryRoles(orgId: string, limit: number, offset: number) {
 async function queryRoles(orgId: string, limit: number, offset: number) {
@@ -37,7 +39,7 @@ async function queryRoles(orgId: string, limit: number, offset: number) {
             isAdmin: roles.isAdmin,
             isAdmin: roles.isAdmin,
             name: roles.name,
             name: roles.name,
             description: roles.description,
             description: roles.description,
-            orgName: orgs.name,
+            orgName: orgs.name
         })
         })
         .from(roles)
         .from(roles)
         .leftJoin(orgs, eq(roles.orgId, orgs.orgId))
         .leftJoin(orgs, eq(roles.orgId, orgs.orgId))
@@ -100,13 +102,13 @@ export async function listRoles(
                 pagination: {
                 pagination: {
                     total: totalCount,
                     total: totalCount,
                     limit,
                     limit,
-                    offset,
-                },
+                    offset
+                }
             },
             },
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Roles retrieved successfully",
             message: "Roles retrieved successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 11 - 7
server/routers/role/removeRoleAction.ts

@@ -9,13 +9,17 @@ import createHttpError from "http-errors";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const removeRoleActionParamsSchema = z.object({
-    roleId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const removeRoleActionParamsSchema = z
+    .object({
+        roleId: z.string().transform(Number).pipe(z.number().int().positive())
+    })
+    .strict();
 
 
-const removeRoleActionSchema = z.object({
-    actionId: z.string(),
-});
+const removeRoleActionSchema = z
+    .object({
+        actionId: z.string()
+    })
+    .strict();
 
 
 export async function removeRoleAction(
 export async function removeRoleAction(
     req: Request,
     req: Request,
@@ -71,7 +75,7 @@ export async function removeRoleAction(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Action removed from role successfully",
             message: "Action removed from role successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 14 - 7
server/routers/role/removeRoleResource.ts

@@ -9,13 +9,20 @@ import createHttpError from "http-errors";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const removeRoleResourceParamsSchema = z.object({
-    roleId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const removeRoleResourceParamsSchema = z
+    .object({
+        roleId: z.string().transform(Number).pipe(z.number().int().positive())
+    })
+    .strict();
 
 
-const removeRoleResourceSchema = z.object({
-    resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const removeRoleResourceSchema = z
+    .object({
+        resourceId: z
+            .string()
+            .transform(Number)
+            .pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export async function removeRoleResource(
 export async function removeRoleResource(
     req: Request,
     req: Request,
@@ -71,7 +78,7 @@ export async function removeRoleResource(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Resource removed from role successfully",
             message: "Resource removed from role successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 11 - 7
server/routers/role/removeRoleSite.ts

@@ -9,13 +9,17 @@ import createHttpError from "http-errors";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const removeRoleSiteParamsSchema = z.object({
-    roleId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const removeRoleSiteParamsSchema = z
+    .object({
+        roleId: z.string().transform(Number).pipe(z.number().int().positive())
+    })
+    .strict();
 
 
-const removeRoleSiteSchema = z.object({
-    siteId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const removeRoleSiteSchema = z
+    .object({
+        siteId: z.string().transform(Number).pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export async function removeRoleSite(
 export async function removeRoleSite(
     req: Request,
     req: Request,
@@ -85,7 +89,7 @@ export async function removeRoleSite(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Site removed from role successfully",
             message: "Site removed from role successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 8 - 6
server/routers/role/updateRole.ts

@@ -9,18 +9,20 @@ import createHttpError from "http-errors";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const updateRoleParamsSchema = z.object({
-    roleId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const updateRoleParamsSchema = z
+    .object({
+        roleId: z.string().transform(Number).pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 const updateRoleBodySchema = z
 const updateRoleBodySchema = z
     .object({
     .object({
         name: z.string().min(1).max(255).optional(),
         name: z.string().min(1).max(255).optional(),
-        description: z.string().optional(),
+        description: z.string().optional()
     })
     })
     .strict()
     .strict()
     .refine((data) => Object.keys(data).length > 0, {
     .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 updateRole(
 export async function updateRole(
@@ -96,7 +98,7 @@ export async function updateRole(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Role updated successfully",
             message: "Role updated successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 19 - 26
server/routers/site/createSite.ts

@@ -1,7 +1,7 @@
 import { Request, Response, NextFunction } from "express";
 import { Request, Response, NextFunction } from "express";
 import { z } from "zod";
 import { z } from "zod";
 import { db } from "@server/db";
 import { db } from "@server/db";
-import { roles, userSites, sites, roleSites } from "@server/db/schema";
+import { roles, userSites, sites, roleSites, Site } from "@server/db/schema";
 import response from "@server/utils/response";
 import response from "@server/utils/response";
 import HttpCode from "@server/types/HttpCode";
 import HttpCode from "@server/types/HttpCode";
 import createHttpError from "http-errors";
 import createHttpError from "http-errors";
@@ -14,9 +14,11 @@ import { hash } from "@node-rs/argon2";
 import { newts } from "@server/db/schema";
 import { newts } from "@server/db/schema";
 import moment from "moment";
 import moment from "moment";
 
 
-const createSiteParamsSchema = z.object({
-    orgId: z.string(),
-});
+const createSiteParamsSchema = z
+    .object({
+        orgId: z.string()
+    })
+    .strict();
 
 
 const createSiteSchema = z
 const createSiteSchema = z
     .object({
     .object({
@@ -27,18 +29,13 @@ const createSiteSchema = z
         subnet: z.string(),
         subnet: z.string(),
         newtId: z.string().optional(),
         newtId: z.string().optional(),
         secret: z.string().optional(),
         secret: z.string().optional(),
-        type: z.string(),
+        type: z.string()
     })
     })
     .strict();
     .strict();
 
 
 export type CreateSiteBody = z.infer<typeof createSiteSchema>;
 export type CreateSiteBody = z.infer<typeof createSiteSchema>;
 
 
-export type CreateSiteResponse = {
-    name: string;
-    siteId: number;
-    orgId: string;
-    niceId: string;
-};
+export type CreateSiteResponse = Site;
 
 
 export async function createSite(
 export async function createSite(
     req: Request,
     req: Request,
@@ -85,14 +82,14 @@ export async function createSite(
             name,
             name,
             niceId,
             niceId,
             subnet,
             subnet,
-            type,
+            type
         };
         };
 
 
         if (pubKey && type == "wireguard") {
         if (pubKey && type == "wireguard") {
             // we dont add the pubKey for newts because the newt will generate it
             // we dont add the pubKey for newts because the newt will generate it
             payload = {
             payload = {
                 ...payload,
                 ...payload,
-                pubKey,
+                pubKey
             };
             };
         }
         }
 
 
@@ -112,14 +109,14 @@ export async function createSite(
 
 
         await db.insert(roleSites).values({
         await db.insert(roleSites).values({
             roleId: adminRole[0].roleId,
             roleId: adminRole[0].roleId,
-            siteId: newSite.siteId,
+            siteId: newSite.siteId
         });
         });
 
 
         if (req.userOrgRoleId != adminRole[0].roleId) {
         if (req.userOrgRoleId != adminRole[0].roleId) {
             // make sure the user can access the site
             // make sure the user can access the site
             db.insert(userSites).values({
             db.insert(userSites).values({
                 userId: req.user?.userId!,
                 userId: req.user?.userId!,
-                siteId: newSite.siteId,
+                siteId: newSite.siteId
             });
             });
         }
         }
 
 
@@ -129,14 +126,14 @@ export async function createSite(
                 memoryCost: 19456,
                 memoryCost: 19456,
                 timeCost: 2,
                 timeCost: 2,
                 outputLen: 32,
                 outputLen: 32,
-                parallelism: 1,
+                parallelism: 1
             });
             });
 
 
             await db.insert(newts).values({
             await db.insert(newts).values({
                 newtId: newtId!,
                 newtId: newtId!,
                 secretHash,
                 secretHash,
                 siteId: newSite.siteId,
                 siteId: newSite.siteId,
-                dateCreated: moment().toISOString(),
+                dateCreated: moment().toISOString()
             });
             });
         } else if (type == "wireguard") {
         } else if (type == "wireguard") {
             if (!pubKey) {
             if (!pubKey) {
@@ -149,23 +146,19 @@ export async function createSite(
             }
             }
             await addPeer(exitNodeId, {
             await addPeer(exitNodeId, {
                 publicKey: pubKey,
                 publicKey: pubKey,
-                allowedIps: [],
+                allowedIps: []
             });
             });
         }
         }
 
 
-        return response(res, {
-            data: {
-                name: newSite.name,
-                niceId: newSite.niceId,
-                siteId: newSite.siteId,
-                orgId: newSite.orgId,
-            },
+        return response<CreateSiteResponse>(res, {
+            data: newSite,
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Site created successfully",
             message: "Site created successfully",
-            status: HttpCode.CREATED,
+            status: HttpCode.CREATED
         });
         });
     } catch (error) {
     } catch (error) {
+        logger.error(error);
         return next(
         return next(
             createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
             createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
         );
         );

+ 7 - 5
server/routers/site/deleteSite.ts

@@ -11,9 +11,11 @@ import { deletePeer } from "../gerbil/peers";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 import { sendToClient } from "../ws";
 import { sendToClient } from "../ws";
 
 
-const deleteSiteSchema = z.object({
-    siteId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const deleteSiteSchema = z
+    .object({
+        siteId: z.string().transform(Number).pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export async function deleteSite(
 export async function deleteSite(
     req: Request,
     req: Request,
@@ -60,7 +62,7 @@ export async function deleteSite(
                 if (deletedNewt) {
                 if (deletedNewt) {
                     const payload = {
                     const payload = {
                         type: `newt/terminate`,
                         type: `newt/terminate`,
-                        data: {},
+                        data: {}
                     };
                     };
                     sendToClient(deletedNewt.newtId, payload);
                     sendToClient(deletedNewt.newtId, payload);
 
 
@@ -79,7 +81,7 @@ export async function deleteSite(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Site deleted successfully",
             message: "Site deleted successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 15 - 13
server/routers/site/getSite.ts

@@ -10,16 +10,18 @@ import logger from "@server/logger";
 import stoi from "@server/utils/stoi";
 import stoi from "@server/utils/stoi";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const getSiteSchema = z.object({
-    siteId: z
-        .string()
-        .optional()
-        .transform(stoi)
-        .pipe(z.number().int().positive().optional())
-        .optional(),
-    niceId: z.string().optional(),
-    orgId: z.string().optional(),
-});
+const getSiteSchema = z
+    .object({
+        siteId: z
+            .string()
+            .optional()
+            .transform(stoi)
+            .pipe(z.number().int().positive().optional())
+            .optional(),
+        niceId: z.string().optional(),
+        orgId: z.string().optional()
+    })
+    .strict();
 
 
 export type GetSiteResponse = {
 export type GetSiteResponse = {
     siteId: number;
     siteId: number;
@@ -79,15 +81,15 @@ export async function getSite(
                 siteId: site[0].siteId,
                 siteId: site[0].siteId,
                 niceId: site[0].niceId,
                 niceId: site[0].niceId,
                 name: site[0].name,
                 name: site[0].name,
-                subnet: site[0].subnet,
+                subnet: site[0].subnet
             },
             },
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Site retrieved successfully",
             message: "Site retrieved successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
-        logger.error("Error from getSite: ", error);
+        logger.error(error);
         return next(
         return next(
             createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
             createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
         );
         );

+ 7 - 5
server/routers/site/listSiteRoles.ts

@@ -9,9 +9,11 @@ import createHttpError from "http-errors";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const listSiteRolesSchema = z.object({
-    siteId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const listSiteRolesSchema = z
+    .object({
+        siteId: z.string().transform(Number).pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export async function listSiteRoles(
 export async function listSiteRoles(
     req: Request,
     req: Request,
@@ -36,7 +38,7 @@ export async function listSiteRoles(
                 roleId: roles.roleId,
                 roleId: roles.roleId,
                 name: roles.name,
                 name: roles.name,
                 description: roles.description,
                 description: roles.description,
-                isAdmin: roles.isAdmin,
+                isAdmin: roles.isAdmin
             })
             })
             .from(roleSites)
             .from(roleSites)
             .innerJoin(roles, eq(roleSites.roleId, roles.roleId))
             .innerJoin(roles, eq(roleSites.roleId, roles.roleId))
@@ -47,7 +49,7 @@ export async function listSiteRoles(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Site roles retrieved successfully",
             message: "Site roles retrieved successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 27 - 26
server/routers/site/listSites.ts

@@ -1,5 +1,6 @@
 import { db } from "@server/db";
 import { db } from "@server/db";
 import { orgs, roleSites, sites, userSites } from "@server/db/schema";
 import { orgs, roleSites, sites, userSites } from "@server/db/schema";
+import logger from "@server/logger";
 import HttpCode from "@server/types/HttpCode";
 import HttpCode from "@server/types/HttpCode";
 import response from "@server/utils/response";
 import response from "@server/utils/response";
 import { and, count, eq, inArray, or, sql } from "drizzle-orm";
 import { and, count, eq, inArray, or, sql } from "drizzle-orm";
@@ -8,9 +9,11 @@ import createHttpError from "http-errors";
 import { z } from "zod";
 import { z } from "zod";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const listSitesParamsSchema = z.object({
-    orgId: z.string(),
-});
+const listSitesParamsSchema = z
+    .object({
+        orgId: z.string()
+    })
+    .strict();
 
 
 const listSitesSchema = z.object({
 const listSitesSchema = z.object({
     limit: z
     limit: z
@@ -24,7 +27,7 @@ const listSitesSchema = z.object({
         .optional()
         .optional()
         .default("0")
         .default("0")
         .transform(Number)
         .transform(Number)
-        .pipe(z.number().int().nonnegative()),
+        .pipe(z.number().int().nonnegative())
 });
 });
 
 
 function querySites(orgId: string, accessibleSiteIds: number[]) {
 function querySites(orgId: string, accessibleSiteIds: number[]) {
@@ -39,15 +42,15 @@ function querySites(orgId: string, accessibleSiteIds: number[]) {
             megabytesOut: sites.megabytesOut,
             megabytesOut: sites.megabytesOut,
             orgName: orgs.name,
             orgName: orgs.name,
             type: sites.type,
             type: sites.type,
-            online: sites.online,
+            online: sites.online
         })
         })
         .from(sites)
         .from(sites)
         .leftJoin(orgs, eq(sites.orgId, orgs.orgId))
         .leftJoin(orgs, eq(sites.orgId, orgs.orgId))
         .where(
         .where(
             and(
             and(
                 inArray(sites.siteId, accessibleSiteIds),
                 inArray(sites.siteId, accessibleSiteIds),
-                eq(sites.orgId, orgId),
-            ),
+                eq(sites.orgId, orgId)
+            )
         );
         );
 }
 }
 
 
@@ -59,7 +62,7 @@ export type ListSitesResponse = {
 export async function listSites(
 export async function listSites(
     req: Request,
     req: Request,
     res: Response,
     res: Response,
-    next: NextFunction,
+    next: NextFunction
 ): Promise<any> {
 ): Promise<any> {
     try {
     try {
         const parsedQuery = listSitesSchema.safeParse(req.query);
         const parsedQuery = listSitesSchema.safeParse(req.query);
@@ -67,8 +70,8 @@ export async function listSites(
             return next(
             return next(
                 createHttpError(
                 createHttpError(
                     HttpCode.BAD_REQUEST,
                     HttpCode.BAD_REQUEST,
-                    fromError(parsedQuery.error),
-                ),
+                    fromError(parsedQuery.error)
+                )
             );
             );
         }
         }
         const { limit, offset } = parsedQuery.data;
         const { limit, offset } = parsedQuery.data;
@@ -78,8 +81,8 @@ export async function listSites(
             return next(
             return next(
                 createHttpError(
                 createHttpError(
                     HttpCode.BAD_REQUEST,
                     HttpCode.BAD_REQUEST,
-                    fromError(parsedParams.error),
-                ),
+                    fromError(parsedParams.error)
+                )
             );
             );
         }
         }
         const { orgId } = parsedParams.data;
         const { orgId } = parsedParams.data;
@@ -88,22 +91,22 @@ export async function listSites(
             return next(
             return next(
                 createHttpError(
                 createHttpError(
                     HttpCode.FORBIDDEN,
                     HttpCode.FORBIDDEN,
-                    "User does not have access to this organization",
-                ),
+                    "User does not have access to this organization"
+                )
             );
             );
         }
         }
 
 
         const accessibleSites = await db
         const accessibleSites = await db
             .select({
             .select({
-                siteId: sql<number>`COALESCE(${userSites.siteId}, ${roleSites.siteId})`,
+                siteId: sql<number>`COALESCE(${userSites.siteId}, ${roleSites.siteId})`
             })
             })
             .from(userSites)
             .from(userSites)
             .fullJoin(roleSites, eq(userSites.siteId, roleSites.siteId))
             .fullJoin(roleSites, eq(userSites.siteId, roleSites.siteId))
             .where(
             .where(
                 or(
                 or(
                     eq(userSites.userId, req.user!.userId),
                     eq(userSites.userId, req.user!.userId),
-                    eq(roleSites.roleId, req.userOrgRoleId!),
-                ),
+                    eq(roleSites.roleId, req.userOrgRoleId!)
+                )
             );
             );
 
 
         const accessibleSiteIds = accessibleSites.map((site) => site.siteId);
         const accessibleSiteIds = accessibleSites.map((site) => site.siteId);
@@ -115,8 +118,8 @@ export async function listSites(
             .where(
             .where(
                 and(
                 and(
                     inArray(sites.siteId, accessibleSiteIds),
                     inArray(sites.siteId, accessibleSiteIds),
-                    eq(sites.orgId, orgId),
-                ),
+                    eq(sites.orgId, orgId)
+                )
             );
             );
 
 
         const sitesList = await baseQuery.limit(limit).offset(offset);
         const sitesList = await baseQuery.limit(limit).offset(offset);
@@ -129,20 +132,18 @@ export async function listSites(
                 pagination: {
                 pagination: {
                     total: totalCount,
                     total: totalCount,
                     limit,
                     limit,
-                    offset,
-                },
+                    offset
+                }
             },
             },
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Sites retrieved successfully",
             message: "Sites retrieved successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
+        logger.error(error);
         return next(
         return next(
-            createHttpError(
-                HttpCode.INTERNAL_SERVER_ERROR,
-                "An error occurred",
-            ),
+            createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
         );
         );
     }
     }
 }
 }

+ 8 - 6
server/routers/site/updateSite.ts

@@ -9,14 +9,16 @@ import createHttpError from "http-errors";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const updateSiteParamsSchema = z.object({
-    siteId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const updateSiteParamsSchema = z
+    .object({
+        siteId: z.string().transform(Number).pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 const updateSiteBodySchema = z
 const updateSiteBodySchema = z
     .object({
     .object({
         name: z.string().min(1).max(255).optional(),
         name: z.string().min(1).max(255).optional(),
-        subdomain: z.string().min(1).max(255).optional(),
+        subdomain: z.string().min(1).max(255).optional()
         // pubKey: z.string().optional(),
         // pubKey: z.string().optional(),
         // subnet: z.string().optional(),
         // subnet: z.string().optional(),
         // exitNode: z.number().int().positive().optional(),
         // exitNode: z.number().int().positive().optional(),
@@ -25,7 +27,7 @@ const updateSiteBodySchema = z
     })
     })
     .strict()
     .strict()
     .refine((data) => Object.keys(data).length > 0, {
     .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(
 export async function updateSite(
@@ -77,7 +79,7 @@ export async function updateSite(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Site updated successfully",
             message: "Site updated successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 19 - 11
server/routers/target/createTarget.ts

@@ -12,9 +12,14 @@ import { isIpInCidr } from "@server/utils/ip";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 import { addTargets } from "../newt/targets";
 import { addTargets } from "../newt/targets";
 
 
-const createTargetParamsSchema = z.object({
-    resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const createTargetParamsSchema = z
+    .object({
+        resourceId: z
+            .string()
+            .transform(Number)
+            .pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 const createTargetSchema = z
 const createTargetSchema = z
     .object({
     .object({
@@ -22,7 +27,7 @@ const createTargetSchema = z
         method: z.string().min(1).max(10),
         method: z.string().min(1).max(10),
         port: z.number().int().min(1).max(65535),
         port: z.number().int().min(1).max(65535),
         protocol: z.string().optional(),
         protocol: z.string().optional(),
-        enabled: z.boolean().default(true),
+        enabled: z.boolean().default(true)
     })
     })
     .strict();
     .strict();
 
 
@@ -61,7 +66,7 @@ export async function createTarget(
         // get the resource
         // get the resource
         const [resource] = await db
         const [resource] = await db
             .select({
             .select({
-                siteId: resources.siteId,
+                siteId: resources.siteId
             })
             })
             .from(resources)
             .from(resources)
             .where(eq(resources.resourceId, resourceId));
             .where(eq(resources.resourceId, resourceId));
@@ -91,7 +96,10 @@ export async function createTarget(
         }
         }
 
 
         // make sure the target is within the site subnet
         // make sure the target is within the site subnet
-        if (site.type == "wireguard" && !isIpInCidr(targetData.ip, site.subnet!)) {
+        if (
+            site.type == "wireguard" &&
+            !isIpInCidr(targetData.ip, site.subnet!)
+        ) {
             return next(
             return next(
                 createHttpError(
                 createHttpError(
                     HttpCode.BAD_REQUEST,
                     HttpCode.BAD_REQUEST,
@@ -102,7 +110,7 @@ export async function createTarget(
 
 
         // Fetch resources for this site
         // Fetch resources for this site
         const resourcesRes = await db.query.resources.findMany({
         const resourcesRes = await db.query.resources.findMany({
-            where: eq(resources.siteId, site.siteId),
+            where: eq(resources.siteId, site.siteId)
         });
         });
 
 
         // TODO: is this all inefficient?
         // TODO: is this all inefficient?
@@ -112,7 +120,7 @@ export async function createTarget(
         await Promise.all(
         await Promise.all(
             resourcesRes.map(async (resource) => {
             resourcesRes.map(async (resource) => {
                 const targetsRes = await db.query.targets.findMany({
                 const targetsRes = await db.query.targets.findMany({
-                    where: eq(targets.resourceId, resource.resourceId),
+                    where: eq(targets.resourceId, resource.resourceId)
                 });
                 });
                 targetsRes.forEach((target) => {
                 targetsRes.forEach((target) => {
                     targetIps.push(`${target.ip}/32`);
                     targetIps.push(`${target.ip}/32`);
@@ -147,7 +155,7 @@ export async function createTarget(
                 resourceId,
                 resourceId,
                 protocol: "tcp", // hard code for now
                 protocol: "tcp", // hard code for now
                 internalPort,
                 internalPort,
-                ...targetData,
+                ...targetData
             })
             })
             .returning();
             .returning();
 
 
@@ -155,7 +163,7 @@ export async function createTarget(
             if (site.type == "wireguard") {
             if (site.type == "wireguard") {
                 await addPeer(site.exitNodeId!, {
                 await addPeer(site.exitNodeId!, {
                     publicKey: site.pubKey,
                     publicKey: site.pubKey,
-                    allowedIps: targetIps.flat(),
+                    allowedIps: targetIps.flat()
                 });
                 });
             } else if (site.type == "newt") {
             } else if (site.type == "newt") {
                 // get the newt on the site by querying the newt table for siteId
                 // get the newt on the site by querying the newt table for siteId
@@ -174,7 +182,7 @@ export async function createTarget(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Target created successfully",
             message: "Target created successfully",
-            status: HttpCode.CREATED,
+            status: HttpCode.CREATED
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 10 - 8
server/routers/target/deleteTarget.ts

@@ -11,9 +11,11 @@ import { addPeer } from "../gerbil/peers";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 import { removeTargets } from "../newt/targets";
 import { removeTargets } from "../newt/targets";
 
 
-const deleteTargetSchema = z.object({
-    targetId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const deleteTargetSchema = z
+    .object({
+        targetId: z.string().transform(Number).pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export async function deleteTarget(
 export async function deleteTarget(
     req: Request,
     req: Request,
@@ -49,7 +51,7 @@ export async function deleteTarget(
         // get the resource
         // get the resource
         const [resource] = await db
         const [resource] = await db
             .select({
             .select({
-                siteId: resources.siteId,
+                siteId: resources.siteId
             })
             })
             .from(resources)
             .from(resources)
             .where(eq(resources.resourceId, deletedTarget.resourceId!));
             .where(eq(resources.resourceId, deletedTarget.resourceId!));
@@ -83,14 +85,14 @@ export async function deleteTarget(
                 // TODO: is this all inefficient?
                 // TODO: is this all inefficient?
                 // Fetch resources for this site
                 // Fetch resources for this site
                 const resourcesRes = await db.query.resources.findMany({
                 const resourcesRes = await db.query.resources.findMany({
-                    where: eq(resources.siteId, site.siteId),
+                    where: eq(resources.siteId, site.siteId)
                 });
                 });
 
 
                 // Fetch targets for all resources of this site
                 // Fetch targets for all resources of this site
                 const targetIps = await Promise.all(
                 const targetIps = await Promise.all(
                     resourcesRes.map(async (resource) => {
                     resourcesRes.map(async (resource) => {
                         const targetsRes = await db.query.targets.findMany({
                         const targetsRes = await db.query.targets.findMany({
-                            where: eq(targets.resourceId, resource.resourceId),
+                            where: eq(targets.resourceId, resource.resourceId)
                         });
                         });
                         return targetsRes.map((target) => `${target.ip}/32`);
                         return targetsRes.map((target) => `${target.ip}/32`);
                     })
                     })
@@ -98,7 +100,7 @@ export async function deleteTarget(
 
 
                 await addPeer(site.exitNodeId!, {
                 await addPeer(site.exitNodeId!, {
                     publicKey: site.pubKey,
                     publicKey: site.pubKey,
-                    allowedIps: targetIps.flat(),
+                    allowedIps: targetIps.flat()
                 });
                 });
             } else if (site.type == "newt") {
             } else if (site.type == "newt") {
                 // get the newt on the site by querying the newt table for siteId
                 // get the newt on the site by querying the newt table for siteId
@@ -117,7 +119,7 @@ export async function deleteTarget(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Target deleted successfully",
             message: "Target deleted successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 6 - 4
server/routers/target/getTarget.ts

@@ -9,9 +9,11 @@ import createHttpError from "http-errors";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const getTargetSchema = z.object({
-    targetId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const getTargetSchema = z
+    .object({
+        targetId: z.string().transform(Number).pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export async function getTarget(
 export async function getTarget(
     req: Request,
     req: Request,
@@ -51,7 +53,7 @@ export async function getTarget(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Target retrieved successfully",
             message: "Target retrieved successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 13 - 8
server/routers/target/listTargets.ts

@@ -9,9 +9,14 @@ import { z } from "zod";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 import logger from "@server/logger";
 import logger from "@server/logger";
 
 
-const listTargetsParamsSchema = z.object({
-    resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const listTargetsParamsSchema = z
+    .object({
+        resourceId: z
+            .string()
+            .transform(Number)
+            .pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 const listTargetsSchema = z.object({
 const listTargetsSchema = z.object({
     limit: z
     limit: z
@@ -25,7 +30,7 @@ const listTargetsSchema = z.object({
         .optional()
         .optional()
         .default("0")
         .default("0")
         .transform(Number)
         .transform(Number)
-        .pipe(z.number().int().nonnegative()),
+        .pipe(z.number().int().nonnegative())
 });
 });
 
 
 function queryTargets(resourceId: number) {
 function queryTargets(resourceId: number) {
@@ -37,7 +42,7 @@ function queryTargets(resourceId: number) {
             port: targets.port,
             port: targets.port,
             protocol: targets.protocol,
             protocol: targets.protocol,
             enabled: targets.enabled,
             enabled: targets.enabled,
-            resourceId: targets.resourceId,
+            resourceId: targets.resourceId
             // resourceName: resources.name,
             // resourceName: resources.name,
         })
         })
         .from(targets)
         .from(targets)
@@ -97,13 +102,13 @@ export async function listTargets(
                 pagination: {
                 pagination: {
                     total: totalCount,
                     total: totalCount,
                     limit,
                     limit,
-                    offset,
-                },
+                    offset
+                }
             },
             },
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Targets retrieved successfully",
             message: "Targets retrieved successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 12 - 10
server/routers/target/updateTarget.ts

@@ -11,20 +11,22 @@ import { fromError } from "zod-validation-error";
 import { addPeer } from "../gerbil/peers";
 import { addPeer } from "../gerbil/peers";
 import { addTargets } from "../newt/targets";
 import { addTargets } from "../newt/targets";
 
 
-const updateTargetParamsSchema = z.object({
-    targetId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const updateTargetParamsSchema = z
+    .object({
+        targetId: z.string().transform(Number).pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 const updateTargetBodySchema = z
 const updateTargetBodySchema = z
     .object({
     .object({
         ip: z.string().ip().optional(), // for now we cant update the ip; you will have to delete
         ip: z.string().ip().optional(), // for now we cant update the ip; you will have to delete
         method: z.string().min(1).max(10).optional(),
         method: z.string().min(1).max(10).optional(),
         port: z.number().int().min(1).max(65535).optional(),
         port: z.number().int().min(1).max(65535).optional(),
-        enabled: z.boolean().optional(),
+        enabled: z.boolean().optional()
     })
     })
     .strict()
     .strict()
     .refine((data) => Object.keys(data).length > 0, {
     .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(
 export async function updateTarget(
@@ -74,7 +76,7 @@ export async function updateTarget(
         // get the resource
         // get the resource
         const [resource] = await db
         const [resource] = await db
             .select({
             .select({
-                siteId: resources.siteId,
+                siteId: resources.siteId
             })
             })
             .from(resources)
             .from(resources)
             .where(eq(resources.resourceId, updatedTarget.resourceId!));
             .where(eq(resources.resourceId, updatedTarget.resourceId!));
@@ -107,14 +109,14 @@ export async function updateTarget(
                 // TODO: is this all inefficient?
                 // TODO: is this all inefficient?
                 // Fetch resources for this site
                 // Fetch resources for this site
                 const resourcesRes = await db.query.resources.findMany({
                 const resourcesRes = await db.query.resources.findMany({
-                    where: eq(resources.siteId, site.siteId),
+                    where: eq(resources.siteId, site.siteId)
                 });
                 });
 
 
                 // Fetch targets for all resources of this site
                 // Fetch targets for all resources of this site
                 const targetIps = await Promise.all(
                 const targetIps = await Promise.all(
                     resourcesRes.map(async (resource) => {
                     resourcesRes.map(async (resource) => {
                         const targetsRes = await db.query.targets.findMany({
                         const targetsRes = await db.query.targets.findMany({
-                            where: eq(targets.resourceId, resource.resourceId),
+                            where: eq(targets.resourceId, resource.resourceId)
                         });
                         });
                         return targetsRes.map((target) => `${target.ip}/32`);
                         return targetsRes.map((target) => `${target.ip}/32`);
                     })
                     })
@@ -122,7 +124,7 @@ export async function updateTarget(
 
 
                 await addPeer(site.exitNodeId!, {
                 await addPeer(site.exitNodeId!, {
                     publicKey: site.pubKey,
                     publicKey: site.pubKey,
-                    allowedIps: targetIps.flat(),
+                    allowedIps: targetIps.flat()
                 });
                 });
             } else if (site.type == "newt") {
             } else if (site.type == "newt") {
                 // get the newt on the site by querying the newt table for siteId
                 // get the newt on the site by querying the newt table for siteId
@@ -140,7 +142,7 @@ export async function updateTarget(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Target updated successfully",
             message: "Target updated successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 9 - 7
server/routers/user/acceptInvite.ts

@@ -11,10 +11,12 @@ import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 import { isWithinExpirationDate } from "oslo";
 import { isWithinExpirationDate } from "oslo";
 
 
-const acceptInviteBodySchema = z.object({
-    token: z.string(),
-    inviteId: z.string(),
-});
+const acceptInviteBodySchema = z
+    .object({
+        token: z.string(),
+        inviteId: z.string()
+    })
+    .strict();
 
 
 export type AcceptInviteResponse = {
 export type AcceptInviteResponse = {
     accepted: boolean;
     accepted: boolean;
@@ -64,7 +66,7 @@ export async function acceptInvite(
             memoryCost: 19456,
             memoryCost: 19456,
             timeCost: 2,
             timeCost: 2,
             outputLen: 32,
             outputLen: 32,
-            parallelism: 1,
+            parallelism: 1
         });
         });
         if (!validToken) {
         if (!validToken) {
             return next(
             return next(
@@ -121,7 +123,7 @@ export async function acceptInvite(
         await db.insert(userOrgs).values({
         await db.insert(userOrgs).values({
             userId: existingUser[0].userId,
             userId: existingUser[0].userId,
             orgId: existingInvite[0].orgId,
             orgId: existingInvite[0].orgId,
-            roleId: existingInvite[0].roleId,
+            roleId: existingInvite[0].roleId
         });
         });
 
 
         // delete the invite
         // delete the invite
@@ -132,7 +134,7 @@ export async function acceptInvite(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Invite accepted",
             message: "Invite accepted",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 9 - 7
server/routers/user/addUserAction.ts

@@ -9,11 +9,13 @@ import logger from "@server/logger";
 import { eq } from "drizzle-orm";
 import { eq } from "drizzle-orm";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const addUserActionSchema = z.object({
-    userId: z.string(),
-    actionId: z.string(),
-    orgId: z.string(),
-});
+const addUserActionSchema = z
+    .object({
+        userId: z.string(),
+        actionId: z.string(),
+        orgId: z.string()
+    })
+    .strict();
 
 
 export async function addUserAction(
 export async function addUserAction(
     req: Request,
     req: Request,
@@ -52,7 +54,7 @@ export async function addUserAction(
             .values({
             .values({
                 userId,
                 userId,
                 actionId,
                 actionId,
-                orgId,
+                orgId
             })
             })
             .returning();
             .returning();
 
 
@@ -61,7 +63,7 @@ export async function addUserAction(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Action added to user successfully",
             message: "Action added to user successfully",
-            status: HttpCode.CREATED,
+            status: HttpCode.CREATED
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 7 - 5
server/routers/user/addUserRole.ts

@@ -10,10 +10,12 @@ import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 import stoi from "@server/utils/stoi";
 import stoi from "@server/utils/stoi";
 
 
-const addUserRoleParamsSchema = z.object({
-    userId: z.string(),
-    roleId: z.string().transform(stoi).pipe(z.number()),
-});
+const addUserRoleParamsSchema = z
+    .object({
+        userId: z.string(),
+        roleId: z.string().transform(stoi).pipe(z.number())
+    })
+    .strict();
 
 
 export type AddUserRoleResponse = z.infer<typeof addUserRoleParamsSchema>;
 export type AddUserRoleResponse = z.infer<typeof addUserRoleParamsSchema>;
 
 
@@ -96,7 +98,7 @@ export async function addUserRole(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Role added to user successfully",
             message: "Role added to user successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 9 - 7
server/routers/user/addUserSite.ts

@@ -9,10 +9,12 @@ import logger from "@server/logger";
 import { eq } from "drizzle-orm";
 import { eq } from "drizzle-orm";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const addUserSiteSchema = z.object({
-    userId: z.string(),
-    siteId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const addUserSiteSchema = z
+    .object({
+        userId: z.string(),
+        siteId: z.string().transform(Number).pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export async function addUserSite(
 export async function addUserSite(
     req: Request,
     req: Request,
@@ -36,7 +38,7 @@ export async function addUserSite(
             .insert(userSites)
             .insert(userSites)
             .values({
             .values({
                 userId,
                 userId,
-                siteId,
+                siteId
             })
             })
             .returning();
             .returning();
 
 
@@ -48,7 +50,7 @@ export async function addUserSite(
         for (const resource of siteResources) {
         for (const resource of siteResources) {
             await db.insert(userResources).values({
             await db.insert(userResources).values({
                 userId,
                 userId,
-                resourceId: resource.resourceId,
+                resourceId: resource.resourceId
             });
             });
         }
         }
 
 
@@ -57,7 +59,7 @@ export async function addUserSite(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Site added to user successfully",
             message: "Site added to user successfully",
-            status: HttpCode.CREATED,
+            status: HttpCode.CREATED
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 8 - 6
server/routers/user/getOrgUser.ts

@@ -19,7 +19,7 @@ async function queryUser(orgId: string, userId: string) {
             roleId: userOrgs.roleId,
             roleId: userOrgs.roleId,
             roleName: roles.name,
             roleName: roles.name,
             isOwner: userOrgs.isOwner,
             isOwner: userOrgs.isOwner,
-            isAdmin: roles.isAdmin,
+            isAdmin: roles.isAdmin
         })
         })
         .from(userOrgs)
         .from(userOrgs)
         .leftJoin(roles, eq(userOrgs.roleId, roles.roleId))
         .leftJoin(roles, eq(userOrgs.roleId, roles.roleId))
@@ -33,10 +33,12 @@ export type GetOrgUserResponse = NonNullable<
     Awaited<ReturnType<typeof queryUser>>
     Awaited<ReturnType<typeof queryUser>>
 >;
 >;
 
 
-const getOrgUserParamsSchema = z.object({
-    userId: z.string(),
-    orgId: z.string(),
-});
+const getOrgUserParamsSchema = z
+    .object({
+        userId: z.string(),
+        orgId: z.string()
+    })
+    .strict();
 
 
 export async function getOrgUser(
 export async function getOrgUser(
     req: Request,
     req: Request,
@@ -109,7 +111,7 @@ export async function getOrgUser(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "User retrieved successfully",
             message: "User retrieved successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 53 - 45
server/routers/user/inviteUser.ts

@@ -6,7 +6,6 @@ import { and, eq } from "drizzle-orm";
 import response from "@server/utils/response";
 import response from "@server/utils/response";
 import HttpCode from "@server/types/HttpCode";
 import HttpCode from "@server/types/HttpCode";
 import createHttpError from "http-errors";
 import createHttpError from "http-errors";
-import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { alphabet, generateRandomString } from "oslo/crypto";
 import { alphabet, generateRandomString } from "oslo/crypto";
 import { createDate, TimeSpan } from "oslo";
 import { createDate, TimeSpan } from "oslo";
@@ -16,15 +15,20 @@ import { fromError } from "zod-validation-error";
 import { sendEmail } from "@server/emails";
 import { sendEmail } from "@server/emails";
 import SendInviteLink from "@server/emails/templates/SendInviteLink";
 import SendInviteLink from "@server/emails/templates/SendInviteLink";
 
 
-const inviteUserParamsSchema = z.object({
-    orgId: z.string(),
-});
-
-const inviteUserBodySchema = z.object({
-    email: z.string().email(),
-    roleId: z.number(),
-    validHours: z.number().gt(0).lte(168),
-});
+const inviteUserParamsSchema = z
+    .object({
+        orgId: z.string()
+    })
+    .strict();
+
+const inviteUserBodySchema = z
+    .object({
+        email: z.string().email(),
+        roleId: z.number(),
+        validHours: z.number().gt(0).lte(168),
+        sendEmail: z.boolean().optional()
+    })
+    .strict();
 
 
 export type InviteUserBody = z.infer<typeof inviteUserBodySchema>;
 export type InviteUserBody = z.infer<typeof inviteUserBodySchema>;
 
 
@@ -38,7 +42,7 @@ const inviteTracker: Record<string, { timestamps: number[] }> = {};
 export async function inviteUser(
 export async function inviteUser(
     req: Request,
     req: Request,
     res: Response,
     res: Response,
-    next: NextFunction,
+    next: NextFunction
 ): Promise<any> {
 ): Promise<any> {
     try {
     try {
         const parsedParams = inviteUserParamsSchema.safeParse(req.params);
         const parsedParams = inviteUserParamsSchema.safeParse(req.params);
@@ -46,8 +50,8 @@ export async function inviteUser(
             return next(
             return next(
                 createHttpError(
                 createHttpError(
                     HttpCode.BAD_REQUEST,
                     HttpCode.BAD_REQUEST,
-                    fromError(parsedParams.error).toString(),
-                ),
+                    fromError(parsedParams.error).toString()
+                )
             );
             );
         }
         }
 
 
@@ -56,13 +60,18 @@ export async function inviteUser(
             return next(
             return next(
                 createHttpError(
                 createHttpError(
                     HttpCode.BAD_REQUEST,
                     HttpCode.BAD_REQUEST,
-                    fromError(parsedBody.error).toString(),
-                ),
+                    fromError(parsedBody.error).toString()
+                )
             );
             );
         }
         }
 
 
         const { orgId } = parsedParams.data;
         const { orgId } = parsedParams.data;
-        const { email, validHours, roleId } = parsedBody.data;
+        const {
+            email,
+            validHours,
+            roleId,
+            sendEmail: doEmail
+        } = parsedBody.data;
 
 
         const currentTime = Date.now();
         const currentTime = Date.now();
         const oneHourAgo = currentTime - 3600000;
         const oneHourAgo = currentTime - 3600000;
@@ -79,8 +88,8 @@ export async function inviteUser(
             return next(
             return next(
                 createHttpError(
                 createHttpError(
                     HttpCode.TOO_MANY_REQUESTS,
                     HttpCode.TOO_MANY_REQUESTS,
-                    "User has already been invited 3 times in the last hour",
-                ),
+                    "User has already been invited 3 times in the last hour"
+                )
             );
             );
         }
         }
 
 
@@ -93,7 +102,7 @@ export async function inviteUser(
             .limit(1);
             .limit(1);
         if (!org.length) {
         if (!org.length) {
             return next(
             return next(
-                createHttpError(HttpCode.NOT_FOUND, "Organization not found"),
+                createHttpError(HttpCode.NOT_FOUND, "Organization not found")
             );
             );
         }
         }
 
 
@@ -107,14 +116,14 @@ export async function inviteUser(
             return next(
             return next(
                 createHttpError(
                 createHttpError(
                     HttpCode.BAD_REQUEST,
                     HttpCode.BAD_REQUEST,
-                    "User is already a member of this organization",
-                ),
+                    "User is already a member of this organization"
+                )
             );
             );
         }
         }
 
 
         const inviteId = generateRandomString(
         const inviteId = generateRandomString(
             10,
             10,
-            alphabet("a-z", "A-Z", "0-9"),
+            alphabet("a-z", "A-Z", "0-9")
         );
         );
         const token = generateRandomString(32, alphabet("a-z", "A-Z", "0-9"));
         const token = generateRandomString(32, alphabet("a-z", "A-Z", "0-9"));
         const expiresAt = createDate(new TimeSpan(validHours, "h")).getTime();
         const expiresAt = createDate(new TimeSpan(validHours, "h")).getTime();
@@ -125,7 +134,7 @@ export async function inviteUser(
         await db
         await db
             .delete(userInvites)
             .delete(userInvites)
             .where(
             .where(
-                and(eq(userInvites.email, email), eq(userInvites.orgId, orgId)),
+                and(eq(userInvites.email, email), eq(userInvites.orgId, orgId))
             )
             )
             .execute();
             .execute();
 
 
@@ -135,43 +144,42 @@ export async function inviteUser(
             email,
             email,
             expiresAt,
             expiresAt,
             tokenHash,
             tokenHash,
-            roleId,
+            roleId
         });
         });
 
 
         const inviteLink = `${config.app.base_url}/invite?token=${inviteId}-${token}`;
         const inviteLink = `${config.app.base_url}/invite?token=${inviteId}-${token}`;
 
 
-        await sendEmail(
-            SendInviteLink({
-                email,
-                inviteLink,
-                expiresInDays: (validHours / 24).toString(),
-                orgName: org[0].name || orgId,
-                inviterName: req.user?.email,
-            }),
-            {
-                to: email,
-                from: config.email?.no_reply,
-                subject: "You're invited to join a Fossorial organization",
-            },
-        );
+        if (doEmail) {
+            await sendEmail(
+                SendInviteLink({
+                    email,
+                    inviteLink,
+                    expiresInDays: (validHours / 24).toString(),
+                    orgName: org[0].name || orgId,
+                    inviterName: req.user?.email
+                }),
+                {
+                    to: email,
+                    from: config.email?.no_reply,
+                    subject: "You're invited to join a Fossorial organization"
+                }
+            );
+        }
 
 
         return response<InviteUserResponse>(res, {
         return response<InviteUserResponse>(res, {
             data: {
             data: {
                 inviteLink,
                 inviteLink,
-                expiresAt,
+                expiresAt
             },
             },
             success: true,
             success: true,
             error: false,
             error: false,
             message: "User invited successfully",
             message: "User invited successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
-        console.error(error);
+        logger.error(error);
         return next(
         return next(
-            createHttpError(
-                HttpCode.INTERNAL_SERVER_ERROR,
-                "An error occurred",
-            ),
+            createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
         );
         );
     }
     }
 }
 }

+ 25 - 21
server/routers/user/listUsers.ts

@@ -8,24 +8,28 @@ import createHttpError from "http-errors";
 import { sql } from "drizzle-orm";
 import { sql } from "drizzle-orm";
 import logger from "@server/logger";
 import logger from "@server/logger";
 
 
-const listUsersParamsSchema = z.object({
-    orgId: z.string(),
-});
+const listUsersParamsSchema = z
+    .object({
+        orgId: z.string()
+    })
+    .strict();
 
 
-const listUsersSchema = z.object({
-    limit: z
-        .string()
-        .optional()
-        .default("1000")
-        .transform(Number)
-        .pipe(z.number().int().nonnegative()),
-    offset: z
-        .string()
-        .optional()
-        .default("0")
-        .transform(Number)
-        .pipe(z.number().int().nonnegative()),
-});
+const listUsersSchema = z
+    .object({
+        limit: z
+            .string()
+            .optional()
+            .default("1000")
+            .transform(Number)
+            .pipe(z.number().int().nonnegative()),
+        offset: z
+            .string()
+            .optional()
+            .default("0")
+            .transform(Number)
+            .pipe(z.number().int().nonnegative())
+    })
+    .strict();
 
 
 async function queryUsers(orgId: string, limit: number, offset: number) {
 async function queryUsers(orgId: string, limit: number, offset: number) {
     return await db
     return await db
@@ -37,7 +41,7 @@ async function queryUsers(orgId: string, limit: number, offset: number) {
             orgId: userOrgs.orgId,
             orgId: userOrgs.orgId,
             roleId: userOrgs.roleId,
             roleId: userOrgs.roleId,
             roleName: roles.name,
             roleName: roles.name,
-            isOwner: userOrgs.isOwner,
+            isOwner: userOrgs.isOwner
         })
         })
         .from(users)
         .from(users)
         .leftJoin(userOrgs, sql`${users.userId} = ${userOrgs.userId}`)
         .leftJoin(userOrgs, sql`${users.userId} = ${userOrgs.userId}`)
@@ -97,13 +101,13 @@ export async function listUsers(
                 pagination: {
                 pagination: {
                     total: count,
                     total: count,
                     limit,
                     limit,
-                    offset,
-                },
+                    offset
+                }
             },
             },
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Users retrieved successfully",
             message: "Users retrieved successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 12 - 8
server/routers/user/removeUserAction.ts

@@ -9,14 +9,18 @@ import createHttpError from "http-errors";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const removeUserActionParamsSchema = z.object({
-    userId: z.string(),
-});
+const removeUserActionParamsSchema = z
+    .object({
+        userId: z.string()
+    })
+    .strict();
 
 
-const removeUserActionSchema = z.object({
-    actionId: z.string(),
-    orgId: z.string(),
-});
+const removeUserActionSchema = z
+    .object({
+        actionId: z.string(),
+        orgId: z.string()
+    })
+    .strict();
 
 
 export async function removeUserAction(
 export async function removeUserAction(
     req: Request,
     req: Request,
@@ -73,7 +77,7 @@ export async function removeUserAction(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Action removed from user successfully",
             message: "Action removed from user successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 7 - 5
server/routers/user/removeUserOrg.ts

@@ -9,10 +9,12 @@ import createHttpError from "http-errors";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const removeUserSchema = z.object({
-    userId: z.string(),
-    orgId: z.string(),
-});
+const removeUserSchema = z
+    .object({
+        userId: z.string(),
+        orgId: z.string()
+    })
+    .strict();
 
 
 export async function removeUserOrg(
 export async function removeUserOrg(
     req: Request,
     req: Request,
@@ -70,7 +72,7 @@ export async function removeUserOrg(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "User remove from org successfully",
             message: "User remove from org successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 10 - 5
server/routers/user/removeUserResource.ts

@@ -9,10 +9,15 @@ import createHttpError from "http-errors";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const removeUserResourceSchema = z.object({
-    userId: z.string(),
-    resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
-});
+const removeUserResourceSchema = z
+    .object({
+        userId: z.string(),
+        resourceId: z
+            .string()
+            .transform(Number)
+            .pipe(z.number().int().positive())
+    })
+    .strict();
 
 
 export async function removeUserResource(
 export async function removeUserResource(
     req: Request,
     req: Request,
@@ -56,7 +61,7 @@ export async function removeUserResource(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Resource removed from user successfully",
             message: "Resource removed from user successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 11 - 7
server/routers/user/removeUserSite.ts

@@ -9,13 +9,17 @@ import createHttpError from "http-errors";
 import logger from "@server/logger";
 import logger from "@server/logger";
 import { fromError } from "zod-validation-error";
 import { fromError } from "zod-validation-error";
 
 
-const removeUserSiteParamsSchema = z.object({
-    userId: z.string(),
-});
+const removeUserSiteParamsSchema = z
+    .object({
+        userId: z.string()
+    })
+    .strict();
 
 
-const removeUserSiteSchema = z.object({
-    siteId: z.number().int().positive(),
-});
+const removeUserSiteSchema = z
+    .object({
+        siteId: z.number().int().positive()
+    })
+    .strict();
 
 
 export async function removeUserSite(
 export async function removeUserSite(
     req: Request,
     req: Request,
@@ -85,7 +89,7 @@ export async function removeUserSite(
             success: true,
             success: true,
             error: false,
             error: false,
             message: "Site removed from user successfully",
             message: "Site removed from user successfully",
-            status: HttpCode.OK,
+            status: HttpCode.OK
         });
         });
     } catch (error) {
     } catch (error) {
         logger.error(error);
         logger.error(error);

+ 1 - 1
src/app/[orgId]/settings/access/roles/components/CreateRoleForm.tsx

@@ -126,7 +126,7 @@ export default function CreateRoleForm({
                         <Form {...form}>
                         <Form {...form}>
                             <form
                             <form
                                 onSubmit={form.handleSubmit(onSubmit)}
                                 onSubmit={form.handleSubmit(onSubmit)}
-                                className="space-y-8:w"
+                                className="space-y-8"
                                 id="create-role-form"
                                 id="create-role-form"
                             >
                             >
                                 <FormField
                                 <FormField

+ 14 - 17
src/app/[orgId]/settings/access/roles/components/RolesTable.tsx

@@ -5,7 +5,7 @@ import {
     DropdownMenu,
     DropdownMenu,
     DropdownMenuContent,
     DropdownMenuContent,
     DropdownMenuItem,
     DropdownMenuItem,
-    DropdownMenuTrigger,
+    DropdownMenuTrigger
 } from "@app/components/ui/dropdown-menu";
 } from "@app/components/ui/dropdown-menu";
 import { Button } from "@app/components/ui/button";
 import { Button } from "@app/components/ui/button";
 import { ArrowUpDown, Crown, MoreHorizontal } from "lucide-react";
 import { ArrowUpDown, Crown, MoreHorizontal } from "lucide-react";
@@ -54,11 +54,11 @@ export default function UsersTable({ roles: r }: RolesTableProps) {
                         <ArrowUpDown className="ml-2 h-4 w-4" />
                         <ArrowUpDown className="ml-2 h-4 w-4" />
                     </Button>
                     </Button>
                 );
                 );
-            },
+            }
         },
         },
         {
         {
             accessorKey: "description",
             accessorKey: "description",
-            header: "Description",
+            header: "Description"
         },
         },
         {
         {
             id: "actions",
             id: "actions",
@@ -90,16 +90,15 @@ export default function UsersTable({ roles: r }: RolesTableProps) {
                                         </Button>
                                         </Button>
                                     </DropdownMenuTrigger>
                                     </DropdownMenuTrigger>
                                     <DropdownMenuContent align="end">
                                     <DropdownMenuContent align="end">
-                                        <DropdownMenuItem>
-                                            <button
-                                                className="text-red-500"
-                                                onClick={() => {
-                                                    setIsDeleteModalOpen(true);
-                                                    setUserToRemove(roleRow);
-                                                }}
-                                            >
+                                        <DropdownMenuItem
+                                            onClick={() => {
+                                                setIsDeleteModalOpen(true);
+                                                setUserToRemove(roleRow);
+                                            }}
+                                        >
+                                            <span className="text-red-500">
                                                 Delete Role
                                                 Delete Role
-                                            </button>
+                                            </span>
                                         </DropdownMenuItem>
                                         </DropdownMenuItem>
                                     </DropdownMenuContent>
                                     </DropdownMenuContent>
                                 </DropdownMenu>
                                 </DropdownMenu>
@@ -107,8 +106,8 @@ export default function UsersTable({ roles: r }: RolesTableProps) {
                         </div>
                         </div>
                     </>
                     </>
                 );
                 );
-            },
-        },
+            }
+        }
     ];
     ];
 
 
     return (
     return (
@@ -128,9 +127,7 @@ export default function UsersTable({ roles: r }: RolesTableProps) {
                     roleToDelete={roleToRemove}
                     roleToDelete={roleToRemove}
                     afterDelete={() => {
                     afterDelete={() => {
                         setRoles((prev) =>
                         setRoles((prev) =>
-                            prev.filter(
-                                (r) => r.roleId !== roleToRemove.roleId,
-                            ),
+                            prev.filter((r) => r.roleId !== roleToRemove.roleId)
                         );
                         );
                         setUserToRemove(null);
                         setUserToRemove(null);
                     }}
                     }}

+ 56 - 19
src/app/[orgId]/settings/access/users/components/InviteUserForm.tsx

@@ -7,7 +7,7 @@ import {
     FormField,
     FormField,
     FormItem,
     FormItem,
     FormLabel,
     FormLabel,
-    FormMessage,
+    FormMessage
 } from "@app/components/ui/form";
 } from "@app/components/ui/form";
 import { Input } from "@app/components/ui/input";
 import { Input } from "@app/components/ui/input";
 import {
 import {
@@ -15,7 +15,7 @@ import {
     SelectContent,
     SelectContent,
     SelectItem,
     SelectItem,
     SelectTrigger,
     SelectTrigger,
-    SelectValue,
+    SelectValue
 } from "@app/components/ui/select";
 } from "@app/components/ui/select";
 import { useToast } from "@app/hooks/useToast";
 import { useToast } from "@app/hooks/useToast";
 import { zodResolver } from "@hookform/resolvers/zod";
 import { zodResolver } from "@hookform/resolvers/zod";
@@ -33,13 +33,14 @@ import {
     CredenzaDescription,
     CredenzaDescription,
     CredenzaFooter,
     CredenzaFooter,
     CredenzaHeader,
     CredenzaHeader,
-    CredenzaTitle,
+    CredenzaTitle
 } from "@app/components/Credenza";
 } from "@app/components/Credenza";
 import { useOrgContext } from "@app/hooks/useOrgContext";
 import { useOrgContext } from "@app/hooks/useOrgContext";
 import { ListRolesResponse } from "@server/routers/role";
 import { ListRolesResponse } from "@server/routers/role";
 import { formatAxiosError } from "@app/lib/utils";
 import { formatAxiosError } from "@app/lib/utils";
 import { createApiClient } from "@app/api";
 import { createApiClient } from "@app/api";
 import { useEnvContext } from "@app/hooks/useEnvContext";
 import { useEnvContext } from "@app/hooks/useEnvContext";
+import { Checkbox } from "@app/components/ui/checkbox";
 
 
 type InviteUserFormProps = {
 type InviteUserFormProps = {
     open: boolean;
     open: boolean;
@@ -49,14 +50,16 @@ type InviteUserFormProps = {
 const formSchema = z.object({
 const formSchema = z.object({
     email: z.string().email({ message: "Invalid email address" }),
     email: z.string().email({ message: "Invalid email address" }),
     validForHours: z.string().min(1, { message: "Please select a duration" }),
     validForHours: z.string().min(1, { message: "Please select a duration" }),
-    roleId: z.string().min(1, { message: "Please select a role" }),
+    roleId: z.string().min(1, { message: "Please select a role" })
 });
 });
 
 
 export default function InviteUserForm({ open, setOpen }: InviteUserFormProps) {
 export default function InviteUserForm({ open, setOpen }: InviteUserFormProps) {
     const { toast } = useToast();
     const { toast } = useToast();
     const { org } = useOrgContext();
     const { org } = useOrgContext();
 
 
-    const api = createApiClient(useEnvContext());
+    const { env } = useEnvContext();
+
+    const api = createApiClient({ env });
 
 
     const [inviteLink, setInviteLink] = useState<string | null>(null);
     const [inviteLink, setInviteLink] = useState<string | null>(null);
     const [loading, setLoading] = useState(false);
     const [loading, setLoading] = useState(false);
@@ -64,6 +67,8 @@ export default function InviteUserForm({ open, setOpen }: InviteUserFormProps) {
 
 
     const [roles, setRoles] = useState<{ roleId: number; name: string }[]>([]);
     const [roles, setRoles] = useState<{ roleId: number; name: string }[]>([]);
 
 
+    const [sendEmail, setSendEmail] = useState(env.EMAIL_ENABLED === "true");
+
     const validFor = [
     const validFor = [
         { hours: 24, name: "1 day" },
         { hours: 24, name: "1 day" },
         { hours: 48, name: "2 days" },
         { hours: 48, name: "2 days" },
@@ -71,7 +76,7 @@ export default function InviteUserForm({ open, setOpen }: InviteUserFormProps) {
         { hours: 96, name: "4 days" },
         { hours: 96, name: "4 days" },
         { hours: 120, name: "5 days" },
         { hours: 120, name: "5 days" },
         { hours: 144, name: "6 days" },
         { hours: 144, name: "6 days" },
-        { hours: 168, name: "7 days" },
+        { hours: 168, name: "7 days" }
     ];
     ];
 
 
     const form = useForm<z.infer<typeof formSchema>>({
     const form = useForm<z.infer<typeof formSchema>>({
@@ -79,8 +84,8 @@ export default function InviteUserForm({ open, setOpen }: InviteUserFormProps) {
         defaultValues: {
         defaultValues: {
             email: "",
             email: "",
             validForHours: "72",
             validForHours: "72",
-            roleId: "",
-        },
+            roleId: ""
+        }
     });
     });
 
 
     useEffect(() => {
     useEffect(() => {
@@ -90,9 +95,9 @@ export default function InviteUserForm({ open, setOpen }: InviteUserFormProps) {
 
 
         async function fetchRoles() {
         async function fetchRoles() {
             const res = await api
             const res = await api
-                .get<AxiosResponse<ListRolesResponse>>(
-                    `/org/${org?.org.orgId}/roles`
-                )
+                .get<
+                    AxiosResponse<ListRolesResponse>
+                >(`/org/${org?.org.orgId}/roles`)
                 .catch((e) => {
                 .catch((e) => {
                     console.error(e);
                     console.error(e);
                     toast({
                     toast({
@@ -101,7 +106,7 @@ export default function InviteUserForm({ open, setOpen }: InviteUserFormProps) {
                         description: formatAxiosError(
                         description: formatAxiosError(
                             e,
                             e,
                             "An error occurred while fetching the roles"
                             "An error occurred while fetching the roles"
-                        ),
+                        )
                     });
                     });
                 });
                 });
 
 
@@ -127,6 +132,7 @@ export default function InviteUserForm({ open, setOpen }: InviteUserFormProps) {
                     email: values.email,
                     email: values.email,
                     roleId: parseInt(values.roleId),
                     roleId: parseInt(values.roleId),
                     validHours: parseInt(values.validForHours),
                     validHours: parseInt(values.validForHours),
+                    sendEmail: sendEmail
                 } as InviteUserBody
                 } as InviteUserBody
             )
             )
             .catch((e) => {
             .catch((e) => {
@@ -136,7 +142,7 @@ export default function InviteUserForm({ open, setOpen }: InviteUserFormProps) {
                     description: formatAxiosError(
                     description: formatAxiosError(
                         e,
                         e,
                         "An error occurred while inviting the user"
                         "An error occurred while inviting the user"
-                    ),
+                    )
                 });
                 });
             });
             });
 
 
@@ -145,7 +151,7 @@ export default function InviteUserForm({ open, setOpen }: InviteUserFormProps) {
             toast({
             toast({
                 variant: "default",
                 variant: "default",
                 title: "User invited",
                 title: "User invited",
-                description: "The user has been successfully invited.",
+                description: "The user has been successfully invited."
             });
             });
 
 
             setExpiresInDays(parseInt(values.validForHours) / 24);
             setExpiresInDays(parseInt(values.validForHours) / 24);
@@ -198,6 +204,27 @@ export default function InviteUserForm({ open, setOpen }: InviteUserFormProps) {
                                                 </FormItem>
                                                 </FormItem>
                                             )}
                                             )}
                                         />
                                         />
+
+                                        {env.EMAIL_ENABLED === "true" && (
+                                            <div className="flex items-center space-x-2">
+                                                <Checkbox
+                                                    id="send-email"
+                                                    checked={sendEmail}
+                                                    onCheckedChange={(e) =>
+                                                        setSendEmail(
+                                                            e as boolean
+                                                        )
+                                                    }
+                                                />
+                                                <label
+                                                    htmlFor="send-email"
+                                                    className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
+                                                >
+                                                    Send invite email to user
+                                                </label>
+                                            </div>
+                                        )}
+
                                         <FormField
                                         <FormField
                                             control={form.control}
                                             control={form.control}
                                             name="roleId"
                                             name="roleId"
@@ -281,11 +308,21 @@ export default function InviteUserForm({ open, setOpen }: InviteUserFormProps) {
 
 
                             {inviteLink && (
                             {inviteLink && (
                                 <div className="max-w-md space-y-4">
                                 <div className="max-w-md space-y-4">
-                                    <p>
-                                        The user has been successfully invited.
-                                        They must access the link below to
-                                        accept the invitation.
-                                    </p>
+                                    {sendEmail && (
+                                        <p>
+                                            An email has been sent to the user
+                                            with the access link below. They
+                                            must access the link to accept the
+                                            invitation.
+                                        </p>
+                                    )}
+                                    {!sendEmail && (
+                                        <p>
+                                            The user has been invited. They must
+                                            access the link below to accept the
+                                            invitation.
+                                        </p>
+                                    )}
                                     <p>
                                     <p>
                                         The invite will expire in{" "}
                                         The invite will expire in{" "}
                                         <b>
                                         <b>

+ 23 - 24
src/app/[orgId]/settings/access/users/components/UsersTable.tsx

@@ -5,7 +5,7 @@ import {
     DropdownMenu,
     DropdownMenu,
     DropdownMenuContent,
     DropdownMenuContent,
     DropdownMenuItem,
     DropdownMenuItem,
-    DropdownMenuTrigger,
+    DropdownMenuTrigger
 } from "@app/components/ui/dropdown-menu";
 } from "@app/components/ui/dropdown-menu";
 import { Button } from "@app/components/ui/button";
 import { Button } from "@app/components/ui/button";
 import { ArrowRight, ArrowUpDown, Crown, MoreHorizontal } from "lucide-react";
 import { ArrowRight, ArrowUpDown, Crown, MoreHorizontal } from "lucide-react";
@@ -43,7 +43,7 @@ export default function UsersTable({ users: u }: UsersTableProps) {
 
 
     const router = useRouter();
     const router = useRouter();
 
 
-    const api  = createApiClient(useEnvContext());
+    const api = createApiClient(useEnvContext());
 
 
     const user = useUserContext();
     const user = useUserContext();
     const { org } = useOrgContext();
     const { org } = useOrgContext();
@@ -64,7 +64,7 @@ export default function UsersTable({ users: u }: UsersTableProps) {
                         <ArrowUpDown className="ml-2 h-4 w-4" />
                         <ArrowUpDown className="ml-2 h-4 w-4" />
                     </Button>
                     </Button>
                 );
                 );
-            },
+            }
         },
         },
         {
         {
             accessorKey: "status",
             accessorKey: "status",
@@ -80,7 +80,7 @@ export default function UsersTable({ users: u }: UsersTableProps) {
                         <ArrowUpDown className="ml-2 h-4 w-4" />
                         <ArrowUpDown className="ml-2 h-4 w-4" />
                     </Button>
                     </Button>
                 );
                 );
-            },
+            }
         },
         },
         {
         {
             accessorKey: "role",
             accessorKey: "role",
@@ -108,7 +108,7 @@ export default function UsersTable({ users: u }: UsersTableProps) {
                         <span>{userRow.role}</span>
                         <span>{userRow.role}</span>
                     </div>
                     </div>
                 );
                 );
-            },
+            }
         },
         },
         {
         {
             id: "actions",
             id: "actions",
@@ -149,20 +149,19 @@ export default function UsersTable({ users: u }: UsersTableProps) {
                                                 </Link>
                                                 </Link>
                                             </DropdownMenuItem>
                                             </DropdownMenuItem>
                                             {userRow.email !== user?.email && (
                                             {userRow.email !== user?.email && (
-                                                <DropdownMenuItem>
-                                                    <button
-                                                        className="text-red-500"
-                                                        onClick={() => {
-                                                            setIsDeleteModalOpen(
-                                                                true,
-                                                            );
-                                                            setSelectedUser(
-                                                                userRow,
-                                                            );
-                                                        }}
-                                                    >
+                                                <DropdownMenuItem
+                                                    onClick={() => {
+                                                        setIsDeleteModalOpen(
+                                                            true
+                                                        );
+                                                        setSelectedUser(
+                                                            userRow
+                                                        );
+                                                    }}
+                                                >
+                                                    <span className="text-red-500">
                                                         Remove User
                                                         Remove User
-                                                    </button>
+                                                    </span>
                                                 </DropdownMenuItem>
                                                 </DropdownMenuItem>
                                             )}
                                             )}
                                         </DropdownMenuContent>
                                         </DropdownMenuContent>
@@ -183,8 +182,8 @@ export default function UsersTable({ users: u }: UsersTableProps) {
                         </div>
                         </div>
                     </>
                     </>
                 );
                 );
-            },
-        },
+            }
+        }
     ];
     ];
 
 
     async function removeUser() {
     async function removeUser() {
@@ -197,8 +196,8 @@ export default function UsersTable({ users: u }: UsersTableProps) {
                         title: "Failed to remove user",
                         title: "Failed to remove user",
                         description: formatAxiosError(
                         description: formatAxiosError(
                             e,
                             e,
-                            "An error occurred while removing the user.",
-                        ),
+                            "An error occurred while removing the user."
+                        )
                     });
                     });
                 });
                 });
 
 
@@ -206,11 +205,11 @@ export default function UsersTable({ users: u }: UsersTableProps) {
                 toast({
                 toast({
                     variant: "default",
                     variant: "default",
                     title: "User removed",
                     title: "User removed",
-                    description: `The user ${selectedUser.email} has been removed from the organization.`,
+                    description: `The user ${selectedUser.email} has been removed from the organization.`
                 });
                 });
 
 
                 setUsers((prev) =>
                 setUsers((prev) =>
-                    prev.filter((u) => u.id !== selectedUser?.id),
+                    prev.filter((u) => u.id !== selectedUser?.id)
                 );
                 );
             }
             }
         }
         }

+ 66 - 57
src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx

@@ -637,63 +637,72 @@ export default function ResourceAuthenticationPage() {
                                 </span>
                                 </span>
                             </div>
                             </div>
 
 
-                            <Form {...whitelistForm}>
-                                <form className="space-y-8">
-                                    <FormField
-                                        control={whitelistForm.control}
-                                        name="emails"
-                                        render={({ field }) => (
-                                            <FormItem className="flex flex-col items-start">
-                                                <FormLabel>
-                                                    Whitelisted Emails
-                                                </FormLabel>
-                                                <FormControl>
-                                                    <TagInput
-                                                        {...field}
-                                                        activeTagIndex={
-                                                            activeEmailTagIndex
-                                                        }
-                                                        validateTag={(tag) => {
-                                                            return z
-                                                                .string()
-                                                                .email()
-                                                                .safeParse(tag)
-                                                                .success;
-                                                        }}
-                                                        setActiveTagIndex={
-                                                            setActiveEmailTagIndex
-                                                        }
-                                                        placeholder="Enter an email"
-                                                        tags={
-                                                            whitelistForm.getValues()
-                                                                .emails
-                                                        }
-                                                        setTags={(newRoles) => {
-                                                            whitelistForm.setValue(
-                                                                "emails",
-                                                                newRoles as [
-                                                                    Tag,
-                                                                    ...Tag[]
-                                                                ]
-                                                            );
-                                                        }}
-                                                        allowDuplicates={false}
-                                                        sortTags={true}
-                                                        styleClasses={{
-                                                            tag: {
-                                                                body: "bg-muted hover:bg-accent text-foreground py-2 px-3 rounded-full"
-                                                            },
-                                                            input: "border-none bg-transparent text-inherit placeholder:text-inherit shadow-none",
-                                                            inlineTagsContainer:
-                                                                "bg-transparent"
-                                                        }}
-                                                    />
-                                                </FormControl>
-                                            </FormItem>
-                                        )}
-                                    />
-                                </form>
-                            </Form>
+                            {whitelistEnabled && (
+                                <Form {...whitelistForm}>
+                                    <form className="space-y-8">
+                                        <FormField
+                                            control={whitelistForm.control}
+                                            name="emails"
+                                            render={({ field }) => (
+                                                <FormItem className="flex flex-col items-start">
+                                                    <FormLabel>
+                                                        Whitelisted Emails
+                                                    </FormLabel>
+                                                    <FormControl>
+                                                        <TagInput
+                                                            {...field}
+                                                            activeTagIndex={
+                                                                activeEmailTagIndex
+                                                            }
+                                                            validateTag={(
+                                                                tag
+                                                            ) => {
+                                                                return z
+                                                                    .string()
+                                                                    .email()
+                                                                    .safeParse(
+                                                                        tag
+                                                                    ).success;
+                                                            }}
+                                                            setActiveTagIndex={
+                                                                setActiveEmailTagIndex
+                                                            }
+                                                            placeholder="Enter an email"
+                                                            tags={
+                                                                whitelistForm.getValues()
+                                                                    .emails
+                                                            }
+                                                            setTags={(
+                                                                newRoles
+                                                            ) => {
+                                                                whitelistForm.setValue(
+                                                                    "emails",
+                                                                    newRoles as [
+                                                                        Tag,
+                                                                        ...Tag[]
+                                                                    ]
+                                                                );
+                                                            }}
+                                                            allowDuplicates={
+                                                                false
+                                                            }
+                                                            sortTags={true}
+                                                            styleClasses={{
+                                                                tag: {
+                                                                    body: "bg-muted hover:bg-accent text-foreground py-2 px-3 rounded-full"
+                                                                },
+                                                                input: "border-none bg-transparent text-inherit placeholder:text-inherit shadow-none",
+                                                                inlineTagsContainer:
+                                                                    "bg-transparent"
+                                                            }}
+                                                        />
+                                                    </FormControl>
+                                                </FormItem>
+                                            )}
+                                        />
+                                    </form>
+                                </Form>
+                            )}
 
 
                             <Button
                             <Button
                                 loading={loadingSaveWhitelist}
                                 loading={loadingSaveWhitelist}

+ 21 - 24
src/app/[orgId]/settings/resources/components/ResourcesTable.tsx

@@ -6,7 +6,7 @@ import {
     DropdownMenu,
     DropdownMenu,
     DropdownMenuContent,
     DropdownMenuContent,
     DropdownMenuItem,
     DropdownMenuItem,
-    DropdownMenuTrigger,
+    DropdownMenuTrigger
 } from "@app/components/ui/dropdown-menu";
 } from "@app/components/ui/dropdown-menu";
 import { Button } from "@app/components/ui/button";
 import { Button } from "@app/components/ui/button";
 import {
 import {
@@ -17,7 +17,7 @@ import {
     Check,
     Check,
     ArrowUpRight,
     ArrowUpRight,
     ShieldOff,
     ShieldOff,
-    ShieldCheck,
+    ShieldCheck
 } from "lucide-react";
 } from "lucide-react";
 import Link from "next/link";
 import Link from "next/link";
 import { useRouter } from "next/navigation";
 import { useRouter } from "next/navigation";
@@ -64,7 +64,7 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) {
                 toast({
                 toast({
                     variant: "destructive",
                     variant: "destructive",
                     title: "Error deleting resource",
                     title: "Error deleting resource",
-                    description: formatAxiosError(e, "Error deleting resource"),
+                    description: formatAxiosError(e, "Error deleting resource")
                 });
                 });
             })
             })
             .then(() => {
             .then(() => {
@@ -88,7 +88,7 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) {
                         <ArrowUpDown className="ml-2 h-4 w-4" />
                         <ArrowUpDown className="ml-2 h-4 w-4" />
                     </Button>
                     </Button>
                 );
                 );
-            },
+            }
         },
         },
         {
         {
             accessorKey: "site",
             accessorKey: "site",
@@ -117,7 +117,7 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) {
                         <ArrowUpRight className="ml-2 h-4 w-4" />
                         <ArrowUpRight className="ml-2 h-4 w-4" />
                     </Button>
                     </Button>
                 );
                 );
-            },
+            }
         },
         },
         {
         {
             accessorKey: "domain",
             accessorKey: "domain",
@@ -139,16 +139,16 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) {
                             className="h-6 w-6 p-0"
                             className="h-6 w-6 p-0"
                             onClick={() => {
                             onClick={() => {
                                 navigator.clipboard.writeText(
                                 navigator.clipboard.writeText(
-                                    resourceRow.domain,
+                                    resourceRow.domain
                                 );
                                 );
                                 const originalIcon = document.querySelector(
                                 const originalIcon = document.querySelector(
-                                    `#icon-${resourceRow.id}`,
+                                    `#icon-${resourceRow.id}`
                                 );
                                 );
                                 if (originalIcon) {
                                 if (originalIcon) {
                                     originalIcon.classList.add("hidden");
                                     originalIcon.classList.add("hidden");
                                 }
                                 }
                                 const checkIcon = document.querySelector(
                                 const checkIcon = document.querySelector(
-                                    `#check-icon-${resourceRow.id}`,
+                                    `#check-icon-${resourceRow.id}`
                                 );
                                 );
                                 if (checkIcon) {
                                 if (checkIcon) {
                                     checkIcon.classList.remove("hidden");
                                     checkIcon.classList.remove("hidden");
@@ -156,7 +156,7 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) {
                                         checkIcon.classList.add("hidden");
                                         checkIcon.classList.add("hidden");
                                         if (originalIcon) {
                                         if (originalIcon) {
                                             originalIcon.classList.remove(
                                             originalIcon.classList.remove(
-                                                "hidden",
+                                                "hidden"
                                             );
                                             );
                                         }
                                         }
                                     }, 2000);
                                     }, 2000);
@@ -175,7 +175,7 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) {
                         </Button>
                         </Button>
                     </div>
                     </div>
                 );
                 );
-            },
+            }
         },
         },
         {
         {
             accessorKey: "hasAuth",
             accessorKey: "hasAuth",
@@ -209,7 +209,7 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) {
                         )}
                         )}
                     </div>
                     </div>
                 );
                 );
-            },
+            }
         },
         },
         {
         {
             id: "actions",
             id: "actions",
@@ -241,18 +241,15 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) {
                                             View settings
                                             View settings
                                         </Link>
                                         </Link>
                                     </DropdownMenuItem>
                                     </DropdownMenuItem>
-                                    <DropdownMenuItem>
-                                        <button
-                                            onClick={() => {
-                                                setSelectedResource(
-                                                    resourceRow,
-                                                );
-                                                setIsDeleteModalOpen(true);
-                                            }}
-                                            className="text-red-500"
-                                        >
+                                    <DropdownMenuItem
+                                        onClick={() => {
+                                            setSelectedResource(resourceRow);
+                                            setIsDeleteModalOpen(true);
+                                        }}
+                                    >
+                                        <span className="text-red-500">
                                             Delete
                                             Delete
-                                        </button>
+                                        </span>
                                     </DropdownMenuItem>
                                     </DropdownMenuItem>
                                 </DropdownMenuContent>
                                 </DropdownMenuContent>
                             </DropdownMenu>
                             </DropdownMenu>
@@ -267,8 +264,8 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) {
                         </div>
                         </div>
                     </>
                     </>
                 );
                 );
-            },
-        },
+            }
+        }
     ];
     ];
 
 
     return (
     return (

+ 14 - 4
src/app/[orgId]/settings/share-links/components/CreateShareLinkForm.tsx

@@ -197,12 +197,16 @@ export default function CreateShareLinkForm({
             const link = constructShareLink(
             const link = constructShareLink(
                 values.resourceId,
                 values.resourceId,
                 token.accessTokenId,
                 token.accessTokenId,
-                token.tokenHash
+                token.accessToken
             );
             );
             setLink(link);
             setLink(link);
             onCreated?.({
             onCreated?.({
-                ...token,
-                resourceName: values.resourceName
+                accessTokenId: token.accessTokenId,
+                resourceId: token.resourceId,
+                resourceName: values.resourceName,
+                title: token.title,
+                createdAt: token.createdAt,
+                expiresAt: token.expiresAt
             });
             });
         }
         }
 
 
@@ -285,7 +289,9 @@ export default function CreateShareLinkForm({
                                                                                 r
                                                                                 r
                                                                             ) => (
                                                                             ) => (
                                                                                 <CommandItem
                                                                                 <CommandItem
-                                                                                    value={r.name}
+                                                                                    value={
+                                                                                        r.name
+                                                                                    }
                                                                                     key={
                                                                                     key={
                                                                                         r.resourceId
                                                                                         r.resourceId
                                                                                     }
                                                                                     }
@@ -441,6 +447,10 @@ export default function CreateShareLinkForm({
                             )}
                             )}
                             {link && (
                             {link && (
                                 <div className="max-w-md space-y-4">
                                 <div className="max-w-md space-y-4">
+                                    <p>
+                                        You will be able to see this link once.
+                                        Make sure to copy it.
+                                    </p>
                                     <p>
                                     <p>
                                         Anyone with this link can access the
                                         Anyone with this link can access the
                                         resource. Share it with care.
                                         resource. Share it with care.

+ 76 - 68
src/app/[orgId]/settings/share-links/components/ShareLinksTable.tsx

@@ -34,9 +34,14 @@ import moment from "moment";
 import CreateShareLinkForm from "./CreateShareLinkForm";
 import CreateShareLinkForm from "./CreateShareLinkForm";
 import { constructShareLink } from "@app/lib/shareLinks";
 import { constructShareLink } from "@app/lib/shareLinks";
 
 
-export type ShareLinkRow = ArrayElement<
-    ListAccessTokensResponse["accessTokens"]
->;
+export type ShareLinkRow = {
+    accessTokenId: string;
+    resourceId: number;
+    resourceName: string;
+    title: string | null;
+    createdAt: number;
+    expiresAt: number | null;
+};
 
 
 type ShareLinksTableProps = {
 type ShareLinksTableProps = {
     shareLinks: ShareLinkRow[];
     shareLinks: ShareLinkRow[];
@@ -64,7 +69,10 @@ export default function ShareLinksTable({
         await api.delete(`/access-token/${id}`).catch((e) => {
         await api.delete(`/access-token/${id}`).catch((e) => {
             toast({
             toast({
                 title: "Failed to delete link",
                 title: "Failed to delete link",
-                description: formatAxiosError(e, "An error occurred deleting link"),
+                description: formatAxiosError(
+                    e,
+                    "An error occurred deleting link"
+                )
             });
             });
         });
         });
 
 
@@ -73,7 +81,7 @@ export default function ShareLinksTable({
 
 
         toast({
         toast({
             title: "Link deleted",
             title: "Link deleted",
-            description: "The link has been deleted",
+            description: "The link has been deleted"
         });
         });
     }
     }
 
 
@@ -123,69 +131,69 @@ export default function ShareLinksTable({
                 );
                 );
             }
             }
         },
         },
-        {
-            accessorKey: "domain",
-            header: "Link",
-            cell: ({ row }) => {
-                const r = row.original;
-
-                const link = constructShareLink(
-                    r.resourceId,
-                    r.accessTokenId,
-                    r.tokenHash
-                );
-
-                return (
-                    <div className="flex items-center">
-                        <Link
-                            href={link}
-                            target="_blank"
-                            rel="noopener noreferrer"
-                            className="hover:underline mr-2"
-                        >
-                            {formatLink(link)}
-                        </Link>
-                        <Button
-                            variant="ghost"
-                            className="h-6 w-6 p-0"
-                            onClick={() => {
-                                navigator.clipboard.writeText(link);
-                                const originalIcon = document.querySelector(
-                                    `#icon-${r.accessTokenId}`
-                                );
-                                if (originalIcon) {
-                                    originalIcon.classList.add("hidden");
-                                }
-                                const checkIcon = document.querySelector(
-                                    `#check-icon-${r.accessTokenId}`
-                                );
-                                if (checkIcon) {
-                                    checkIcon.classList.remove("hidden");
-                                    setTimeout(() => {
-                                        checkIcon.classList.add("hidden");
-                                        if (originalIcon) {
-                                            originalIcon.classList.remove(
-                                                "hidden"
-                                            );
-                                        }
-                                    }, 2000);
-                                }
-                            }}
-                        >
-                            <Copy
-                                id={`icon-${r.accessTokenId}`}
-                                className="h-4 w-4"
-                            />
-                            <Check
-                                id={`check-icon-${r.accessTokenId}`}
-                                className="hidden text-green-500 h-4 w-4"
-                            />
-                            <span className="sr-only">Copy link</span>
-                        </Button>
-                    </div>
-                );
-            }
-        },
+        // {
+        //     accessorKey: "domain",
+        //     header: "Link",
+        //     cell: ({ row }) => {
+        //         const r = row.original;
+        //
+        //         const link = constructShareLink(
+        //             r.resourceId,
+        //             r.accessTokenId,
+        //             r.tokenHash
+        //         );
+        //
+        //         return (
+        //             <div className="flex items-center">
+        //                 <Link
+        //                     href={link}
+        //                     target="_blank"
+        //                     rel="noopener noreferrer"
+        //                     className="hover:underline mr-2"
+        //                 >
+        //                     {formatLink(link)}
+        //                 </Link>
+        //                 <Button
+        //                     variant="ghost"
+        //                     className="h-6 w-6 p-0"
+        //                     onClick={() => {
+        //                         navigator.clipboard.writeText(link);
+        //                         const originalIcon = document.querySelector(
+        //                             `#icon-${r.accessTokenId}`
+        //                         );
+        //                         if (originalIcon) {
+        //                             originalIcon.classList.add("hidden");
+        //                         }
+        //                         const checkIcon = document.querySelector(
+        //                             `#check-icon-${r.accessTokenId}`
+        //                         );
+        //                         if (checkIcon) {
+        //                             checkIcon.classList.remove("hidden");
+        //                             setTimeout(() => {
+        //                                 checkIcon.classList.add("hidden");
+        //                                 if (originalIcon) {
+        //                                     originalIcon.classList.remove(
+        //                                         "hidden"
+        //                                     );
+        //                                 }
+        //                             }, 2000);
+        //                         }
+        //                     }}
+        //                 >
+        //                     <Copy
+        //                         id={`icon-${r.accessTokenId}`}
+        //                         className="h-4 w-4"
+        //                     />
+        //                     <Check
+        //                         id={`check-icon-${r.accessTokenId}`}
+        //                         className="hidden text-green-500 h-4 w-4"
+        //                     />
+        //                     <span className="sr-only">Copy link</span>
+        //                 </Button>
+        //             </div>
+        //         );
+        //     }
+        // },
         {
         {
             accessorKey: "createdAt",
             accessorKey: "createdAt",
             header: ({ column }) => {
             header: ({ column }) => {

+ 3 - 3
src/app/[orgId]/settings/share-links/page.tsx

@@ -46,9 +46,9 @@ export default async function ShareLinksPage(props: ShareLinksPageProps) {
         redirect(`/${params.orgId}/settings/resources`);
         redirect(`/${params.orgId}/settings/resources`);
     }
     }
 
 
-    const rows: ShareLinkRow[] = tokens.map((token) => {
-        return token;
-    });
+    const rows: ShareLinkRow[] = tokens.map(
+        (token) => ({ ...token }) as ShareLinkRow
+    );
 
 
     return (
     return (
         <>
         <>

+ 50 - 24
src/app/[orgId]/settings/sites/components/CreateSiteForm.tsx

@@ -8,7 +8,7 @@ import {
     FormField,
     FormField,
     FormItem,
     FormItem,
     FormLabel,
     FormLabel,
-    FormMessage,
+    FormMessage
 } from "@app/components/ui/form";
 } from "@app/components/ui/form";
 import { Input } from "@app/components/ui/input";
 import { Input } from "@app/components/ui/input";
 import { useToast } from "@app/hooks/useToast";
 import { useToast } from "@app/hooks/useToast";
@@ -24,11 +24,15 @@ import {
     CredenzaDescription,
     CredenzaDescription,
     CredenzaFooter,
     CredenzaFooter,
     CredenzaHeader,
     CredenzaHeader,
-    CredenzaTitle,
+    CredenzaTitle
 } from "@app/components/Credenza";
 } from "@app/components/Credenza";
 import { useOrgContext } from "@app/hooks/useOrgContext";
 import { useOrgContext } from "@app/hooks/useOrgContext";
 import { useParams, useRouter } from "next/navigation";
 import { useParams, useRouter } from "next/navigation";
-import { CreateSiteBody, PickSiteDefaultsResponse } from "@server/routers/site";
+import {
+    CreateSiteBody,
+    CreateSiteResponse,
+    PickSiteDefaultsResponse
+} from "@server/routers/site";
 import { generateKeypair } from "../[niceId]/components/wireguardConfig";
 import { generateKeypair } from "../[niceId]/components/wireguardConfig";
 import CopyTextBox from "@app/components/CopyTextBox";
 import CopyTextBox from "@app/components/CopyTextBox";
 import { Checkbox } from "@app/components/ui/checkbox";
 import { Checkbox } from "@app/components/ui/checkbox";
@@ -37,42 +41,49 @@ import {
     SelectContent,
     SelectContent,
     SelectItem,
     SelectItem,
     SelectTrigger,
     SelectTrigger,
-    SelectValue,
+    SelectValue
 } from "@app/components/ui/select";
 } from "@app/components/ui/select";
 import { formatAxiosError } from "@app/lib/utils";
 import { formatAxiosError } from "@app/lib/utils";
 import { createApiClient } from "@app/api";
 import { createApiClient } from "@app/api";
 import { useEnvContext } from "@app/hooks/useEnvContext";
 import { useEnvContext } from "@app/hooks/useEnvContext";
+import { SiteRow } from "./SitesTable";
+import { AxiosResponse } from "axios";
 
 
 const method = [
 const method = [
     { label: "Newt", value: "newt" },
     { label: "Newt", value: "newt" },
-    { label: "Wireguard", value: "wireguard" },
+    { label: "WireGuard", value: "wireguard" }
 ] as const;
 ] as const;
 
 
-const accountFormSchema = z.object({
+const createSiteFormSchema = z.object({
     name: z
     name: z
         .string()
         .string()
         .min(2, {
         .min(2, {
-            message: "Name must be at least 2 characters.",
+            message: "Name must be at least 2 characters."
         })
         })
         .max(30, {
         .max(30, {
-            message: "Name must not be longer than 30 characters.",
+            message: "Name must not be longer than 30 characters."
         }),
         }),
-    method: z.enum(["wireguard", "newt"]),
+    method: z.enum(["wireguard", "newt"])
 });
 });
 
 
-type AccountFormValues = z.infer<typeof accountFormSchema>;
+type CreateSiteFormValues = z.infer<typeof createSiteFormSchema>;
 
 
-const defaultValues: Partial<AccountFormValues> = {
+const defaultValues: Partial<CreateSiteFormValues> = {
     name: "",
     name: "",
-    method: "newt",
+    method: "newt"
 };
 };
 
 
 type CreateSiteFormProps = {
 type CreateSiteFormProps = {
     open: boolean;
     open: boolean;
     setOpen: (open: boolean) => void;
     setOpen: (open: boolean) => void;
+    onCreate?: (site: SiteRow) => void;
 };
 };
 
 
-export default function CreateSiteForm({ open, setOpen }: CreateSiteFormProps) {
+export default function CreateSiteForm({
+    open,
+    setOpen,
+    onCreate
+}: CreateSiteFormProps) {
     const { toast } = useToast();
     const { toast } = useToast();
 
 
     const api = createApiClient(useEnvContext());
     const api = createApiClient(useEnvContext());
@@ -96,9 +107,9 @@ export default function CreateSiteForm({ open, setOpen }: CreateSiteFormProps) {
         setIsChecked(checked);
         setIsChecked(checked);
     };
     };
 
 
-    const form = useForm<AccountFormValues>({
-        resolver: zodResolver(accountFormSchema),
-        defaultValues,
+    const form = useForm<CreateSiteFormValues>({
+        resolver: zodResolver(createSiteFormSchema),
+        defaultValues
     });
     });
 
 
     useEffect(() => {
     useEffect(() => {
@@ -114,7 +125,7 @@ export default function CreateSiteForm({ open, setOpen }: CreateSiteFormProps) {
                     toast({
                     toast({
                         variant: "destructive",
                         variant: "destructive",
                         title: "Error picking site defaults",
                         title: "Error picking site defaults",
-                        description: formatAxiosError(e),
+                        description: formatAxiosError(e)
                     });
                     });
                 })
                 })
                 .then((res) => {
                 .then((res) => {
@@ -125,7 +136,7 @@ export default function CreateSiteForm({ open, setOpen }: CreateSiteFormProps) {
         }
         }
     }, [open]);
     }, [open]);
 
 
-    async function onSubmit(data: AccountFormValues) {
+    async function onSubmit(data: CreateSiteFormValues) {
         setLoading(true);
         setLoading(true);
         if (!siteDefaults || !keypair) {
         if (!siteDefaults || !keypair) {
             return;
             return;
@@ -135,29 +146,44 @@ export default function CreateSiteForm({ open, setOpen }: CreateSiteFormProps) {
             subnet: siteDefaults.subnet,
             subnet: siteDefaults.subnet,
             exitNodeId: siteDefaults.exitNodeId,
             exitNodeId: siteDefaults.exitNodeId,
             pubKey: keypair.publicKey,
             pubKey: keypair.publicKey,
-            type: data.method,
+            type: data.method
         };
         };
         if (data.method === "newt") {
         if (data.method === "newt") {
             payload.secret = siteDefaults.newtSecret;
             payload.secret = siteDefaults.newtSecret;
             payload.newtId = siteDefaults.newtId;
             payload.newtId = siteDefaults.newtId;
         }
         }
         const res = await api
         const res = await api
-            .put(`/org/${orgId}/site/`, payload)
+            .put<
+                AxiosResponse<CreateSiteResponse>
+            >(`/org/${orgId}/site/`, payload)
             .catch((e) => {
             .catch((e) => {
                 toast({
                 toast({
                     variant: "destructive",
                     variant: "destructive",
                     title: "Error creating site",
                     title: "Error creating site",
-                    description: formatAxiosError(e),
+                    description: formatAxiosError(e)
                 });
                 });
             });
             });
 
 
         if (res && res.status === 201) {
         if (res && res.status === 201) {
             const niceId = res.data.data.niceId;
             const niceId = res.data.data.niceId;
             // navigate to the site page
             // navigate to the site page
-            router.push(`/${orgId}/settings/sites/${niceId}`);
+            // router.push(`/${orgId}/settings/sites/${niceId}`);
 
 
             // close the modal
             // close the modal
             setOpen(false);
             setOpen(false);
+
+            const data = res.data.data;
+
+            onCreate?.({
+                name: data.name,
+                id: data.siteId,
+                nice: data.niceId.toString(),
+                mbIn: "0 MB",
+                mbOut: "0 MB",
+                orgId: orgId as string,
+                type: data.type as any,
+                online: false
+            });
         }
         }
 
 
         setLoading(false);
         setLoading(false);
@@ -275,8 +301,8 @@ PersistentKeepalive = 5`
                                         {form.watch("method") === "wireguard" &&
                                         {form.watch("method") === "wireguard" &&
                                         !isLoading ? (
                                         !isLoading ? (
                                             <CopyTextBox text={wgConfig} />
                                             <CopyTextBox text={wgConfig} />
-                                        ) : form.watch("method") === "wireguard" &&
-                                          isLoading ? (
+                                        ) : form.watch("method") ===
+                                              "wireguard" && isLoading ? (
                                             <p>
                                             <p>
                                                 Loading WireGuard
                                                 Loading WireGuard
                                                 configuration...
                                                 configuration...

+ 34 - 33
src/app/[orgId]/settings/sites/components/SitesTable.tsx

@@ -6,10 +6,16 @@ import {
     DropdownMenu,
     DropdownMenu,
     DropdownMenuContent,
     DropdownMenuContent,
     DropdownMenuItem,
     DropdownMenuItem,
-    DropdownMenuTrigger,
+    DropdownMenuTrigger
 } from "@app/components/ui/dropdown-menu";
 } from "@app/components/ui/dropdown-menu";
 import { Button } from "@app/components/ui/button";
 import { Button } from "@app/components/ui/button";
-import { ArrowRight, ArrowUpDown, Check, MoreHorizontal, X } from "lucide-react";
+import {
+    ArrowRight,
+    ArrowUpDown,
+    Check,
+    MoreHorizontal,
+    X
+} from "lucide-react";
 import Link from "next/link";
 import Link from "next/link";
 import { useRouter } from "next/navigation";
 import { useRouter } from "next/navigation";
 import { AxiosResponse } from "axios";
 import { AxiosResponse } from "axios";
@@ -45,14 +51,10 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
     const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
     const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
     const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
     const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
     const [selectedSite, setSelectedSite] = useState<SiteRow | null>(null);
     const [selectedSite, setSelectedSite] = useState<SiteRow | null>(null);
+    const [rows, setRows] = useState<SiteRow[]>(sites);
 
 
     const api = createApiClient(useEnvContext());
     const api = createApiClient(useEnvContext());
 
 
-    const callApi = async () => {
-        const res = await api.put<AxiosResponse<any>>(`/newt`);
-        console.log(res);
-    };
-
     const deleteSite = (siteId: number) => {
     const deleteSite = (siteId: number) => {
         api.delete(`/site/${siteId}`)
         api.delete(`/site/${siteId}`)
             .catch((e) => {
             .catch((e) => {
@@ -60,7 +62,7 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
                 toast({
                 toast({
                     variant: "destructive",
                     variant: "destructive",
                     title: "Error deleting site",
                     title: "Error deleting site",
-                    description: formatAxiosError(e, "Error deleting site"),
+                    description: formatAxiosError(e, "Error deleting site")
                 });
                 });
             })
             })
             .then(() => {
             .then(() => {
@@ -84,7 +86,7 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
                         <ArrowUpDown className="ml-2 h-4 w-4" />
                         <ArrowUpDown className="ml-2 h-4 w-4" />
                     </Button>
                     </Button>
                 );
                 );
-            },
+            }
         },
         },
         {
         {
             accessorKey: "nice",
             accessorKey: "nice",
@@ -100,7 +102,7 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
                         <ArrowUpDown className="ml-2 h-4 w-4" />
                         <ArrowUpDown className="ml-2 h-4 w-4" />
                     </Button>
                     </Button>
                 );
                 );
-            },
+            }
         },
         },
         {
         {
             accessorKey: "mbIn",
             accessorKey: "mbIn",
@@ -116,7 +118,7 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
                         <ArrowUpDown className="ml-2 h-4 w-4" />
                         <ArrowUpDown className="ml-2 h-4 w-4" />
                     </Button>
                     </Button>
                 );
                 );
-            },
+            }
         },
         },
         {
         {
             accessorKey: "mbOut",
             accessorKey: "mbOut",
@@ -132,7 +134,7 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
                         <ArrowUpDown className="ml-2 h-4 w-4" />
                         <ArrowUpDown className="ml-2 h-4 w-4" />
                     </Button>
                     </Button>
                 );
                 );
-            },
+            }
         },
         },
         {
         {
             accessorKey: "type",
             accessorKey: "type",
@@ -167,7 +169,7 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
                         </div>
                         </div>
                     );
                     );
                 }
                 }
-            },
+            }
         },
         },
         {
         {
             accessorKey: "online",
             accessorKey: "online",
@@ -187,23 +189,23 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
             cell: ({ row }) => {
             cell: ({ row }) => {
                 const originalRow = row.original;
                 const originalRow = row.original;
                 console.log(originalRow.online);
                 console.log(originalRow.online);
-                
+
                 if (originalRow.online) {
                 if (originalRow.online) {
                     return (
                     return (
                         <span className="text-green-500 flex items-center space-x-2">
                         <span className="text-green-500 flex items-center space-x-2">
-                        <Check className="w-4 h-4" />
-                        <span>Online</span>
+                            <Check className="w-4 h-4" />
+                            <span>Online</span>
                         </span>
                         </span>
                     );
                     );
                 } else {
                 } else {
                     return (
                     return (
                         <span className="text-red-500 flex items-center space-x-2">
                         <span className="text-red-500 flex items-center space-x-2">
-                        <X className="w-4 h-4" />
-                        <span>Offline</span>
+                            <X className="w-4 h-4" />
+                            <span>Offline</span>
                         </span>
                         </span>
                     );
                     );
                 }
                 }
-            },
+            }
         },
         },
         {
         {
             id: "actions",
             id: "actions",
@@ -229,16 +231,13 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
                                         View settings
                                         View settings
                                     </Link>
                                     </Link>
                                 </DropdownMenuItem>
                                 </DropdownMenuItem>
-                                <DropdownMenuItem>
-                                    <button
-                                        onClick={() => {
-                                            setSelectedSite(siteRow);
-                                            setIsDeleteModalOpen(true);
-                                        }}
-                                        className="text-red-500"
-                                    >
-                                        Delete
-                                    </button>
+                                <DropdownMenuItem
+                                    onClick={() => {
+                                        setSelectedSite(siteRow);
+                                        setIsDeleteModalOpen(true);
+                                    }}
+                                >
+                                    <span className="text-red-500">Delete</span>
                                 </DropdownMenuItem>
                                 </DropdownMenuItem>
                             </DropdownMenuContent>
                             </DropdownMenuContent>
                         </DropdownMenu>
                         </DropdownMenu>
@@ -252,8 +251,8 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
                         </Link>
                         </Link>
                     </div>
                     </div>
                 );
                 );
-            },
-        },
+            }
+        }
     ];
     ];
 
 
     return (
     return (
@@ -261,6 +260,9 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
             <CreateSiteForm
             <CreateSiteForm
                 open={isCreateModalOpen}
                 open={isCreateModalOpen}
                 setOpen={setIsCreateModalOpen}
                 setOpen={setIsCreateModalOpen}
+                onCreate={(val) => {
+                    setRows([val, ...rows]);
+                }}
             />
             />
 
 
             {selectedSite && (
             {selectedSite && (
@@ -302,12 +304,11 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
 
 
             <SitesDataTable
             <SitesDataTable
                 columns={columns}
                 columns={columns}
-                data={sites}
+                data={rows}
                 addSite={() => {
                 addSite={() => {
                     setIsCreateModalOpen(true);
                     setIsCreateModalOpen(true);
                 }}
                 }}
             />
             />
-            {/* <button onClick={callApi}>Create Newt</button> */}
         </>
         </>
     );
     );
 }
 }

+ 84 - 0
src/app/auth/resource/[resourceId]/components/AccessToken.tsx

@@ -0,0 +1,84 @@
+"use client";
+
+import { createApiClient } from "@app/api";
+import { Button } from "@app/components/ui/button";
+import {
+    Card,
+    CardContent,
+    CardFooter,
+    CardHeader,
+    CardTitle
+} from "@app/components/ui/card";
+import { useEnvContext } from "@app/hooks/useEnvContext";
+import { AuthWithAccessTokenResponse } from "@server/routers/resource";
+import { AxiosResponse } from "axios";
+import { Loader2 } from "lucide-react";
+import Link from "next/link";
+import { useEffect, useState } from "react";
+
+type AccessTokenProps = {
+    accessTokenId: string | undefined;
+    accessToken: string | undefined;
+    resourceId: number;
+    redirectUrl: string;
+};
+
+export default function AccessToken({
+    accessTokenId,
+    accessToken,
+    resourceId,
+    redirectUrl
+}: AccessTokenProps) {
+    const [loading, setLoading] = useState(true);
+
+    const api = createApiClient(useEnvContext());
+
+    useEffect(() => {
+        if (!accessTokenId || !accessToken) {
+            setLoading(false);
+            return;
+        }
+
+        async function check() {
+            try {
+                const res = await api.post<
+                    AxiosResponse<AuthWithAccessTokenResponse>
+                >(`/auth/resource/${resourceId}/access-token`, {
+                    accessToken,
+                    accessTokenId
+                });
+
+                if (res.data.data.session) {
+                    window.location.href = redirectUrl;
+                }
+            } catch (e) {
+                console.error("Error checking access token", e);
+            } finally {
+                setLoading(false);
+            }
+        }
+
+        check();
+    }, [accessTokenId, accessToken]);
+
+    return loading ? (
+        <div></div>
+    ) : (
+        <Card className="w-full max-w-md">
+            <CardHeader>
+                <CardTitle className="text-center text-2xl font-bold">
+                    Access URL Invalid
+                </CardTitle>
+            </CardHeader>
+            <CardContent>
+                This shared access URL is invalid. Please contact the resource
+                owner for a new URL.
+                <div className="text-center mt-4">
+                    <Button>
+                        <Link href="/">Go Home</Link>
+                    </Button>
+                </div>
+            </CardContent>
+        </Card>
+    );
+}

+ 0 - 32
src/app/auth/resource/[resourceId]/components/AccessTokenInvalid.tsx

@@ -1,32 +0,0 @@
-"use client";
-
-import { Button } from "@app/components/ui/button";
-import {
-    Card,
-    CardContent,
-    CardFooter,
-    CardHeader,
-    CardTitle
-} from "@app/components/ui/card";
-import Link from "next/link";
-
-export default function AccessTokenInvalid() {
-    return (
-        <Card className="w-full max-w-md">
-            <CardHeader>
-                <CardTitle className="text-center text-2xl font-bold">
-                    Acess URL Invalid
-                </CardTitle>
-            </CardHeader>
-            <CardContent>
-                This shared access URL is invalid. Please contact the resource
-                owner for a new URL.
-                <div className="text-center mt-4">
-                    <Button>
-                        <Link href="/">Go Home</Link>
-                    </Button>
-                </div>
-            </CardContent>
-        </Card>
-    );
-}

+ 16 - 30
src/app/auth/resource/[resourceId]/page.tsx

@@ -14,7 +14,8 @@ import ResourceNotFound from "./components/ResourceNotFound";
 import ResourceAccessDenied from "./components/ResourceAccessDenied";
 import ResourceAccessDenied from "./components/ResourceAccessDenied";
 import { cookies } from "next/headers";
 import { cookies } from "next/headers";
 import { CheckResourceSessionResponse } from "@server/routers/auth";
 import { CheckResourceSessionResponse } from "@server/routers/auth";
-import AccessTokenInvalid from "./components/AccessTokenInvalid";
+import AccessTokenInvalid from "./components/AccessToken";
+import AccessToken from "./components/AccessToken";
 
 
 export default async function ResourceAuthPage(props: {
 export default async function ResourceAuthPage(props: {
     params: Promise<{ resourceId: number }>;
     params: Promise<{ resourceId: number }>;
@@ -50,35 +51,6 @@ export default async function ResourceAuthPage(props: {
 
 
     const redirectUrl = searchParams.redirect || authInfo.url;
     const redirectUrl = searchParams.redirect || authInfo.url;
 
 
-    if (searchParams.token) {
-        let doRedirect = false;
-        try {
-            const res = await internal.post<
-                AxiosResponse<AuthWithAccessTokenResponse>
-            >(
-                `/auth/resource/${params.resourceId}/access-token`,
-                {
-                    accessToken: searchParams.token
-                },
-                await authCookieHeader()
-            );
-
-            if (res.data.data.session) {
-                doRedirect = true;
-            }
-        } catch (e) {
-            return (
-                <div className="w-full max-w-md">
-                    <AccessTokenInvalid />
-                </div>
-            );
-        }
-
-        if (doRedirect) {
-            redirect(redirectUrl);
-        }
-    }
-
     const hasAuth =
     const hasAuth =
         authInfo.password ||
         authInfo.password ||
         authInfo.pincode ||
         authInfo.pincode ||
@@ -146,6 +118,20 @@ export default async function ResourceAuthPage(props: {
         }
         }
     }
     }
 
 
+    if (searchParams.token) {
+        const [accessTokenId, accessToken] = searchParams.token.split(".");
+        return (
+            <div className="w-full max-w-md">
+                <AccessToken
+                    accessToken={accessToken}
+                    accessTokenId={accessTokenId}
+                    resourceId={params.resourceId}
+                    redirectUrl={redirectUrl}
+                />
+            </div>
+        );
+    }
+
     return (
     return (
         <>
         <>
             {userIsUnauthorized && isSSOOnly ? (
             {userIsUnauthorized && isSSOOnly ? (