From f9b03943c3023914ba426da0f3a4a3f405bf74b7 Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 9 Dec 2025 10:56:14 -0500 Subject: [PATCH] Format all files --- .eslintrc.json | 5 +- .vscode/extensions.json | 2 +- .vscode/settings.json | 2 +- components.json | 2 +- drizzle.pg.config.ts | 4 +- drizzle.sqlite.config.ts | 4 +- esbuild.mjs | 95 +- eslint.config.js | 32 +- postcss.config.mjs | 4 +- server/auth/password.ts | 6 +- server/auth/passwordSchema.ts | 9 +- server/auth/sessions/newt.ts | 22 +- server/auth/sessions/olm.ts | 22 +- server/cleanup.ts | 2 +- server/db/countries.ts | 1014 ++++++++--------- server/db/names.json | 2 +- server/db/pg/schema/privateSchema.ts | 86 +- server/db/pg/schema/schema.ts | 2 +- server/db/queries/verifySessionQueries.ts | 5 +- server/db/sqlite/migrate.ts | 2 +- server/db/sqlite/schema/privateSchema.ts | 102 +- server/emails/index.ts | 11 +- .../templates/NotifyUsageLimitApproaching.tsx | 27 +- .../templates/NotifyUsageLimitReached.tsx | 40 +- server/integrationApiServer.ts | 2 +- server/lib/billing/features.ts | 33 +- server/lib/billing/index.ts | 2 +- server/lib/billing/limitSet.ts | 6 +- server/lib/billing/tiers.ts | 24 +- server/lib/billing/usageService.ts | 2 +- .../blueprints/applyNewtDockerBlueprint.ts | 5 +- .../lib/blueprints/parseDockerContainers.ts | 15 +- server/lib/blueprints/types.ts | 9 +- server/lib/cache.ts | 2 +- server/lib/calculateUserClientsForOrgs.ts | 5 +- server/lib/certificates.ts | 6 +- server/lib/cleanupLogs.test.ts | 127 ++- server/lib/domainUtils.ts | 27 +- server/lib/encryption.ts | 62 +- server/lib/exitNodes/getCurrentExitNodeId.ts | 2 +- server/lib/exitNodes/index.ts | 2 +- server/lib/exitNodes/subnet.ts | 2 +- server/lib/geoip.ts | 2 +- server/lib/idp/generateRedirectUrl.ts | 6 +- server/lib/ip.test.ts | 12 +- server/lib/ip.ts | 9 +- server/lib/logAccessAudit.ts | 2 +- server/lib/readConfigFile.ts | 8 +- server/lib/rebuildClientAssociations.ts | 50 +- server/lib/resend.ts | 8 +- server/lib/response.ts | 4 +- server/lib/s3.ts | 2 +- server/lib/serverIpService.ts | 6 +- server/lib/stoi.ts | 9 +- server/lib/traefik/TraefikConfigManager.ts | 13 +- server/lib/traefik/index.ts | 2 +- server/lib/traefik/traefikConfig.test.ts | 237 ++-- server/lib/traefik/utils.ts | 17 +- server/lib/validators.test.ts | 288 ++++- server/lib/validators.ts | 18 +- server/middlewares/formatError.ts | 2 +- server/middlewares/getUserOrgs.ts | 10 +- server/middlewares/integration/index.ts | 2 +- .../integration/verifyAccessTokenAccess.ts | 1 - .../integration/verifyApiKeyApiKeyAccess.ts | 7 +- .../verifyApiKeySetResourceClients.ts | 8 +- .../verifyApiKeySetResourceUsers.ts | 3 +- .../verifyApiKeySiteResourceAccess.ts | 9 +- server/middlewares/notFound.ts | 2 +- server/middlewares/requestTimeout.ts | 18 +- server/middlewares/verifySiteAccess.ts | 5 +- server/nextServer.ts | 5 +- .../private/auth/sessions/remoteExitNode.ts | 58 +- server/private/cleanup.ts | 2 +- server/private/lib/billing/index.ts | 2 +- server/private/lib/certificates.ts | 1 - server/private/lib/checkOrgAccessPolicy.ts | 9 +- server/private/lib/exitNodes/exitNodeComms.ts | 4 +- server/private/lib/exitNodes/exitNodes.ts | 86 +- server/private/lib/exitNodes/index.ts | 2 +- server/private/lib/lock.ts | 4 +- server/private/lib/rateLimit.test.ts | 151 ++- server/private/lib/rateLimit.ts | 182 ++- server/private/lib/rateLimitStore.ts | 5 +- server/private/lib/redis.ts | 241 ++-- server/private/lib/redisStore.ts | 42 +- server/private/lib/resend.ts | 12 +- server/private/lib/traefik/index.ts | 2 +- server/private/license/licenseJwt.ts | 5 +- server/private/middlewares/logActionAudit.ts | 7 +- .../middlewares/verifyCertificateAccess.ts | 14 +- server/private/middlewares/verifyIdpAccess.ts | 7 +- .../middlewares/verifyRemoteExitNode.ts | 3 +- .../routers/auditLogs/exportAccessAuditLog.ts | 19 +- .../routers/auditLogs/exportActionAuditLog.ts | 21 +- server/private/routers/auditLogs/index.ts | 2 +- .../routers/auditLogs/queryAccessAuditLog.ts | 15 +- .../routers/auditLogs/queryActionAuditLog.ts | 20 +- server/private/routers/auth/index.ts | 2 +- server/private/routers/auth/quickStart.ts | 10 +- .../routers/billing/createCheckoutSession.ts | 6 +- .../routers/billing/createPortalSession.ts | 4 +- .../routers/billing/getOrgSubscription.ts | 4 +- server/private/routers/billing/getOrgUsage.ts | 27 +- .../billing/hooks/handleCustomerDeleted.ts | 4 +- .../hooks/handleSubscriptionDeleted.ts | 15 +- server/private/routers/billing/index.ts | 2 +- .../routers/billing/internalGetOrgTier.ts | 4 +- .../routers/billing/subscriptionLifecycle.ts | 13 +- server/private/routers/billing/webhooks.ts | 19 +- .../routers/certificates/getCertificate.ts | 21 +- server/private/routers/certificates/index.ts | 2 +- .../certificates/restartCertificate.ts | 13 +- .../checkDomainNamespaceAvailability.ts | 4 +- server/private/routers/domain/index.ts | 2 +- .../routers/domain/listDomainNamespaces.ts | 26 +- server/private/routers/hybrid.ts | 124 +- server/private/routers/integration.ts | 11 +- .../routers/license/activateLicense.ts | 4 +- .../routers/license/deleteLicenseKey.ts | 4 +- .../routers/loginPage/createLoginPage.ts | 31 +- .../routers/loginPage/deleteLoginPage.ts | 8 +- .../private/routers/loginPage/getLoginPage.ts | 4 +- .../routers/loginPage/updateLoginPage.ts | 10 +- .../private/routers/misc/sendSupportEmail.ts | 6 +- server/private/routers/org/index.ts | 2 +- .../routers/org/sendUsageNotifications.ts | 50 +- .../routers/orgIdp/createOrgOidcIdp.ts | 31 +- server/private/routers/orgIdp/deleteOrgIdp.ts | 15 +- server/private/routers/orgIdp/getOrgIdp.ts | 5 +- server/private/routers/orgIdp/index.ts | 2 +- server/private/routers/orgIdp/listOrgIdps.ts | 30 +- .../routers/orgIdp/updateOrgOidcIdp.ts | 24 +- server/private/routers/re-key/index.ts | 2 +- .../routers/re-key/reGenerateClientSecret.ts | 7 +- .../re-key/reGenerateExitNodeSecret.ts | 26 +- .../routers/re-key/reGenerateSiteSecret.ts | 21 +- .../remoteExitNode/createRemoteExitNode.ts | 6 +- .../remoteExitNode/deleteRemoteExitNode.ts | 6 +- .../remoteExitNode/getRemoteExitNode.ts | 6 +- .../remoteExitNode/getRemoteExitNodeToken.ts | 8 +- .../handleRemoteExitNodePingMessage.ts | 21 +- .../handleRemoteExitNodeRegisterMessage.ts | 8 +- .../remoteExitNode/listRemoteExitNodes.ts | 4 +- .../pickRemoteExitNodeDefaults.ts | 4 +- .../quickStartRemoteExitNode.ts | 4 +- server/private/routers/ws/index.ts | 2 +- server/private/routers/ws/messageHandlers.ts | 2 +- server/private/routers/ws/ws.ts | 178 ++- .../routers/accessToken/deleteAccessToken.ts | 4 +- .../accessToken/generateAccessToken.ts | 15 +- .../routers/accessToken/listAccessTokens.ts | 3 +- server/routers/apiKeys/createRootApiKey.ts | 4 +- server/routers/apiKeys/listApiKeyActions.ts | 3 +- server/routers/apiKeys/setApiKeyActions.ts | 7 +- server/routers/apiKeys/setApiKeyOrgs.ts | 7 +- server/routers/auditLogs/generateCSV.ts | 20 +- server/routers/auditLogs/types.ts | 2 +- server/routers/auth/changePassword.ts | 32 +- server/routers/auth/checkResourceSession.ts | 16 +- server/routers/auth/disable2fa.ts | 11 +- server/routers/auth/index.ts | 2 +- server/routers/auth/pollDeviceWebAuth.ts | 14 +- server/routers/auth/requestPasswordReset.ts | 4 +- server/routers/auth/requestTotpSecret.ts | 15 +- server/routers/auth/resetPassword.ts | 10 +- server/routers/auth/securityKey.ts | 163 ++- server/routers/auth/signup.ts | 14 +- server/routers/auth/types.ts | 2 +- server/routers/auth/validateSetupToken.ts | 11 +- server/routers/auth/verifyEmail.ts | 4 +- server/routers/auth/verifyTotp.ts | 8 +- server/routers/badger/exchangeSession.ts | 13 +- server/routers/badger/verifySession.test.ts | 227 +++- server/routers/billing/types.ts | 1 - server/routers/billing/webhooks.ts | 2 +- .../routers/blueprints/applyJSONBlueprint.ts | 8 +- server/routers/blueprints/getBlueprint.ts | 9 +- server/routers/blueprints/listBlueprints.ts | 30 +- .../routers/certificates/createCertificate.ts | 8 +- server/routers/certificates/types.ts | 2 +- server/routers/client/listClients.ts | 95 +- server/routers/client/pickClientDefaults.ts | 4 +- server/routers/client/targets.ts | 20 +- server/routers/client/terminate.ts | 5 +- server/routers/domain/createOrgDomain.ts | 33 +- server/routers/domain/deleteOrgDomain.ts | 11 +- server/routers/domain/getDNSRecords.ts | 17 +- server/routers/domain/getDomain.ts | 12 +- server/routers/domain/index.ts | 2 +- server/routers/domain/listDomains.ts | 30 +- server/routers/domain/restartOrgDomain.ts | 6 +- server/routers/domain/types.ts | 2 +- server/routers/domain/updateDomain.ts | 16 +- server/routers/external.ts | 22 +- server/routers/generatedLicense/types.ts | 2 +- server/routers/gerbil/createExitNode.ts | 5 +- server/routers/gerbil/getConfig.ts | 2 +- server/routers/gerbil/index.ts | 2 +- server/routers/hybrid.ts | 2 +- server/routers/idp/createIdpOrgPolicy.ts | 12 +- server/routers/idp/createOidcIdp.ts | 22 +- server/routers/idp/deleteIdp.ts | 15 +- server/routers/idp/deleteIdpOrgPolicy.ts | 6 +- server/routers/idp/generateOidcUrl.ts | 4 +- server/routers/idp/getIdp.ts | 10 +- server/routers/idp/index.ts | 2 +- server/routers/idp/listIdpOrgPolicies.ts | 26 +- server/routers/idp/listIdps.ts | 26 +- server/routers/idp/updateIdpOrgPolicy.ts | 12 +- server/routers/idp/updateOidcIdp.ts | 26 +- server/routers/license/types.ts | 2 +- server/routers/loginPage/types.ts | 2 +- server/routers/newt/createNewt.ts | 13 +- .../newt/handleNewtPingRequestMessage.ts | 6 +- .../routers/newt/handleNewtRegisterMessage.ts | 9 +- .../newt/handleReceiveBandwidthMessage.ts | 6 +- server/routers/newt/handleSocketMessages.ts | 6 +- server/routers/newt/index.ts | 2 +- server/routers/newt/peers.ts | 6 +- server/routers/newt/targets.ts | 34 +- server/routers/olm/createOlm.ts | 13 +- server/routers/olm/handleOlmPingMessage.ts | 7 +- .../olm/handleOlmServerPeerAddMessage.ts | 4 +- server/routers/olm/index.ts | 2 +- server/routers/org/checkId.ts | 4 +- server/routers/org/getOrg.ts | 4 +- server/routers/org/getOrgOverview.ts | 4 +- server/routers/org/updateOrg.ts | 7 +- server/routers/orgIdp/types.ts | 10 +- server/routers/remoteExitNode/types.ts | 12 +- .../resource/addEmailToResourceWhitelist.ts | 24 +- server/routers/resource/addRoleToResource.ts | 6 +- server/routers/resource/addUserToResource.ts | 1 - .../routers/resource/authWithAccessToken.ts | 18 +- server/routers/resource/authWithPassword.ts | 11 +- server/routers/resource/authWithPincode.ts | 11 +- server/routers/resource/authWithWhitelist.ts | 13 +- server/routers/resource/createResource.ts | 14 +- server/routers/resource/createResourceRule.ts | 19 +- server/routers/resource/deleteResource.ts | 7 +- server/routers/resource/deleteResourceRule.ts | 9 +- server/routers/resource/getExchangeToken.ts | 7 +- server/routers/resource/getResource.ts | 31 +- .../routers/resource/getResourceAuthInfo.ts | 4 +- .../routers/resource/getResourceWhitelist.ts | 7 +- server/routers/resource/listResourceRoles.ts | 7 +- server/routers/resource/listResourceRules.ts | 7 +- server/routers/resource/listResourceUsers.ts | 7 +- .../removeEmailFromResourceWhitelist.ts | 24 +- .../resource/removeRoleFromResource.ts | 10 +- .../resource/removeUserFromResource.ts | 5 +- .../routers/resource/setResourceHeaderAuth.ts | 10 +- .../routers/resource/setResourcePassword.ts | 4 +- server/routers/resource/setResourcePincode.ts | 10 +- server/routers/resource/setResourceRoles.ts | 23 +- server/routers/resource/setResourceUsers.ts | 11 +- .../routers/resource/setResourceWhitelist.ts | 28 +- server/routers/resource/updateResource.ts | 33 +- server/routers/resource/updateResourceRule.ts | 12 +- server/routers/role/addRoleAction.ts | 8 +- server/routers/role/addRoleSite.ts | 8 +- server/routers/role/createRole.ts | 10 +- server/routers/role/deleteRole.ts | 8 +- server/routers/role/getRole.ts | 4 +- server/routers/role/listRoleActions.ts | 4 +- server/routers/role/listRoleResources.ts | 4 +- server/routers/role/listRoleSites.ts | 4 +- server/routers/role/listRoles.ts | 4 +- server/routers/role/removeRoleAction.ts | 8 +- server/routers/role/removeRoleResource.ts | 11 +- server/routers/role/removeRoleSite.ts | 8 +- server/routers/role/updateRole.ts | 7 +- server/routers/site/createSite.ts | 34 +- server/routers/site/deleteSite.ts | 11 +- server/routers/site/index.ts | 2 +- server/routers/site/listSiteRoles.ts | 4 +- server/routers/site/listSites.ts | 4 +- server/routers/site/pickSiteDefaults.ts | 14 +- server/routers/site/socketIntegration.ts | 19 +- server/routers/site/updateSite.ts | 18 +- .../siteResource/addClientToSiteResource.ts | 8 +- .../siteResource/addRoleToSiteResource.ts | 1 - .../siteResource/addUserToSiteResource.ts | 1 - .../siteResource/deleteSiteResource.ts | 5 +- .../routers/siteResource/getSiteResource.ts | 67 +- .../siteResource/listAllSiteResourcesByOrg.ts | 25 +- .../siteResource/listSiteResourceClients.ts | 5 +- .../siteResource/listSiteResourceRoles.ts | 1 - .../siteResource/listSiteResourceUsers.ts | 1 - .../routers/siteResource/listSiteResources.ts | 34 +- .../removeClientFromSiteResource.ts | 4 +- .../removeRoleFromSiteResource.ts | 3 +- .../removeUserFromSiteResource.ts | 1 - .../siteResource/setSiteResourceClients.ts | 18 +- .../siteResource/setSiteResourceRoles.ts | 12 +- .../siteResource/setSiteResourceUsers.ts | 9 +- .../supporterKey/validateSupporterKey.ts | 6 +- server/routers/target/createTarget.ts | 80 +- server/routers/target/deleteTarget.ts | 4 +- server/routers/target/getTarget.ts | 11 +- .../target/handleHealthcheckStatusMessage.ts | 25 +- server/routers/target/helpers.ts | 5 +- server/routers/target/listTargets.ts | 18 +- server/routers/target/updateTarget.ts | 42 +- server/routers/traefik/index.ts | 2 +- .../routers/traefik/traefikConfigProvider.ts | 2 +- server/routers/user/acceptInvite.ts | 6 +- server/routers/user/addUserAction.ts | 8 +- server/routers/user/addUserSite.ts | 7 +- .../user/adminGeneratePasswordResetCode.ts | 16 +- server/routers/user/adminGetUser.ts | 4 +- server/routers/user/adminListUsers.ts | 26 +- server/routers/user/adminUpdateUser2FA.ts | 14 +- server/routers/user/createOrgUser.ts | 35 +- server/routers/user/getOrgUser.ts | 6 +- server/routers/user/inviteUser.ts | 23 +- server/routers/user/listInvitations.ts | 30 +- server/routers/user/listUsers.ts | 32 +- server/routers/user/removeInvitation.ts | 6 +- server/routers/user/removeUserAction.ts | 10 +- server/routers/user/removeUserOrg.ts | 6 +- server/routers/user/removeUserResource.ts | 9 +- server/routers/user/removeUserSite.ts | 8 +- server/routers/user/updateOrgUser.ts | 9 +- server/routers/ws/index.ts | 2 +- server/routers/ws/types.ts | 6 +- server/routers/ws/ws.ts | 256 +++-- server/setup/ensureSetupToken.ts | 4 +- server/setup/migrationsPg.ts | 2 +- server/setup/scriptsPg/1.12.0.ts | 125 +- server/setup/scriptsPg/1.13.0.ts | 4 +- server/setup/scriptsPg/1.7.0.ts | 2 +- server/setup/scriptsPg/1.9.0.ts | 54 +- server/setup/scriptsSqlite/1.0.0-beta13.ts | 4 +- server/setup/scriptsSqlite/1.0.0-beta3.ts | 2 +- server/setup/scriptsSqlite/1.0.0-beta6.ts | 4 +- server/setup/scriptsSqlite/1.0.0-beta9.ts | 7 +- server/setup/scriptsSqlite/1.10.0.ts | 16 +- server/setup/scriptsSqlite/1.10.1.ts | 30 +- server/setup/scriptsSqlite/1.11.0.ts | 129 ++- server/setup/scriptsSqlite/1.12.0.ts | 9 +- server/setup/scriptsSqlite/1.13.0.ts | 12 +- server/setup/scriptsSqlite/1.5.0.ts | 8 +- server/setup/scriptsSqlite/1.6.0.ts | 4 +- server/setup/scriptsSqlite/1.9.0.ts | 38 +- server/types/HttpCode.ts | 2 +- .../settings/(private)/billing/layout.tsx | 18 +- .../settings/(private)/idp/create/page.tsx | 14 +- .../remote-exit-nodes/ExitNodesDataTable.tsx | 7 +- .../[remoteExitNodeId]/layout.tsx | 2 +- .../remote-exit-nodes/create/page.tsx | 2 +- .../settings/access/invitations/page.tsx | 12 +- .../[orgId]/settings/access/roles/page.tsx | 6 +- .../settings/access/users/create/page.tsx | 3 +- .../[orgId]/settings/access/users/page.tsx | 14 +- .../settings/api-keys/[apiKeyId]/layout.tsx | 8 +- .../settings/api-keys/[apiKeyId]/page.tsx | 4 +- .../api-keys/[apiKeyId]/permissions/page.tsx | 20 +- .../[orgId]/settings/api-keys/create/page.tsx | 44 +- src/app/[orgId]/settings/api-keys/page.tsx | 10 +- .../clients/machine/[niceId]/layout.tsx | 5 +- src/app/[orgId]/settings/general/layout.tsx | 26 +- .../[orgId]/settings/logs/request/page.tsx | 10 +- .../proxy/[niceId]/authentication/page.tsx | 7 +- .../resources/proxy/[niceId]/general/page.tsx | 64 +- .../resources/proxy/[niceId]/layout.tsx | 19 +- .../resources/proxy/[niceId]/rules/page.tsx | 364 ++++-- src/app/[orgId]/settings/share-links/page.tsx | 8 +- .../settings/sites/[niceId]/general/page.tsx | 29 +- .../sites/[niceId]/wireguardConfig.ts | 64 +- .../[orgId]/settings/sites/create/page.tsx | 5 +- src/app/[orgId]/settings/sites/page.tsx | 12 +- src/app/admin/api-keys/[apiKeyId]/layout.tsx | 6 +- .../api-keys/[apiKeyId]/permissions/page.tsx | 20 +- src/app/admin/api-keys/create/page.tsx | 46 +- src/app/admin/api-keys/page.tsx | 4 +- src/app/admin/idp/[idpId]/general/page.tsx | 118 +- src/app/admin/idp/[idpId]/layout.tsx | 8 +- src/app/admin/idp/[idpId]/policies/page.tsx | 94 +- src/app/admin/idp/create/page.tsx | 128 ++- src/app/admin/idp/page.tsx | 4 +- src/app/admin/license/layout.tsx | 1 - src/app/admin/license/page.tsx | 4 +- src/app/admin/users/AdminUsersTable.tsx | 2 +- src/app/admin/users/[userId]/layout.tsx | 12 +- src/app/admin/users/[userId]/page.tsx | 2 +- src/app/admin/users/page.tsx | 12 +- src/app/auth/(private)/org/page.tsx | 4 +- src/app/auth/2fa/setup/page.tsx | 4 +- .../auth/idp/[idpId]/oidc/callback/page.tsx | 8 +- src/app/auth/login/device/page.tsx | 4 +- src/app/auth/login/device/success/page.tsx | 4 +- src/app/auth/login/page.tsx | 6 +- .../auth/reset-password/ResetPasswordForm.tsx | 101 +- src/app/auth/verify-email/page.tsx | 5 +- src/app/not-found.tsx | 5 +- src/app/setup/page.tsx | 4 +- src/components/AccessPageHeaderAndNav.tsx | 16 +- src/components/AccessToken.tsx | 23 +- src/components/AccessTokenUsage.tsx | 26 +- src/components/AdminIdpDataTable.tsx | 6 +- src/components/AdminIdpTable.tsx | 4 +- src/components/AdminUsersDataTable.tsx | 9 +- src/components/ApiKeysDataTable.tsx | 7 +- src/components/ApiKeysTable.tsx | 8 +- src/components/AutoLoginHandler.tsx | 3 +- src/components/BrandingLogo.tsx | 2 +- src/components/ChangePasswordDialog.tsx | 15 +- src/components/ChangePasswordForm.tsx | 73 +- src/components/ClientsDataTable.tsx | 4 +- src/components/ColumnFilter.tsx | 29 +- src/components/ContainersSelector.tsx | 18 +- src/components/CopyTextBox.tsx | 4 +- src/components/CopyToClipboard.tsx | 8 +- src/components/CreateDomainForm.tsx | 358 +++--- .../CreateInternalResourceDialog.tsx | 4 +- src/components/CreateRoleForm.tsx | 26 +- src/components/CreateShareLinkForm.tsx | 93 +- src/components/Credenza.tsx | 13 +- src/components/DNSRecordTable.tsx | 13 +- src/components/DNSRecordsDataTable.tsx | 10 +- src/components/DataTablePagination.tsx | 82 +- src/components/DeleteRoleForm.tsx | 122 +- src/components/DeviceAuthConfirmation.tsx | 20 +- src/components/Disable2FaForm.tsx | 30 +- src/components/EditInternalResourceDialog.tsx | 18 +- src/components/Enable2FaDialog.tsx | 22 +- src/components/ExitNodeInfoCard.tsx | 36 +- src/components/HeadersInput.tsx | 67 +- src/components/HorizontalTabs.tsx | 2 +- src/components/IdpCreateWizard.tsx | 146 ++- src/components/InvitationsDataTable.tsx | 11 +- src/components/InvitationsTable.tsx | 10 +- src/components/Layout.tsx | 10 +- src/components/LicenseKeysDataTable.tsx | 3 +- src/components/LicenseViolation.tsx | 6 +- src/components/LocaleSwitcher.tsx | 2 +- src/components/LogDataTable.tsx | 133 ++- src/components/LoginForm.tsx | 10 +- src/components/OrgApiKeysDataTable.tsx | 7 +- src/components/OrgApiKeysTable.tsx | 20 +- src/components/OrgPolicyResult.tsx | 11 +- src/components/OrgSelector.tsx | 51 +- src/components/OrganizationLandingCard.tsx | 24 +- src/components/PathMatchRenameModal.tsx | 39 +- src/components/PermissionsSelectBox.tsx | 88 +- src/components/PolicyDataTable.tsx | 9 +- src/components/PolicyTable.tsx | 41 +- src/components/ProductUpdates.tsx | 8 +- src/components/ProfessionalContentOverlay.tsx | 4 +- src/components/ProfileIcon.tsx | 16 +- src/components/QRContainer.tsx | 14 +- src/components/RefreshButton.tsx | 6 +- src/components/RegenerateInvitationForm.tsx | 72 +- src/components/ResetPasswordForm.tsx | 5 +- src/components/ResourceAccessDenied.tsx | 8 +- src/components/ResourceAuthPortal.tsx | 8 +- src/components/ResourceInfoBox.tsx | 15 +- src/components/ResourceNotFound.tsx | 9 +- src/components/RestartDomainButton.tsx | 5 +- src/components/RolesDataTable.tsx | 13 +- src/components/RolesTable.tsx | 2 +- src/components/SecurityFeaturesAlert.tsx | 1 - src/components/SecurityKeyForm.tsx | 16 +- src/components/SetResourceHeaderAuthForm.tsx | 35 +- src/components/SetResourcePasswordForm.tsx | 22 +- src/components/SetResourcePincodeForm.tsx | 22 +- src/components/ShareLinksDataTable.tsx | 11 +- src/components/ShareLinksSplash.tsx | 14 +- src/components/ShareLinksTable.tsx | 7 +- src/components/SidebarNav.tsx | 2 +- src/components/SidebarSupportButton.tsx | 151 +-- src/components/SiteInfoCard.tsx | 12 +- src/components/SitePriceCalculator.tsx | 22 +- src/components/SitesDataTable.tsx | 7 +- src/components/SitesSplashCard.tsx | 26 +- src/components/StrategySelect.tsx | 8 +- src/components/SupporterMessage.tsx | 7 +- src/components/SupporterStatus.tsx | 100 +- src/components/TopbarNav.tsx | 4 +- src/components/TwoFactorSetupForm.tsx | 2 +- src/components/UsersDataTable.tsx | 13 +- src/components/ValidateOidcToken.tsx | 3 +- src/components/private/AuthPageSettings.tsx | 3 +- src/components/private/CertificateStatus.tsx | 28 +- src/components/private/OrgIdpTable.tsx | 23 +- src/components/private/RegionSelector.tsx | 8 +- src/components/tags/autocomplete.tsx | 2 +- src/components/tags/tag-input.tsx | 3 +- src/components/tags/tag-popover.tsx | 4 +- src/components/ui/avatar.tsx | 54 +- src/components/ui/badge.tsx | 3 +- src/components/ui/breadcrumb.tsx | 144 +-- src/components/ui/button.tsx | 3 +- src/components/ui/calendar.tsx | 391 ++++--- src/components/ui/card.tsx | 6 +- src/components/ui/checkbox.tsx | 8 +- src/components/ui/command.tsx | 4 +- src/components/ui/data-table.tsx | 250 ++-- src/components/ui/progress.tsx | 36 +- src/components/ui/radio-group.tsx | 50 +- src/components/ui/scroll-area.tsx | 85 +- src/components/ui/separator.tsx | 38 +- src/components/ui/sheet.tsx | 168 ++- src/components/ui/tabs.tsx | 60 +- src/components/ui/textarea.tsx | 27 +- src/contexts/domainContext.ts | 2 +- src/contexts/remoteExitNodeContext.ts | 8 +- src/hooks/useCertificate.ts | 51 +- src/hooks/useClientContext.ts | 4 +- src/hooks/useDomainContext.ts | 6 +- src/hooks/useRemoteExitNodeContext.ts | 8 +- src/hooks/useSiteContext.ts | 4 +- src/hooks/useToast.ts | 26 +- src/i18n/config.ts | 21 +- src/i18n/request.ts | 16 +- src/lib/cleanRedirect.ts | 5 +- src/lib/dataSize.ts | 32 +- src/lib/docker.ts | 14 +- src/lib/parseHostTarget.ts | 51 +- src/lib/shareLinks.ts | 4 +- src/lib/subdomain-utils.ts | 94 +- src/lib/wireguard.ts | 24 +- src/middleware.ts | 30 +- src/providers/ApiKeyProvider.tsx | 2 +- src/providers/DomainProvider.tsx | 4 +- src/providers/OrgProvider.tsx | 6 +- src/providers/OrgUserProvider.tsx | 6 +- src/providers/RemoteExitNodeProvider.tsx | 13 +- src/providers/ResourceProvider.tsx | 4 +- src/providers/SiteProvider.tsx | 2 +- src/providers/UserProvider.tsx | 2 +- src/services/locale.ts | 47 +- src/types/canvas-confetti.d.ts | 2 +- 535 files changed, 7670 insertions(+), 5626 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index a9468a44..98ef693e 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,6 +1,3 @@ { - "extends": [ - "next/core-web-vitals", - "next/typescript" - ] + "extends": ["next/core-web-vitals", "next/typescript"] } diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 79e80ed9..974188b8 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,3 @@ { "recommendations": ["esbenp.prettier-vscode"] -} \ No newline at end of file +} diff --git a/.vscode/settings.json b/.vscode/settings.json index d429abe2..77440d96 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -19,4 +19,4 @@ "editor.defaultFormatter": "esbenp.prettier-vscode" }, "editor.formatOnSave": true -} \ No newline at end of file +} diff --git a/components.json b/components.json index 97d8c8c0..13f7efef 100644 --- a/components.json +++ b/components.json @@ -17,4 +17,4 @@ "lib": "@/lib", "hooks": "@/hooks" } -} \ No newline at end of file +} diff --git a/drizzle.pg.config.ts b/drizzle.pg.config.ts index febd5f45..ba4ca8fe 100644 --- a/drizzle.pg.config.ts +++ b/drizzle.pg.config.ts @@ -1,9 +1,7 @@ import { defineConfig } from "drizzle-kit"; import path from "path"; -const schema = [ - path.join("server", "db", "pg", "schema"), -]; +const schema = [path.join("server", "db", "pg", "schema")]; export default defineConfig({ dialect: "postgresql", diff --git a/drizzle.sqlite.config.ts b/drizzle.sqlite.config.ts index 4912c256..d8344f94 100644 --- a/drizzle.sqlite.config.ts +++ b/drizzle.sqlite.config.ts @@ -2,9 +2,7 @@ import { APP_PATH } from "@server/lib/consts"; import { defineConfig } from "drizzle-kit"; import path from "path"; -const schema = [ - path.join("server", "db", "sqlite", "schema"), -]; +const schema = [path.join("server", "db", "sqlite", "schema")]; export default defineConfig({ dialect: "sqlite", diff --git a/esbuild.mjs b/esbuild.mjs index 7f67fe81..0157c34a 100644 --- a/esbuild.mjs +++ b/esbuild.mjs @@ -24,20 +24,20 @@ const argv = yargs(hideBin(process.argv)) alias: "e", describe: "Entry point file", type: "string", - demandOption: true, + demandOption: true }) .option("out", { alias: "o", describe: "Output file path", type: "string", - demandOption: true, + demandOption: true }) .option("build", { alias: "b", describe: "Build type (oss, saas, enterprise)", type: "string", choices: ["oss", "saas", "enterprise"], - default: "oss", + default: "oss" }) .help() .alias("help", "h").argv; @@ -66,7 +66,9 @@ function privateImportGuardPlugin() { // Check if the importing file is NOT in server/private const normalizedImporter = path.normalize(importingFile); - const isInServerPrivate = normalizedImporter.includes(path.normalize("server/private")); + const isInServerPrivate = normalizedImporter.includes( + path.normalize("server/private") + ); if (!isInServerPrivate) { const violation = { @@ -79,8 +81,8 @@ function privateImportGuardPlugin() { console.log(`PRIVATE IMPORT VIOLATION:`); console.log(` File: ${importingFile}`); console.log(` Import: ${args.path}`); - console.log(` Resolve dir: ${args.resolveDir || 'N/A'}`); - console.log(''); + console.log(` Resolve dir: ${args.resolveDir || "N/A"}`); + console.log(""); } // Return null to let the default resolver handle it @@ -89,16 +91,20 @@ function privateImportGuardPlugin() { build.onEnd((result) => { if (violations.length > 0) { - console.log(`\nSUMMARY: Found ${violations.length} private import violation(s):`); + console.log( + `\nSUMMARY: Found ${violations.length} private import violation(s):` + ); violations.forEach((v, i) => { - console.log(` ${i + 1}. ${path.relative(process.cwd(), v.file)} imports ${v.importPath}`); + console.log( + ` ${i + 1}. ${path.relative(process.cwd(), v.file)} imports ${v.importPath}` + ); }); - console.log(''); + console.log(""); result.errors.push({ text: `Private import violations detected: ${violations.length} violation(s) found`, location: null, - notes: violations.map(v => ({ + notes: violations.map((v) => ({ text: `${path.relative(process.cwd(), v.file)} imports ${v.importPath}`, location: null })) @@ -121,7 +127,9 @@ function dynamicImportGuardPlugin() { // Check if the importing file is NOT in server/private const normalizedImporter = path.normalize(importingFile); - const isInServerPrivate = normalizedImporter.includes(path.normalize("server/private")); + const isInServerPrivate = normalizedImporter.includes( + path.normalize("server/private") + ); if (isInServerPrivate) { const violation = { @@ -134,8 +142,8 @@ function dynamicImportGuardPlugin() { console.log(`DYNAMIC IMPORT VIOLATION:`); console.log(` File: ${importingFile}`); console.log(` Import: ${args.path}`); - console.log(` Resolve dir: ${args.resolveDir || 'N/A'}`); - console.log(''); + console.log(` Resolve dir: ${args.resolveDir || "N/A"}`); + console.log(""); } // Return null to let the default resolver handle it @@ -144,16 +152,20 @@ function dynamicImportGuardPlugin() { build.onEnd((result) => { if (violations.length > 0) { - console.log(`\nSUMMARY: Found ${violations.length} dynamic import violation(s):`); + console.log( + `\nSUMMARY: Found ${violations.length} dynamic import violation(s):` + ); violations.forEach((v, i) => { - console.log(` ${i + 1}. ${path.relative(process.cwd(), v.file)} imports ${v.importPath}`); + console.log( + ` ${i + 1}. ${path.relative(process.cwd(), v.file)} imports ${v.importPath}` + ); }); - console.log(''); + console.log(""); result.errors.push({ text: `Dynamic import violations detected: ${violations.length} violation(s) found`, location: null, - notes: violations.map(v => ({ + notes: violations.map((v) => ({ text: `${path.relative(process.cwd(), v.file)} imports ${v.importPath}`, location: null })) @@ -172,21 +184,28 @@ function dynamicImportSwitcherPlugin(buildValue) { const switches = []; build.onStart(() => { - console.log(`Dynamic import switcher using build type: ${buildValue}`); + console.log( + `Dynamic import switcher using build type: ${buildValue}` + ); }); build.onResolve({ filter: /^#dynamic\// }, (args) => { // Extract the path after #dynamic/ - const dynamicPath = args.path.replace(/^#dynamic\//, ''); + const dynamicPath = args.path.replace(/^#dynamic\//, ""); // Determine the replacement based on build type let replacement; if (buildValue === "oss") { replacement = `#open/${dynamicPath}`; - } else if (buildValue === "saas" || buildValue === "enterprise") { + } else if ( + buildValue === "saas" || + buildValue === "enterprise" + ) { replacement = `#closed/${dynamicPath}`; // We use #closed here so that the route guards dont complain after its been changed but this is the same as #private } else { - console.warn(`Unknown build type '${buildValue}', defaulting to #open/`); + console.warn( + `Unknown build type '${buildValue}', defaulting to #open/` + ); replacement = `#open/${dynamicPath}`; } @@ -201,8 +220,10 @@ function dynamicImportSwitcherPlugin(buildValue) { console.log(`DYNAMIC IMPORT SWITCH:`); console.log(` File: ${args.importer}`); console.log(` Original: ${args.path}`); - console.log(` Switched to: ${replacement} (build: ${buildValue})`); - console.log(''); + console.log( + ` Switched to: ${replacement} (build: ${buildValue})` + ); + console.log(""); // Rewrite the import path and let the normal resolution continue return build.resolve(replacement, { @@ -215,12 +236,18 @@ function dynamicImportSwitcherPlugin(buildValue) { build.onEnd((result) => { if (switches.length > 0) { - console.log(`\nDYNAMIC IMPORT SUMMARY: Switched ${switches.length} import(s) for build type '${buildValue}':`); + console.log( + `\nDYNAMIC IMPORT SUMMARY: Switched ${switches.length} import(s) for build type '${buildValue}':` + ); switches.forEach((s, i) => { - console.log(` ${i + 1}. ${path.relative(process.cwd(), s.file)}`); - console.log(` ${s.originalPath} → ${s.replacementPath}`); + console.log( + ` ${i + 1}. ${path.relative(process.cwd(), s.file)}` + ); + console.log( + ` ${s.originalPath} → ${s.replacementPath}` + ); }); - console.log(''); + console.log(""); } }); } @@ -235,7 +262,7 @@ esbuild format: "esm", minify: false, banner: { - js: banner, + js: banner }, platform: "node", external: ["body-parser"], @@ -244,20 +271,22 @@ esbuild dynamicImportGuardPlugin(), dynamicImportSwitcherPlugin(argv.build), nodeExternalsPlugin({ - packagePath: getPackagePaths(), - }), + packagePath: getPackagePaths() + }) ], sourcemap: "inline", - target: "node22", + target: "node22" }) .then((result) => { // Check if there were any errors in the build result if (result.errors && result.errors.length > 0) { - console.error(`Build failed with ${result.errors.length} error(s):`); + console.error( + `Build failed with ${result.errors.length} error(s):` + ); result.errors.forEach((error, i) => { console.error(`${i + 1}. ${error.text}`); if (error.notes) { - error.notes.forEach(note => { + error.notes.forEach((note) => { console.error(` - ${note.text}`); }); } diff --git a/eslint.config.js b/eslint.config.js index dfc194bc..ae921d45 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,19 +1,19 @@ -import tseslint from 'typescript-eslint'; +import tseslint from "typescript-eslint"; export default tseslint.config({ - files: ["**/*.{ts,tsx,js,jsx}"], - languageOptions: { - parser: tseslint.parser, - parserOptions: { - ecmaVersion: "latest", - sourceType: "module", - ecmaFeatures: { - jsx: true - } + files: ["**/*.{ts,tsx,js,jsx}"], + languageOptions: { + parser: tseslint.parser, + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + ecmaFeatures: { + jsx: true + } + } + }, + rules: { + semi: "error", + "prefer-const": "warn" } - }, - rules: { - "semi": "error", - "prefer-const": "warn" - } -}); \ No newline at end of file +}); diff --git a/postcss.config.mjs b/postcss.config.mjs index 9d3299ad..19b5e42f 100644 --- a/postcss.config.mjs +++ b/postcss.config.mjs @@ -1,8 +1,8 @@ /** @type {import('postcss-load-config').Config} */ const config = { plugins: { - "@tailwindcss/postcss": {}, - }, + "@tailwindcss/postcss": {} + } }; export default config; diff --git a/server/auth/password.ts b/server/auth/password.ts index dd1a3d1b..a25af4c9 100644 --- a/server/auth/password.ts +++ b/server/auth/password.ts @@ -2,13 +2,13 @@ import { hash, verify } from "@node-rs/argon2"; export async function verifyPassword( password: string, - hash: string, + hash: string ): Promise { const validPassword = await verify(hash, password, { memoryCost: 19456, timeCost: 2, outputLen: 32, - parallelism: 1, + parallelism: 1 }); return validPassword; } @@ -18,7 +18,7 @@ export async function hashPassword(password: string): Promise { memoryCost: 19456, timeCost: 2, outputLen: 32, - parallelism: 1, + parallelism: 1 }); return passwordHash; diff --git a/server/auth/passwordSchema.ts b/server/auth/passwordSchema.ts index 9c399092..740f9a5d 100644 --- a/server/auth/passwordSchema.ts +++ b/server/auth/passwordSchema.ts @@ -4,10 +4,13 @@ export const passwordSchema = z .string() .min(8, { message: "Password must be at least 8 characters long" }) .max(128, { message: "Password must be at most 128 characters long" }) - .regex(/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[~!`@#$%^&*()_\-+={}[\]|\\:;"'<>,.\/?]).*$/, { - message: `Your password must meet the following conditions: + .regex( + /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[~!`@#$%^&*()_\-+={}[\]|\\:;"'<>,.\/?]).*$/, + { + message: `Your password must meet the following conditions: at least one uppercase English letter, at least one lowercase English letter, at least one digit, at least one special character.` - }); + } + ); diff --git a/server/auth/sessions/newt.ts b/server/auth/sessions/newt.ts index 5e55c491..96c37894 100644 --- a/server/auth/sessions/newt.ts +++ b/server/auth/sessions/newt.ts @@ -1,6 +1,4 @@ -import { - encodeHexLowerCase, -} from "@oslojs/encoding"; +import { encodeHexLowerCase } from "@oslojs/encoding"; import { sha256 } from "@oslojs/crypto/sha2"; import { Newt, newts, newtSessions, NewtSession } from "@server/db"; import { db } from "@server/db"; @@ -10,25 +8,25 @@ export const EXPIRES = 1000 * 60 * 60 * 24 * 30; export async function createNewtSession( token: string, - newtId: string, + newtId: string ): Promise { const sessionId = encodeHexLowerCase( - sha256(new TextEncoder().encode(token)), + sha256(new TextEncoder().encode(token)) ); const session: NewtSession = { sessionId: sessionId, newtId, - expiresAt: new Date(Date.now() + EXPIRES).getTime(), + expiresAt: new Date(Date.now() + EXPIRES).getTime() }; await db.insert(newtSessions).values(session); return session; } export async function validateNewtSessionToken( - token: string, + token: string ): Promise { const sessionId = encodeHexLowerCase( - sha256(new TextEncoder().encode(token)), + sha256(new TextEncoder().encode(token)) ); const result = await db .select({ newt: newts, session: newtSessions }) @@ -45,14 +43,12 @@ export async function validateNewtSessionToken( .where(eq(newtSessions.sessionId, session.sessionId)); return { session: null, newt: null }; } - if (Date.now() >= session.expiresAt - (EXPIRES / 2)) { - session.expiresAt = new Date( - Date.now() + EXPIRES, - ).getTime(); + if (Date.now() >= session.expiresAt - EXPIRES / 2) { + session.expiresAt = new Date(Date.now() + EXPIRES).getTime(); await db .update(newtSessions) .set({ - expiresAt: session.expiresAt, + expiresAt: session.expiresAt }) .where(eq(newtSessions.sessionId, session.sessionId)); } diff --git a/server/auth/sessions/olm.ts b/server/auth/sessions/olm.ts index 89a0e81e..a51ec79a 100644 --- a/server/auth/sessions/olm.ts +++ b/server/auth/sessions/olm.ts @@ -1,6 +1,4 @@ -import { - encodeHexLowerCase, -} from "@oslojs/encoding"; +import { encodeHexLowerCase } from "@oslojs/encoding"; import { sha256 } from "@oslojs/crypto/sha2"; import { Olm, olms, olmSessions, OlmSession } from "@server/db"; import { db } from "@server/db"; @@ -10,25 +8,25 @@ export const EXPIRES = 1000 * 60 * 60 * 24 * 30; export async function createOlmSession( token: string, - olmId: string, + olmId: string ): Promise { const sessionId = encodeHexLowerCase( - sha256(new TextEncoder().encode(token)), + sha256(new TextEncoder().encode(token)) ); const session: OlmSession = { sessionId: sessionId, olmId, - expiresAt: new Date(Date.now() + EXPIRES).getTime(), + expiresAt: new Date(Date.now() + EXPIRES).getTime() }; await db.insert(olmSessions).values(session); return session; } export async function validateOlmSessionToken( - token: string, + token: string ): Promise { const sessionId = encodeHexLowerCase( - sha256(new TextEncoder().encode(token)), + sha256(new TextEncoder().encode(token)) ); const result = await db .select({ olm: olms, session: olmSessions }) @@ -45,14 +43,12 @@ export async function validateOlmSessionToken( .where(eq(olmSessions.sessionId, session.sessionId)); return { session: null, olm: null }; } - if (Date.now() >= session.expiresAt - (EXPIRES / 2)) { - session.expiresAt = new Date( - Date.now() + EXPIRES, - ).getTime(); + if (Date.now() >= session.expiresAt - EXPIRES / 2) { + session.expiresAt = new Date(Date.now() + EXPIRES).getTime(); await db .update(olmSessions) .set({ - expiresAt: session.expiresAt, + expiresAt: session.expiresAt }) .where(eq(olmSessions.sessionId, session.sessionId)); } diff --git a/server/cleanup.ts b/server/cleanup.ts index a8985439..e494fcdc 100644 --- a/server/cleanup.ts +++ b/server/cleanup.ts @@ -10,4 +10,4 @@ export async function initCleanup() { // Handle process termination process.on("SIGTERM", () => cleanup()); process.on("SIGINT", () => cleanup()); -} \ No newline at end of file +} diff --git a/server/db/countries.ts b/server/db/countries.ts index 2907fd69..749f1183 100644 --- a/server/db/countries.ts +++ b/server/db/countries.ts @@ -1,1014 +1,1014 @@ export const COUNTRIES = [ { - "name": "ALL COUNTRIES", - "code": "ALL" // THIS IS AN INVALID CC SO IT WILL NEVER MATCH + name: "ALL COUNTRIES", + code: "ALL" // THIS IS AN INVALID CC SO IT WILL NEVER MATCH }, { - "name": "Afghanistan", - "code": "AF" + name: "Afghanistan", + code: "AF" }, { - "name": "Albania", - "code": "AL" + name: "Albania", + code: "AL" }, { - "name": "Algeria", - "code": "DZ" + name: "Algeria", + code: "DZ" }, { - "name": "American Samoa", - "code": "AS" + name: "American Samoa", + code: "AS" }, { - "name": "Andorra", - "code": "AD" + name: "Andorra", + code: "AD" }, { - "name": "Angola", - "code": "AO" + name: "Angola", + code: "AO" }, { - "name": "Anguilla", - "code": "AI" + name: "Anguilla", + code: "AI" }, { - "name": "Antarctica", - "code": "AQ" + name: "Antarctica", + code: "AQ" }, { - "name": "Antigua and Barbuda", - "code": "AG" + name: "Antigua and Barbuda", + code: "AG" }, { - "name": "Argentina", - "code": "AR" + name: "Argentina", + code: "AR" }, { - "name": "Armenia", - "code": "AM" + name: "Armenia", + code: "AM" }, { - "name": "Aruba", - "code": "AW" + name: "Aruba", + code: "AW" }, { - "name": "Asia/Pacific Region", - "code": "AP" + name: "Asia/Pacific Region", + code: "AP" }, { - "name": "Australia", - "code": "AU" + name: "Australia", + code: "AU" }, { - "name": "Austria", - "code": "AT" + name: "Austria", + code: "AT" }, { - "name": "Azerbaijan", - "code": "AZ" + name: "Azerbaijan", + code: "AZ" }, { - "name": "Bahamas", - "code": "BS" + name: "Bahamas", + code: "BS" }, { - "name": "Bahrain", - "code": "BH" + name: "Bahrain", + code: "BH" }, { - "name": "Bangladesh", - "code": "BD" + name: "Bangladesh", + code: "BD" }, { - "name": "Barbados", - "code": "BB" + name: "Barbados", + code: "BB" }, { - "name": "Belarus", - "code": "BY" + name: "Belarus", + code: "BY" }, { - "name": "Belgium", - "code": "BE" + name: "Belgium", + code: "BE" }, { - "name": "Belize", - "code": "BZ" + name: "Belize", + code: "BZ" }, { - "name": "Benin", - "code": "BJ" + name: "Benin", + code: "BJ" }, { - "name": "Bermuda", - "code": "BM" + name: "Bermuda", + code: "BM" }, { - "name": "Bhutan", - "code": "BT" + name: "Bhutan", + code: "BT" }, { - "name": "Bolivia", - "code": "BO" + name: "Bolivia", + code: "BO" }, { - "name": "Bonaire, Sint Eustatius and Saba", - "code": "BQ" + name: "Bonaire, Sint Eustatius and Saba", + code: "BQ" }, { - "name": "Bosnia and Herzegovina", - "code": "BA" + name: "Bosnia and Herzegovina", + code: "BA" }, { - "name": "Botswana", - "code": "BW" + name: "Botswana", + code: "BW" }, { - "name": "Bouvet Island", - "code": "BV" + name: "Bouvet Island", + code: "BV" }, { - "name": "Brazil", - "code": "BR" + name: "Brazil", + code: "BR" }, { - "name": "British Indian Ocean Territory", - "code": "IO" + name: "British Indian Ocean Territory", + code: "IO" }, { - "name": "Brunei Darussalam", - "code": "BN" + name: "Brunei Darussalam", + code: "BN" }, { - "name": "Bulgaria", - "code": "BG" + name: "Bulgaria", + code: "BG" }, { - "name": "Burkina Faso", - "code": "BF" + name: "Burkina Faso", + code: "BF" }, { - "name": "Burundi", - "code": "BI" + name: "Burundi", + code: "BI" }, { - "name": "Cambodia", - "code": "KH" + name: "Cambodia", + code: "KH" }, { - "name": "Cameroon", - "code": "CM" + name: "Cameroon", + code: "CM" }, { - "name": "Canada", - "code": "CA" + name: "Canada", + code: "CA" }, { - "name": "Cape Verde", - "code": "CV" + name: "Cape Verde", + code: "CV" }, { - "name": "Cayman Islands", - "code": "KY" + name: "Cayman Islands", + code: "KY" }, { - "name": "Central African Republic", - "code": "CF" + name: "Central African Republic", + code: "CF" }, { - "name": "Chad", - "code": "TD" + name: "Chad", + code: "TD" }, { - "name": "Chile", - "code": "CL" + name: "Chile", + code: "CL" }, { - "name": "China", - "code": "CN" + name: "China", + code: "CN" }, { - "name": "Christmas Island", - "code": "CX" + name: "Christmas Island", + code: "CX" }, { - "name": "Cocos (Keeling) Islands", - "code": "CC" + name: "Cocos (Keeling) Islands", + code: "CC" }, { - "name": "Colombia", - "code": "CO" + name: "Colombia", + code: "CO" }, { - "name": "Comoros", - "code": "KM" + name: "Comoros", + code: "KM" }, { - "name": "Congo", - "code": "CG" + name: "Congo", + code: "CG" }, { - "name": "Congo, The Democratic Republic of the", - "code": "CD" + name: "Congo, The Democratic Republic of the", + code: "CD" }, { - "name": "Cook Islands", - "code": "CK" + name: "Cook Islands", + code: "CK" }, { - "name": "Costa Rica", - "code": "CR" + name: "Costa Rica", + code: "CR" }, { - "name": "Croatia", - "code": "HR" + name: "Croatia", + code: "HR" }, { - "name": "Cuba", - "code": "CU" + name: "Cuba", + code: "CU" }, { - "name": "Curaçao", - "code": "CW" + name: "Curaçao", + code: "CW" }, { - "name": "Cyprus", - "code": "CY" + name: "Cyprus", + code: "CY" }, { - "name": "Czech Republic", - "code": "CZ" + name: "Czech Republic", + code: "CZ" }, { - "name": "Côte d'Ivoire", - "code": "CI" + name: "Côte d'Ivoire", + code: "CI" }, { - "name": "Denmark", - "code": "DK" + name: "Denmark", + code: "DK" }, { - "name": "Djibouti", - "code": "DJ" + name: "Djibouti", + code: "DJ" }, { - "name": "Dominica", - "code": "DM" + name: "Dominica", + code: "DM" }, { - "name": "Dominican Republic", - "code": "DO" + name: "Dominican Republic", + code: "DO" }, { - "name": "Ecuador", - "code": "EC" + name: "Ecuador", + code: "EC" }, { - "name": "Egypt", - "code": "EG" + name: "Egypt", + code: "EG" }, { - "name": "El Salvador", - "code": "SV" + name: "El Salvador", + code: "SV" }, { - "name": "Equatorial Guinea", - "code": "GQ" + name: "Equatorial Guinea", + code: "GQ" }, { - "name": "Eritrea", - "code": "ER" + name: "Eritrea", + code: "ER" }, { - "name": "Estonia", - "code": "EE" + name: "Estonia", + code: "EE" }, { - "name": "Ethiopia", - "code": "ET" + name: "Ethiopia", + code: "ET" }, { - "name": "Falkland Islands (Malvinas)", - "code": "FK" + name: "Falkland Islands (Malvinas)", + code: "FK" }, { - "name": "Faroe Islands", - "code": "FO" + name: "Faroe Islands", + code: "FO" }, { - "name": "Fiji", - "code": "FJ" + name: "Fiji", + code: "FJ" }, { - "name": "Finland", - "code": "FI" + name: "Finland", + code: "FI" }, { - "name": "France", - "code": "FR" + name: "France", + code: "FR" }, { - "name": "French Guiana", - "code": "GF" + name: "French Guiana", + code: "GF" }, { - "name": "French Polynesia", - "code": "PF" + name: "French Polynesia", + code: "PF" }, { - "name": "French Southern Territories", - "code": "TF" + name: "French Southern Territories", + code: "TF" }, { - "name": "Gabon", - "code": "GA" + name: "Gabon", + code: "GA" }, { - "name": "Gambia", - "code": "GM" + name: "Gambia", + code: "GM" }, { - "name": "Georgia", - "code": "GE" + name: "Georgia", + code: "GE" }, { - "name": "Germany", - "code": "DE" + name: "Germany", + code: "DE" }, { - "name": "Ghana", - "code": "GH" + name: "Ghana", + code: "GH" }, { - "name": "Gibraltar", - "code": "GI" + name: "Gibraltar", + code: "GI" }, { - "name": "Greece", - "code": "GR" + name: "Greece", + code: "GR" }, { - "name": "Greenland", - "code": "GL" + name: "Greenland", + code: "GL" }, { - "name": "Grenada", - "code": "GD" + name: "Grenada", + code: "GD" }, { - "name": "Guadeloupe", - "code": "GP" + name: "Guadeloupe", + code: "GP" }, { - "name": "Guam", - "code": "GU" + name: "Guam", + code: "GU" }, { - "name": "Guatemala", - "code": "GT" + name: "Guatemala", + code: "GT" }, { - "name": "Guernsey", - "code": "GG" + name: "Guernsey", + code: "GG" }, { - "name": "Guinea", - "code": "GN" + name: "Guinea", + code: "GN" }, { - "name": "Guinea-Bissau", - "code": "GW" + name: "Guinea-Bissau", + code: "GW" }, { - "name": "Guyana", - "code": "GY" + name: "Guyana", + code: "GY" }, { - "name": "Haiti", - "code": "HT" + name: "Haiti", + code: "HT" }, { - "name": "Heard Island and Mcdonald Islands", - "code": "HM" + name: "Heard Island and Mcdonald Islands", + code: "HM" }, { - "name": "Holy See (Vatican City State)", - "code": "VA" + name: "Holy See (Vatican City State)", + code: "VA" }, { - "name": "Honduras", - "code": "HN" + name: "Honduras", + code: "HN" }, { - "name": "Hong Kong", - "code": "HK" + name: "Hong Kong", + code: "HK" }, { - "name": "Hungary", - "code": "HU" + name: "Hungary", + code: "HU" }, { - "name": "Iceland", - "code": "IS" + name: "Iceland", + code: "IS" }, { - "name": "India", - "code": "IN" + name: "India", + code: "IN" }, { - "name": "Indonesia", - "code": "ID" + name: "Indonesia", + code: "ID" }, { - "name": "Iran, Islamic Republic Of", - "code": "IR" + name: "Iran, Islamic Republic Of", + code: "IR" }, { - "name": "Iraq", - "code": "IQ" + name: "Iraq", + code: "IQ" }, { - "name": "Ireland", - "code": "IE" + name: "Ireland", + code: "IE" }, { - "name": "Isle of Man", - "code": "IM" + name: "Isle of Man", + code: "IM" }, { - "name": "Israel", - "code": "IL" + name: "Israel", + code: "IL" }, { - "name": "Italy", - "code": "IT" + name: "Italy", + code: "IT" }, { - "name": "Jamaica", - "code": "JM" + name: "Jamaica", + code: "JM" }, { - "name": "Japan", - "code": "JP" + name: "Japan", + code: "JP" }, { - "name": "Jersey", - "code": "JE" + name: "Jersey", + code: "JE" }, { - "name": "Jordan", - "code": "JO" + name: "Jordan", + code: "JO" }, { - "name": "Kazakhstan", - "code": "KZ" + name: "Kazakhstan", + code: "KZ" }, { - "name": "Kenya", - "code": "KE" + name: "Kenya", + code: "KE" }, { - "name": "Kiribati", - "code": "KI" + name: "Kiribati", + code: "KI" }, { - "name": "Korea, Republic of", - "code": "KR" + name: "Korea, Republic of", + code: "KR" }, { - "name": "Kuwait", - "code": "KW" + name: "Kuwait", + code: "KW" }, { - "name": "Kyrgyzstan", - "code": "KG" + name: "Kyrgyzstan", + code: "KG" }, { - "name": "Laos", - "code": "LA" + name: "Laos", + code: "LA" }, { - "name": "Latvia", - "code": "LV" + name: "Latvia", + code: "LV" }, { - "name": "Lebanon", - "code": "LB" + name: "Lebanon", + code: "LB" }, { - "name": "Lesotho", - "code": "LS" + name: "Lesotho", + code: "LS" }, { - "name": "Liberia", - "code": "LR" + name: "Liberia", + code: "LR" }, { - "name": "Libyan Arab Jamahiriya", - "code": "LY" + name: "Libyan Arab Jamahiriya", + code: "LY" }, { - "name": "Liechtenstein", - "code": "LI" + name: "Liechtenstein", + code: "LI" }, { - "name": "Lithuania", - "code": "LT" + name: "Lithuania", + code: "LT" }, { - "name": "Luxembourg", - "code": "LU" + name: "Luxembourg", + code: "LU" }, { - "name": "Macao", - "code": "MO" + name: "Macao", + code: "MO" }, { - "name": "Madagascar", - "code": "MG" + name: "Madagascar", + code: "MG" }, { - "name": "Malawi", - "code": "MW" + name: "Malawi", + code: "MW" }, { - "name": "Malaysia", - "code": "MY" + name: "Malaysia", + code: "MY" }, { - "name": "Maldives", - "code": "MV" + name: "Maldives", + code: "MV" }, { - "name": "Mali", - "code": "ML" + name: "Mali", + code: "ML" }, { - "name": "Malta", - "code": "MT" + name: "Malta", + code: "MT" }, { - "name": "Marshall Islands", - "code": "MH" + name: "Marshall Islands", + code: "MH" }, { - "name": "Martinique", - "code": "MQ" + name: "Martinique", + code: "MQ" }, { - "name": "Mauritania", - "code": "MR" + name: "Mauritania", + code: "MR" }, { - "name": "Mauritius", - "code": "MU" + name: "Mauritius", + code: "MU" }, { - "name": "Mayotte", - "code": "YT" + name: "Mayotte", + code: "YT" }, { - "name": "Mexico", - "code": "MX" + name: "Mexico", + code: "MX" }, { - "name": "Micronesia, Federated States of", - "code": "FM" + name: "Micronesia, Federated States of", + code: "FM" }, { - "name": "Moldova, Republic of", - "code": "MD" + name: "Moldova, Republic of", + code: "MD" }, { - "name": "Monaco", - "code": "MC" + name: "Monaco", + code: "MC" }, { - "name": "Mongolia", - "code": "MN" + name: "Mongolia", + code: "MN" }, { - "name": "Montenegro", - "code": "ME" + name: "Montenegro", + code: "ME" }, { - "name": "Montserrat", - "code": "MS" + name: "Montserrat", + code: "MS" }, { - "name": "Morocco", - "code": "MA" + name: "Morocco", + code: "MA" }, { - "name": "Mozambique", - "code": "MZ" + name: "Mozambique", + code: "MZ" }, { - "name": "Myanmar", - "code": "MM" + name: "Myanmar", + code: "MM" }, { - "name": "Namibia", - "code": "NA" + name: "Namibia", + code: "NA" }, { - "name": "Nauru", - "code": "NR" + name: "Nauru", + code: "NR" }, { - "name": "Nepal", - "code": "NP" + name: "Nepal", + code: "NP" }, { - "name": "Netherlands", - "code": "NL" + name: "Netherlands", + code: "NL" }, { - "name": "Netherlands Antilles", - "code": "AN" + name: "Netherlands Antilles", + code: "AN" }, { - "name": "New Caledonia", - "code": "NC" + name: "New Caledonia", + code: "NC" }, { - "name": "New Zealand", - "code": "NZ" + name: "New Zealand", + code: "NZ" }, { - "name": "Nicaragua", - "code": "NI" + name: "Nicaragua", + code: "NI" }, { - "name": "Niger", - "code": "NE" + name: "Niger", + code: "NE" }, { - "name": "Nigeria", - "code": "NG" + name: "Nigeria", + code: "NG" }, { - "name": "Niue", - "code": "NU" + name: "Niue", + code: "NU" }, { - "name": "Norfolk Island", - "code": "NF" + name: "Norfolk Island", + code: "NF" }, { - "name": "North Korea", - "code": "KP" + name: "North Korea", + code: "KP" }, { - "name": "North Macedonia", - "code": "MK" + name: "North Macedonia", + code: "MK" }, { - "name": "Northern Mariana Islands", - "code": "MP" + name: "Northern Mariana Islands", + code: "MP" }, { - "name": "Norway", - "code": "NO" + name: "Norway", + code: "NO" }, { - "name": "Oman", - "code": "OM" + name: "Oman", + code: "OM" }, { - "name": "Pakistan", - "code": "PK" + name: "Pakistan", + code: "PK" }, { - "name": "Palau", - "code": "PW" + name: "Palau", + code: "PW" }, { - "name": "Palestinian Territory, Occupied", - "code": "PS" + name: "Palestinian Territory, Occupied", + code: "PS" }, { - "name": "Panama", - "code": "PA" + name: "Panama", + code: "PA" }, { - "name": "Papua New Guinea", - "code": "PG" + name: "Papua New Guinea", + code: "PG" }, { - "name": "Paraguay", - "code": "PY" + name: "Paraguay", + code: "PY" }, { - "name": "Peru", - "code": "PE" + name: "Peru", + code: "PE" }, { - "name": "Philippines", - "code": "PH" + name: "Philippines", + code: "PH" }, { - "name": "Pitcairn Islands", - "code": "PN" + name: "Pitcairn Islands", + code: "PN" }, { - "name": "Poland", - "code": "PL" + name: "Poland", + code: "PL" }, { - "name": "Portugal", - "code": "PT" + name: "Portugal", + code: "PT" }, { - "name": "Puerto Rico", - "code": "PR" + name: "Puerto Rico", + code: "PR" }, { - "name": "Qatar", - "code": "QA" + name: "Qatar", + code: "QA" }, { - "name": "Reunion", - "code": "RE" + name: "Reunion", + code: "RE" }, { - "name": "Romania", - "code": "RO" + name: "Romania", + code: "RO" }, { - "name": "Russian Federation", - "code": "RU" + name: "Russian Federation", + code: "RU" }, { - "name": "Rwanda", - "code": "RW" + name: "Rwanda", + code: "RW" }, { - "name": "Saint Barthélemy", - "code": "BL" + name: "Saint Barthélemy", + code: "BL" }, { - "name": "Saint Helena", - "code": "SH" + name: "Saint Helena", + code: "SH" }, { - "name": "Saint Kitts and Nevis", - "code": "KN" + name: "Saint Kitts and Nevis", + code: "KN" }, { - "name": "Saint Lucia", - "code": "LC" + name: "Saint Lucia", + code: "LC" }, { - "name": "Saint Martin", - "code": "MF" + name: "Saint Martin", + code: "MF" }, { - "name": "Saint Pierre and Miquelon", - "code": "PM" + name: "Saint Pierre and Miquelon", + code: "PM" }, { - "name": "Saint Vincent and the Grenadines", - "code": "VC" + name: "Saint Vincent and the Grenadines", + code: "VC" }, { - "name": "Samoa", - "code": "WS" + name: "Samoa", + code: "WS" }, { - "name": "San Marino", - "code": "SM" + name: "San Marino", + code: "SM" }, { - "name": "Sao Tome and Principe", - "code": "ST" + name: "Sao Tome and Principe", + code: "ST" }, { - "name": "Saudi Arabia", - "code": "SA" + name: "Saudi Arabia", + code: "SA" }, { - "name": "Senegal", - "code": "SN" + name: "Senegal", + code: "SN" }, { - "name": "Serbia", - "code": "RS" + name: "Serbia", + code: "RS" }, { - "name": "Serbia and Montenegro", - "code": "CS" + name: "Serbia and Montenegro", + code: "CS" }, { - "name": "Seychelles", - "code": "SC" + name: "Seychelles", + code: "SC" }, { - "name": "Sierra Leone", - "code": "SL" + name: "Sierra Leone", + code: "SL" }, { - "name": "Singapore", - "code": "SG" + name: "Singapore", + code: "SG" }, { - "name": "Sint Maarten", - "code": "SX" + name: "Sint Maarten", + code: "SX" }, { - "name": "Slovakia", - "code": "SK" + name: "Slovakia", + code: "SK" }, { - "name": "Slovenia", - "code": "SI" + name: "Slovenia", + code: "SI" }, { - "name": "Solomon Islands", - "code": "SB" + name: "Solomon Islands", + code: "SB" }, { - "name": "Somalia", - "code": "SO" + name: "Somalia", + code: "SO" }, { - "name": "South Africa", - "code": "ZA" + name: "South Africa", + code: "ZA" }, { - "name": "South Georgia and the South Sandwich Islands", - "code": "GS" + name: "South Georgia and the South Sandwich Islands", + code: "GS" }, { - "name": "South Sudan", - "code": "SS" + name: "South Sudan", + code: "SS" }, { - "name": "Spain", - "code": "ES" + name: "Spain", + code: "ES" }, { - "name": "Sri Lanka", - "code": "LK" + name: "Sri Lanka", + code: "LK" }, { - "name": "Sudan", - "code": "SD" + name: "Sudan", + code: "SD" }, { - "name": "Suriname", - "code": "SR" + name: "Suriname", + code: "SR" }, { - "name": "Svalbard and Jan Mayen", - "code": "SJ" + name: "Svalbard and Jan Mayen", + code: "SJ" }, { - "name": "Swaziland", - "code": "SZ" + name: "Swaziland", + code: "SZ" }, { - "name": "Sweden", - "code": "SE" + name: "Sweden", + code: "SE" }, { - "name": "Switzerland", - "code": "CH" + name: "Switzerland", + code: "CH" }, { - "name": "Syrian Arab Republic", - "code": "SY" + name: "Syrian Arab Republic", + code: "SY" }, { - "name": "Taiwan", - "code": "TW" + name: "Taiwan", + code: "TW" }, { - "name": "Tajikistan", - "code": "TJ" + name: "Tajikistan", + code: "TJ" }, { - "name": "Tanzania, United Republic of", - "code": "TZ" + name: "Tanzania, United Republic of", + code: "TZ" }, { - "name": "Thailand", - "code": "TH" + name: "Thailand", + code: "TH" }, { - "name": "Timor-Leste", - "code": "TL" + name: "Timor-Leste", + code: "TL" }, { - "name": "Togo", - "code": "TG" + name: "Togo", + code: "TG" }, { - "name": "Tokelau", - "code": "TK" + name: "Tokelau", + code: "TK" }, { - "name": "Tonga", - "code": "TO" + name: "Tonga", + code: "TO" }, { - "name": "Trinidad and Tobago", - "code": "TT" + name: "Trinidad and Tobago", + code: "TT" }, { - "name": "Tunisia", - "code": "TN" + name: "Tunisia", + code: "TN" }, { - "name": "Turkey", - "code": "TR" + name: "Turkey", + code: "TR" }, { - "name": "Turkmenistan", - "code": "TM" + name: "Turkmenistan", + code: "TM" }, { - "name": "Turks and Caicos Islands", - "code": "TC" + name: "Turks and Caicos Islands", + code: "TC" }, { - "name": "Tuvalu", - "code": "TV" + name: "Tuvalu", + code: "TV" }, { - "name": "Uganda", - "code": "UG" + name: "Uganda", + code: "UG" }, { - "name": "Ukraine", - "code": "UA" + name: "Ukraine", + code: "UA" }, { - "name": "United Arab Emirates", - "code": "AE" + name: "United Arab Emirates", + code: "AE" }, { - "name": "United Kingdom", - "code": "GB" + name: "United Kingdom", + code: "GB" }, { - "name": "United States", - "code": "US" + name: "United States", + code: "US" }, { - "name": "United States Minor Outlying Islands", - "code": "UM" + name: "United States Minor Outlying Islands", + code: "UM" }, { - "name": "Uruguay", - "code": "UY" + name: "Uruguay", + code: "UY" }, { - "name": "Uzbekistan", - "code": "UZ" + name: "Uzbekistan", + code: "UZ" }, { - "name": "Vanuatu", - "code": "VU" + name: "Vanuatu", + code: "VU" }, { - "name": "Venezuela", - "code": "VE" + name: "Venezuela", + code: "VE" }, { - "name": "Vietnam", - "code": "VN" + name: "Vietnam", + code: "VN" }, { - "name": "Virgin Islands, British", - "code": "VG" + name: "Virgin Islands, British", + code: "VG" }, { - "name": "Virgin Islands, U.S.", - "code": "VI" + name: "Virgin Islands, U.S.", + code: "VI" }, { - "name": "Wallis and Futuna", - "code": "WF" + name: "Wallis and Futuna", + code: "WF" }, { - "name": "Western Sahara", - "code": "EH" + name: "Western Sahara", + code: "EH" }, { - "name": "Yemen", - "code": "YE" + name: "Yemen", + code: "YE" }, { - "name": "Zambia", - "code": "ZM" + name: "Zambia", + code: "ZM" }, { - "name": "Zimbabwe", - "code": "ZW" + name: "Zimbabwe", + code: "ZW" }, { - "name": "Åland Islands", - "code": "AX" + name: "Åland Islands", + code: "AX" } -]; \ No newline at end of file +]; diff --git a/server/db/names.json b/server/db/names.json index fdf545fb..eb104691 100644 --- a/server/db/names.json +++ b/server/db/names.json @@ -1708,4 +1708,4 @@ "Desert Box Turtle", "African Striped Weasel" ] -} \ No newline at end of file +} diff --git a/server/db/pg/schema/privateSchema.ts b/server/db/pg/schema/privateSchema.ts index 17d262c6..cb809b71 100644 --- a/server/db/pg/schema/privateSchema.ts +++ b/server/db/pg/schema/privateSchema.ts @@ -215,42 +215,56 @@ export const sessionTransferToken = pgTable("sessionTransferToken", { expiresAt: bigint("expiresAt", { mode: "number" }).notNull() }); -export const actionAuditLog = pgTable("actionAuditLog", { - id: serial("id").primaryKey(), - timestamp: bigint("timestamp", { mode: "number" }).notNull(), // this is EPOCH time in seconds - orgId: varchar("orgId") - .notNull() - .references(() => orgs.orgId, { onDelete: "cascade" }), - actorType: varchar("actorType", { length: 50 }).notNull(), - actor: varchar("actor", { length: 255 }).notNull(), - actorId: varchar("actorId", { length: 255 }).notNull(), - action: varchar("action", { length: 100 }).notNull(), - metadata: text("metadata") -}, (table) => ([ - index("idx_actionAuditLog_timestamp").on(table.timestamp), - index("idx_actionAuditLog_org_timestamp").on(table.orgId, table.timestamp) -])); +export const actionAuditLog = pgTable( + "actionAuditLog", + { + id: serial("id").primaryKey(), + timestamp: bigint("timestamp", { mode: "number" }).notNull(), // this is EPOCH time in seconds + orgId: varchar("orgId") + .notNull() + .references(() => orgs.orgId, { onDelete: "cascade" }), + actorType: varchar("actorType", { length: 50 }).notNull(), + actor: varchar("actor", { length: 255 }).notNull(), + actorId: varchar("actorId", { length: 255 }).notNull(), + action: varchar("action", { length: 100 }).notNull(), + metadata: text("metadata") + }, + (table) => [ + index("idx_actionAuditLog_timestamp").on(table.timestamp), + index("idx_actionAuditLog_org_timestamp").on( + table.orgId, + table.timestamp + ) + ] +); -export const accessAuditLog = pgTable("accessAuditLog", { - id: serial("id").primaryKey(), - timestamp: bigint("timestamp", { mode: "number" }).notNull(), // this is EPOCH time in seconds - orgId: varchar("orgId") - .notNull() - .references(() => orgs.orgId, { onDelete: "cascade" }), - actorType: varchar("actorType", { length: 50 }), - actor: varchar("actor", { length: 255 }), - actorId: varchar("actorId", { length: 255 }), - resourceId: integer("resourceId"), - ip: varchar("ip", { length: 45 }), - type: varchar("type", { length: 100 }).notNull(), - action: boolean("action").notNull(), - location: text("location"), - userAgent: text("userAgent"), - metadata: text("metadata") -}, (table) => ([ - index("idx_identityAuditLog_timestamp").on(table.timestamp), - index("idx_identityAuditLog_org_timestamp").on(table.orgId, table.timestamp) -])); +export const accessAuditLog = pgTable( + "accessAuditLog", + { + id: serial("id").primaryKey(), + timestamp: bigint("timestamp", { mode: "number" }).notNull(), // this is EPOCH time in seconds + orgId: varchar("orgId") + .notNull() + .references(() => orgs.orgId, { onDelete: "cascade" }), + actorType: varchar("actorType", { length: 50 }), + actor: varchar("actor", { length: 255 }), + actorId: varchar("actorId", { length: 255 }), + resourceId: integer("resourceId"), + ip: varchar("ip", { length: 45 }), + type: varchar("type", { length: 100 }).notNull(), + action: boolean("action").notNull(), + location: text("location"), + userAgent: text("userAgent"), + metadata: text("metadata") + }, + (table) => [ + index("idx_identityAuditLog_timestamp").on(table.timestamp), + index("idx_identityAuditLog_org_timestamp").on( + table.orgId, + table.timestamp + ) + ] +); export type Limit = InferSelectModel; export type Account = InferSelectModel; @@ -270,4 +284,4 @@ export type RemoteExitNodeSession = InferSelectModel< export type ExitNodeOrg = InferSelectModel; export type LoginPage = InferSelectModel; export type ActionAuditLog = InferSelectModel; -export type AccessAuditLog = InferSelectModel; \ No newline at end of file +export type AccessAuditLog = InferSelectModel; diff --git a/server/db/pg/schema/schema.ts b/server/db/pg/schema/schema.ts index a0020a0e..71877f2f 100644 --- a/server/db/pg/schema/schema.ts +++ b/server/db/pg/schema/schema.ts @@ -177,7 +177,7 @@ export const targetHealthCheck = pgTable("targetHealthCheck", { hcMethod: varchar("hcMethod").default("GET"), hcStatus: integer("hcStatus"), // http code hcHealth: text("hcHealth").default("unknown"), // "unknown", "healthy", "unhealthy" - hcTlsServerName: text("hcTlsServerName"), + hcTlsServerName: text("hcTlsServerName") }); export const exitNodes = pgTable("exitNodes", { diff --git a/server/db/queries/verifySessionQueries.ts b/server/db/queries/verifySessionQueries.ts index 85bd7cc7..774c4e53 100644 --- a/server/db/queries/verifySessionQueries.ts +++ b/server/db/queries/verifySessionQueries.ts @@ -52,10 +52,7 @@ export async function getResourceByDomain( resourceHeaderAuth, eq(resourceHeaderAuth.resourceId, resources.resourceId) ) - .innerJoin( - orgs, - eq(orgs.orgId, resources.orgId) - ) + .innerJoin(orgs, eq(orgs.orgId, resources.orgId)) .where(eq(resources.fullDomain, domain)) .limit(1); diff --git a/server/db/sqlite/migrate.ts b/server/db/sqlite/migrate.ts index e4a730d0..7c337ae2 100644 --- a/server/db/sqlite/migrate.ts +++ b/server/db/sqlite/migrate.ts @@ -8,7 +8,7 @@ const runMigrations = async () => { console.log("Running migrations..."); try { migrate(db as any, { - migrationsFolder: migrationsFolder, + migrationsFolder: migrationsFolder }); console.log("Migrations completed successfully."); } catch (error) { diff --git a/server/db/sqlite/schema/privateSchema.ts b/server/db/sqlite/schema/privateSchema.ts index 65396770..975a949b 100644 --- a/server/db/sqlite/schema/privateSchema.ts +++ b/server/db/sqlite/schema/privateSchema.ts @@ -29,7 +29,9 @@ export const certificates = sqliteTable("certificates", { }); export const dnsChallenge = sqliteTable("dnsChallenges", { - dnsChallengeId: integer("dnsChallengeId").primaryKey({ autoIncrement: true }), + dnsChallengeId: integer("dnsChallengeId").primaryKey({ + autoIncrement: true + }), domain: text("domain").notNull(), token: text("token").notNull(), keyAuthorization: text("keyAuthorization").notNull(), @@ -61,9 +63,7 @@ export const customers = sqliteTable("customers", { }); export const subscriptions = sqliteTable("subscriptions", { - subscriptionId: text("subscriptionId") - .primaryKey() - .notNull(), + subscriptionId: text("subscriptionId").primaryKey().notNull(), customerId: text("customerId") .notNull() .references(() => customers.customerId, { onDelete: "cascade" }), @@ -75,7 +75,9 @@ export const subscriptions = sqliteTable("subscriptions", { }); export const subscriptionItems = sqliteTable("subscriptionItems", { - subscriptionItemId: integer("subscriptionItemId").primaryKey({ autoIncrement: true }), + subscriptionItemId: integer("subscriptionItemId").primaryKey({ + autoIncrement: true + }), subscriptionId: text("subscriptionId") .notNull() .references(() => subscriptions.subscriptionId, { @@ -129,7 +131,9 @@ export const limits = sqliteTable("limits", { }); export const usageNotifications = sqliteTable("usageNotifications", { - notificationId: integer("notificationId").primaryKey({ autoIncrement: true }), + notificationId: integer("notificationId").primaryKey({ + autoIncrement: true + }), orgId: text("orgId") .notNull() .references(() => orgs.orgId, { onDelete: "cascade" }), @@ -210,42 +214,56 @@ export const sessionTransferToken = sqliteTable("sessionTransferToken", { expiresAt: integer("expiresAt").notNull() }); -export const actionAuditLog = sqliteTable("actionAuditLog", { - id: integer("id").primaryKey({ autoIncrement: true }), - timestamp: integer("timestamp").notNull(), // this is EPOCH time in seconds - orgId: text("orgId") - .notNull() - .references(() => orgs.orgId, { onDelete: "cascade" }), - actorType: text("actorType").notNull(), - actor: text("actor").notNull(), - actorId: text("actorId").notNull(), - action: text("action").notNull(), - metadata: text("metadata") -}, (table) => ([ - index("idx_actionAuditLog_timestamp").on(table.timestamp), - index("idx_actionAuditLog_org_timestamp").on(table.orgId, table.timestamp) -])); +export const actionAuditLog = sqliteTable( + "actionAuditLog", + { + id: integer("id").primaryKey({ autoIncrement: true }), + timestamp: integer("timestamp").notNull(), // this is EPOCH time in seconds + orgId: text("orgId") + .notNull() + .references(() => orgs.orgId, { onDelete: "cascade" }), + actorType: text("actorType").notNull(), + actor: text("actor").notNull(), + actorId: text("actorId").notNull(), + action: text("action").notNull(), + metadata: text("metadata") + }, + (table) => [ + index("idx_actionAuditLog_timestamp").on(table.timestamp), + index("idx_actionAuditLog_org_timestamp").on( + table.orgId, + table.timestamp + ) + ] +); -export const accessAuditLog = sqliteTable("accessAuditLog", { - id: integer("id").primaryKey({ autoIncrement: true }), - timestamp: integer("timestamp").notNull(), // this is EPOCH time in seconds - orgId: text("orgId") - .notNull() - .references(() => orgs.orgId, { onDelete: "cascade" }), - actorType: text("actorType"), - actor: text("actor"), - actorId: text("actorId"), - resourceId: integer("resourceId"), - ip: text("ip"), - location: text("location"), - type: text("type").notNull(), - action: integer("action", { mode: "boolean" }).notNull(), - userAgent: text("userAgent"), - metadata: text("metadata") -}, (table) => ([ - index("idx_identityAuditLog_timestamp").on(table.timestamp), - index("idx_identityAuditLog_org_timestamp").on(table.orgId, table.timestamp) -])); +export const accessAuditLog = sqliteTable( + "accessAuditLog", + { + id: integer("id").primaryKey({ autoIncrement: true }), + timestamp: integer("timestamp").notNull(), // this is EPOCH time in seconds + orgId: text("orgId") + .notNull() + .references(() => orgs.orgId, { onDelete: "cascade" }), + actorType: text("actorType"), + actor: text("actor"), + actorId: text("actorId"), + resourceId: integer("resourceId"), + ip: text("ip"), + location: text("location"), + type: text("type").notNull(), + action: integer("action", { mode: "boolean" }).notNull(), + userAgent: text("userAgent"), + metadata: text("metadata") + }, + (table) => [ + index("idx_identityAuditLog_timestamp").on(table.timestamp), + index("idx_identityAuditLog_org_timestamp").on( + table.orgId, + table.timestamp + ) + ] +); export type Limit = InferSelectModel; export type Account = InferSelectModel; @@ -265,4 +283,4 @@ export type RemoteExitNodeSession = InferSelectModel< export type ExitNodeOrg = InferSelectModel; export type LoginPage = InferSelectModel; export type ActionAuditLog = InferSelectModel; -export type AccessAuditLog = InferSelectModel; \ No newline at end of file +export type AccessAuditLog = InferSelectModel; diff --git a/server/emails/index.ts b/server/emails/index.ts index 42cfa39c..01cc6610 100644 --- a/server/emails/index.ts +++ b/server/emails/index.ts @@ -18,10 +18,13 @@ function createEmailClient() { host: emailConfig.smtp_host, port: emailConfig.smtp_port, secure: emailConfig.smtp_secure || false, - auth: (emailConfig.smtp_user && emailConfig.smtp_pass) ? { - user: emailConfig.smtp_user, - pass: emailConfig.smtp_pass - } : null + auth: + emailConfig.smtp_user && emailConfig.smtp_pass + ? { + user: emailConfig.smtp_user, + pass: emailConfig.smtp_pass + } + : null } as SMTPTransport.Options; if (emailConfig.smtp_tls_reject_unauthorized !== undefined) { diff --git a/server/emails/templates/NotifyUsageLimitApproaching.tsx b/server/emails/templates/NotifyUsageLimitApproaching.tsx index beab0300..161b3676 100644 --- a/server/emails/templates/NotifyUsageLimitApproaching.tsx +++ b/server/emails/templates/NotifyUsageLimitApproaching.tsx @@ -19,7 +19,13 @@ interface Props { billingLink: string; // Link to billing page } -export const NotifyUsageLimitApproaching = ({ email, limitName, currentUsage, usageLimit, billingLink }: Props) => { +export const NotifyUsageLimitApproaching = ({ + email, + limitName, + currentUsage, + usageLimit, + billingLink +}: Props) => { const previewText = `Your usage for ${limitName} is approaching the limit.`; const usagePercentage = Math.round((currentUsage / usageLimit) * 100); @@ -37,23 +43,32 @@ export const NotifyUsageLimitApproaching = ({ email, limitName, currentUsage, us Hi there, - We wanted to let you know that your usage for {limitName} is approaching your plan limit. + We wanted to let you know that your usage for{" "} + {limitName} is approaching your + plan limit. - Current Usage: {currentUsage} of {usageLimit} ({usagePercentage}%) + Current Usage: {currentUsage} of{" "} + {usageLimit} ({usagePercentage}%) - Once you reach your limit, some functionality may be restricted or your sites may disconnect until you upgrade your plan or your usage resets. + Once you reach your limit, some functionality may be + restricted or your sites may disconnect until you + upgrade your plan or your usage resets. - To avoid any interruption to your service, we recommend upgrading your plan or monitoring your usage closely. You can upgrade your plan here. + To avoid any interruption to your service, we + recommend upgrading your plan or monitoring your + usage closely. You can{" "} + upgrade your plan here. - If you have any questions or need assistance, please don't hesitate to reach out to our support team. + If you have any questions or need assistance, please + don't hesitate to reach out to our support team. diff --git a/server/emails/templates/NotifyUsageLimitReached.tsx b/server/emails/templates/NotifyUsageLimitReached.tsx index 783d1b0e..59841670 100644 --- a/server/emails/templates/NotifyUsageLimitReached.tsx +++ b/server/emails/templates/NotifyUsageLimitReached.tsx @@ -19,7 +19,13 @@ interface Props { billingLink: string; // Link to billing page } -export const NotifyUsageLimitReached = ({ email, limitName, currentUsage, usageLimit, billingLink }: Props) => { +export const NotifyUsageLimitReached = ({ + email, + limitName, + currentUsage, + usageLimit, + billingLink +}: Props) => { const previewText = `You've reached your ${limitName} usage limit - Action required`; const usagePercentage = Math.round((currentUsage / usageLimit) * 100); @@ -32,30 +38,48 @@ export const NotifyUsageLimitReached = ({ email, limitName, currentUsage, usageL - Usage Limit Reached - Action Required + + Usage Limit Reached - Action Required + Hi there, - You have reached your usage limit for {limitName}. + You have reached your usage limit for{" "} + {limitName}. - Current Usage: {currentUsage} of {usageLimit} ({usagePercentage}%) + Current Usage: {currentUsage} of{" "} + {usageLimit} ({usagePercentage}%) - Important: Your functionality may now be restricted and your sites may disconnect until you either upgrade your plan or your usage resets. To prevent any service interruption, immediate action is recommended. + Important: Your functionality may + now be restricted and your sites may disconnect + until you either upgrade your plan or your usage + resets. To prevent any service interruption, + immediate action is recommended. What you can do: -
Upgrade your plan immediately to restore full functionality -
• Monitor your usage to stay within limits in the future +
•{" "} + + Upgrade your plan immediately + {" "} + to restore full functionality +
• Monitor your usage to stay within limits in + the future
- If you have any questions or need immediate assistance, please contact our support team right away. + If you have any questions or need immediate + assistance, please contact our support team right + away. diff --git a/server/integrationApiServer.ts b/server/integrationApiServer.ts index 3416004c..0ef0c0af 100644 --- a/server/integrationApiServer.ts +++ b/server/integrationApiServer.ts @@ -5,7 +5,7 @@ import config from "@server/lib/config"; import logger from "@server/logger"; import { errorHandlerMiddleware, - notFoundMiddleware, + notFoundMiddleware } from "@server/middlewares"; import { authenticated, unauthenticated } from "#dynamic/routers/integration"; import { logIncomingMiddleware } from "./middlewares/logIncoming"; diff --git a/server/lib/billing/features.ts b/server/lib/billing/features.ts index b72543cc..d074894a 100644 --- a/server/lib/billing/features.ts +++ b/server/lib/billing/features.ts @@ -25,16 +25,22 @@ export const FeatureMeterIdsSandbox: Record = { }; export function getFeatureMeterId(featureId: FeatureId): string { - if (process.env.ENVIRONMENT == "prod" && process.env.SANDBOX_MODE !== "true") { + if ( + process.env.ENVIRONMENT == "prod" && + process.env.SANDBOX_MODE !== "true" + ) { return FeatureMeterIds[featureId]; } else { return FeatureMeterIdsSandbox[featureId]; } } -export function getFeatureIdByMetricId(metricId: string): FeatureId | undefined { - return (Object.entries(FeatureMeterIds) as [FeatureId, string][]) - .find(([_, v]) => v === metricId)?.[0]; +export function getFeatureIdByMetricId( + metricId: string +): FeatureId | undefined { + return (Object.entries(FeatureMeterIds) as [FeatureId, string][]).find( + ([_, v]) => v === metricId + )?.[0]; } export type FeaturePriceSet = { @@ -43,7 +49,8 @@ export type FeaturePriceSet = { [FeatureId.DOMAINS]?: string; // Optional since domains are not billed }; -export const standardFeaturePriceSet: FeaturePriceSet = { // Free tier matches the freeLimitSet +export const standardFeaturePriceSet: FeaturePriceSet = { + // Free tier matches the freeLimitSet [FeatureId.SITE_UPTIME]: "price_1RrQc4D3Ee2Ir7WmaJGZ3MtF", [FeatureId.USERS]: "price_1RrQeJD3Ee2Ir7WmgveP3xea", [FeatureId.EGRESS_DATA_MB]: "price_1RrQXFD3Ee2Ir7WmvGDlgxQk", @@ -51,7 +58,8 @@ export const standardFeaturePriceSet: FeaturePriceSet = { // Free tier matches t [FeatureId.REMOTE_EXIT_NODES]: "price_1S46weD3Ee2Ir7Wm94KEHI4h" }; -export const standardFeaturePriceSetSandbox: FeaturePriceSet = { // Free tier matches the freeLimitSet +export const standardFeaturePriceSetSandbox: FeaturePriceSet = { + // Free tier matches the freeLimitSet [FeatureId.SITE_UPTIME]: "price_1RefFBDCpkOb237BPrKZ8IEU", [FeatureId.USERS]: "price_1ReNa4DCpkOb237Bc67G5muF", [FeatureId.EGRESS_DATA_MB]: "price_1Rfp9LDCpkOb237BwuN5Oiu0", @@ -60,15 +68,20 @@ export const standardFeaturePriceSetSandbox: FeaturePriceSet = { // Free tier ma }; export function getStandardFeaturePriceSet(): FeaturePriceSet { - if (process.env.ENVIRONMENT == "prod" && process.env.SANDBOX_MODE !== "true") { + if ( + process.env.ENVIRONMENT == "prod" && + process.env.SANDBOX_MODE !== "true" + ) { return standardFeaturePriceSet; } else { return standardFeaturePriceSetSandbox; } } -export function getLineItems(featurePriceSet: FeaturePriceSet): Stripe.Checkout.SessionCreateParams.LineItem[] { +export function getLineItems( + featurePriceSet: FeaturePriceSet +): Stripe.Checkout.SessionCreateParams.LineItem[] { return Object.entries(featurePriceSet).map(([featureId, priceId]) => ({ - price: priceId, + price: priceId })); -} \ No newline at end of file +} diff --git a/server/lib/billing/index.ts b/server/lib/billing/index.ts index 6c3ef792..54c9ee2e 100644 --- a/server/lib/billing/index.ts +++ b/server/lib/billing/index.ts @@ -2,4 +2,4 @@ export * from "./limitSet"; export * from "./features"; export * from "./limitsService"; export * from "./getOrgTierData"; -export * from "./createCustomer"; \ No newline at end of file +export * from "./createCustomer"; diff --git a/server/lib/billing/limitSet.ts b/server/lib/billing/limitSet.ts index 153d8ae8..820b121a 100644 --- a/server/lib/billing/limitSet.ts +++ b/server/lib/billing/limitSet.ts @@ -12,7 +12,7 @@ export const sandboxLimitSet: LimitSet = { [FeatureId.USERS]: { value: 1, description: "Sandbox limit" }, [FeatureId.EGRESS_DATA_MB]: { value: 1000, description: "Sandbox limit" }, // 1 GB [FeatureId.DOMAINS]: { value: 0, description: "Sandbox limit" }, - [FeatureId.REMOTE_EXIT_NODES]: { value: 0, description: "Sandbox limit" }, + [FeatureId.REMOTE_EXIT_NODES]: { value: 0, description: "Sandbox limit" } }; export const freeLimitSet: LimitSet = { @@ -29,7 +29,7 @@ export const freeLimitSet: LimitSet = { export const subscribedLimitSet: LimitSet = { [FeatureId.SITE_UPTIME]: { value: 2232000, - description: "Contact us to increase soft limit.", + description: "Contact us to increase soft limit." }, // 50 sites up for 31 days [FeatureId.USERS]: { value: 150, @@ -38,7 +38,7 @@ export const subscribedLimitSet: LimitSet = { [FeatureId.EGRESS_DATA_MB]: { value: 12000000, description: "Contact us to increase soft limit." - }, // 12000 GB + }, // 12000 GB [FeatureId.DOMAINS]: { value: 25, description: "Contact us to increase soft limit." diff --git a/server/lib/billing/tiers.ts b/server/lib/billing/tiers.ts index 6ccf8898..ae49a48f 100644 --- a/server/lib/billing/tiers.ts +++ b/server/lib/billing/tiers.ts @@ -1,22 +1,32 @@ export enum TierId { - STANDARD = "standard", + STANDARD = "standard" } export type TierPriceSet = { [key in TierId]: string; }; -export const tierPriceSet: TierPriceSet = { // Free tier matches the freeLimitSet - [TierId.STANDARD]: "price_1RrQ9cD3Ee2Ir7Wmqdy3KBa0", +export const tierPriceSet: TierPriceSet = { + // Free tier matches the freeLimitSet + [TierId.STANDARD]: "price_1RrQ9cD3Ee2Ir7Wmqdy3KBa0" }; -export const tierPriceSetSandbox: TierPriceSet = { // Free tier matches the freeLimitSet +export const tierPriceSetSandbox: TierPriceSet = { + // Free tier matches the freeLimitSet // when matching tier the keys closer to 0 index are matched first so list the tiers in descending order of value - [TierId.STANDARD]: "price_1RrAYJDCpkOb237By2s1P32m", + [TierId.STANDARD]: "price_1RrAYJDCpkOb237By2s1P32m" }; -export function getTierPriceSet(environment?: string, sandbox_mode?: boolean): TierPriceSet { - if ((process.env.ENVIRONMENT == "prod" && process.env.SANDBOX_MODE !== "true") || (environment === "prod" && sandbox_mode !== true)) { // THIS GETS LOADED CLIENT SIDE AND SERVER SIDE +export function getTierPriceSet( + environment?: string, + sandbox_mode?: boolean +): TierPriceSet { + if ( + (process.env.ENVIRONMENT == "prod" && + process.env.SANDBOX_MODE !== "true") || + (environment === "prod" && sandbox_mode !== true) + ) { + // THIS GETS LOADED CLIENT SIDE AND SERVER SIDE return tierPriceSet; } else { return tierPriceSetSandbox; diff --git a/server/lib/billing/usageService.ts b/server/lib/billing/usageService.ts index 8e6f5e9c..0fde8eba 100644 --- a/server/lib/billing/usageService.ts +++ b/server/lib/billing/usageService.ts @@ -19,7 +19,7 @@ import logger from "@server/logger"; import { sendToClient } from "#dynamic/routers/ws"; import { build } from "@server/build"; import { s3Client } from "@server/lib/s3"; -import cache from "@server/lib/cache"; +import cache from "@server/lib/cache"; interface StripeEvent { identifier?: string; diff --git a/server/lib/blueprints/applyNewtDockerBlueprint.ts b/server/lib/blueprints/applyNewtDockerBlueprint.ts index 0fe7c3fe..f27cc05b 100644 --- a/server/lib/blueprints/applyNewtDockerBlueprint.ts +++ b/server/lib/blueprints/applyNewtDockerBlueprint.ts @@ -34,7 +34,10 @@ export async function applyNewtDockerBlueprint( return; } - if (isEmptyObject(blueprint["proxy-resources"]) && isEmptyObject(blueprint["client-resources"])) { + if ( + isEmptyObject(blueprint["proxy-resources"]) && + isEmptyObject(blueprint["client-resources"]) + ) { return; } diff --git a/server/lib/blueprints/parseDockerContainers.ts b/server/lib/blueprints/parseDockerContainers.ts index 1510e6e1..f2cdcfa2 100644 --- a/server/lib/blueprints/parseDockerContainers.ts +++ b/server/lib/blueprints/parseDockerContainers.ts @@ -84,12 +84,20 @@ export function processContainerLabels(containers: Container[]): { // Process proxy resources if (Object.keys(proxyResourceLabels).length > 0) { - processResourceLabels(proxyResourceLabels, container, result["proxy-resources"]); + processResourceLabels( + proxyResourceLabels, + container, + result["proxy-resources"] + ); } // Process client resources if (Object.keys(clientResourceLabels).length > 0) { - processResourceLabels(clientResourceLabels, container, result["client-resources"]); + processResourceLabels( + clientResourceLabels, + container, + result["client-resources"] + ); } }); @@ -161,8 +169,7 @@ function processResourceLabels( const finalTarget = { ...target }; if (!finalTarget.hostname) { finalTarget.hostname = - container.name || - container.hostname; + container.name || container.hostname; } if (!finalTarget.port) { const containerPort = diff --git a/server/lib/blueprints/types.ts b/server/lib/blueprints/types.ts index 9a184a1f..23e2176f 100644 --- a/server/lib/blueprints/types.ts +++ b/server/lib/blueprints/types.ts @@ -312,7 +312,7 @@ export const ConfigSchema = z }; delete (data as any)["public-resources"]; } - + // Merge private-resources into client-resources if (data["private-resources"]) { data["client-resources"] = { @@ -321,10 +321,13 @@ export const ConfigSchema = z }; delete (data as any)["private-resources"]; } - + return data as { "proxy-resources": Record>; - "client-resources": Record>; + "client-resources": Record< + string, + z.infer + >; sites: Record>; }; }) diff --git a/server/lib/cache.ts b/server/lib/cache.ts index efa7d201..82c80280 100644 --- a/server/lib/cache.ts +++ b/server/lib/cache.ts @@ -2,4 +2,4 @@ import NodeCache from "node-cache"; export const cache = new NodeCache({ stdTTL: 3600, checkperiod: 120 }); -export default cache; \ No newline at end of file +export default cache; diff --git a/server/lib/calculateUserClientsForOrgs.ts b/server/lib/calculateUserClientsForOrgs.ts index 9cdae547..ac3d719f 100644 --- a/server/lib/calculateUserClientsForOrgs.ts +++ b/server/lib/calculateUserClientsForOrgs.ts @@ -166,7 +166,10 @@ export async function calculateUserClientsForOrgs( ]; // Get next available subnet - const newSubnet = await getNextAvailableClientSubnet(orgId, transaction); + const newSubnet = await getNextAvailableClientSubnet( + orgId, + transaction + ); if (!newSubnet) { logger.warn( `Skipping org ${orgId} for OLM ${olm.olmId} (user ${userId}): no available subnet found` diff --git a/server/lib/certificates.ts b/server/lib/certificates.ts index a6c51c96..f5860ff3 100644 --- a/server/lib/certificates.ts +++ b/server/lib/certificates.ts @@ -1,4 +1,6 @@ -export async function getValidCertificatesForDomains(domains: Set): Promise< +export async function getValidCertificatesForDomains( + domains: Set +): Promise< Array<{ id: number; domain: string; @@ -10,4 +12,4 @@ export async function getValidCertificatesForDomains(domains: Set): Prom }> > { return []; // stub -} \ No newline at end of file +} diff --git a/server/lib/cleanupLogs.test.ts b/server/lib/cleanupLogs.test.ts index a65e7b01..dc9326e1 100644 --- a/server/lib/cleanupLogs.test.ts +++ b/server/lib/cleanupLogs.test.ts @@ -7,7 +7,10 @@ function dateToTimestamp(dateStr: string): number { // Testable version of calculateCutoffTimestamp that accepts a "now" timestamp // This matches the logic in cleanupLogs.ts but allows injecting the current time -function calculateCutoffTimestampWithNow(retentionDays: number, nowTimestamp: number): number { +function calculateCutoffTimestampWithNow( + retentionDays: number, + nowTimestamp: number +): number { if (retentionDays === 9001) { // Special case: data is erased at the end of the year following the year it was generated // This means we delete logs from 2 years ago or older (logs from year Y are deleted after Dec 31 of year Y+1) @@ -28,7 +31,7 @@ function testCalculateCutoffTimestamp() { { const now = dateToTimestamp("2025-12-06T12:00:00Z"); const result = calculateCutoffTimestampWithNow(30, now); - const expected = now - (30 * 24 * 60 * 60); + const expected = now - 30 * 24 * 60 * 60; assertEquals(result, expected, "30 days retention calculation failed"); } @@ -36,7 +39,7 @@ function testCalculateCutoffTimestamp() { { const now = dateToTimestamp("2025-06-15T00:00:00Z"); const result = calculateCutoffTimestampWithNow(90, now); - const expected = now - (90 * 24 * 60 * 60); + const expected = now - 90 * 24 * 60 * 60; assertEquals(result, expected, "90 days retention calculation failed"); } @@ -48,7 +51,11 @@ function testCalculateCutoffTimestamp() { const now = dateToTimestamp("2025-12-06T12:00:00Z"); const result = calculateCutoffTimestampWithNow(9001, now); const expected = dateToTimestamp("2024-01-01T00:00:00Z"); - assertEquals(result, expected, "9001 retention (Dec 2025) - should cutoff at Jan 1, 2024"); + assertEquals( + result, + expected, + "9001 retention (Dec 2025) - should cutoff at Jan 1, 2024" + ); } // Test 4: Special case 9001 - January 2026 @@ -58,7 +65,11 @@ function testCalculateCutoffTimestamp() { const now = dateToTimestamp("2026-01-15T12:00:00Z"); const result = calculateCutoffTimestampWithNow(9001, now); const expected = dateToTimestamp("2025-01-01T00:00:00Z"); - assertEquals(result, expected, "9001 retention (Jan 2026) - should cutoff at Jan 1, 2025"); + assertEquals( + result, + expected, + "9001 retention (Jan 2026) - should cutoff at Jan 1, 2025" + ); } // Test 5: Special case 9001 - December 31, 2025 at 23:59:59 UTC @@ -68,7 +79,11 @@ function testCalculateCutoffTimestamp() { const now = dateToTimestamp("2025-12-31T23:59:59Z"); const result = calculateCutoffTimestampWithNow(9001, now); const expected = dateToTimestamp("2024-01-01T00:00:00Z"); - assertEquals(result, expected, "9001 retention (Dec 31, 2025 23:59:59) - should cutoff at Jan 1, 2024"); + assertEquals( + result, + expected, + "9001 retention (Dec 31, 2025 23:59:59) - should cutoff at Jan 1, 2024" + ); } // Test 6: Special case 9001 - January 1, 2026 at 00:00:01 UTC @@ -78,7 +93,11 @@ function testCalculateCutoffTimestamp() { const now = dateToTimestamp("2026-01-01T00:00:01Z"); const result = calculateCutoffTimestampWithNow(9001, now); const expected = dateToTimestamp("2025-01-01T00:00:00Z"); - assertEquals(result, expected, "9001 retention (Jan 1, 2026 00:00:01) - should cutoff at Jan 1, 2025"); + assertEquals( + result, + expected, + "9001 retention (Jan 1, 2026 00:00:01) - should cutoff at Jan 1, 2025" + ); } // Test 7: Special case 9001 - Mid year 2025 @@ -87,7 +106,11 @@ function testCalculateCutoffTimestamp() { const now = dateToTimestamp("2025-06-15T12:00:00Z"); const result = calculateCutoffTimestampWithNow(9001, now); const expected = dateToTimestamp("2024-01-01T00:00:00Z"); - assertEquals(result, expected, "9001 retention (mid 2025) - should cutoff at Jan 1, 2024"); + assertEquals( + result, + expected, + "9001 retention (mid 2025) - should cutoff at Jan 1, 2024" + ); } // Test 8: Special case 9001 - Early 2024 @@ -96,14 +119,18 @@ function testCalculateCutoffTimestamp() { const now = dateToTimestamp("2024-02-01T12:00:00Z"); const result = calculateCutoffTimestampWithNow(9001, now); const expected = dateToTimestamp("2023-01-01T00:00:00Z"); - assertEquals(result, expected, "9001 retention (early 2024) - should cutoff at Jan 1, 2023"); + assertEquals( + result, + expected, + "9001 retention (early 2024) - should cutoff at Jan 1, 2023" + ); } // Test 9: 1 day retention { const now = dateToTimestamp("2025-12-06T12:00:00Z"); const result = calculateCutoffTimestampWithNow(1, now); - const expected = now - (1 * 24 * 60 * 60); + const expected = now - 1 * 24 * 60 * 60; assertEquals(result, expected, "1 day retention calculation failed"); } @@ -111,7 +138,7 @@ function testCalculateCutoffTimestamp() { { const now = dateToTimestamp("2025-12-06T12:00:00Z"); const result = calculateCutoffTimestampWithNow(365, now); - const expected = now - (365 * 24 * 60 * 60); + const expected = now - 365 * 24 * 60 * 60; assertEquals(result, expected, "365 days retention calculation failed"); } @@ -123,11 +150,19 @@ function testCalculateCutoffTimestamp() { const cutoff = calculateCutoffTimestampWithNow(9001, now); const logFromDec2023 = dateToTimestamp("2023-12-31T23:59:59Z"); const logFromJan2024 = dateToTimestamp("2024-01-01T00:00:00Z"); - + // Log from Dec 2023 should be before cutoff (deleted) - assertEquals(logFromDec2023 < cutoff, true, "Log from Dec 2023 should be deleted"); + assertEquals( + logFromDec2023 < cutoff, + true, + "Log from Dec 2023 should be deleted" + ); // Log from Jan 2024 should be at or after cutoff (kept) - assertEquals(logFromJan2024 >= cutoff, true, "Log from Jan 2024 should be kept"); + assertEquals( + logFromJan2024 >= cutoff, + true, + "Log from Jan 2024 should be kept" + ); } // Test 12: Verify 9001 in 2026 - logs from 2024 should now be deleted @@ -136,11 +171,19 @@ function testCalculateCutoffTimestamp() { const cutoff = calculateCutoffTimestampWithNow(9001, now); const logFromDec2024 = dateToTimestamp("2024-12-31T23:59:59Z"); const logFromJan2025 = dateToTimestamp("2025-01-01T00:00:00Z"); - + // Log from Dec 2024 should be before cutoff (deleted) - assertEquals(logFromDec2024 < cutoff, true, "Log from Dec 2024 should be deleted in 2026"); + assertEquals( + logFromDec2024 < cutoff, + true, + "Log from Dec 2024 should be deleted in 2026" + ); // Log from Jan 2025 should be at or after cutoff (kept) - assertEquals(logFromJan2025 >= cutoff, true, "Log from Jan 2025 should be kept in 2026"); + assertEquals( + logFromJan2025 >= cutoff, + true, + "Log from Jan 2025 should be kept in 2026" + ); } // Test 13: Edge case - exactly at year boundary for 9001 @@ -149,7 +192,11 @@ function testCalculateCutoffTimestamp() { const now = dateToTimestamp("2025-01-01T00:00:00Z"); const result = calculateCutoffTimestampWithNow(9001, now); const expected = dateToTimestamp("2024-01-01T00:00:00Z"); - assertEquals(result, expected, "9001 retention (Jan 1, 2025 00:00:00) - should cutoff at Jan 1, 2024"); + assertEquals( + result, + expected, + "9001 retention (Jan 1, 2025 00:00:00) - should cutoff at Jan 1, 2024" + ); } // Test 14: Verify data from 2024 is kept throughout 2025 when using 9001 @@ -157,18 +204,29 @@ function testCalculateCutoffTimestamp() { { // Running in June 2025 const nowJune2025 = dateToTimestamp("2025-06-15T12:00:00Z"); - const cutoffJune2025 = calculateCutoffTimestampWithNow(9001, nowJune2025); + const cutoffJune2025 = calculateCutoffTimestampWithNow( + 9001, + nowJune2025 + ); const logFromJuly2024 = dateToTimestamp("2024-07-15T12:00:00Z"); - + // Log from July 2024 should be KEPT in June 2025 - assertEquals(logFromJuly2024 >= cutoffJune2025, true, "Log from July 2024 should be kept in June 2025"); - + assertEquals( + logFromJuly2024 >= cutoffJune2025, + true, + "Log from July 2024 should be kept in June 2025" + ); + // Running in January 2026 const nowJan2026 = dateToTimestamp("2026-01-15T12:00:00Z"); const cutoffJan2026 = calculateCutoffTimestampWithNow(9001, nowJan2026); - + // Log from July 2024 should be DELETED in January 2026 - assertEquals(logFromJuly2024 < cutoffJan2026, true, "Log from July 2024 should be deleted in Jan 2026"); + assertEquals( + logFromJuly2024 < cutoffJan2026, + true, + "Log from July 2024 should be deleted in Jan 2026" + ); } // Test 15: Verify the exact requirement - data from 2024 must be purged on December 31, 2025 @@ -176,16 +234,27 @@ function testCalculateCutoffTimestamp() { // On Jan 1, 2026 (now 2026), data from 2024 can be deleted { const logFromMid2024 = dateToTimestamp("2024-06-15T12:00:00Z"); - + // Dec 31, 2025 23:59:59 - still 2025, log should be kept const nowDec31_2025 = dateToTimestamp("2025-12-31T23:59:59Z"); - const cutoffDec31 = calculateCutoffTimestampWithNow(9001, nowDec31_2025); - assertEquals(logFromMid2024 >= cutoffDec31, true, "Log from mid-2024 should be kept on Dec 31, 2025"); - + const cutoffDec31 = calculateCutoffTimestampWithNow( + 9001, + nowDec31_2025 + ); + assertEquals( + logFromMid2024 >= cutoffDec31, + true, + "Log from mid-2024 should be kept on Dec 31, 2025" + ); + // Jan 1, 2026 00:00:00 - now 2026, log can be deleted const nowJan1_2026 = dateToTimestamp("2026-01-01T00:00:00Z"); const cutoffJan1 = calculateCutoffTimestampWithNow(9001, nowJan1_2026); - assertEquals(logFromMid2024 < cutoffJan1, true, "Log from mid-2024 should be deleted on Jan 1, 2026"); + assertEquals( + logFromMid2024 < cutoffJan1, + true, + "Log from mid-2024 should be deleted on Jan 1, 2026" + ); } console.log("All calculateCutoffTimestamp tests passed!"); diff --git a/server/lib/domainUtils.ts b/server/lib/domainUtils.ts index d043ca51..3562df68 100644 --- a/server/lib/domainUtils.ts +++ b/server/lib/domainUtils.ts @@ -4,18 +4,20 @@ import { eq, and } from "drizzle-orm"; import { subdomainSchema } from "@server/lib/schemas"; import { fromError } from "zod-validation-error"; -export type DomainValidationResult = { - success: true; - fullDomain: string; - subdomain: string | null; -} | { - success: false; - error: string; -}; +export type DomainValidationResult = + | { + success: true; + fullDomain: string; + subdomain: string | null; + } + | { + success: false; + error: string; + }; /** * Validates a domain and constructs the full domain based on domain type and subdomain. - * + * * @param domainId - The ID of the domain to validate * @param orgId - The organization ID to check domain access * @param subdomain - Optional subdomain to append (for ns and wildcard domains) @@ -34,7 +36,10 @@ export async function validateAndConstructDomain( .where(eq(domains.domainId, domainId)) .leftJoin( orgDomains, - and(eq(orgDomains.orgId, orgId), eq(orgDomains.domainId, domainId)) + and( + eq(orgDomains.orgId, orgId), + eq(orgDomains.domainId, domainId) + ) ); // Check if domain exists @@ -106,7 +111,7 @@ export async function validateAndConstructDomain( } catch (error) { return { success: false, - error: `An error occurred while validating domain: ${error instanceof Error ? error.message : 'Unknown error'}` + error: `An error occurred while validating domain: ${error instanceof Error ? error.message : "Unknown error"}` }; } } diff --git a/server/lib/encryption.ts b/server/lib/encryption.ts index 7959fa4b..79caecd1 100644 --- a/server/lib/encryption.ts +++ b/server/lib/encryption.ts @@ -1,39 +1,39 @@ -import crypto from 'crypto'; +import crypto from "crypto"; export function encryptData(data: string, key: Buffer): string { - const algorithm = 'aes-256-gcm'; - const iv = crypto.randomBytes(16); - const cipher = crypto.createCipheriv(algorithm, key, iv); - - let encrypted = cipher.update(data, 'utf8', 'hex'); - encrypted += cipher.final('hex'); - - const authTag = cipher.getAuthTag(); - - // Combine IV, auth tag, and encrypted data - return iv.toString('hex') + ':' + authTag.toString('hex') + ':' + encrypted; + const algorithm = "aes-256-gcm"; + const iv = crypto.randomBytes(16); + const cipher = crypto.createCipheriv(algorithm, key, iv); + + let encrypted = cipher.update(data, "utf8", "hex"); + encrypted += cipher.final("hex"); + + const authTag = cipher.getAuthTag(); + + // Combine IV, auth tag, and encrypted data + return iv.toString("hex") + ":" + authTag.toString("hex") + ":" + encrypted; } // Helper function to decrypt data (you'll need this to read certificates) export function decryptData(encryptedData: string, key: Buffer): string { - const algorithm = 'aes-256-gcm'; - const parts = encryptedData.split(':'); - - if (parts.length !== 3) { - throw new Error('Invalid encrypted data format'); - } - - const iv = Buffer.from(parts[0], 'hex'); - const authTag = Buffer.from(parts[1], 'hex'); - const encrypted = parts[2]; - - const decipher = crypto.createDecipheriv(algorithm, key, iv); - decipher.setAuthTag(authTag); - - let decrypted = decipher.update(encrypted, 'hex', 'utf8'); - decrypted += decipher.final('utf8'); - - return decrypted; + const algorithm = "aes-256-gcm"; + const parts = encryptedData.split(":"); + + if (parts.length !== 3) { + throw new Error("Invalid encrypted data format"); + } + + const iv = Buffer.from(parts[0], "hex"); + const authTag = Buffer.from(parts[1], "hex"); + const encrypted = parts[2]; + + const decipher = crypto.createDecipheriv(algorithm, key, iv); + decipher.setAuthTag(authTag); + + let decrypted = decipher.update(encrypted, "hex", "utf8"); + decrypted += decipher.final("utf8"); + + return decrypted; } -// openssl rand -hex 32 > config/encryption.key \ No newline at end of file +// openssl rand -hex 32 > config/encryption.key diff --git a/server/lib/exitNodes/getCurrentExitNodeId.ts b/server/lib/exitNodes/getCurrentExitNodeId.ts index d895ce42..1e5c10e3 100644 --- a/server/lib/exitNodes/getCurrentExitNodeId.ts +++ b/server/lib/exitNodes/getCurrentExitNodeId.ts @@ -30,4 +30,4 @@ export async function getCurrentExitNodeId(): Promise { } } return currentExitNodeId; -} \ No newline at end of file +} diff --git a/server/lib/exitNodes/index.ts b/server/lib/exitNodes/index.ts index ba30ccc2..d1477a68 100644 --- a/server/lib/exitNodes/index.ts +++ b/server/lib/exitNodes/index.ts @@ -1,4 +1,4 @@ export * from "./exitNodes"; export * from "./exitNodeComms"; export * from "./subnet"; -export * from "./getCurrentExitNodeId"; \ No newline at end of file +export * from "./getCurrentExitNodeId"; diff --git a/server/lib/exitNodes/subnet.ts b/server/lib/exitNodes/subnet.ts index c06f1d05..49e28bd5 100644 --- a/server/lib/exitNodes/subnet.ts +++ b/server/lib/exitNodes/subnet.ts @@ -27,4 +27,4 @@ export async function getNextAvailableSubnet(): Promise { "/" + subnet.split("/")[1]; return subnet; -} \ No newline at end of file +} diff --git a/server/lib/geoip.ts b/server/lib/geoip.ts index 5bc29ef9..8eea4d6f 100644 --- a/server/lib/geoip.ts +++ b/server/lib/geoip.ts @@ -30,4 +30,4 @@ export async function getCountryCodeForIp( } return; -} \ No newline at end of file +} diff --git a/server/lib/idp/generateRedirectUrl.ts b/server/lib/idp/generateRedirectUrl.ts index 077ac6f6..cf55e161 100644 --- a/server/lib/idp/generateRedirectUrl.ts +++ b/server/lib/idp/generateRedirectUrl.ts @@ -33,7 +33,11 @@ export async function generateOidcRedirectUrl( ) .limit(1); - if (res?.loginPage && res.loginPage.domainId && res.loginPage.fullDomain) { + if ( + res?.loginPage && + res.loginPage.domainId && + res.loginPage.fullDomain + ) { baseUrl = `${method}://${res.loginPage.fullDomain}`; } } diff --git a/server/lib/ip.test.ts b/server/lib/ip.test.ts index 67a2faaa..70436e05 100644 --- a/server/lib/ip.test.ts +++ b/server/lib/ip.test.ts @@ -4,7 +4,7 @@ import { assertEquals } from "@test/assert"; // Test cases function testFindNextAvailableCidr() { console.log("Running findNextAvailableCidr tests..."); - + // Test 0: Basic IPv4 allocation with a subnet in the wrong range { const existing = ["100.90.130.1/30", "100.90.128.4/30"]; @@ -23,7 +23,11 @@ function testFindNextAvailableCidr() { { const existing = ["10.0.0.0/16", "10.2.0.0/16"]; const result = findNextAvailableCidr(existing, 16, "10.0.0.0/8"); - assertEquals(result, "10.1.0.0/16", "Finding gap between allocations failed"); + assertEquals( + result, + "10.1.0.0/16", + "Finding gap between allocations failed" + ); } // Test 3: No available space @@ -33,7 +37,7 @@ function testFindNextAvailableCidr() { assertEquals(result, null, "No available space test failed"); } - // Test 4: Empty existing + // Test 4: Empty existing { const existing: string[] = []; const result = findNextAvailableCidr(existing, 30, "10.0.0.0/8"); @@ -137,4 +141,4 @@ try { } catch (error) { console.error("Test failed:", error); process.exit(1); -} \ No newline at end of file +} diff --git a/server/lib/ip.ts b/server/lib/ip.ts index 3949a5f6..f3dabf43 100644 --- a/server/lib/ip.ts +++ b/server/lib/ip.ts @@ -247,7 +247,10 @@ export async function getNextAvailableClientSubnet( orgId: string, transaction: Transaction | typeof db = db ): Promise { - const [org] = await transaction.select().from(orgs).where(eq(orgs.orgId, orgId)); + const [org] = await transaction + .select() + .from(orgs) + .where(eq(orgs.orgId, orgId)); if (!org) { throw new Error(`Organization with ID ${orgId} not found`); @@ -360,7 +363,9 @@ export async function getNextAvailableOrgSubnet(): Promise { return subnet; } -export function generateRemoteSubnets(allSiteResources: SiteResource[]): string[] { +export function generateRemoteSubnets( + allSiteResources: SiteResource[] +): string[] { const remoteSubnets = allSiteResources .filter((sr) => { if (sr.mode === "cidr") return true; diff --git a/server/lib/logAccessAudit.ts b/server/lib/logAccessAudit.ts index 82ddda67..5f3601da 100644 --- a/server/lib/logAccessAudit.ts +++ b/server/lib/logAccessAudit.ts @@ -14,4 +14,4 @@ export async function logAccessAudit(data: { requestIp?: string; }) { return; -} \ No newline at end of file +} diff --git a/server/lib/readConfigFile.ts b/server/lib/readConfigFile.ts index ac819619..fe610663 100644 --- a/server/lib/readConfigFile.ts +++ b/server/lib/readConfigFile.ts @@ -14,7 +14,8 @@ export const configSchema = z .object({ app: z .object({ - dashboard_url: z.url() + dashboard_url: z + .url() .pipe(z.url()) .transform((url) => url.toLowerCase()) .optional(), @@ -255,7 +256,10 @@ export const configSchema = z .object({ block_size: z.number().positive().gt(0).optional().default(24), subnet_group: z.string().optional().default("100.90.128.0/24"), - utility_subnet_group: z.string().optional().default("100.96.128.0/24") //just hardcode this for now as well + utility_subnet_group: z + .string() + .optional() + .default("100.96.128.0/24") //just hardcode this for now as well }) .optional() .default({ diff --git a/server/lib/rebuildClientAssociations.ts b/server/lib/rebuildClientAssociations.ts index 9095b9bc..8729edaf 100644 --- a/server/lib/rebuildClientAssociations.ts +++ b/server/lib/rebuildClientAssociations.ts @@ -32,7 +32,7 @@ import logger from "@server/logger"; import { generateAliasConfig, generateRemoteSubnets, - generateSubnetProxyTargets, + generateSubnetProxyTargets } from "@server/lib/ip"; import { addPeerData, @@ -109,21 +109,22 @@ export async function getClientSiteResourceAccess( const directClientIds = allClientSiteResources.map((row) => row.clientId); // Get full client details for directly associated clients - const directClients = directClientIds.length > 0 - ? await trx - .select({ - clientId: clients.clientId, - pubKey: clients.pubKey, - subnet: clients.subnet - }) - .from(clients) - .where( - and( - inArray(clients.clientId, directClientIds), - eq(clients.orgId, siteResource.orgId) // filter by org to prevent cross-org associations + const directClients = + directClientIds.length > 0 + ? await trx + .select({ + clientId: clients.clientId, + pubKey: clients.pubKey, + subnet: clients.subnet + }) + .from(clients) + .where( + and( + inArray(clients.clientId, directClientIds), + eq(clients.orgId, siteResource.orgId) // filter by org to prevent cross-org associations + ) ) - ) - : []; + : []; // Merge user-based clients with directly associated clients const allClientsMap = new Map( @@ -731,9 +732,10 @@ async function handleSubnetProxyTargetUpdates( ); // Only remove remote subnet if no other resource uses the same destination - const remoteSubnetsToRemove = destinationStillInUse.length > 0 - ? [] - : generateRemoteSubnets([siteResource]); + const remoteSubnetsToRemove = + destinationStillInUse.length > 0 + ? [] + : generateRemoteSubnets([siteResource]); olmJobs.push( removePeerData( @@ -817,7 +819,10 @@ export async function rebuildClientAssociationsFromClient( .from(roleSiteResources) .innerJoin( siteResources, - eq(siteResources.siteResourceId, roleSiteResources.siteResourceId) + eq( + siteResources.siteResourceId, + roleSiteResources.siteResourceId + ) ) .where( and( @@ -1277,9 +1282,10 @@ async function handleMessagesForClientResources( ); // Only remove remote subnet if no other resource uses the same destination - const remoteSubnetsToRemove = destinationStillInUse.length > 0 - ? [] - : generateRemoteSubnets([resource]); + const remoteSubnetsToRemove = + destinationStillInUse.length > 0 + ? [] + : generateRemoteSubnets([resource]); // Remove peer data from olm olmJobs.push( diff --git a/server/lib/resend.ts b/server/lib/resend.ts index 0af039bb..0c21b1be 100644 --- a/server/lib/resend.ts +++ b/server/lib/resend.ts @@ -1,8 +1,8 @@ export enum AudienceIds { - SignUps = "", - Subscribed = "", - Churned = "", - Newsletter = "" + SignUps = "", + Subscribed = "", + Churned = "", + Newsletter = "" } let resend; diff --git a/server/lib/response.ts b/server/lib/response.ts index ae8461ba..fd8fa89f 100644 --- a/server/lib/response.ts +++ b/server/lib/response.ts @@ -3,14 +3,14 @@ import { Response } from "express"; export const response = ( res: Response, - { data, success, error, message, status }: ResponseT, + { data, success, error, message, status }: ResponseT ) => { return res.status(status).send({ data, success, error, message, - status, + status }); }; diff --git a/server/lib/s3.ts b/server/lib/s3.ts index 5fc3318f..17314ed7 100644 --- a/server/lib/s3.ts +++ b/server/lib/s3.ts @@ -1,5 +1,5 @@ import { S3Client } from "@aws-sdk/client-s3"; export const s3Client = new S3Client({ - region: process.env.S3_REGION || "us-east-1", + region: process.env.S3_REGION || "us-east-1" }); diff --git a/server/lib/serverIpService.ts b/server/lib/serverIpService.ts index 8c16fd43..7f423f9b 100644 --- a/server/lib/serverIpService.ts +++ b/server/lib/serverIpService.ts @@ -6,7 +6,7 @@ let serverIp: string | null = null; const services = [ "https://checkip.amazonaws.com", "https://ifconfig.io/ip", - "https://api.ipify.org", + "https://api.ipify.org" ]; export async function fetchServerIp() { @@ -17,7 +17,9 @@ export async function fetchServerIp() { logger.debug("Detected public IP: " + serverIp); return; } catch (err: any) { - console.warn(`Failed to fetch server IP from ${url}: ${err.message || err.code}`); + console.warn( + `Failed to fetch server IP from ${url}: ${err.message || err.code}` + ); } } diff --git a/server/lib/stoi.ts b/server/lib/stoi.ts index ebc789e6..3c869858 100644 --- a/server/lib/stoi.ts +++ b/server/lib/stoi.ts @@ -1,8 +1,7 @@ export default function stoi(val: any) { if (typeof val === "string") { - return parseInt(val); + return parseInt(val); + } else { + return val; } - else { - return val; - } -} \ No newline at end of file +} diff --git a/server/lib/traefik/TraefikConfigManager.ts b/server/lib/traefik/TraefikConfigManager.ts index 151e6517..92e08b6a 100644 --- a/server/lib/traefik/TraefikConfigManager.ts +++ b/server/lib/traefik/TraefikConfigManager.ts @@ -195,7 +195,9 @@ export class TraefikConfigManager { state.set(domain, { exists: certExists && keyExists, - lastModified: lastModified ? Math.floor(lastModified.getTime() / 1000) : null, + lastModified: lastModified + ? Math.floor(lastModified.getTime() / 1000) + : null, expiresAt, wildcard }); @@ -464,7 +466,9 @@ export class TraefikConfigManager { config.getRawConfig().traefik.site_types, build == "oss", // filter out the namespace domains in open source build != "oss", // generate the login pages on the cloud and hybrid, - build == "saas" ? false : config.getRawConfig().traefik.allow_raw_resources // dont allow raw resources on saas otherwise use config + build == "saas" + ? false + : config.getRawConfig().traefik.allow_raw_resources // dont allow raw resources on saas otherwise use config ); const domains = new Set(); @@ -788,7 +792,10 @@ export class TraefikConfigManager { // Store the certificate expiry time if (cert.expiresAt) { - const expiresAtPath = path.join(domainDir, ".expires_at"); + const expiresAtPath = path.join( + domainDir, + ".expires_at" + ); fs.writeFileSync( expiresAtPath, cert.expiresAt.toString(), diff --git a/server/lib/traefik/index.ts b/server/lib/traefik/index.ts index 5630028c..0fc483fa 100644 --- a/server/lib/traefik/index.ts +++ b/server/lib/traefik/index.ts @@ -1 +1 @@ -export * from "./getTraefikConfig"; \ No newline at end of file +export * from "./getTraefikConfig"; diff --git a/server/lib/traefik/traefikConfig.test.ts b/server/lib/traefik/traefikConfig.test.ts index 88e5da49..36ad4e68 100644 --- a/server/lib/traefik/traefikConfig.test.ts +++ b/server/lib/traefik/traefikConfig.test.ts @@ -2,234 +2,249 @@ import { assertEquals } from "@test/assert"; import { isDomainCoveredByWildcard } from "./TraefikConfigManager"; function runTests() { - console.log('Running wildcard domain coverage tests...'); - + console.log("Running wildcard domain coverage tests..."); + // Test case 1: Basic wildcard certificate at example.com const basicWildcardCerts = new Map([ - ['example.com', { exists: true, wildcard: true }] + ["example.com", { exists: true, wildcard: true }] ]); - + // Should match first-level subdomains assertEquals( - isDomainCoveredByWildcard('level1.example.com', basicWildcardCerts), + isDomainCoveredByWildcard("level1.example.com", basicWildcardCerts), true, - 'Wildcard cert at example.com should match level1.example.com' + "Wildcard cert at example.com should match level1.example.com" ); - + assertEquals( - isDomainCoveredByWildcard('api.example.com', basicWildcardCerts), + isDomainCoveredByWildcard("api.example.com", basicWildcardCerts), true, - 'Wildcard cert at example.com should match api.example.com' + "Wildcard cert at example.com should match api.example.com" ); - + assertEquals( - isDomainCoveredByWildcard('www.example.com', basicWildcardCerts), + isDomainCoveredByWildcard("www.example.com", basicWildcardCerts), true, - 'Wildcard cert at example.com should match www.example.com' + "Wildcard cert at example.com should match www.example.com" ); - + // Should match the root domain (exact match) assertEquals( - isDomainCoveredByWildcard('example.com', basicWildcardCerts), + isDomainCoveredByWildcard("example.com", basicWildcardCerts), true, - 'Wildcard cert at example.com should match example.com itself' + "Wildcard cert at example.com should match example.com itself" ); - + // Should NOT match second-level subdomains assertEquals( - isDomainCoveredByWildcard('level2.level1.example.com', basicWildcardCerts), + isDomainCoveredByWildcard( + "level2.level1.example.com", + basicWildcardCerts + ), false, - 'Wildcard cert at example.com should NOT match level2.level1.example.com' + "Wildcard cert at example.com should NOT match level2.level1.example.com" ); - + assertEquals( - isDomainCoveredByWildcard('deep.nested.subdomain.example.com', basicWildcardCerts), + isDomainCoveredByWildcard( + "deep.nested.subdomain.example.com", + basicWildcardCerts + ), false, - 'Wildcard cert at example.com should NOT match deep.nested.subdomain.example.com' + "Wildcard cert at example.com should NOT match deep.nested.subdomain.example.com" ); - + // Should NOT match different domains assertEquals( - isDomainCoveredByWildcard('test.otherdomain.com', basicWildcardCerts), + isDomainCoveredByWildcard("test.otherdomain.com", basicWildcardCerts), false, - 'Wildcard cert at example.com should NOT match test.otherdomain.com' + "Wildcard cert at example.com should NOT match test.otherdomain.com" ); - + assertEquals( - isDomainCoveredByWildcard('notexample.com', basicWildcardCerts), + isDomainCoveredByWildcard("notexample.com", basicWildcardCerts), false, - 'Wildcard cert at example.com should NOT match notexample.com' + "Wildcard cert at example.com should NOT match notexample.com" ); - + // Test case 2: Multiple wildcard certificates const multipleWildcardCerts = new Map([ - ['example.com', { exists: true, wildcard: true }], - ['test.org', { exists: true, wildcard: true }], - ['api.service.net', { exists: true, wildcard: true }] + ["example.com", { exists: true, wildcard: true }], + ["test.org", { exists: true, wildcard: true }], + ["api.service.net", { exists: true, wildcard: true }] ]); - + assertEquals( - isDomainCoveredByWildcard('app.example.com', multipleWildcardCerts), + isDomainCoveredByWildcard("app.example.com", multipleWildcardCerts), true, - 'Should match subdomain of first wildcard cert' + "Should match subdomain of first wildcard cert" ); - + assertEquals( - isDomainCoveredByWildcard('staging.test.org', multipleWildcardCerts), + isDomainCoveredByWildcard("staging.test.org", multipleWildcardCerts), true, - 'Should match subdomain of second wildcard cert' + "Should match subdomain of second wildcard cert" ); - + assertEquals( - isDomainCoveredByWildcard('v1.api.service.net', multipleWildcardCerts), + isDomainCoveredByWildcard("v1.api.service.net", multipleWildcardCerts), true, - 'Should match subdomain of third wildcard cert' + "Should match subdomain of third wildcard cert" ); - + assertEquals( - isDomainCoveredByWildcard('deep.nested.api.service.net', multipleWildcardCerts), + isDomainCoveredByWildcard( + "deep.nested.api.service.net", + multipleWildcardCerts + ), false, - 'Should NOT match multi-level subdomain of third wildcard cert' + "Should NOT match multi-level subdomain of third wildcard cert" ); - + // Test exact domain matches for multiple certs assertEquals( - isDomainCoveredByWildcard('example.com', multipleWildcardCerts), + isDomainCoveredByWildcard("example.com", multipleWildcardCerts), true, - 'Should match exact domain of first wildcard cert' + "Should match exact domain of first wildcard cert" ); - + assertEquals( - isDomainCoveredByWildcard('test.org', multipleWildcardCerts), + isDomainCoveredByWildcard("test.org", multipleWildcardCerts), true, - 'Should match exact domain of second wildcard cert' + "Should match exact domain of second wildcard cert" ); - + assertEquals( - isDomainCoveredByWildcard('api.service.net', multipleWildcardCerts), + isDomainCoveredByWildcard("api.service.net", multipleWildcardCerts), true, - 'Should match exact domain of third wildcard cert' + "Should match exact domain of third wildcard cert" ); - + // Test case 3: Non-wildcard certificates (should not match anything) const nonWildcardCerts = new Map([ - ['example.com', { exists: true, wildcard: false }], - ['specific.domain.com', { exists: true, wildcard: false }] + ["example.com", { exists: true, wildcard: false }], + ["specific.domain.com", { exists: true, wildcard: false }] ]); - + assertEquals( - isDomainCoveredByWildcard('sub.example.com', nonWildcardCerts), + isDomainCoveredByWildcard("sub.example.com", nonWildcardCerts), false, - 'Non-wildcard cert should not match subdomains' + "Non-wildcard cert should not match subdomains" ); - + assertEquals( - isDomainCoveredByWildcard('example.com', nonWildcardCerts), + isDomainCoveredByWildcard("example.com", nonWildcardCerts), false, - 'Non-wildcard cert should not match even exact domain via this function' + "Non-wildcard cert should not match even exact domain via this function" ); - + // Test case 4: Non-existent certificates (should not match) const nonExistentCerts = new Map([ - ['example.com', { exists: false, wildcard: true }], - ['missing.com', { exists: false, wildcard: true }] + ["example.com", { exists: false, wildcard: true }], + ["missing.com", { exists: false, wildcard: true }] ]); - + assertEquals( - isDomainCoveredByWildcard('sub.example.com', nonExistentCerts), + isDomainCoveredByWildcard("sub.example.com", nonExistentCerts), false, - 'Non-existent wildcard cert should not match' + "Non-existent wildcard cert should not match" ); - + // Test case 5: Edge cases with special domain names const specialDomainCerts = new Map([ - ['localhost', { exists: true, wildcard: true }], - ['127-0-0-1.nip.io', { exists: true, wildcard: true }], - ['xn--e1afmkfd.xn--p1ai', { exists: true, wildcard: true }] // IDN domain + ["localhost", { exists: true, wildcard: true }], + ["127-0-0-1.nip.io", { exists: true, wildcard: true }], + ["xn--e1afmkfd.xn--p1ai", { exists: true, wildcard: true }] // IDN domain ]); - + assertEquals( - isDomainCoveredByWildcard('app.localhost', specialDomainCerts), + isDomainCoveredByWildcard("app.localhost", specialDomainCerts), true, - 'Should match subdomain of localhost wildcard' + "Should match subdomain of localhost wildcard" ); - + assertEquals( - isDomainCoveredByWildcard('test.127-0-0-1.nip.io', specialDomainCerts), + isDomainCoveredByWildcard("test.127-0-0-1.nip.io", specialDomainCerts), true, - 'Should match subdomain of nip.io wildcard' + "Should match subdomain of nip.io wildcard" ); - + assertEquals( - isDomainCoveredByWildcard('sub.xn--e1afmkfd.xn--p1ai', specialDomainCerts), + isDomainCoveredByWildcard( + "sub.xn--e1afmkfd.xn--p1ai", + specialDomainCerts + ), true, - 'Should match subdomain of IDN wildcard' + "Should match subdomain of IDN wildcard" ); - + // Test case 6: Empty input and edge cases const emptyCerts = new Map(); - + assertEquals( - isDomainCoveredByWildcard('any.domain.com', emptyCerts), + isDomainCoveredByWildcard("any.domain.com", emptyCerts), false, - 'Empty certificate map should not match any domain' + "Empty certificate map should not match any domain" ); - + // Test case 7: Domains with single character components const singleCharCerts = new Map([ - ['a.com', { exists: true, wildcard: true }], - ['x.y.z', { exists: true, wildcard: true }] + ["a.com", { exists: true, wildcard: true }], + ["x.y.z", { exists: true, wildcard: true }] ]); - + assertEquals( - isDomainCoveredByWildcard('b.a.com', singleCharCerts), + isDomainCoveredByWildcard("b.a.com", singleCharCerts), true, - 'Should match single character subdomain' + "Should match single character subdomain" ); - + assertEquals( - isDomainCoveredByWildcard('w.x.y.z', singleCharCerts), + isDomainCoveredByWildcard("w.x.y.z", singleCharCerts), true, - 'Should match single character subdomain of multi-part domain' + "Should match single character subdomain of multi-part domain" ); - + assertEquals( - isDomainCoveredByWildcard('v.w.x.y.z', singleCharCerts), + isDomainCoveredByWildcard("v.w.x.y.z", singleCharCerts), false, - 'Should NOT match multi-level subdomain of single char domain' + "Should NOT match multi-level subdomain of single char domain" ); - + // Test case 8: Domains with numbers and hyphens const numericCerts = new Map([ - ['api-v2.service-1.com', { exists: true, wildcard: true }], - ['123.456.net', { exists: true, wildcard: true }] + ["api-v2.service-1.com", { exists: true, wildcard: true }], + ["123.456.net", { exists: true, wildcard: true }] ]); - + assertEquals( - isDomainCoveredByWildcard('staging.api-v2.service-1.com', numericCerts), + isDomainCoveredByWildcard("staging.api-v2.service-1.com", numericCerts), true, - 'Should match subdomain with hyphens and numbers' + "Should match subdomain with hyphens and numbers" ); - + assertEquals( - isDomainCoveredByWildcard('test.123.456.net', numericCerts), + isDomainCoveredByWildcard("test.123.456.net", numericCerts), true, - 'Should match subdomain with numeric components' + "Should match subdomain with numeric components" ); - + assertEquals( - isDomainCoveredByWildcard('deep.staging.api-v2.service-1.com', numericCerts), + isDomainCoveredByWildcard( + "deep.staging.api-v2.service-1.com", + numericCerts + ), false, - 'Should NOT match multi-level subdomain with hyphens and numbers' + "Should NOT match multi-level subdomain with hyphens and numbers" ); - - console.log('All wildcard domain coverage tests passed!'); + + console.log("All wildcard domain coverage tests passed!"); } // Run all tests try { runTests(); } catch (error) { - console.error('Test failed:', error); + console.error("Test failed:", error); process.exit(1); } diff --git a/server/lib/traefik/utils.ts b/server/lib/traefik/utils.ts index 37ebfa0b..ec0eae5b 100644 --- a/server/lib/traefik/utils.ts +++ b/server/lib/traefik/utils.ts @@ -31,12 +31,17 @@ export function validatePathRewriteConfig( } if (rewritePathType !== "stripPrefix") { - if ((rewritePath && !rewritePathType) || (!rewritePath && rewritePathType)) { - return { isValid: false, error: "Both rewritePath and rewritePathType must be specified together" }; + if ( + (rewritePath && !rewritePathType) || + (!rewritePath && rewritePathType) + ) { + return { + isValid: false, + error: "Both rewritePath and rewritePathType must be specified together" + }; } } - if (!rewritePath || !rewritePathType) { return { isValid: true }; } @@ -68,14 +73,14 @@ export function validatePathRewriteConfig( } } - // Additional validation for stripPrefix if (rewritePathType === "stripPrefix") { if (pathMatchType !== "prefix") { - logger.warn(`stripPrefix rewrite type is most effective with prefix path matching. Current match type: ${pathMatchType}`); + logger.warn( + `stripPrefix rewrite type is most effective with prefix path matching. Current match type: ${pathMatchType}` + ); } } return { isValid: true }; } - diff --git a/server/lib/validators.test.ts b/server/lib/validators.test.ts index e2043c74..c4c564cf 100644 --- a/server/lib/validators.test.ts +++ b/server/lib/validators.test.ts @@ -1,71 +1,247 @@ -import { isValidUrlGlobPattern } from "./validators"; +import { isValidUrlGlobPattern } from "./validators"; import { assertEquals } from "@test/assert"; function runTests() { - console.log('Running URL pattern validation tests...'); - + console.log("Running URL pattern validation tests..."); + // Test valid patterns - assertEquals(isValidUrlGlobPattern('simple'), true, 'Simple path segment should be valid'); - assertEquals(isValidUrlGlobPattern('simple/path'), true, 'Simple path with slash should be valid'); - assertEquals(isValidUrlGlobPattern('/leading/slash'), true, 'Path with leading slash should be valid'); - assertEquals(isValidUrlGlobPattern('path/'), true, 'Path with trailing slash should be valid'); - assertEquals(isValidUrlGlobPattern('path/*'), true, 'Path with wildcard segment should be valid'); - assertEquals(isValidUrlGlobPattern('*'), true, 'Single wildcard should be valid'); - assertEquals(isValidUrlGlobPattern('*/subpath'), true, 'Wildcard with subpath should be valid'); - assertEquals(isValidUrlGlobPattern('path/*/more'), true, 'Path with wildcard in the middle should be valid'); - + assertEquals( + isValidUrlGlobPattern("simple"), + true, + "Simple path segment should be valid" + ); + assertEquals( + isValidUrlGlobPattern("simple/path"), + true, + "Simple path with slash should be valid" + ); + assertEquals( + isValidUrlGlobPattern("/leading/slash"), + true, + "Path with leading slash should be valid" + ); + assertEquals( + isValidUrlGlobPattern("path/"), + true, + "Path with trailing slash should be valid" + ); + assertEquals( + isValidUrlGlobPattern("path/*"), + true, + "Path with wildcard segment should be valid" + ); + assertEquals( + isValidUrlGlobPattern("*"), + true, + "Single wildcard should be valid" + ); + assertEquals( + isValidUrlGlobPattern("*/subpath"), + true, + "Wildcard with subpath should be valid" + ); + assertEquals( + isValidUrlGlobPattern("path/*/more"), + true, + "Path with wildcard in the middle should be valid" + ); + // Test with special characters - assertEquals(isValidUrlGlobPattern('path-with-dash'), true, 'Path with dash should be valid'); - assertEquals(isValidUrlGlobPattern('path_with_underscore'), true, 'Path with underscore should be valid'); - assertEquals(isValidUrlGlobPattern('path.with.dots'), true, 'Path with dots should be valid'); - assertEquals(isValidUrlGlobPattern('path~with~tilde'), true, 'Path with tilde should be valid'); - assertEquals(isValidUrlGlobPattern('path!with!exclamation'), true, 'Path with exclamation should be valid'); - assertEquals(isValidUrlGlobPattern('path$with$dollar'), true, 'Path with dollar should be valid'); - assertEquals(isValidUrlGlobPattern('path&with&ersand'), true, 'Path with ampersand should be valid'); - assertEquals(isValidUrlGlobPattern("path'with'quote"), true, "Path with quote should be valid"); - assertEquals(isValidUrlGlobPattern('path(with)parentheses'), true, 'Path with parentheses should be valid'); - assertEquals(isValidUrlGlobPattern('path+with+plus'), true, 'Path with plus should be valid'); - assertEquals(isValidUrlGlobPattern('path,with,comma'), true, 'Path with comma should be valid'); - assertEquals(isValidUrlGlobPattern('path;with;semicolon'), true, 'Path with semicolon should be valid'); - assertEquals(isValidUrlGlobPattern('path=with=equals'), true, 'Path with equals should be valid'); - assertEquals(isValidUrlGlobPattern('path:with:colon'), true, 'Path with colon should be valid'); - assertEquals(isValidUrlGlobPattern('path@with@at'), true, 'Path with at should be valid'); - + assertEquals( + isValidUrlGlobPattern("path-with-dash"), + true, + "Path with dash should be valid" + ); + assertEquals( + isValidUrlGlobPattern("path_with_underscore"), + true, + "Path with underscore should be valid" + ); + assertEquals( + isValidUrlGlobPattern("path.with.dots"), + true, + "Path with dots should be valid" + ); + assertEquals( + isValidUrlGlobPattern("path~with~tilde"), + true, + "Path with tilde should be valid" + ); + assertEquals( + isValidUrlGlobPattern("path!with!exclamation"), + true, + "Path with exclamation should be valid" + ); + assertEquals( + isValidUrlGlobPattern("path$with$dollar"), + true, + "Path with dollar should be valid" + ); + assertEquals( + isValidUrlGlobPattern("path&with&ersand"), + true, + "Path with ampersand should be valid" + ); + assertEquals( + isValidUrlGlobPattern("path'with'quote"), + true, + "Path with quote should be valid" + ); + assertEquals( + isValidUrlGlobPattern("path(with)parentheses"), + true, + "Path with parentheses should be valid" + ); + assertEquals( + isValidUrlGlobPattern("path+with+plus"), + true, + "Path with plus should be valid" + ); + assertEquals( + isValidUrlGlobPattern("path,with,comma"), + true, + "Path with comma should be valid" + ); + assertEquals( + isValidUrlGlobPattern("path;with;semicolon"), + true, + "Path with semicolon should be valid" + ); + assertEquals( + isValidUrlGlobPattern("path=with=equals"), + true, + "Path with equals should be valid" + ); + assertEquals( + isValidUrlGlobPattern("path:with:colon"), + true, + "Path with colon should be valid" + ); + assertEquals( + isValidUrlGlobPattern("path@with@at"), + true, + "Path with at should be valid" + ); + // Test with percent encoding - assertEquals(isValidUrlGlobPattern('path%20with%20spaces'), true, 'Path with percent-encoded spaces should be valid'); - assertEquals(isValidUrlGlobPattern('path%2Fwith%2Fencoded%2Fslashes'), true, 'Path with percent-encoded slashes should be valid'); - + assertEquals( + isValidUrlGlobPattern("path%20with%20spaces"), + true, + "Path with percent-encoded spaces should be valid" + ); + assertEquals( + isValidUrlGlobPattern("path%2Fwith%2Fencoded%2Fslashes"), + true, + "Path with percent-encoded slashes should be valid" + ); + // Test with wildcards in segments (the fixed functionality) - assertEquals(isValidUrlGlobPattern('padbootstrap*'), true, 'Path with wildcard at the end of segment should be valid'); - assertEquals(isValidUrlGlobPattern('pad*bootstrap'), true, 'Path with wildcard in the middle of segment should be valid'); - assertEquals(isValidUrlGlobPattern('*bootstrap'), true, 'Path with wildcard at the start of segment should be valid'); - assertEquals(isValidUrlGlobPattern('multiple*wildcards*in*segment'), true, 'Path with multiple wildcards in segment should be valid'); - assertEquals(isValidUrlGlobPattern('wild*/cards/in*/different/seg*ments'), true, 'Path with wildcards in different segments should be valid'); - + assertEquals( + isValidUrlGlobPattern("padbootstrap*"), + true, + "Path with wildcard at the end of segment should be valid" + ); + assertEquals( + isValidUrlGlobPattern("pad*bootstrap"), + true, + "Path with wildcard in the middle of segment should be valid" + ); + assertEquals( + isValidUrlGlobPattern("*bootstrap"), + true, + "Path with wildcard at the start of segment should be valid" + ); + assertEquals( + isValidUrlGlobPattern("multiple*wildcards*in*segment"), + true, + "Path with multiple wildcards in segment should be valid" + ); + assertEquals( + isValidUrlGlobPattern("wild*/cards/in*/different/seg*ments"), + true, + "Path with wildcards in different segments should be valid" + ); + // Test invalid patterns - assertEquals(isValidUrlGlobPattern(''), false, 'Empty string should be invalid'); - assertEquals(isValidUrlGlobPattern('//double/slash'), false, 'Path with double slash should be invalid'); - assertEquals(isValidUrlGlobPattern('path//end'), false, 'Path with double slash in the middle should be invalid'); - assertEquals(isValidUrlGlobPattern('invalid'), false, 'Path with invalid characters should be invalid'); - assertEquals(isValidUrlGlobPattern('invalid|char'), false, 'Path with invalid pipe character should be invalid'); - assertEquals(isValidUrlGlobPattern('invalid"char'), false, 'Path with invalid quote character should be invalid'); - assertEquals(isValidUrlGlobPattern('invalid`char'), false, 'Path with invalid backtick character should be invalid'); - assertEquals(isValidUrlGlobPattern('invalid^char'), false, 'Path with invalid caret character should be invalid'); - assertEquals(isValidUrlGlobPattern('invalid\\char'), false, 'Path with invalid backslash character should be invalid'); - assertEquals(isValidUrlGlobPattern('invalid[char]'), false, 'Path with invalid square brackets should be invalid'); - assertEquals(isValidUrlGlobPattern('invalid{char}'), false, 'Path with invalid curly braces should be invalid'); - + assertEquals( + isValidUrlGlobPattern(""), + false, + "Empty string should be invalid" + ); + assertEquals( + isValidUrlGlobPattern("//double/slash"), + false, + "Path with double slash should be invalid" + ); + assertEquals( + isValidUrlGlobPattern("path//end"), + false, + "Path with double slash in the middle should be invalid" + ); + assertEquals( + isValidUrlGlobPattern("invalid"), + false, + "Path with invalid characters should be invalid" + ); + assertEquals( + isValidUrlGlobPattern("invalid|char"), + false, + "Path with invalid pipe character should be invalid" + ); + assertEquals( + isValidUrlGlobPattern('invalid"char'), + false, + "Path with invalid quote character should be invalid" + ); + assertEquals( + isValidUrlGlobPattern("invalid`char"), + false, + "Path with invalid backtick character should be invalid" + ); + assertEquals( + isValidUrlGlobPattern("invalid^char"), + false, + "Path with invalid caret character should be invalid" + ); + assertEquals( + isValidUrlGlobPattern("invalid\\char"), + false, + "Path with invalid backslash character should be invalid" + ); + assertEquals( + isValidUrlGlobPattern("invalid[char]"), + false, + "Path with invalid square brackets should be invalid" + ); + assertEquals( + isValidUrlGlobPattern("invalid{char}"), + false, + "Path with invalid curly braces should be invalid" + ); + // Test invalid percent encoding - assertEquals(isValidUrlGlobPattern('invalid%2'), false, 'Path with incomplete percent encoding should be invalid'); - assertEquals(isValidUrlGlobPattern('invalid%GZ'), false, 'Path with invalid hex in percent encoding should be invalid'); - assertEquals(isValidUrlGlobPattern('invalid%'), false, 'Path with isolated percent sign should be invalid'); - - console.log('All tests passed!'); + assertEquals( + isValidUrlGlobPattern("invalid%2"), + false, + "Path with incomplete percent encoding should be invalid" + ); + assertEquals( + isValidUrlGlobPattern("invalid%GZ"), + false, + "Path with invalid hex in percent encoding should be invalid" + ); + assertEquals( + isValidUrlGlobPattern("invalid%"), + false, + "Path with isolated percent sign should be invalid" + ); + + console.log("All tests passed!"); } // Run all tests try { runTests(); } catch (error) { - console.error('Test failed:', error); -} \ No newline at end of file + console.error("Test failed:", error); +} diff --git a/server/lib/validators.ts b/server/lib/validators.ts index 5bdd7a14..b1efe8b3 100644 --- a/server/lib/validators.ts +++ b/server/lib/validators.ts @@ -2,7 +2,9 @@ import z from "zod"; import ipaddr from "ipaddr.js"; export function isValidCIDR(cidr: string): boolean { - return z.cidrv4().safeParse(cidr).success || z.cidrv6().safeParse(cidr).success; + return ( + z.cidrv4().safeParse(cidr).success || z.cidrv6().safeParse(cidr).success + ); } export function isValidIP(ip: string): boolean { @@ -69,11 +71,11 @@ export function isUrlValid(url: string | undefined) { if (!url) return true; // the link is optional in the schema so if it's empty it's valid var pattern = new RegExp( "^(https?:\\/\\/)?" + // protocol - "((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|" + // domain name - "((\\d{1,3}\\.){3}\\d{1,3}))" + // OR ip (v4) address - "(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*" + // port and path - "(\\?[;&a-z\\d%_.~+=-]*)?" + // query string - "(\\#[-a-z\\d_]*)?$", + "((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|" + // domain name + "((\\d{1,3}\\.){3}\\d{1,3}))" + // OR ip (v4) address + "(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*" + // port and path + "(\\?[;&a-z\\d%_.~+=-]*)?" + // query string + "(\\#[-a-z\\d_]*)?$", "i" ); return !!pattern.test(url); @@ -168,14 +170,14 @@ export function validateHeaders(headers: string): boolean { } export function isSecondLevelDomain(domain: string): boolean { - if (!domain || typeof domain !== 'string') { + if (!domain || typeof domain !== "string") { return false; } const trimmedDomain = domain.trim().toLowerCase(); // Split into parts - const parts = trimmedDomain.split('.'); + const parts = trimmedDomain.split("."); // Should have exactly 2 parts for a second-level domain (e.g., "example.com") if (parts.length !== 2) { diff --git a/server/middlewares/formatError.ts b/server/middlewares/formatError.ts index e96ff296..1e94c1f5 100644 --- a/server/middlewares/formatError.ts +++ b/server/middlewares/formatError.ts @@ -20,6 +20,6 @@ export const errorHandlerMiddleware: ErrorRequestHandler = ( error: true, message: error.message || "Internal Server Error", status: statusCode, - stack: process.env.ENVIRONMENT === "prod" ? null : error.stack, + stack: process.env.ENVIRONMENT === "prod" ? null : error.stack }); }; diff --git a/server/middlewares/getUserOrgs.ts b/server/middlewares/getUserOrgs.ts index 4d042307..d7905700 100644 --- a/server/middlewares/getUserOrgs.ts +++ b/server/middlewares/getUserOrgs.ts @@ -8,13 +8,13 @@ import HttpCode from "@server/types/HttpCode"; export async function getUserOrgs( req: Request, res: Response, - next: NextFunction, + next: NextFunction ) { const userId = req.user?.userId; // Assuming you have user information in the request if (!userId) { return next( - createHttpError(HttpCode.UNAUTHORIZED, "User not authenticated"), + createHttpError(HttpCode.UNAUTHORIZED, "User not authenticated") ); } @@ -22,7 +22,7 @@ export async function getUserOrgs( const userOrganizations = await db .select({ orgId: userOrgs.orgId, - roleId: userOrgs.roleId, + roleId: userOrgs.roleId }) .from(userOrgs) .where(eq(userOrgs.userId, userId)); @@ -38,8 +38,8 @@ export async function getUserOrgs( next( createHttpError( HttpCode.INTERNAL_SERVER_ERROR, - "Error retrieving user organizations", - ), + "Error retrieving user organizations" + ) ); } } diff --git a/server/middlewares/integration/index.ts b/server/middlewares/integration/index.ts index d44eb5a3..2e2e8ff0 100644 --- a/server/middlewares/integration/index.ts +++ b/server/middlewares/integration/index.ts @@ -12,4 +12,4 @@ export * from "./verifyAccessTokenAccess"; export * from "./verifyApiKeyIsRoot"; export * from "./verifyApiKeyApiKeyAccess"; export * from "./verifyApiKeyClientAccess"; -export * from "./verifyApiKeySiteResourceAccess"; \ No newline at end of file +export * from "./verifyApiKeySiteResourceAccess"; diff --git a/server/middlewares/integration/verifyAccessTokenAccess.ts b/server/middlewares/integration/verifyAccessTokenAccess.ts index f5ae8746..c9a84f18 100644 --- a/server/middlewares/integration/verifyAccessTokenAccess.ts +++ b/server/middlewares/integration/verifyAccessTokenAccess.ts @@ -97,7 +97,6 @@ export async function verifyApiKeyAccessTokenAccess( ); } - return next(); } catch (e) { return next( diff --git a/server/middlewares/integration/verifyApiKeyApiKeyAccess.ts b/server/middlewares/integration/verifyApiKeyApiKeyAccess.ts index ad5b7fc4..48fbbf87 100644 --- a/server/middlewares/integration/verifyApiKeyApiKeyAccess.ts +++ b/server/middlewares/integration/verifyApiKeyApiKeyAccess.ts @@ -11,7 +11,7 @@ export async function verifyApiKeyApiKeyAccess( next: NextFunction ) { try { - const {apiKey: callerApiKey } = req; + const { apiKey: callerApiKey } = req; const apiKeyId = req.params.apiKeyId || req.body.apiKeyId || req.query.apiKeyId; @@ -44,7 +44,10 @@ export async function verifyApiKeyApiKeyAccess( .select() .from(apiKeyOrg) .where( - and(eq(apiKeys.apiKeyId, callerApiKey.apiKeyId), eq(apiKeyOrg.orgId, orgId)) + and( + eq(apiKeys.apiKeyId, callerApiKey.apiKeyId), + eq(apiKeyOrg.orgId, orgId) + ) ) .limit(1); diff --git a/server/middlewares/integration/verifyApiKeySetResourceClients.ts b/server/middlewares/integration/verifyApiKeySetResourceClients.ts index cbcb33ae..704f3ef5 100644 --- a/server/middlewares/integration/verifyApiKeySetResourceClients.ts +++ b/server/middlewares/integration/verifyApiKeySetResourceClients.ts @@ -11,9 +11,12 @@ export async function verifyApiKeySetResourceClients( next: NextFunction ) { const apiKey = req.apiKey; - const singleClientId = req.params.clientId || req.body.clientId || req.query.clientId; + const singleClientId = + req.params.clientId || req.body.clientId || req.query.clientId; const { clientIds } = req.body; - const allClientIds = clientIds || (singleClientId ? [parseInt(singleClientId as string)] : []); + const allClientIds = + clientIds || + (singleClientId ? [parseInt(singleClientId as string)] : []); if (!apiKey) { return next( @@ -70,4 +73,3 @@ export async function verifyApiKeySetResourceClients( ); } } - diff --git a/server/middlewares/integration/verifyApiKeySetResourceUsers.ts b/server/middlewares/integration/verifyApiKeySetResourceUsers.ts index db73d134..0d44aa09 100644 --- a/server/middlewares/integration/verifyApiKeySetResourceUsers.ts +++ b/server/middlewares/integration/verifyApiKeySetResourceUsers.ts @@ -11,7 +11,8 @@ export async function verifyApiKeySetResourceUsers( next: NextFunction ) { const apiKey = req.apiKey; - const singleUserId = req.params.userId || req.body.userId || req.query.userId; + const singleUserId = + req.params.userId || req.body.userId || req.query.userId; const { userIds } = req.body; const allUserIds = userIds || (singleUserId ? [singleUserId] : []); diff --git a/server/middlewares/integration/verifyApiKeySiteResourceAccess.ts b/server/middlewares/integration/verifyApiKeySiteResourceAccess.ts index fb3d8287..1fc11c31 100644 --- a/server/middlewares/integration/verifyApiKeySiteResourceAccess.ts +++ b/server/middlewares/integration/verifyApiKeySiteResourceAccess.ts @@ -38,17 +38,12 @@ export async function verifyApiKeySiteResourceAccess( const [siteResource] = await db .select() .from(siteResources) - .where(and( - eq(siteResources.siteResourceId, siteResourceId) - )) + .where(and(eq(siteResources.siteResourceId, siteResourceId))) .limit(1); if (!siteResource) { return next( - createHttpError( - HttpCode.NOT_FOUND, - "Site resource not found" - ) + createHttpError(HttpCode.NOT_FOUND, "Site resource not found") ); } diff --git a/server/middlewares/notFound.ts b/server/middlewares/notFound.ts index 706796c9..8e0ab332 100644 --- a/server/middlewares/notFound.ts +++ b/server/middlewares/notFound.ts @@ -5,7 +5,7 @@ import HttpCode from "@server/types/HttpCode"; export function notFoundMiddleware( req: Request, res: Response, - next: NextFunction, + next: NextFunction ) { if (req.path.startsWith("/api")) { const message = `The requests url is not found - ${req.originalUrl}`; diff --git a/server/middlewares/requestTimeout.ts b/server/middlewares/requestTimeout.ts index 8b5852b7..b0f95a08 100644 --- a/server/middlewares/requestTimeout.ts +++ b/server/middlewares/requestTimeout.ts @@ -1,30 +1,32 @@ -import { Request, Response, NextFunction } from 'express'; -import logger from '@server/logger'; -import createHttpError from 'http-errors'; -import HttpCode from '@server/types/HttpCode'; +import { Request, Response, NextFunction } from "express"; +import logger from "@server/logger"; +import createHttpError from "http-errors"; +import HttpCode from "@server/types/HttpCode"; export function requestTimeoutMiddleware(timeoutMs: number = 30000) { return (req: Request, res: Response, next: NextFunction) => { // Set a timeout for the request const timeout = setTimeout(() => { if (!res.headersSent) { - logger.error(`Request timeout: ${req.method} ${req.url} from ${req.ip}`); + logger.error( + `Request timeout: ${req.method} ${req.url} from ${req.ip}` + ); return next( createHttpError( HttpCode.REQUEST_TIMEOUT, - 'Request timeout - operation took too long to complete' + "Request timeout - operation took too long to complete" ) ); } }, timeoutMs); // Clear timeout when response finishes - res.on('finish', () => { + res.on("finish", () => { clearTimeout(timeout); }); // Clear timeout when response closes - res.on('close', () => { + res.on("close", () => { clearTimeout(timeout); }); diff --git a/server/middlewares/verifySiteAccess.ts b/server/middlewares/verifySiteAccess.ts index 05fc6d27..98858cfb 100644 --- a/server/middlewares/verifySiteAccess.ts +++ b/server/middlewares/verifySiteAccess.ts @@ -76,7 +76,10 @@ export async function verifySiteAccess( .select() .from(userOrgs) .where( - and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, site.orgId)) + and( + eq(userOrgs.userId, userId), + eq(userOrgs.orgId, site.orgId) + ) ) .limit(1); req.userOrg = userOrgRole[0]; diff --git a/server/nextServer.ts b/server/nextServer.ts index 5302b9c8..b862a699 100644 --- a/server/nextServer.ts +++ b/server/nextServer.ts @@ -9,7 +9,10 @@ const nextPort = config.getRawConfig().server.next_port; export async function createNextServer() { // const app = next({ dev }); - const app = next({ dev: process.env.ENVIRONMENT !== "prod", turbopack: true }); + const app = next({ + dev: process.env.ENVIRONMENT !== "prod", + turbopack: true + }); const handle = app.getRequestHandler(); await app.prepare(); diff --git a/server/private/auth/sessions/remoteExitNode.ts b/server/private/auth/sessions/remoteExitNode.ts index fbb2ae1f..da1fb1aa 100644 --- a/server/private/auth/sessions/remoteExitNode.ts +++ b/server/private/auth/sessions/remoteExitNode.ts @@ -11,11 +11,14 @@ * This file is not licensed under the AGPLv3. */ -import { - encodeHexLowerCase, -} from "@oslojs/encoding"; +import { encodeHexLowerCase } from "@oslojs/encoding"; import { sha256 } from "@oslojs/crypto/sha2"; -import { RemoteExitNode, remoteExitNodes, remoteExitNodeSessions, RemoteExitNodeSession } from "@server/db"; +import { + RemoteExitNode, + remoteExitNodes, + remoteExitNodeSessions, + RemoteExitNodeSession +} from "@server/db"; import { db } from "@server/db"; import { eq } from "drizzle-orm"; @@ -23,30 +26,39 @@ export const EXPIRES = 1000 * 60 * 60 * 24 * 30; export async function createRemoteExitNodeSession( token: string, - remoteExitNodeId: string, + remoteExitNodeId: string ): Promise { const sessionId = encodeHexLowerCase( - sha256(new TextEncoder().encode(token)), + sha256(new TextEncoder().encode(token)) ); const session: RemoteExitNodeSession = { sessionId: sessionId, remoteExitNodeId, - expiresAt: new Date(Date.now() + EXPIRES).getTime(), + expiresAt: new Date(Date.now() + EXPIRES).getTime() }; await db.insert(remoteExitNodeSessions).values(session); return session; } export async function validateRemoteExitNodeSessionToken( - token: string, + token: string ): Promise { const sessionId = encodeHexLowerCase( - sha256(new TextEncoder().encode(token)), + sha256(new TextEncoder().encode(token)) ); const result = await db - .select({ remoteExitNode: remoteExitNodes, session: remoteExitNodeSessions }) + .select({ + remoteExitNode: remoteExitNodes, + session: remoteExitNodeSessions + }) .from(remoteExitNodeSessions) - .innerJoin(remoteExitNodes, eq(remoteExitNodeSessions.remoteExitNodeId, remoteExitNodes.remoteExitNodeId)) + .innerJoin( + remoteExitNodes, + eq( + remoteExitNodeSessions.remoteExitNodeId, + remoteExitNodes.remoteExitNodeId + ) + ) .where(eq(remoteExitNodeSessions.sessionId, sessionId)); if (result.length < 1) { return { session: null, remoteExitNode: null }; @@ -58,26 +70,32 @@ export async function validateRemoteExitNodeSessionToken( .where(eq(remoteExitNodeSessions.sessionId, session.sessionId)); return { session: null, remoteExitNode: null }; } - if (Date.now() >= session.expiresAt - (EXPIRES / 2)) { - session.expiresAt = new Date( - Date.now() + EXPIRES, - ).getTime(); + if (Date.now() >= session.expiresAt - EXPIRES / 2) { + session.expiresAt = new Date(Date.now() + EXPIRES).getTime(); await db .update(remoteExitNodeSessions) .set({ - expiresAt: session.expiresAt, + expiresAt: session.expiresAt }) .where(eq(remoteExitNodeSessions.sessionId, session.sessionId)); } return { session, remoteExitNode }; } -export async function invalidateRemoteExitNodeSession(sessionId: string): Promise { - await db.delete(remoteExitNodeSessions).where(eq(remoteExitNodeSessions.sessionId, sessionId)); +export async function invalidateRemoteExitNodeSession( + sessionId: string +): Promise { + await db + .delete(remoteExitNodeSessions) + .where(eq(remoteExitNodeSessions.sessionId, sessionId)); } -export async function invalidateAllRemoteExitNodeSessions(remoteExitNodeId: string): Promise { - await db.delete(remoteExitNodeSessions).where(eq(remoteExitNodeSessions.remoteExitNodeId, remoteExitNodeId)); +export async function invalidateAllRemoteExitNodeSessions( + remoteExitNodeId: string +): Promise { + await db + .delete(remoteExitNodeSessions) + .where(eq(remoteExitNodeSessions.remoteExitNodeId, remoteExitNodeId)); } export type SessionValidationResult = diff --git a/server/private/cleanup.ts b/server/private/cleanup.ts index 8bf5ea3d..e9b30527 100644 --- a/server/private/cleanup.ts +++ b/server/private/cleanup.ts @@ -25,4 +25,4 @@ export async function initCleanup() { // Handle process termination process.on("SIGTERM", () => cleanup()); process.on("SIGINT", () => cleanup()); -} \ No newline at end of file +} diff --git a/server/private/lib/billing/index.ts b/server/private/lib/billing/index.ts index 13ca3761..c2b77d5f 100644 --- a/server/private/lib/billing/index.ts +++ b/server/private/lib/billing/index.ts @@ -12,4 +12,4 @@ */ export * from "./getOrgTierData"; -export * from "./createCustomer"; \ No newline at end of file +export * from "./createCustomer"; diff --git a/server/private/lib/certificates.ts b/server/private/lib/certificates.ts index ec4b73ee..06571cac 100644 --- a/server/private/lib/certificates.ts +++ b/server/private/lib/certificates.ts @@ -55,7 +55,6 @@ export async function getValidCertificatesForDomains( domains: Set, useCache: boolean = true ): Promise> { - loadEncryptData(); // Ensure encryption key is loaded const finalResults: CertificateResult[] = []; diff --git a/server/private/lib/checkOrgAccessPolicy.ts b/server/private/lib/checkOrgAccessPolicy.ts index 2137cd72..7a78803d 100644 --- a/server/private/lib/checkOrgAccessPolicy.ts +++ b/server/private/lib/checkOrgAccessPolicy.ts @@ -12,14 +12,7 @@ */ import { build } from "@server/build"; -import { - db, - Org, - orgs, - ResourceSession, - sessions, - users -} from "@server/db"; +import { db, Org, orgs, ResourceSession, sessions, users } from "@server/db"; import { getOrgTierData } from "#private/lib/billing"; import { TierId } from "@server/lib/billing/tiers"; import license from "#private/license/license"; diff --git a/server/private/lib/exitNodes/exitNodeComms.ts b/server/private/lib/exitNodes/exitNodeComms.ts index 20c850a1..faf1153f 100644 --- a/server/private/lib/exitNodes/exitNodeComms.ts +++ b/server/private/lib/exitNodes/exitNodeComms.ts @@ -66,7 +66,9 @@ export async function sendToExitNode( // logger.debug(`Configured local exit node name: ${config.getRawConfig().gerbil.exit_node_name}`); if (exitNode.name == config.getRawConfig().gerbil.exit_node_name) { - hostname = privateConfig.getRawPrivateConfig().gerbil.local_exit_node_reachable_at; + hostname = + privateConfig.getRawPrivateConfig().gerbil + .local_exit_node_reachable_at; } if (!hostname) { diff --git a/server/private/lib/exitNodes/exitNodes.ts b/server/private/lib/exitNodes/exitNodes.ts index 77149bb0..556fdcf7 100644 --- a/server/private/lib/exitNodes/exitNodes.ts +++ b/server/private/lib/exitNodes/exitNodes.ts @@ -44,43 +44,53 @@ async function checkExitNodeOnlineStatus( const delayBetweenAttempts = 100; // 100ms delay between starting each attempt // Create promises for all attempts with staggered delays - const attemptPromises = Array.from({ length: maxAttempts }, async (_, index) => { - const attemptNumber = index + 1; - - // Add delay before each attempt (except the first) - if (index > 0) { - await new Promise((resolve) => setTimeout(resolve, delayBetweenAttempts * index)); - } + const attemptPromises = Array.from( + { length: maxAttempts }, + async (_, index) => { + const attemptNumber = index + 1; - try { - const response = await axios.get(`http://${endpoint}/ping`, { - timeout: timeoutMs, - validateStatus: (status) => status === 200 - }); - - if (response.status === 200) { - logger.debug( - `Exit node ${endpoint} is online (attempt ${attemptNumber}/${maxAttempts})` + // Add delay before each attempt (except the first) + if (index > 0) { + await new Promise((resolve) => + setTimeout(resolve, delayBetweenAttempts * index) ); - return { success: true, attemptNumber }; } - return { success: false, attemptNumber, error: 'Non-200 status' }; - } catch (error) { - const errorMessage = error instanceof Error ? error.message : "Unknown error"; - logger.debug( - `Exit node ${endpoint} ping failed (attempt ${attemptNumber}/${maxAttempts}): ${errorMessage}` - ); - return { success: false, attemptNumber, error: errorMessage }; + + try { + const response = await axios.get(`http://${endpoint}/ping`, { + timeout: timeoutMs, + validateStatus: (status) => status === 200 + }); + + if (response.status === 200) { + logger.debug( + `Exit node ${endpoint} is online (attempt ${attemptNumber}/${maxAttempts})` + ); + return { success: true, attemptNumber }; + } + return { + success: false, + attemptNumber, + error: "Non-200 status" + }; + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : "Unknown error"; + logger.debug( + `Exit node ${endpoint} ping failed (attempt ${attemptNumber}/${maxAttempts}): ${errorMessage}` + ); + return { success: false, attemptNumber, error: errorMessage }; + } } - }); + ); try { // Wait for the first successful response or all to fail const results = await Promise.allSettled(attemptPromises); - + // Check if any attempt succeeded for (const result of results) { - if (result.status === 'fulfilled' && result.value.success) { + if (result.status === "fulfilled" && result.value.success) { return true; } } @@ -137,7 +147,11 @@ export async function verifyExitNodeOrgAccess( return { hasAccess: false, exitNode }; } -export async function listExitNodes(orgId: string, filterOnline = false, noCloud = false) { +export async function listExitNodes( + orgId: string, + filterOnline = false, + noCloud = false +) { const allExitNodes = await db .select({ exitNodeId: exitNodes.exitNodeId, @@ -166,7 +180,10 @@ export async function listExitNodes(orgId: string, filterOnline = false, noCloud eq(exitNodes.type, "gerbil"), or( // only choose nodes that are in the same region - eq(exitNodes.region, config.getRawPrivateConfig().app.region), + eq( + exitNodes.region, + config.getRawPrivateConfig().app.region + ), isNull(exitNodes.region) // or for enterprise where region is not set ) ), @@ -191,7 +208,7 @@ export async function listExitNodes(orgId: string, filterOnline = false, noCloud // let online: boolean; // if (filterOnline && node.type == "remoteExitNode") { // try { - // const isActuallyOnline = await checkExitNodeOnlineStatus( + // const isActuallyOnline = await checkExitNodeOnlineStatus( // node.endpoint // ); @@ -225,7 +242,8 @@ export async function listExitNodes(orgId: string, filterOnline = false, noCloud node.type === "remoteExitNode" && (!filterOnline || node.online) ); const gerbilExitNodes = allExitNodes.filter( - (node) => node.type === "gerbil" && (!filterOnline || node.online) && !noCloud + (node) => + node.type === "gerbil" && (!filterOnline || node.online) && !noCloud ); // THIS PROVIDES THE FALL @@ -334,7 +352,11 @@ export function selectBestExitNode( return fallbackNode; } -export async function checkExitNodeOrg(exitNodeId: number, orgId: string, trx: Transaction | typeof db = db) { +export async function checkExitNodeOrg( + exitNodeId: number, + orgId: string, + trx: Transaction | typeof db = db +) { const [exitNodeOrg] = await trx .select() .from(exitNodeOrgs) diff --git a/server/private/lib/exitNodes/index.ts b/server/private/lib/exitNodes/index.ts index 098a0580..00113b64 100644 --- a/server/private/lib/exitNodes/index.ts +++ b/server/private/lib/exitNodes/index.ts @@ -12,4 +12,4 @@ */ export * from "./exitNodeComms"; -export * from "./exitNodes"; \ No newline at end of file +export * from "./exitNodes"; diff --git a/server/private/lib/lock.ts b/server/private/lib/lock.ts index 4a12063b..08496f65 100644 --- a/server/private/lib/lock.ts +++ b/server/private/lib/lock.ts @@ -177,7 +177,9 @@ export class LockManager { const exists = value !== null; const ownedByMe = exists && - value!.startsWith(`${config.getRawConfig().gerbil.exit_node_name}:`); + value!.startsWith( + `${config.getRawConfig().gerbil.exit_node_name}:` + ); const owner = exists ? value!.split(":")[0] : undefined; return { diff --git a/server/private/lib/rateLimit.test.ts b/server/private/lib/rateLimit.test.ts index 59952c8c..96adf082 100644 --- a/server/private/lib/rateLimit.test.ts +++ b/server/private/lib/rateLimit.test.ts @@ -14,15 +14,15 @@ // Simple test file for the rate limit service with Redis // Run with: npx ts-node rateLimitService.test.ts -import { RateLimitService } from './rateLimit'; +import { RateLimitService } from "./rateLimit"; function generateClientId() { - return 'client-' + Math.random().toString(36).substring(2, 15); + return "client-" + Math.random().toString(36).substring(2, 15); } async function runTests() { - console.log('Starting Rate Limit Service Tests...\n'); - + console.log("Starting Rate Limit Service Tests...\n"); + const rateLimitService = new RateLimitService(); let testsPassed = 0; let testsTotal = 0; @@ -47,36 +47,54 @@ async function runTests() { } // Test 1: Basic rate limiting - await test('Should allow requests under the limit', async () => { + await test("Should allow requests under the limit", async () => { const clientId = generateClientId(); const maxRequests = 5; for (let i = 0; i < maxRequests - 1; i++) { - const result = await rateLimitService.checkRateLimit(clientId, undefined, maxRequests); + const result = await rateLimitService.checkRateLimit( + clientId, + undefined, + maxRequests + ); assert(!result.isLimited, `Request ${i + 1} should be allowed`); - assert(result.totalHits === i + 1, `Expected ${i + 1} hits, got ${result.totalHits}`); + assert( + result.totalHits === i + 1, + `Expected ${i + 1} hits, got ${result.totalHits}` + ); } }); // Test 2: Rate limit blocking - await test('Should block requests over the limit', async () => { + await test("Should block requests over the limit", async () => { const clientId = generateClientId(); const maxRequests = 30; // Use up all allowed requests for (let i = 0; i < maxRequests - 1; i++) { - const result = await rateLimitService.checkRateLimit(clientId, undefined, maxRequests); + const result = await rateLimitService.checkRateLimit( + clientId, + undefined, + maxRequests + ); assert(!result.isLimited, `Request ${i + 1} should be allowed`); } // Next request should be blocked - const blockedResult = await rateLimitService.checkRateLimit(clientId, undefined, maxRequests); - assert(blockedResult.isLimited, 'Request should be blocked'); - assert(blockedResult.reason === 'global', 'Should be blocked for global reason'); + const blockedResult = await rateLimitService.checkRateLimit( + clientId, + undefined, + maxRequests + ); + assert(blockedResult.isLimited, "Request should be blocked"); + assert( + blockedResult.reason === "global", + "Should be blocked for global reason" + ); }); // Test 3: Message type limits - await test('Should handle message type limits', async () => { + await test("Should handle message type limits", async () => { const clientId = generateClientId(); const globalMax = 10; const messageTypeMax = 2; @@ -84,54 +102,64 @@ async function runTests() { // Send messages of type 'ping' up to the limit for (let i = 0; i < messageTypeMax - 1; i++) { const result = await rateLimitService.checkRateLimit( - clientId, - 'ping', - globalMax, + clientId, + "ping", + globalMax, messageTypeMax ); - assert(!result.isLimited, `Ping message ${i + 1} should be allowed`); + assert( + !result.isLimited, + `Ping message ${i + 1} should be allowed` + ); } // Next 'ping' should be blocked const blockedResult = await rateLimitService.checkRateLimit( - clientId, - 'ping', - globalMax, + clientId, + "ping", + globalMax, messageTypeMax ); - assert(blockedResult.isLimited, 'Ping message should be blocked'); - assert(blockedResult.reason === 'message_type:ping', 'Should be blocked for message type'); + assert(blockedResult.isLimited, "Ping message should be blocked"); + assert( + blockedResult.reason === "message_type:ping", + "Should be blocked for message type" + ); // Other message types should still work const otherResult = await rateLimitService.checkRateLimit( - clientId, - 'pong', - globalMax, + clientId, + "pong", + globalMax, messageTypeMax ); - assert(!otherResult.isLimited, 'Pong message should be allowed'); + assert(!otherResult.isLimited, "Pong message should be allowed"); }); // Test 4: Reset functionality - await test('Should reset client correctly', async () => { + await test("Should reset client correctly", async () => { const clientId = generateClientId(); const maxRequests = 3; // Use up some requests await rateLimitService.checkRateLimit(clientId, undefined, maxRequests); - await rateLimitService.checkRateLimit(clientId, 'test', maxRequests); + await rateLimitService.checkRateLimit(clientId, "test", maxRequests); // Reset the client await rateLimitService.resetKey(clientId); // Should be able to make fresh requests - const result = await rateLimitService.checkRateLimit(clientId, undefined, maxRequests); - assert(!result.isLimited, 'Request after reset should be allowed'); - assert(result.totalHits === 1, 'Should have 1 hit after reset'); + const result = await rateLimitService.checkRateLimit( + clientId, + undefined, + maxRequests + ); + assert(!result.isLimited, "Request after reset should be allowed"); + assert(result.totalHits === 1, "Should have 1 hit after reset"); }); // Test 5: Different clients are independent - await test('Should handle different clients independently', async () => { + await test("Should handle different clients independently", async () => { const client1 = generateClientId(); const client2 = generateClientId(); const maxRequests = 2; @@ -139,43 +167,62 @@ async function runTests() { // Client 1 uses up their limit await rateLimitService.checkRateLimit(client1, undefined, maxRequests); await rateLimitService.checkRateLimit(client1, undefined, maxRequests); - const client1Blocked = await rateLimitService.checkRateLimit(client1, undefined, maxRequests); - assert(client1Blocked.isLimited, 'Client 1 should be blocked'); + const client1Blocked = await rateLimitService.checkRateLimit( + client1, + undefined, + maxRequests + ); + assert(client1Blocked.isLimited, "Client 1 should be blocked"); // Client 2 should still be able to make requests - const client2Result = await rateLimitService.checkRateLimit(client2, undefined, maxRequests); - assert(!client2Result.isLimited, 'Client 2 should not be blocked'); - assert(client2Result.totalHits === 1, 'Client 2 should have 1 hit'); + const client2Result = await rateLimitService.checkRateLimit( + client2, + undefined, + maxRequests + ); + assert(!client2Result.isLimited, "Client 2 should not be blocked"); + assert(client2Result.totalHits === 1, "Client 2 should have 1 hit"); }); // Test 6: Decrement functionality - await test('Should decrement correctly', async () => { + await test("Should decrement correctly", async () => { const clientId = generateClientId(); const maxRequests = 5; // Make some requests await rateLimitService.checkRateLimit(clientId, undefined, maxRequests); await rateLimitService.checkRateLimit(clientId, undefined, maxRequests); - let result = await rateLimitService.checkRateLimit(clientId, undefined, maxRequests); - assert(result.totalHits === 3, 'Should have 3 hits before decrement'); + let result = await rateLimitService.checkRateLimit( + clientId, + undefined, + maxRequests + ); + assert(result.totalHits === 3, "Should have 3 hits before decrement"); // Decrement await rateLimitService.decrementRateLimit(clientId); // Next request should reflect the decrement - result = await rateLimitService.checkRateLimit(clientId, undefined, maxRequests); - assert(result.totalHits === 3, 'Should have 3 hits after decrement + increment'); + result = await rateLimitService.checkRateLimit( + clientId, + undefined, + maxRequests + ); + assert( + result.totalHits === 3, + "Should have 3 hits after decrement + increment" + ); }); // Wait a moment for any pending Redis operations - console.log('\nWaiting for Redis sync...'); - await new Promise(resolve => setTimeout(resolve, 1000)); + console.log("\nWaiting for Redis sync..."); + await new Promise((resolve) => setTimeout(resolve, 1000)); // Force sync to test Redis integration - await test('Should sync to Redis', async () => { + await test("Should sync to Redis", async () => { await rateLimitService.forceSyncAllPendingData(); // If this doesn't throw, Redis sync is working - assert(true, 'Redis sync completed'); + assert(true, "Redis sync completed"); }); // Cleanup @@ -185,18 +232,18 @@ async function runTests() { console.log(`\n--- Test Results ---`); console.log(`✅ Passed: ${testsPassed}/${testsTotal}`); console.log(`❌ Failed: ${testsTotal - testsPassed}/${testsTotal}`); - + if (testsPassed === testsTotal) { - console.log('\n🎉 All tests passed!'); + console.log("\n🎉 All tests passed!"); process.exit(0); } else { - console.log('\n💥 Some tests failed!'); + console.log("\n💥 Some tests failed!"); process.exit(1); } } // Run the tests -runTests().catch(error => { - console.error('Test runner error:', error); +runTests().catch((error) => { + console.error("Test runner error:", error); process.exit(1); -}); \ No newline at end of file +}); diff --git a/server/private/lib/rateLimit.ts b/server/private/lib/rateLimit.ts index 6d4ab44d..984d95c6 100644 --- a/server/private/lib/rateLimit.ts +++ b/server/private/lib/rateLimit.ts @@ -40,7 +40,8 @@ interface RateLimitResult { export class RateLimitService { private localRateLimitTracker: Map = new Map(); - private localMessageTypeRateLimitTracker: Map = new Map(); + private localMessageTypeRateLimitTracker: Map = + new Map(); private cleanupInterval: NodeJS.Timeout | null = null; private forceSyncInterval: NodeJS.Timeout | null = null; @@ -68,12 +69,18 @@ export class RateLimitService { return `ratelimit:${clientId}`; } - private getMessageTypeRateLimitKey(clientId: string, messageType: string): string { + private getMessageTypeRateLimitKey( + clientId: string, + messageType: string + ): string { return `ratelimit:${clientId}:${messageType}`; } // Helper function to clean up old timestamp fields from a Redis hash - private async cleanupOldTimestamps(key: string, windowStart: number): Promise { + private async cleanupOldTimestamps( + key: string, + windowStart: number + ): Promise { if (!redisManager.isRedisEnabled()) return; try { @@ -101,10 +108,15 @@ export class RateLimitService { const batch = fieldsToDelete.slice(i, i + batchSize); await client.hdel(key, ...batch); } - logger.debug(`Cleaned up ${fieldsToDelete.length} old timestamp fields from ${key}`); + logger.debug( + `Cleaned up ${fieldsToDelete.length} old timestamp fields from ${key}` + ); } } catch (error) { - logger.error(`Failed to cleanup old timestamps for key ${key}:`, error); + logger.error( + `Failed to cleanup old timestamps for key ${key}:`, + error + ); // Don't throw - cleanup failures shouldn't block rate limiting } } @@ -114,7 +126,8 @@ export class RateLimitService { clientId: string, tracker: RateLimitTracker ): Promise { - if (!redisManager.isRedisEnabled() || tracker.pendingCount === 0) return; + if (!redisManager.isRedisEnabled() || tracker.pendingCount === 0) + return; try { const currentTime = Math.floor(Date.now() / 1000); @@ -132,7 +145,11 @@ export class RateLimitService { const newValue = ( parseInt(currentValue || "0") + tracker.pendingCount ).toString(); - await redisManager.hset(globalKey, currentTime.toString(), newValue); + await redisManager.hset( + globalKey, + currentTime.toString(), + newValue + ); // Set TTL using the client directly - this prevents the key from persisting forever if (redisManager.getClient()) { @@ -145,7 +162,9 @@ export class RateLimitService { tracker.lastSyncedCount = tracker.count; tracker.pendingCount = 0; - logger.debug(`Synced global rate limit to Redis for client ${clientId}`); + logger.debug( + `Synced global rate limit to Redis for client ${clientId}` + ); } catch (error) { logger.error("Failed to sync global rate limit to Redis:", error); } @@ -156,12 +175,16 @@ export class RateLimitService { messageType: string, tracker: RateLimitTracker ): Promise { - if (!redisManager.isRedisEnabled() || tracker.pendingCount === 0) return; + if (!redisManager.isRedisEnabled() || tracker.pendingCount === 0) + return; try { const currentTime = Math.floor(Date.now() / 1000); const windowStart = currentTime - RATE_LIMIT_WINDOW; - const messageTypeKey = this.getMessageTypeRateLimitKey(clientId, messageType); + const messageTypeKey = this.getMessageTypeRateLimitKey( + clientId, + messageType + ); // Clean up old timestamp fields before writing await this.cleanupOldTimestamps(messageTypeKey, windowStart); @@ -195,12 +218,17 @@ export class RateLimitService { `Synced message type rate limit to Redis for client ${clientId}, type ${messageType}` ); } catch (error) { - logger.error("Failed to sync message type rate limit to Redis:", error); + logger.error( + "Failed to sync message type rate limit to Redis:", + error + ); } } // Initialize local tracker from Redis data - private async initializeLocalTracker(clientId: string): Promise { + private async initializeLocalTracker( + clientId: string + ): Promise { const currentTime = Math.floor(Date.now() / 1000); const windowStart = currentTime - RATE_LIMIT_WINDOW; @@ -215,14 +243,16 @@ export class RateLimitService { try { const globalKey = this.getRateLimitKey(clientId); - + // Clean up old timestamp fields before reading await this.cleanupOldTimestamps(globalKey, windowStart); - + const globalRateLimitData = await redisManager.hgetall(globalKey); let count = 0; - for (const [timestamp, countStr] of Object.entries(globalRateLimitData)) { + for (const [timestamp, countStr] of Object.entries( + globalRateLimitData + )) { const time = parseInt(timestamp); if (time >= windowStart) { count += parseInt(countStr); @@ -236,7 +266,10 @@ export class RateLimitService { lastSyncedCount: count }; } catch (error) { - logger.error("Failed to initialize global tracker from Redis:", error); + logger.error( + "Failed to initialize global tracker from Redis:", + error + ); return { count: 0, windowStart: currentTime, @@ -263,15 +296,21 @@ export class RateLimitService { } try { - const messageTypeKey = this.getMessageTypeRateLimitKey(clientId, messageType); - + const messageTypeKey = this.getMessageTypeRateLimitKey( + clientId, + messageType + ); + // Clean up old timestamp fields before reading await this.cleanupOldTimestamps(messageTypeKey, windowStart); - - const messageTypeRateLimitData = await redisManager.hgetall(messageTypeKey); + + const messageTypeRateLimitData = + await redisManager.hgetall(messageTypeKey); let count = 0; - for (const [timestamp, countStr] of Object.entries(messageTypeRateLimitData)) { + for (const [timestamp, countStr] of Object.entries( + messageTypeRateLimitData + )) { const time = parseInt(timestamp); if (time >= windowStart) { count += parseInt(countStr); @@ -285,7 +324,10 @@ export class RateLimitService { lastSyncedCount: count }; } catch (error) { - logger.error("Failed to initialize message type tracker from Redis:", error); + logger.error( + "Failed to initialize message type tracker from Redis:", + error + ); return { count: 0, windowStart: currentTime, @@ -327,7 +369,10 @@ export class RateLimitService { isLimited: true, reason: "global", totalHits: globalTracker.count, - resetTime: new Date((globalTracker.windowStart + Math.floor(windowMs / 1000)) * 1000) + resetTime: new Date( + (globalTracker.windowStart + Math.floor(windowMs / 1000)) * + 1000 + ) }; } @@ -339,19 +384,32 @@ export class RateLimitService { // Check message type specific rate limit if messageType is provided if (messageType) { const messageTypeKey = `${clientId}:${messageType}`; - let messageTypeTracker = this.localMessageTypeRateLimitTracker.get(messageTypeKey); + let messageTypeTracker = + this.localMessageTypeRateLimitTracker.get(messageTypeKey); - if (!messageTypeTracker || messageTypeTracker.windowStart < windowStart) { + if ( + !messageTypeTracker || + messageTypeTracker.windowStart < windowStart + ) { // New window or first request for this message type - initialize from Redis if available - messageTypeTracker = await this.initializeMessageTypeTracker(clientId, messageType); + messageTypeTracker = await this.initializeMessageTypeTracker( + clientId, + messageType + ); messageTypeTracker.windowStart = currentTime; - this.localMessageTypeRateLimitTracker.set(messageTypeKey, messageTypeTracker); + this.localMessageTypeRateLimitTracker.set( + messageTypeKey, + messageTypeTracker + ); } // Increment message type counters messageTypeTracker.count++; messageTypeTracker.pendingCount++; - this.localMessageTypeRateLimitTracker.set(messageTypeKey, messageTypeTracker); + this.localMessageTypeRateLimitTracker.set( + messageTypeKey, + messageTypeTracker + ); // Check if message type limit would be exceeded if (messageTypeTracker.count >= messageTypeLimit) { @@ -359,25 +417,38 @@ export class RateLimitService { isLimited: true, reason: `message_type:${messageType}`, totalHits: messageTypeTracker.count, - resetTime: new Date((messageTypeTracker.windowStart + Math.floor(windowMs / 1000)) * 1000) + resetTime: new Date( + (messageTypeTracker.windowStart + + Math.floor(windowMs / 1000)) * + 1000 + ) }; } // Sync to Redis if threshold reached if (messageTypeTracker.pendingCount >= REDIS_SYNC_THRESHOLD) { - this.syncMessageTypeRateLimitToRedis(clientId, messageType, messageTypeTracker); + this.syncMessageTypeRateLimitToRedis( + clientId, + messageType, + messageTypeTracker + ); } } return { isLimited: false, totalHits: globalTracker.count, - resetTime: new Date((globalTracker.windowStart + Math.floor(windowMs / 1000)) * 1000) + resetTime: new Date( + (globalTracker.windowStart + Math.floor(windowMs / 1000)) * 1000 + ) }; } // Decrement function for skipSuccessfulRequests/skipFailedRequests functionality - async decrementRateLimit(clientId: string, messageType?: string): Promise { + async decrementRateLimit( + clientId: string, + messageType?: string + ): Promise { // Decrement global counter const globalTracker = this.localRateLimitTracker.get(clientId); if (globalTracker && globalTracker.count > 0) { @@ -389,7 +460,8 @@ export class RateLimitService { // Decrement message type counter if provided if (messageType) { const messageTypeKey = `${clientId}:${messageType}`; - const messageTypeTracker = this.localMessageTypeRateLimitTracker.get(messageTypeKey); + const messageTypeTracker = + this.localMessageTypeRateLimitTracker.get(messageTypeKey); if (messageTypeTracker && messageTypeTracker.count > 0) { messageTypeTracker.count--; messageTypeTracker.pendingCount--; @@ -401,7 +473,7 @@ export class RateLimitService { async resetKey(clientId: string): Promise { // Remove from local tracking this.localRateLimitTracker.delete(clientId); - + // Remove all message type entries for this client for (const [key] of this.localMessageTypeRateLimitTracker) { if (key.startsWith(`${clientId}:`)) { @@ -417,9 +489,13 @@ export class RateLimitService { // Get all message type keys for this client and delete them const client = redisManager.getClient(); if (client) { - const messageTypeKeys = await client.keys(`ratelimit:${clientId}:*`); + const messageTypeKeys = await client.keys( + `ratelimit:${clientId}:*` + ); if (messageTypeKeys.length > 0) { - await Promise.all(messageTypeKeys.map(key => redisManager.del(key))); + await Promise.all( + messageTypeKeys.map((key) => redisManager.del(key)) + ); } } } @@ -431,7 +507,10 @@ export class RateLimitService { const windowStart = currentTime - RATE_LIMIT_WINDOW; // Clean up global rate limit tracking and sync pending data - for (const [clientId, tracker] of this.localRateLimitTracker.entries()) { + for (const [ + clientId, + tracker + ] of this.localRateLimitTracker.entries()) { if (tracker.windowStart < windowStart) { // Sync any pending data before cleanup if (tracker.pendingCount > 0) { @@ -442,12 +521,19 @@ export class RateLimitService { } // Clean up message type rate limit tracking and sync pending data - for (const [key, tracker] of this.localMessageTypeRateLimitTracker.entries()) { + for (const [ + key, + tracker + ] of this.localMessageTypeRateLimitTracker.entries()) { if (tracker.windowStart < windowStart) { // Sync any pending data before cleanup if (tracker.pendingCount > 0) { const [clientId, messageType] = key.split(":", 2); - await this.syncMessageTypeRateLimitToRedis(clientId, messageType, tracker); + await this.syncMessageTypeRateLimitToRedis( + clientId, + messageType, + tracker + ); } this.localMessageTypeRateLimitTracker.delete(key); } @@ -461,17 +547,27 @@ export class RateLimitService { logger.debug("Force syncing all pending rate limit data to Redis..."); // Sync all pending global rate limits - for (const [clientId, tracker] of this.localRateLimitTracker.entries()) { + for (const [ + clientId, + tracker + ] of this.localRateLimitTracker.entries()) { if (tracker.pendingCount > 0) { await this.syncRateLimitToRedis(clientId, tracker); } } // Sync all pending message type rate limits - for (const [key, tracker] of this.localMessageTypeRateLimitTracker.entries()) { + for (const [ + key, + tracker + ] of this.localMessageTypeRateLimitTracker.entries()) { if (tracker.pendingCount > 0) { const [clientId, messageType] = key.split(":", 2); - await this.syncMessageTypeRateLimitToRedis(clientId, messageType, tracker); + await this.syncMessageTypeRateLimitToRedis( + clientId, + messageType, + tracker + ); } } @@ -504,4 +600,4 @@ export class RateLimitService { } // Export singleton instance -export const rateLimitService = new RateLimitService(); \ No newline at end of file +export const rateLimitService = new RateLimitService(); diff --git a/server/private/lib/rateLimitStore.ts b/server/private/lib/rateLimitStore.ts index 20355125..32495cd2 100644 --- a/server/private/lib/rateLimitStore.ts +++ b/server/private/lib/rateLimitStore.ts @@ -17,7 +17,10 @@ import { MemoryStore, Store } from "express-rate-limit"; import RedisStore from "#private/lib/redisStore"; export function createStore(): Store { - if (build != "oss" && privateConfig.getRawPrivateConfig().flags.enable_redis) { + if ( + build != "oss" && + privateConfig.getRawPrivateConfig().flags.enable_redis + ) { const rateLimitStore: Store = new RedisStore({ prefix: "api-rate-limit", // Optional: customize Redis key prefix skipFailedRequests: true, // Don't count failed requests diff --git a/server/private/lib/redis.ts b/server/private/lib/redis.ts index 324a6a74..6b7826ea 100644 --- a/server/private/lib/redis.ts +++ b/server/private/lib/redis.ts @@ -19,7 +19,7 @@ import { build } from "@server/build"; class RedisManager { public client: Redis | null = null; private writeClient: Redis | null = null; // Master for writes - private readClient: Redis | null = null; // Replica for reads + private readClient: Redis | null = null; // Replica for reads private subscriber: Redis | null = null; private publisher: Redis | null = null; private isEnabled: boolean = false; @@ -46,7 +46,8 @@ class RedisManager { this.isEnabled = false; return; } - this.isEnabled = privateConfig.getRawPrivateConfig().flags.enable_redis || false; + this.isEnabled = + privateConfig.getRawPrivateConfig().flags.enable_redis || false; if (this.isEnabled) { this.initializeClients(); } @@ -63,15 +64,19 @@ class RedisManager { } private async triggerReconnectionCallbacks(): Promise { - logger.info(`Triggering ${this.reconnectionCallbacks.size} reconnection callbacks`); - - const promises = Array.from(this.reconnectionCallbacks).map(async (callback) => { - try { - await callback(); - } catch (error) { - logger.error("Error in reconnection callback:", error); + logger.info( + `Triggering ${this.reconnectionCallbacks.size} reconnection callbacks` + ); + + const promises = Array.from(this.reconnectionCallbacks).map( + async (callback) => { + try { + await callback(); + } catch (error) { + logger.error("Error in reconnection callback:", error); + } } - }); + ); await Promise.allSettled(promises); } @@ -79,13 +84,17 @@ class RedisManager { private async resubscribeToChannels(): Promise { if (!this.subscriber || this.subscribers.size === 0) return; - logger.info(`Re-subscribing to ${this.subscribers.size} channels after Redis reconnection`); - + logger.info( + `Re-subscribing to ${this.subscribers.size} channels after Redis reconnection` + ); + try { const channels = Array.from(this.subscribers.keys()); if (channels.length > 0) { await this.subscriber.subscribe(...channels); - logger.info(`Successfully re-subscribed to channels: ${channels.join(', ')}`); + logger.info( + `Successfully re-subscribed to channels: ${channels.join(", ")}` + ); } } catch (error) { logger.error("Failed to re-subscribe to channels:", error); @@ -98,7 +107,7 @@ class RedisManager { host: redisConfig.host!, port: redisConfig.port!, password: redisConfig.password, - db: redisConfig.db, + db: redisConfig.db // tls: { // rejectUnauthorized: // redisConfig.tls?.reject_unauthorized || false @@ -112,7 +121,7 @@ class RedisManager { if (!redisConfig.replicas || redisConfig.replicas.length === 0) { return null; } - + // Use the first replica for simplicity // In production, you might want to implement load balancing across replicas const replica = redisConfig.replicas[0]; @@ -120,7 +129,7 @@ class RedisManager { host: replica.host!, port: replica.port!, password: replica.password, - db: replica.db || redisConfig.db, + db: replica.db || redisConfig.db // tls: { // rejectUnauthorized: // replica.tls?.reject_unauthorized || false @@ -133,7 +142,7 @@ class RedisManager { private initializeClients(): void { const masterConfig = this.getRedisConfig(); const replicaConfig = this.getReplicaRedisConfig(); - + this.hasReplicas = replicaConfig !== null; try { @@ -144,7 +153,7 @@ class RedisManager { maxRetriesPerRequest: 3, keepAlive: 30000, connectTimeout: this.connectionTimeout, - commandTimeout: this.commandTimeout, + commandTimeout: this.commandTimeout }); // Initialize replica connection for reads (if available) @@ -155,7 +164,7 @@ class RedisManager { maxRetriesPerRequest: 3, keepAlive: 30000, connectTimeout: this.connectionTimeout, - commandTimeout: this.commandTimeout, + commandTimeout: this.commandTimeout }); } else { // Fallback to master for reads if no replicas @@ -172,7 +181,7 @@ class RedisManager { maxRetriesPerRequest: 3, keepAlive: 30000, connectTimeout: this.connectionTimeout, - commandTimeout: this.commandTimeout, + commandTimeout: this.commandTimeout }); // Subscriber uses replica if available (reads) @@ -182,7 +191,7 @@ class RedisManager { maxRetriesPerRequest: 3, keepAlive: 30000, connectTimeout: this.connectionTimeout, - commandTimeout: this.commandTimeout, + commandTimeout: this.commandTimeout }); // Add reconnection handlers for write client @@ -202,11 +211,14 @@ class RedisManager { logger.info("Redis write client ready"); this.isWriteHealthy = true; this.updateOverallHealth(); - + // Trigger reconnection callbacks when Redis comes back online if (this.isHealthy) { - this.triggerReconnectionCallbacks().catch(error => { - logger.error("Error triggering reconnection callbacks:", error); + this.triggerReconnectionCallbacks().catch((error) => { + logger.error( + "Error triggering reconnection callbacks:", + error + ); }); } }); @@ -233,11 +245,14 @@ class RedisManager { logger.info("Redis read client ready"); this.isReadHealthy = true; this.updateOverallHealth(); - + // Trigger reconnection callbacks when Redis comes back online if (this.isHealthy) { - this.triggerReconnectionCallbacks().catch(error => { - logger.error("Error triggering reconnection callbacks:", error); + this.triggerReconnectionCallbacks().catch((error) => { + logger.error( + "Error triggering reconnection callbacks:", + error + ); }); } }); @@ -298,8 +313,8 @@ class RedisManager { } ); - const setupMessage = this.hasReplicas - ? "Redis clients initialized successfully with replica support" + const setupMessage = this.hasReplicas + ? "Redis clients initialized successfully with replica support" : "Redis clients initialized successfully (single instance)"; logger.info(setupMessage); @@ -313,7 +328,8 @@ class RedisManager { private updateOverallHealth(): void { // Overall health is true if write is healthy and (read is healthy OR we don't have replicas) - this.isHealthy = this.isWriteHealthy && (this.isReadHealthy || !this.hasReplicas); + this.isHealthy = + this.isWriteHealthy && (this.isReadHealthy || !this.hasReplicas); } private async executeWithRetry( @@ -322,49 +338,61 @@ class RedisManager { fallbackOperation?: () => Promise ): Promise { let lastError: Error | null = null; - + for (let attempt = 0; attempt <= this.maxRetries; attempt++) { try { return await operation(); } catch (error) { lastError = error as Error; - + // If this is the last attempt, try fallback if available if (attempt === this.maxRetries && fallbackOperation) { try { - logger.warn(`${operationName} primary operation failed, trying fallback`); + logger.warn( + `${operationName} primary operation failed, trying fallback` + ); return await fallbackOperation(); } catch (fallbackError) { - logger.error(`${operationName} fallback also failed:`, fallbackError); + logger.error( + `${operationName} fallback also failed:`, + fallbackError + ); throw lastError; } } - + // Don't retry on the last attempt if (attempt === this.maxRetries) { break; } - + // Calculate delay with exponential backoff const delay = Math.min( - this.baseRetryDelay * Math.pow(this.backoffMultiplier, attempt), + this.baseRetryDelay * + Math.pow(this.backoffMultiplier, attempt), this.maxRetryDelay ); - - logger.warn(`${operationName} failed (attempt ${attempt + 1}/${this.maxRetries + 1}), retrying in ${delay}ms:`, error); - + + logger.warn( + `${operationName} failed (attempt ${attempt + 1}/${this.maxRetries + 1}), retrying in ${delay}ms:`, + error + ); + // Wait before retrying - await new Promise(resolve => setTimeout(resolve, delay)); + await new Promise((resolve) => setTimeout(resolve, delay)); } } - - logger.error(`${operationName} failed after ${this.maxRetries + 1} attempts:`, lastError); + + logger.error( + `${operationName} failed after ${this.maxRetries + 1} attempts:`, + lastError + ); throw lastError; } private startHealthMonitoring(): void { if (!this.isEnabled) return; - + // Check health every 30 seconds setInterval(async () => { try { @@ -381,7 +409,7 @@ class RedisManager { private async checkRedisHealth(): Promise { const now = Date.now(); - + // Only check health every 30 seconds if (now - this.lastHealthCheck < this.healthCheckInterval) { return this.isHealthy; @@ -400,24 +428,45 @@ class RedisManager { // Check write client (master) health await Promise.race([ this.writeClient.ping(), - new Promise((_, reject) => - setTimeout(() => reject(new Error('Write client health check timeout')), 2000) + new Promise((_, reject) => + setTimeout( + () => + reject( + new Error("Write client health check timeout") + ), + 2000 + ) ) ]); this.isWriteHealthy = true; // Check read client health if it's different from write client - if (this.hasReplicas && this.readClient && this.readClient !== this.writeClient) { + if ( + this.hasReplicas && + this.readClient && + this.readClient !== this.writeClient + ) { try { await Promise.race([ this.readClient.ping(), - new Promise((_, reject) => - setTimeout(() => reject(new Error('Read client health check timeout')), 2000) + new Promise((_, reject) => + setTimeout( + () => + reject( + new Error( + "Read client health check timeout" + ) + ), + 2000 + ) ) ]); this.isReadHealthy = true; } catch (error) { - logger.error("Redis read client health check failed:", error); + logger.error( + "Redis read client health check failed:", + error + ); this.isReadHealthy = false; } } else { @@ -475,16 +524,13 @@ class RedisManager { if (!this.isRedisEnabled() || !this.writeClient) return false; try { - await this.executeWithRetry( - async () => { - if (ttl) { - await this.writeClient!.setex(key, ttl, value); - } else { - await this.writeClient!.set(key, value); - } - }, - "Redis SET" - ); + await this.executeWithRetry(async () => { + if (ttl) { + await this.writeClient!.setex(key, ttl, value); + } else { + await this.writeClient!.set(key, value); + } + }, "Redis SET"); return true; } catch (error) { logger.error("Redis SET error:", error); @@ -496,9 +542,10 @@ class RedisManager { if (!this.isRedisEnabled() || !this.readClient) return null; try { - const fallbackOperation = (this.hasReplicas && this.writeClient && this.isWriteHealthy) - ? () => this.writeClient!.get(key) - : undefined; + const fallbackOperation = + this.hasReplicas && this.writeClient && this.isWriteHealthy + ? () => this.writeClient!.get(key) + : undefined; return await this.executeWithRetry( () => this.readClient!.get(key), @@ -560,9 +607,10 @@ class RedisManager { if (!this.isRedisEnabled() || !this.readClient) return []; try { - const fallbackOperation = (this.hasReplicas && this.writeClient && this.isWriteHealthy) - ? () => this.writeClient!.smembers(key) - : undefined; + const fallbackOperation = + this.hasReplicas && this.writeClient && this.isWriteHealthy + ? () => this.writeClient!.smembers(key) + : undefined; return await this.executeWithRetry( () => this.readClient!.smembers(key), @@ -598,9 +646,10 @@ class RedisManager { if (!this.isRedisEnabled() || !this.readClient) return null; try { - const fallbackOperation = (this.hasReplicas && this.writeClient && this.isWriteHealthy) - ? () => this.writeClient!.hget(key, field) - : undefined; + const fallbackOperation = + this.hasReplicas && this.writeClient && this.isWriteHealthy + ? () => this.writeClient!.hget(key, field) + : undefined; return await this.executeWithRetry( () => this.readClient!.hget(key, field), @@ -632,9 +681,10 @@ class RedisManager { if (!this.isRedisEnabled() || !this.readClient) return {}; try { - const fallbackOperation = (this.hasReplicas && this.writeClient && this.isWriteHealthy) - ? () => this.writeClient!.hgetall(key) - : undefined; + const fallbackOperation = + this.hasReplicas && this.writeClient && this.isWriteHealthy + ? () => this.writeClient!.hgetall(key) + : undefined; return await this.executeWithRetry( () => this.readClient!.hgetall(key), @@ -658,18 +708,18 @@ class RedisManager { } try { - await this.executeWithRetry( - async () => { - // Add timeout to prevent hanging - return Promise.race([ - this.publisher!.publish(channel, message), - new Promise((_, reject) => - setTimeout(() => reject(new Error('Redis publish timeout')), 3000) + await this.executeWithRetry(async () => { + // Add timeout to prevent hanging + return Promise.race([ + this.publisher!.publish(channel, message), + new Promise((_, reject) => + setTimeout( + () => reject(new Error("Redis publish timeout")), + 3000 ) - ]); - }, - "Redis PUBLISH" - ); + ) + ]); + }, "Redis PUBLISH"); return true; } catch (error) { logger.error("Redis PUBLISH error:", error); @@ -689,17 +739,20 @@ class RedisManager { if (!this.subscribers.has(channel)) { this.subscribers.set(channel, new Set()); // Only subscribe to the channel if it's the first subscriber - await this.executeWithRetry( - async () => { - return Promise.race([ - this.subscriber!.subscribe(channel), - new Promise((_, reject) => - setTimeout(() => reject(new Error('Redis subscribe timeout')), 5000) + await this.executeWithRetry(async () => { + return Promise.race([ + this.subscriber!.subscribe(channel), + new Promise((_, reject) => + setTimeout( + () => + reject( + new Error("Redis subscribe timeout") + ), + 5000 ) - ]); - }, - "Redis SUBSCRIBE" - ); + ) + ]); + }, "Redis SUBSCRIBE"); } this.subscribers.get(channel)!.add(callback); diff --git a/server/private/lib/redisStore.ts b/server/private/lib/redisStore.ts index 235f8f8f..2360e309 100644 --- a/server/private/lib/redisStore.ts +++ b/server/private/lib/redisStore.ts @@ -11,9 +11,9 @@ * This file is not licensed under the AGPLv3. */ -import { Store, Options, IncrementResponse } from 'express-rate-limit'; -import { rateLimitService } from './rateLimit'; -import logger from '@server/logger'; +import { Store, Options, IncrementResponse } from "express-rate-limit"; +import { rateLimitService } from "./rateLimit"; +import logger from "@server/logger"; /** * A Redis-backed rate limiting store for express-rate-limit that optimizes @@ -57,12 +57,14 @@ export default class RedisStore implements Store { * * @param options - Configuration options for the store. */ - constructor(options: { - prefix?: string; - skipFailedRequests?: boolean; - skipSuccessfulRequests?: boolean; - } = {}) { - this.prefix = options.prefix || 'express-rate-limit'; + constructor( + options: { + prefix?: string; + skipFailedRequests?: boolean; + skipSuccessfulRequests?: boolean; + } = {} + ) { + this.prefix = options.prefix || "express-rate-limit"; this.skipFailedRequests = options.skipFailedRequests || false; this.skipSuccessfulRequests = options.skipSuccessfulRequests || false; } @@ -101,7 +103,8 @@ export default class RedisStore implements Store { return { totalHits: result.totalHits || 1, - resetTime: result.resetTime || new Date(Date.now() + this.windowMs) + resetTime: + result.resetTime || new Date(Date.now() + this.windowMs) }; } catch (error) { logger.error(`RedisStore increment error for key ${key}:`, error); @@ -158,7 +161,9 @@ export default class RedisStore implements Store { */ async resetAll(): Promise { try { - logger.warn('RedisStore resetAll called - this operation can be expensive'); + logger.warn( + "RedisStore resetAll called - this operation can be expensive" + ); // Force sync all pending data first await rateLimitService.forceSyncAllPendingData(); @@ -167,9 +172,9 @@ export default class RedisStore implements Store { // scanning all Redis keys with our prefix, which could be expensive. // In production, it's better to let entries expire naturally. - logger.info('RedisStore resetAll completed (pending data synced)'); + logger.info("RedisStore resetAll completed (pending data synced)"); } catch (error) { - logger.error('RedisStore resetAll error:', error); + logger.error("RedisStore resetAll error:", error); // Don't throw - this is an optional method } } @@ -181,7 +186,9 @@ export default class RedisStore implements Store { * @param key - The identifier for a client. * @returns Current hit count and reset time, or null if no data exists. */ - async getHits(key: string): Promise<{ totalHits: number; resetTime: Date } | null> { + async getHits( + key: string + ): Promise<{ totalHits: number; resetTime: Date } | null> { try { const clientId = `${this.prefix}:${key}`; @@ -200,7 +207,8 @@ export default class RedisStore implements Store { return { totalHits: Math.max(0, (result.totalHits || 0) - 1), // Adjust for the decrement - resetTime: result.resetTime || new Date(Date.now() + this.windowMs) + resetTime: + result.resetTime || new Date(Date.now() + this.windowMs) }; } catch (error) { logger.error(`RedisStore getHits error for key ${key}:`, error); @@ -215,9 +223,9 @@ export default class RedisStore implements Store { async shutdown(): Promise { try { // The rateLimitService handles its own cleanup - logger.info('RedisStore shutdown completed'); + logger.info("RedisStore shutdown completed"); } catch (error) { - logger.error('RedisStore shutdown error:', error); + logger.error("RedisStore shutdown error:", error); } } } diff --git a/server/private/lib/resend.ts b/server/private/lib/resend.ts index 17384ea3..42a11c15 100644 --- a/server/private/lib/resend.ts +++ b/server/private/lib/resend.ts @@ -16,10 +16,10 @@ import privateConfig from "#private/lib/config"; import logger from "@server/logger"; export enum AudienceIds { - SignUps = "6c4e77b2-0851-4bd6-bac8-f51f91360f1a", - Subscribed = "870b43fd-387f-44de-8fc1-707335f30b20", - Churned = "f3ae92bd-2fdb-4d77-8746-2118afd62549", - Newsletter = "5500c431-191c-42f0-a5d4-8b6d445b4ea0" + SignUps = "6c4e77b2-0851-4bd6-bac8-f51f91360f1a", + Subscribed = "870b43fd-387f-44de-8fc1-707335f30b20", + Churned = "f3ae92bd-2fdb-4d77-8746-2118afd62549", + Newsletter = "5500c431-191c-42f0-a5d4-8b6d445b4ea0" } const resend = new Resend( @@ -33,7 +33,9 @@ export async function moveEmailToAudience( audienceId: AudienceIds ) { if (process.env.ENVIRONMENT !== "prod") { - logger.debug(`Skipping moving email ${email} to audience ${audienceId} in non-prod environment`); + logger.debug( + `Skipping moving email ${email} to audience ${audienceId} in non-prod environment` + ); return; } const { error, data } = await retryWithBackoff(async () => { diff --git a/server/private/lib/traefik/index.ts b/server/private/lib/traefik/index.ts index 30d83181..5f2c2635 100644 --- a/server/private/lib/traefik/index.ts +++ b/server/private/lib/traefik/index.ts @@ -11,4 +11,4 @@ * This file is not licensed under the AGPLv3. */ -export * from "./getTraefikConfig"; \ No newline at end of file +export * from "./getTraefikConfig"; diff --git a/server/private/license/licenseJwt.ts b/server/private/license/licenseJwt.ts index f137db30..eb27b78f 100644 --- a/server/private/license/licenseJwt.ts +++ b/server/private/license/licenseJwt.ts @@ -19,10 +19,7 @@ import * as crypto from "crypto"; * @param publicKey - The public key used for verification (PEM format) * @returns The decoded payload if validation succeeds, throws an error otherwise */ -function validateJWT( - token: string, - publicKey: string -): Payload { +function validateJWT(token: string, publicKey: string): Payload { // Split the JWT into its three parts const parts = token.split("."); if (parts.length !== 3) { diff --git a/server/private/middlewares/logActionAudit.ts b/server/private/middlewares/logActionAudit.ts index c89a8896..17cc67c0 100644 --- a/server/private/middlewares/logActionAudit.ts +++ b/server/private/middlewares/logActionAudit.ts @@ -41,7 +41,11 @@ async function getActionDays(orgId: string): Promise { } // store the result in cache - cache.set(`org_${orgId}_actionDays`, org.settingsLogRetentionDaysAction, 300); + cache.set( + `org_${orgId}_actionDays`, + org.settingsLogRetentionDaysAction, + 300 + ); return org.settingsLogRetentionDaysAction; } @@ -141,4 +145,3 @@ export function logActionAudit(action: ActionsEnum) { } }; } - diff --git a/server/private/middlewares/verifyCertificateAccess.ts b/server/private/middlewares/verifyCertificateAccess.ts index 1708215e..dcc57dca 100644 --- a/server/private/middlewares/verifyCertificateAccess.ts +++ b/server/private/middlewares/verifyCertificateAccess.ts @@ -28,7 +28,8 @@ export async function verifyCertificateAccess( try { // Assume user/org access is already verified const orgId = req.params.orgId; - const certId = req.params.certId || req.body?.certId || req.query?.certId; + const certId = + req.params.certId || req.body?.certId || req.query?.certId; let domainId = req.params.domainId || req.body?.domainId || req.query?.domainId; @@ -39,10 +40,12 @@ export async function verifyCertificateAccess( } if (!domainId) { - if (!certId) { return next( - createHttpError(HttpCode.BAD_REQUEST, "Must provide either certId or domainId") + createHttpError( + HttpCode.BAD_REQUEST, + "Must provide either certId or domainId" + ) ); } @@ -75,7 +78,10 @@ export async function verifyCertificateAccess( if (!domainId) { return next( - createHttpError(HttpCode.BAD_REQUEST, "Must provide either certId or domainId") + createHttpError( + HttpCode.BAD_REQUEST, + "Must provide either certId or domainId" + ) ); } diff --git a/server/private/middlewares/verifyIdpAccess.ts b/server/private/middlewares/verifyIdpAccess.ts index 87397a3d..41095684 100644 --- a/server/private/middlewares/verifyIdpAccess.ts +++ b/server/private/middlewares/verifyIdpAccess.ts @@ -24,8 +24,7 @@ export async function verifyIdpAccess( ) { try { const userId = req.user!.userId; - const idpId = - req.params.idpId || req.body.idpId || req.query.idpId; + const idpId = req.params.idpId || req.body.idpId || req.query.idpId; const orgId = req.params.orgId; if (!userId) { @@ -50,9 +49,7 @@ export async function verifyIdpAccess( .select() .from(idp) .innerJoin(idpOrg, eq(idp.idpId, idpOrg.idpId)) - .where( - and(eq(idp.idpId, idpId), eq(idpOrg.orgId, orgId)) - ) + .where(and(eq(idp.idpId, idpId), eq(idpOrg.orgId, orgId))) .limit(1); if (!idpRes || !idpRes.idp || !idpRes.idpOrg) { diff --git a/server/private/middlewares/verifyRemoteExitNode.ts b/server/private/middlewares/verifyRemoteExitNode.ts index 2f6d99d2..8abdc47e 100644 --- a/server/private/middlewares/verifyRemoteExitNode.ts +++ b/server/private/middlewares/verifyRemoteExitNode.ts @@ -26,7 +26,8 @@ export const verifySessionRemoteExitNodeMiddleware = async ( // get the token from the auth header const token = req.headers["authorization"]?.split(" ")[1] || ""; - const { session, remoteExitNode } = await validateRemoteExitNodeSessionToken(token); + const { session, remoteExitNode } = + await validateRemoteExitNodeSessionToken(token); if (!session || !remoteExitNode) { if (config.getRawConfig().app.log_failed_attempts) { diff --git a/server/private/routers/auditLogs/exportAccessAuditLog.ts b/server/private/routers/auditLogs/exportAccessAuditLog.ts index 89aef6cb..fbeca932 100644 --- a/server/private/routers/auditLogs/exportAccessAuditLog.ts +++ b/server/private/routers/auditLogs/exportAccessAuditLog.ts @@ -19,7 +19,11 @@ import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; import { fromError } from "zod-validation-error"; import logger from "@server/logger"; -import { queryAccessAuditLogsParams, queryAccessAuditLogsQuery, queryAccess } from "./queryAccessAuditLog"; +import { + queryAccessAuditLogsParams, + queryAccessAuditLogsQuery, + queryAccess +} from "./queryAccessAuditLog"; import { generateCSV } from "@server/routers/auditLogs/generateCSV"; registry.registerPath({ @@ -67,10 +71,13 @@ export async function exportAccessAuditLogs( const log = await baseQuery.limit(data.limit).offset(data.offset); const csvData = generateCSV(log); - - res.setHeader('Content-Type', 'text/csv'); - res.setHeader('Content-Disposition', `attachment; filename="access-audit-logs-${data.orgId}-${Date.now()}.csv"`); - + + res.setHeader("Content-Type", "text/csv"); + res.setHeader( + "Content-Disposition", + `attachment; filename="access-audit-logs-${data.orgId}-${Date.now()}.csv"` + ); + return res.send(csvData); } catch (error) { logger.error(error); @@ -78,4 +85,4 @@ export async function exportAccessAuditLogs( createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") ); } -} \ No newline at end of file +} diff --git a/server/private/routers/auditLogs/exportActionAuditLog.ts b/server/private/routers/auditLogs/exportActionAuditLog.ts index 12c9ff8b..1fc4d743 100644 --- a/server/private/routers/auditLogs/exportActionAuditLog.ts +++ b/server/private/routers/auditLogs/exportActionAuditLog.ts @@ -19,7 +19,11 @@ import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; import { fromError } from "zod-validation-error"; import logger from "@server/logger"; -import { queryActionAuditLogsParams, queryActionAuditLogsQuery, queryAction } from "./queryActionAuditLog"; +import { + queryActionAuditLogsParams, + queryActionAuditLogsQuery, + queryAction +} from "./queryActionAuditLog"; import { generateCSV } from "@server/routers/auditLogs/generateCSV"; registry.registerPath({ @@ -60,17 +64,20 @@ export async function exportActionAuditLogs( ); } - const data = { ...parsedQuery.data, ...parsedParams.data }; + const data = { ...parsedQuery.data, ...parsedParams.data }; const baseQuery = queryAction(data); const log = await baseQuery.limit(data.limit).offset(data.offset); const csvData = generateCSV(log); - - res.setHeader('Content-Type', 'text/csv'); - res.setHeader('Content-Disposition', `attachment; filename="action-audit-logs-${data.orgId}-${Date.now()}.csv"`); - + + res.setHeader("Content-Type", "text/csv"); + res.setHeader( + "Content-Disposition", + `attachment; filename="action-audit-logs-${data.orgId}-${Date.now()}.csv"` + ); + return res.send(csvData); } catch (error) { logger.error(error); @@ -78,4 +85,4 @@ export async function exportActionAuditLogs( createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") ); } -} \ No newline at end of file +} diff --git a/server/private/routers/auditLogs/index.ts b/server/private/routers/auditLogs/index.ts index ac623c4c..e1849a61 100644 --- a/server/private/routers/auditLogs/index.ts +++ b/server/private/routers/auditLogs/index.ts @@ -14,4 +14,4 @@ export * from "./queryActionAuditLog"; export * from "./exportActionAuditLog"; export * from "./queryAccessAuditLog"; -export * from "./exportAccessAuditLog"; \ No newline at end of file +export * from "./exportAccessAuditLog"; diff --git a/server/private/routers/auditLogs/queryAccessAuditLog.ts b/server/private/routers/auditLogs/queryAccessAuditLog.ts index 769dcf55..5d3162aa 100644 --- a/server/private/routers/auditLogs/queryAccessAuditLog.ts +++ b/server/private/routers/auditLogs/queryAccessAuditLog.ts @@ -44,7 +44,8 @@ export const queryAccessAuditLogsQuery = z.object({ .openapi({ type: "string", format: "date-time", - description: "End time as ISO date string (defaults to current time)" + description: + "End time as ISO date string (defaults to current time)" }), action: z .union([z.boolean(), z.string()]) @@ -181,9 +182,15 @@ async function queryUniqueFilterAttributes( .where(baseConditions); return { - actors: uniqueActors.map(row => row.actor).filter((actor): actor is string => actor !== null), - resources: uniqueResources.filter((row): row is { id: number; name: string | null } => row.id !== null), - locations: uniqueLocations.map(row => row.locations).filter((location): location is string => location !== null) + actors: uniqueActors + .map((row) => row.actor) + .filter((actor): actor is string => actor !== null), + resources: uniqueResources.filter( + (row): row is { id: number; name: string | null } => row.id !== null + ), + locations: uniqueLocations + .map((row) => row.locations) + .filter((location): location is string => location !== null) }; } diff --git a/server/private/routers/auditLogs/queryActionAuditLog.ts b/server/private/routers/auditLogs/queryActionAuditLog.ts index d4a43879..eca583b4 100644 --- a/server/private/routers/auditLogs/queryActionAuditLog.ts +++ b/server/private/routers/auditLogs/queryActionAuditLog.ts @@ -44,7 +44,8 @@ export const queryActionAuditLogsQuery = z.object({ .openapi({ type: "string", format: "date-time", - description: "End time as ISO date string (defaults to current time)" + description: + "End time as ISO date string (defaults to current time)" }), action: z.string().optional(), actorType: z.string().optional(), @@ -68,8 +69,9 @@ export const queryActionAuditLogsParams = z.object({ orgId: z.string() }); -export const queryActionAuditLogsCombined = - queryActionAuditLogsQuery.merge(queryActionAuditLogsParams); +export const queryActionAuditLogsCombined = queryActionAuditLogsQuery.merge( + queryActionAuditLogsParams +); type Q = z.infer; function getWhere(data: Q) { @@ -78,7 +80,9 @@ function getWhere(data: Q) { lt(actionAuditLog.timestamp, data.timeEnd), eq(actionAuditLog.orgId, data.orgId), data.actor ? eq(actionAuditLog.actor, data.actor) : undefined, - data.actorType ? eq(actionAuditLog.actorType, data.actorType) : undefined, + data.actorType + ? eq(actionAuditLog.actorType, data.actorType) + : undefined, data.actorId ? eq(actionAuditLog.actorId, data.actorId) : undefined, data.action ? eq(actionAuditLog.action, data.action) : undefined ); @@ -135,8 +139,12 @@ async function queryUniqueFilterAttributes( .where(baseConditions); return { - actors: uniqueActors.map(row => row.actor).filter((actor): actor is string => actor !== null), - actions: uniqueActions.map(row => row.action).filter((action): action is string => action !== null), + actors: uniqueActors + .map((row) => row.actor) + .filter((actor): actor is string => actor !== null), + actions: uniqueActions + .map((row) => row.action) + .filter((action): action is string => action !== null) }; } diff --git a/server/private/routers/auth/index.ts b/server/private/routers/auth/index.ts index 39a60031..535d5887 100644 --- a/server/private/routers/auth/index.ts +++ b/server/private/routers/auth/index.ts @@ -13,4 +13,4 @@ export * from "./transferSession"; export * from "./getSessionTransferToken"; -export * from "./quickStart"; \ No newline at end of file +export * from "./quickStart"; diff --git a/server/private/routers/auth/quickStart.ts b/server/private/routers/auth/quickStart.ts index 02023a0b..612a3951 100644 --- a/server/private/routers/auth/quickStart.ts +++ b/server/private/routers/auth/quickStart.ts @@ -395,7 +395,8 @@ export async function quickStart( .values({ targetId: newTarget[0].targetId, hcEnabled: false - }).returning(); + }) + .returning(); // add the new target to the targetIps array targetIps.push(`${ip}/32`); @@ -406,7 +407,12 @@ export async function quickStart( .where(eq(newts.siteId, siteId!)) .limit(1); - await addTargets(newt.newtId, newTarget, newHealthcheck, resource.protocol); + await addTargets( + newt.newtId, + newTarget, + newHealthcheck, + resource.protocol + ); // Set resource pincode if provided if (pincode) { diff --git a/server/private/routers/billing/createCheckoutSession.ts b/server/private/routers/billing/createCheckoutSession.ts index e0e08a20..a2d8080f 100644 --- a/server/private/routers/billing/createCheckoutSession.ts +++ b/server/private/routers/billing/createCheckoutSession.ts @@ -26,8 +26,8 @@ import { getLineItems, getStandardFeaturePriceSet } from "@server/lib/billing"; import { getTierPriceSet, TierId } from "@server/lib/billing/tiers"; const createCheckoutSessionSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); export async function createCheckoutSession( req: Request, @@ -72,7 +72,7 @@ export async function createCheckoutSession( billing_address_collection: "required", line_items: [ { - price: standardTierPrice, // Use the standard tier + price: standardTierPrice, // Use the standard tier quantity: 1 }, ...getLineItems(getStandardFeaturePriceSet()) diff --git a/server/private/routers/billing/createPortalSession.ts b/server/private/routers/billing/createPortalSession.ts index a3a2f04f..9ebe84e0 100644 --- a/server/private/routers/billing/createPortalSession.ts +++ b/server/private/routers/billing/createPortalSession.ts @@ -24,8 +24,8 @@ import { fromError } from "zod-validation-error"; import stripe from "#private/lib/stripe"; const createPortalSessionSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); export async function createPortalSession( req: Request, diff --git a/server/private/routers/billing/getOrgSubscription.ts b/server/private/routers/billing/getOrgSubscription.ts index adc4ee04..e1f8316e 100644 --- a/server/private/routers/billing/getOrgSubscription.ts +++ b/server/private/routers/billing/getOrgSubscription.ts @@ -34,8 +34,8 @@ import { } from "@server/db"; const getOrgSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); registry.registerPath({ method: "get", diff --git a/server/private/routers/billing/getOrgUsage.ts b/server/private/routers/billing/getOrgUsage.ts index 9e605cca..1a343730 100644 --- a/server/private/routers/billing/getOrgUsage.ts +++ b/server/private/routers/billing/getOrgUsage.ts @@ -28,8 +28,8 @@ import { FeatureId } from "@server/lib/billing"; import { GetOrgUsageResponse } from "@server/routers/billing/types"; const getOrgSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); registry.registerPath({ method: "get", @@ -78,11 +78,23 @@ export async function getOrgUsage( // Get usage for org const usageData = []; - const siteUptime = await usageService.getUsage(orgId, FeatureId.SITE_UPTIME); + const siteUptime = await usageService.getUsage( + orgId, + FeatureId.SITE_UPTIME + ); const users = await usageService.getUsageDaily(orgId, FeatureId.USERS); - const domains = await usageService.getUsageDaily(orgId, FeatureId.DOMAINS); - const remoteExitNodes = await usageService.getUsageDaily(orgId, FeatureId.REMOTE_EXIT_NODES); - const egressData = await usageService.getUsage(orgId, FeatureId.EGRESS_DATA_MB); + const domains = await usageService.getUsageDaily( + orgId, + FeatureId.DOMAINS + ); + const remoteExitNodes = await usageService.getUsageDaily( + orgId, + FeatureId.REMOTE_EXIT_NODES + ); + const egressData = await usageService.getUsage( + orgId, + FeatureId.EGRESS_DATA_MB + ); if (siteUptime) { usageData.push(siteUptime); @@ -100,7 +112,8 @@ export async function getOrgUsage( usageData.push(remoteExitNodes); } - const orgLimits = await db.select() + const orgLimits = await db + .select() .from(limits) .where(eq(limits.orgId, orgId)); diff --git a/server/private/routers/billing/hooks/handleCustomerDeleted.ts b/server/private/routers/billing/hooks/handleCustomerDeleted.ts index aa2e6964..e4140353 100644 --- a/server/private/routers/billing/hooks/handleCustomerDeleted.ts +++ b/server/private/routers/billing/hooks/handleCustomerDeleted.ts @@ -31,9 +31,7 @@ export async function handleCustomerDeleted( return; } - await db - .delete(customers) - .where(eq(customers.customerId, customer.id)); + await db.delete(customers).where(eq(customers.customerId, customer.id)); } catch (error) { logger.error( `Error handling customer created event for ID ${customer.id}:`, diff --git a/server/private/routers/billing/hooks/handleSubscriptionDeleted.ts b/server/private/routers/billing/hooks/handleSubscriptionDeleted.ts index 114a4b30..7a7d9149 100644 --- a/server/private/routers/billing/hooks/handleSubscriptionDeleted.ts +++ b/server/private/routers/billing/hooks/handleSubscriptionDeleted.ts @@ -12,7 +12,14 @@ */ import Stripe from "stripe"; -import { subscriptions, db, subscriptionItems, customers, userOrgs, users } from "@server/db"; +import { + subscriptions, + db, + subscriptionItems, + customers, + userOrgs, + users +} from "@server/db"; import { eq, and } from "drizzle-orm"; import logger from "@server/logger"; import { handleSubscriptionLifesycle } from "../subscriptionLifecycle"; @@ -43,7 +50,6 @@ export async function handleSubscriptionDeleted( .delete(subscriptionItems) .where(eq(subscriptionItems.subscriptionId, subscription.id)); - // Lookup customer to get orgId const [customer] = await db .select() @@ -58,10 +64,7 @@ export async function handleSubscriptionDeleted( return; } - await handleSubscriptionLifesycle( - customer.orgId, - subscription.status - ); + await handleSubscriptionLifesycle(customer.orgId, subscription.status); const [orgUserRes] = await db .select() diff --git a/server/private/routers/billing/index.ts b/server/private/routers/billing/index.ts index 913ae865..59fce8d6 100644 --- a/server/private/routers/billing/index.ts +++ b/server/private/routers/billing/index.ts @@ -15,4 +15,4 @@ export * from "./createCheckoutSession"; export * from "./createPortalSession"; export * from "./getOrgSubscription"; export * from "./getOrgUsage"; -export * from "./internalGetOrgTier"; \ No newline at end of file +export * from "./internalGetOrgTier"; diff --git a/server/private/routers/billing/internalGetOrgTier.ts b/server/private/routers/billing/internalGetOrgTier.ts index ec114cca..92bbc2ba 100644 --- a/server/private/routers/billing/internalGetOrgTier.ts +++ b/server/private/routers/billing/internalGetOrgTier.ts @@ -22,8 +22,8 @@ import { getOrgTierData } from "#private/lib/billing"; import { GetOrgTierResponse } from "@server/routers/billing/types"; const getOrgSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); export async function getOrgTier( req: Request, diff --git a/server/private/routers/billing/subscriptionLifecycle.ts b/server/private/routers/billing/subscriptionLifecycle.ts index 06b2a2a8..0fc75835 100644 --- a/server/private/routers/billing/subscriptionLifecycle.ts +++ b/server/private/routers/billing/subscriptionLifecycle.ts @@ -11,11 +11,18 @@ * This file is not licensed under the AGPLv3. */ -import { freeLimitSet, limitsService, subscribedLimitSet } from "@server/lib/billing"; +import { + freeLimitSet, + limitsService, + subscribedLimitSet +} from "@server/lib/billing"; import { usageService } from "@server/lib/billing/usageService"; import logger from "@server/logger"; -export async function handleSubscriptionLifesycle(orgId: string, status: string) { +export async function handleSubscriptionLifesycle( + orgId: string, + status: string +) { switch (status) { case "active": await limitsService.applyLimitSetToOrg(orgId, subscribedLimitSet); @@ -42,4 +49,4 @@ export async function handleSubscriptionLifesycle(orgId: string, status: string) default: break; } -} \ No newline at end of file +} diff --git a/server/private/routers/billing/webhooks.ts b/server/private/routers/billing/webhooks.ts index 24ad1074..9c64350c 100644 --- a/server/private/routers/billing/webhooks.ts +++ b/server/private/routers/billing/webhooks.ts @@ -32,12 +32,13 @@ export async function billingWebhookHandler( next: NextFunction ): Promise { let event: Stripe.Event = req.body; - const endpointSecret = privateConfig.getRawPrivateConfig().stripe?.webhook_secret; + const endpointSecret = + privateConfig.getRawPrivateConfig().stripe?.webhook_secret; if (!endpointSecret) { - logger.warn("Stripe webhook secret is not configured. Webhook events will not be priocessed."); - return next( - createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "") + logger.warn( + "Stripe webhook secret is not configured. Webhook events will not be priocessed." ); + return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "")); } // Only verify the event if you have an endpoint secret defined. @@ -49,7 +50,10 @@ export async function billingWebhookHandler( if (!signature) { logger.info("No stripe signature found in headers."); return next( - createHttpError(HttpCode.BAD_REQUEST, "No stripe signature found in headers") + createHttpError( + HttpCode.BAD_REQUEST, + "No stripe signature found in headers" + ) ); } @@ -62,7 +66,10 @@ export async function billingWebhookHandler( } catch (err) { logger.error(`Webhook signature verification failed.`, err); return next( - createHttpError(HttpCode.UNAUTHORIZED, "Webhook signature verification failed") + createHttpError( + HttpCode.UNAUTHORIZED, + "Webhook signature verification failed" + ) ); } } diff --git a/server/private/routers/certificates/getCertificate.ts b/server/private/routers/certificates/getCertificate.ts index 4ff8184e..d06a1bad 100644 --- a/server/private/routers/certificates/getCertificate.ts +++ b/server/private/routers/certificates/getCertificate.ts @@ -24,10 +24,10 @@ import { registry } from "@server/openApi"; import { GetCertificateResponse } from "@server/routers/certificates/types"; const getCertificateSchema = z.strictObject({ - domainId: z.string(), - domain: z.string().min(1).max(255), - orgId: z.string() - }); + domainId: z.string(), + domain: z.string().min(1).max(255), + orgId: z.string() +}); async function query(domainId: string, domain: string) { const [domainRecord] = await db @@ -42,8 +42,8 @@ async function query(domainId: string, domain: string) { let existing: any[] = []; if (domainRecord.type == "ns") { - const domainLevelDown = domain.split('.').slice(1).join('.'); - + const domainLevelDown = domain.split(".").slice(1).join("."); + existing = await db .select({ certId: certificates.certId, @@ -64,7 +64,7 @@ async function query(domainId: string, domain: string) { eq(certificates.wildcard, true), // only NS domains can have wildcard certs or( eq(certificates.domain, domain), - eq(certificates.domain, domainLevelDown), + eq(certificates.domain, domainLevelDown) ) ) ); @@ -102,8 +102,7 @@ registry.registerPath({ tags: ["Certificate"], request: { params: z.object({ - domainId: z - .string(), + domainId: z.string(), domain: z.string().min(1).max(255), orgId: z.string() }) @@ -133,7 +132,9 @@ export async function getCertificate( if (!cert) { logger.warn(`Certificate not found for domain: ${domainId}`); - return next(createHttpError(HttpCode.NOT_FOUND, "Certificate not found")); + return next( + createHttpError(HttpCode.NOT_FOUND, "Certificate not found") + ); } return response(res, { diff --git a/server/private/routers/certificates/index.ts b/server/private/routers/certificates/index.ts index e1b81ae1..b1543e5d 100644 --- a/server/private/routers/certificates/index.ts +++ b/server/private/routers/certificates/index.ts @@ -12,4 +12,4 @@ */ export * from "./getCertificate"; -export * from "./restartCertificate"; \ No newline at end of file +export * from "./restartCertificate"; diff --git a/server/private/routers/certificates/restartCertificate.ts b/server/private/routers/certificates/restartCertificate.ts index a6ee5460..0e4b1910 100644 --- a/server/private/routers/certificates/restartCertificate.ts +++ b/server/private/routers/certificates/restartCertificate.ts @@ -25,9 +25,9 @@ import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; const restartCertificateParamsSchema = z.strictObject({ - certId: z.string().transform(stoi).pipe(z.int().positive()), - orgId: z.string() - }); + certId: z.string().transform(stoi).pipe(z.int().positive()), + orgId: z.string() +}); registry.registerPath({ method: "post", @@ -36,10 +36,7 @@ registry.registerPath({ tags: ["Certificate"], request: { params: z.object({ - certId: z - .string() - .transform(stoi) - .pipe(z.int().positive()), + certId: z.string().transform(stoi).pipe(z.int().positive()), orgId: z.string() }) }, @@ -94,7 +91,7 @@ export async function restartCertificate( .set({ status: "pending", errorMessage: null, - lastRenewalAttempt: Math.floor(Date.now() / 1000) + lastRenewalAttempt: Math.floor(Date.now() / 1000) }) .where(eq(certificates.certId, certId)); diff --git a/server/private/routers/domain/checkDomainNamespaceAvailability.ts b/server/private/routers/domain/checkDomainNamespaceAvailability.ts index 6c9cb23c..db9a4b46 100644 --- a/server/private/routers/domain/checkDomainNamespaceAvailability.ts +++ b/server/private/routers/domain/checkDomainNamespaceAvailability.ts @@ -26,8 +26,8 @@ import { CheckDomainAvailabilityResponse } from "@server/routers/domain/types"; const paramsSchema = z.strictObject({}); const querySchema = z.strictObject({ - subdomain: z.string() - }); + subdomain: z.string() +}); registry.registerPath({ method: "get", diff --git a/server/private/routers/domain/index.ts b/server/private/routers/domain/index.ts index da9cec3f..3f4bbbf2 100644 --- a/server/private/routers/domain/index.ts +++ b/server/private/routers/domain/index.ts @@ -12,4 +12,4 @@ */ export * from "./checkDomainNamespaceAvailability"; -export * from "./listDomainNamespaces"; \ No newline at end of file +export * from "./listDomainNamespaces"; diff --git a/server/private/routers/domain/listDomainNamespaces.ts b/server/private/routers/domain/listDomainNamespaces.ts index 29d5d201..180613a8 100644 --- a/server/private/routers/domain/listDomainNamespaces.ts +++ b/server/private/routers/domain/listDomainNamespaces.ts @@ -26,19 +26,19 @@ import { OpenAPITags, registry } from "@server/openApi"; const paramsSchema = z.strictObject({}); const querySchema = z.strictObject({ - limit: z - .string() - .optional() - .default("1000") - .transform(Number) - .pipe(z.int().nonnegative()), - offset: z - .string() - .optional() - .default("0") - .transform(Number) - .pipe(z.int().nonnegative()) - }); + limit: z + .string() + .optional() + .default("1000") + .transform(Number) + .pipe(z.int().nonnegative()), + offset: z + .string() + .optional() + .default("0") + .transform(Number) + .pipe(z.int().nonnegative()) +}); async function query(limit: number, offset: number) { const res = await db diff --git a/server/private/routers/hybrid.ts b/server/private/routers/hybrid.ts index a61f37b2..3accc500 100644 --- a/server/private/routers/hybrid.ts +++ b/server/private/routers/hybrid.ts @@ -79,86 +79,72 @@ import semver from "semver"; // Zod schemas for request validation const getResourceByDomainParamsSchema = z.strictObject({ - domain: z.string().min(1, "Domain is required") - }); + domain: z.string().min(1, "Domain is required") +}); const getUserSessionParamsSchema = z.strictObject({ - userSessionId: z.string().min(1, "User session ID is required") - }); + userSessionId: z.string().min(1, "User session ID is required") +}); const getUserOrgRoleParamsSchema = z.strictObject({ - userId: z.string().min(1, "User ID is required"), - orgId: z.string().min(1, "Organization ID is required") - }); + userId: z.string().min(1, "User ID is required"), + orgId: z.string().min(1, "Organization ID is required") +}); const getRoleResourceAccessParamsSchema = z.strictObject({ - roleId: z - .string() - .transform(Number) - .pipe( - z.int().positive("Role ID must be a positive integer") - ), - resourceId: z - .string() - .transform(Number) - .pipe( - z.int() - .positive("Resource ID must be a positive integer") - ) - }); + roleId: z + .string() + .transform(Number) + .pipe(z.int().positive("Role ID must be a positive integer")), + resourceId: z + .string() + .transform(Number) + .pipe(z.int().positive("Resource ID must be a positive integer")) +}); const getUserResourceAccessParamsSchema = z.strictObject({ - userId: z.string().min(1, "User ID is required"), - resourceId: z - .string() - .transform(Number) - .pipe( - z.int() - .positive("Resource ID must be a positive integer") - ) - }); + userId: z.string().min(1, "User ID is required"), + resourceId: z + .string() + .transform(Number) + .pipe(z.int().positive("Resource ID must be a positive integer")) +}); const getResourceRulesParamsSchema = z.strictObject({ - resourceId: z - .string() - .transform(Number) - .pipe( - z.int() - .positive("Resource ID must be a positive integer") - ) - }); + resourceId: z + .string() + .transform(Number) + .pipe(z.int().positive("Resource ID must be a positive integer")) +}); const validateResourceSessionTokenParamsSchema = z.strictObject({ - resourceId: z - .string() - .transform(Number) - .pipe( - z.int() - .positive("Resource ID must be a positive integer") - ) - }); + resourceId: z + .string() + .transform(Number) + .pipe(z.int().positive("Resource ID must be a positive integer")) +}); const validateResourceSessionTokenBodySchema = z.strictObject({ - token: z.string().min(1, "Token is required") - }); + token: z.string().min(1, "Token is required") +}); const validateResourceAccessTokenBodySchema = z.strictObject({ - accessTokenId: z.string().optional(), - resourceId: z.number().optional(), - accessToken: z.string() - }); + accessTokenId: z.string().optional(), + resourceId: z.number().optional(), + accessToken: z.string() +}); // Certificates by domains query validation const getCertificatesByDomainsQuerySchema = z.strictObject({ - // Accept domains as string or array (domains or domains[]) - domains: z - .union([z.array(z.string().min(1)), z.string().min(1)]) - .optional(), - // Handle array format from query parameters (domains[]) - "domains[]": z - .union([z.array(z.string().min(1)), z.string().min(1)]) - .optional() - }); + // Accept domains as string or array (domains or domains[]) + domains: z + .union([z.array(z.string().min(1)), z.string().min(1)]) + .optional(), + // Handle array format from query parameters (domains[]) + "domains[]": z + .union([z.array(z.string().min(1)), z.string().min(1)]) + .optional() +}); // Type exports for request schemas export type GetResourceByDomainParams = z.infer< @@ -566,8 +552,8 @@ hybridRouter.get( ); const getOrgLoginPageParamsSchema = z.strictObject({ - orgId: z.string().min(1) - }); + orgId: z.string().min(1) +}); hybridRouter.get( "/org/:orgId/login-page", @@ -1408,8 +1394,16 @@ hybridRouter.post( ); } - const { olmId, newtId, ip, port, timestamp, token, publicKey, reachableAt } = - parsedParams.data; + const { + olmId, + newtId, + ip, + port, + timestamp, + token, + publicKey, + reachableAt + } = parsedParams.data; const destinations = await updateAndGenerateEndpointDestinations( olmId, diff --git a/server/private/routers/integration.ts b/server/private/routers/integration.ts index 7ce378d1..9eefff8f 100644 --- a/server/private/routers/integration.ts +++ b/server/private/routers/integration.ts @@ -18,7 +18,7 @@ import * as logs from "#private/routers/auditLogs"; import { verifyApiKeyHasAction, verifyApiKeyIsRoot, - verifyApiKeyOrgAccess, + verifyApiKeyOrgAccess } from "@server/middlewares"; import { verifyValidSubscription, @@ -26,7 +26,10 @@ import { } from "#private/middlewares"; import { ActionsEnum } from "@server/auth/actions"; -import { unauthenticated as ua, authenticated as a } from "@server/routers/integration"; +import { + unauthenticated as ua, + authenticated as a +} from "@server/routers/integration"; import { logActionAudit } from "#private/middlewares"; export const unauthenticated = ua; @@ -37,7 +40,7 @@ authenticated.post( verifyApiKeyIsRoot, // We are the only ones who can use root key so its fine verifyApiKeyHasAction(ActionsEnum.sendUsageNotification), logActionAudit(ActionsEnum.sendUsageNotification), - org.sendUsageNotification, + org.sendUsageNotification ); authenticated.delete( @@ -45,7 +48,7 @@ authenticated.delete( verifyApiKeyIsRoot, verifyApiKeyHasAction(ActionsEnum.deleteIdp), logActionAudit(ActionsEnum.deleteIdp), - orgIdp.deleteOrgIdp, + orgIdp.deleteOrgIdp ); authenticated.get( diff --git a/server/private/routers/license/activateLicense.ts b/server/private/routers/license/activateLicense.ts index 55b7827e..f6c8d266 100644 --- a/server/private/routers/license/activateLicense.ts +++ b/server/private/routers/license/activateLicense.ts @@ -21,8 +21,8 @@ import { z } from "zod"; import { fromError } from "zod-validation-error"; const bodySchema = z.strictObject({ - licenseKey: z.string().min(1).max(255) - }); + licenseKey: z.string().min(1).max(255) +}); export async function activateLicense( req: Request, diff --git a/server/private/routers/license/deleteLicenseKey.ts b/server/private/routers/license/deleteLicenseKey.ts index 6f5469fc..80212e6a 100644 --- a/server/private/routers/license/deleteLicenseKey.ts +++ b/server/private/routers/license/deleteLicenseKey.ts @@ -24,8 +24,8 @@ import { licenseKey } from "@server/db"; import license from "#private/license/license"; const paramsSchema = z.strictObject({ - licenseKey: z.string().min(1).max(255) - }); + licenseKey: z.string().min(1).max(255) +}); export async function deleteLicenseKey( req: Request, diff --git a/server/private/routers/loginPage/createLoginPage.ts b/server/private/routers/loginPage/createLoginPage.ts index 75744026..b5e8ccff 100644 --- a/server/private/routers/loginPage/createLoginPage.ts +++ b/server/private/routers/loginPage/createLoginPage.ts @@ -36,13 +36,13 @@ import { build } from "@server/build"; import { CreateLoginPageResponse } from "@server/routers/loginPage/types"; const paramsSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); const bodySchema = z.strictObject({ - subdomain: z.string().nullable().optional(), - domainId: z.string() - }); + subdomain: z.string().nullable().optional(), + domainId: z.string() +}); export type CreateLoginPageBody = z.infer; @@ -149,12 +149,20 @@ export async function createLoginPage( let returned: LoginPage | undefined; await db.transaction(async (trx) => { - const orgSites = await trx .select() .from(sites) - .innerJoin(exitNodes, eq(exitNodes.exitNodeId, sites.exitNodeId)) - .where(and(eq(sites.orgId, orgId), eq(exitNodes.type, "gerbil"), eq(exitNodes.online, true))) + .innerJoin( + exitNodes, + eq(exitNodes.exitNodeId, sites.exitNodeId) + ) + .where( + and( + eq(sites.orgId, orgId), + eq(exitNodes.type, "gerbil"), + eq(exitNodes.online, true) + ) + ) .limit(10); let exitNodesList = orgSites.map((s) => s.exitNodes); @@ -163,7 +171,12 @@ export async function createLoginPage( exitNodesList = await trx .select() .from(exitNodes) - .where(and(eq(exitNodes.type, "gerbil"), eq(exitNodes.online, true))) + .where( + and( + eq(exitNodes.type, "gerbil"), + eq(exitNodes.online, true) + ) + ) .limit(10); } diff --git a/server/private/routers/loginPage/deleteLoginPage.ts b/server/private/routers/loginPage/deleteLoginPage.ts index 5271ebd8..0d17a731 100644 --- a/server/private/routers/loginPage/deleteLoginPage.ts +++ b/server/private/routers/loginPage/deleteLoginPage.ts @@ -78,15 +78,11 @@ export async function deleteLoginPage( // if (!leftoverLinks.length) { await db .delete(loginPage) - .where( - eq(loginPage.loginPageId, parsedParams.data.loginPageId) - ); + .where(eq(loginPage.loginPageId, parsedParams.data.loginPageId)); await db .delete(loginPageOrg) - .where( - eq(loginPageOrg.loginPageId, parsedParams.data.loginPageId) - ); + .where(eq(loginPageOrg.loginPageId, parsedParams.data.loginPageId)); // } return response(res, { diff --git a/server/private/routers/loginPage/getLoginPage.ts b/server/private/routers/loginPage/getLoginPage.ts index b3bde203..73f6a357 100644 --- a/server/private/routers/loginPage/getLoginPage.ts +++ b/server/private/routers/loginPage/getLoginPage.ts @@ -23,8 +23,8 @@ import { fromError } from "zod-validation-error"; import { GetLoginPageResponse } from "@server/routers/loginPage/types"; const paramsSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); async function query(orgId: string) { const [res] = await db diff --git a/server/private/routers/loginPage/updateLoginPage.ts b/server/private/routers/loginPage/updateLoginPage.ts index 0d02b124..bda614d3 100644 --- a/server/private/routers/loginPage/updateLoginPage.ts +++ b/server/private/routers/loginPage/updateLoginPage.ts @@ -35,7 +35,8 @@ const paramsSchema = z }) .strict(); -const bodySchema = z.strictObject({ +const bodySchema = z + .strictObject({ subdomain: subdomainSchema.nullable().optional(), domainId: z.string().optional() }) @@ -86,7 +87,7 @@ export async function updateLoginPage( const { loginPageId, orgId } = parsedParams.data; - if (build === "saas"){ + if (build === "saas") { const { tier } = await getOrgTierData(orgId); const subscribed = tier === TierId.STANDARD; if (!subscribed) { @@ -182,7 +183,10 @@ export async function updateLoginPage( } // update the full domain if it has changed - if (fullDomain && fullDomain !== existingLoginPage?.fullDomain) { + if ( + fullDomain && + fullDomain !== existingLoginPage?.fullDomain + ) { await db .update(loginPage) .set({ fullDomain }) diff --git a/server/private/routers/misc/sendSupportEmail.ts b/server/private/routers/misc/sendSupportEmail.ts index f1f7a919..404a2501 100644 --- a/server/private/routers/misc/sendSupportEmail.ts +++ b/server/private/routers/misc/sendSupportEmail.ts @@ -23,9 +23,9 @@ import SupportEmail from "@server/emails/templates/SupportEmail"; import config from "@server/lib/config"; const bodySchema = z.strictObject({ - body: z.string().min(1), - subject: z.string().min(1).max(255) - }); + body: z.string().min(1), + subject: z.string().min(1).max(255) +}); export async function sendSupportEmail( req: Request, diff --git a/server/private/routers/org/index.ts b/server/private/routers/org/index.ts index 189c5323..8d11c42d 100644 --- a/server/private/routers/org/index.ts +++ b/server/private/routers/org/index.ts @@ -11,4 +11,4 @@ * This file is not licensed under the AGPLv3. */ -export * from "./sendUsageNotifications"; \ No newline at end of file +export * from "./sendUsageNotifications"; diff --git a/server/private/routers/org/sendUsageNotifications.ts b/server/private/routers/org/sendUsageNotifications.ts index 3ef27f91..4aa42152 100644 --- a/server/private/routers/org/sendUsageNotifications.ts +++ b/server/private/routers/org/sendUsageNotifications.ts @@ -35,10 +35,12 @@ const sendUsageNotificationBodySchema = z.object({ notificationType: z.enum(["approaching_70", "approaching_90", "reached"]), limitName: z.string(), currentUsage: z.number(), - usageLimit: z.number(), + usageLimit: z.number() }); -type SendUsageNotificationRequest = z.infer; +type SendUsageNotificationRequest = z.infer< + typeof sendUsageNotificationBodySchema +>; export type SendUsageNotificationResponse = { success: boolean; @@ -97,17 +99,13 @@ async function getOrgAdmins(orgId: string) { .where( and( eq(userOrgs.orgId, orgId), - or( - eq(userOrgs.isOwner, true), - eq(roles.isAdmin, true) - ) + or(eq(userOrgs.isOwner, true), eq(roles.isAdmin, true)) ) ); // Filter to only include users with verified emails - const orgAdmins = admins.filter(admin => - admin.email && - admin.email.length > 0 + const orgAdmins = admins.filter( + (admin) => admin.email && admin.email.length > 0 ); return orgAdmins; @@ -119,7 +117,9 @@ export async function sendUsageNotification( next: NextFunction ): Promise { try { - const parsedParams = sendUsageNotificationParamsSchema.safeParse(req.params); + const parsedParams = sendUsageNotificationParamsSchema.safeParse( + req.params + ); if (!parsedParams.success) { return next( createHttpError( @@ -140,12 +140,8 @@ export async function sendUsageNotification( } const { orgId } = parsedParams.data; - const { - notificationType, - limitName, - currentUsage, - usageLimit, - } = parsedBody.data; + const { notificationType, limitName, currentUsage, usageLimit } = + parsedBody.data; // Verify organization exists const org = await db @@ -192,7 +188,10 @@ export async function sendUsageNotification( let template; let subject; - if (notificationType === "approaching_70" || notificationType === "approaching_90") { + if ( + notificationType === "approaching_70" || + notificationType === "approaching_90" + ) { template = NotifyUsageLimitApproaching({ email: admin.email, limitName, @@ -220,10 +219,15 @@ export async function sendUsageNotification( emailsSent++; adminEmails.push(admin.email); - - logger.info(`Usage notification sent to admin ${admin.email} for org ${orgId}`); + + logger.info( + `Usage notification sent to admin ${admin.email} for org ${orgId}` + ); } catch (emailError) { - logger.error(`Failed to send usage notification to ${admin.email}:`, emailError); + logger.error( + `Failed to send usage notification to ${admin.email}:`, + emailError + ); // Continue with other admins even if one fails } } @@ -239,11 +243,13 @@ export async function sendUsageNotification( message: `Usage notifications sent to ${emailsSent} administrators`, status: HttpCode.OK }); - } catch (error) { logger.error("Error sending usage notifications:", error); return next( - createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "Failed to send usage notifications") + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to send usage notifications" + ) ); } } diff --git a/server/private/routers/orgIdp/createOrgOidcIdp.ts b/server/private/routers/orgIdp/createOrgOidcIdp.ts index c3ce774e..709f6167 100644 --- a/server/private/routers/orgIdp/createOrgOidcIdp.ts +++ b/server/private/routers/orgIdp/createOrgOidcIdp.ts @@ -32,19 +32,19 @@ import { CreateOrgIdpResponse } from "@server/routers/orgIdp/types"; const paramsSchema = z.strictObject({ orgId: z.string().nonempty() }); const bodySchema = z.strictObject({ - name: z.string().nonempty(), - clientId: z.string().nonempty(), - clientSecret: z.string().nonempty(), - authUrl: z.url(), - tokenUrl: z.url(), - identifierPath: z.string().nonempty(), - emailPath: z.string().optional(), - namePath: z.string().optional(), - scopes: z.string().nonempty(), - autoProvision: z.boolean().optional(), - variant: z.enum(["oidc", "google", "azure"]).optional().default("oidc"), - roleMapping: z.string().optional() - }); + name: z.string().nonempty(), + clientId: z.string().nonempty(), + clientSecret: z.string().nonempty(), + authUrl: z.url(), + tokenUrl: z.url(), + identifierPath: z.string().nonempty(), + emailPath: z.string().optional(), + namePath: z.string().optional(), + scopes: z.string().nonempty(), + autoProvision: z.boolean().optional(), + variant: z.enum(["oidc", "google", "azure"]).optional().default("oidc"), + roleMapping: z.string().optional() +}); // registry.registerPath({ // method: "put", @@ -158,7 +158,10 @@ export async function createOrgOidcIdp( }); }); - const redirectUrl = await generateOidcRedirectUrl(idpId as number, orgId); + const redirectUrl = await generateOidcRedirectUrl( + idpId as number, + orgId + ); return response(res, { data: { diff --git a/server/private/routers/orgIdp/deleteOrgIdp.ts b/server/private/routers/orgIdp/deleteOrgIdp.ts index ca0112b2..721b91cb 100644 --- a/server/private/routers/orgIdp/deleteOrgIdp.ts +++ b/server/private/routers/orgIdp/deleteOrgIdp.ts @@ -66,12 +66,7 @@ export async function deleteOrgIdp( .where(eq(idp.idpId, idpId)); if (!existingIdp) { - return next( - createHttpError( - HttpCode.NOT_FOUND, - "IdP not found" - ) - ); + return next(createHttpError(HttpCode.NOT_FOUND, "IdP not found")); } // Delete the IDP and its related records in a transaction @@ -82,14 +77,10 @@ export async function deleteOrgIdp( .where(eq(idpOidcConfig.idpId, idpId)); // Delete IDP-org mappings - await trx - .delete(idpOrg) - .where(eq(idpOrg.idpId, idpId)); + await trx.delete(idpOrg).where(eq(idpOrg.idpId, idpId)); // Delete the IDP itself - await trx - .delete(idp) - .where(eq(idp.idpId, idpId)); + await trx.delete(idp).where(eq(idp.idpId, idpId)); }); return response(res, { diff --git a/server/private/routers/orgIdp/getOrgIdp.ts b/server/private/routers/orgIdp/getOrgIdp.ts index 3ba85412..01ddc0f7 100644 --- a/server/private/routers/orgIdp/getOrgIdp.ts +++ b/server/private/routers/orgIdp/getOrgIdp.ts @@ -93,7 +93,10 @@ export async function getOrgIdp( idpRes.idpOidcConfig!.clientId = decrypt(clientId, key); } - const redirectUrl = await generateOidcRedirectUrl(idpRes.idp.idpId, orgId); + const redirectUrl = await generateOidcRedirectUrl( + idpRes.idp.idpId, + orgId + ); return response(res, { data: { diff --git a/server/private/routers/orgIdp/index.ts b/server/private/routers/orgIdp/index.ts index 562582c6..9cf937a4 100644 --- a/server/private/routers/orgIdp/index.ts +++ b/server/private/routers/orgIdp/index.ts @@ -15,4 +15,4 @@ export * from "./createOrgOidcIdp"; export * from "./getOrgIdp"; export * from "./listOrgIdps"; export * from "./updateOrgOidcIdp"; -export * from "./deleteOrgIdp"; \ No newline at end of file +export * from "./deleteOrgIdp"; diff --git a/server/private/routers/orgIdp/listOrgIdps.ts b/server/private/routers/orgIdp/listOrgIdps.ts index 646d808c..36cbc627 100644 --- a/server/private/routers/orgIdp/listOrgIdps.ts +++ b/server/private/routers/orgIdp/listOrgIdps.ts @@ -25,23 +25,23 @@ import { OpenAPITags, registry } from "@server/openApi"; import { ListOrgIdpsResponse } from "@server/routers/orgIdp/types"; const querySchema = z.strictObject({ - limit: z - .string() - .optional() - .default("1000") - .transform(Number) - .pipe(z.int().nonnegative()), - offset: z - .string() - .optional() - .default("0") - .transform(Number) - .pipe(z.int().nonnegative()) - }); + limit: z + .string() + .optional() + .default("1000") + .transform(Number) + .pipe(z.int().nonnegative()), + offset: z + .string() + .optional() + .default("0") + .transform(Number) + .pipe(z.int().nonnegative()) +}); const paramsSchema = z.strictObject({ - orgId: z.string().nonempty() - }); + orgId: z.string().nonempty() +}); async function query(orgId: string, limit: number, offset: number) { const res = await db diff --git a/server/private/routers/orgIdp/updateOrgOidcIdp.ts b/server/private/routers/orgIdp/updateOrgOidcIdp.ts index 3826f6b3..f29e4fc2 100644 --- a/server/private/routers/orgIdp/updateOrgOidcIdp.ts +++ b/server/private/routers/orgIdp/updateOrgOidcIdp.ts @@ -36,18 +36,18 @@ const paramsSchema = z .strict(); const bodySchema = z.strictObject({ - name: z.string().optional(), - clientId: z.string().optional(), - clientSecret: z.string().optional(), - authUrl: z.string().optional(), - tokenUrl: z.string().optional(), - identifierPath: z.string().optional(), - emailPath: z.string().optional(), - namePath: z.string().optional(), - scopes: z.string().optional(), - autoProvision: z.boolean().optional(), - roleMapping: z.string().optional() - }); + name: z.string().optional(), + clientId: z.string().optional(), + clientSecret: z.string().optional(), + authUrl: z.string().optional(), + tokenUrl: z.string().optional(), + identifierPath: z.string().optional(), + emailPath: z.string().optional(), + namePath: z.string().optional(), + scopes: z.string().optional(), + autoProvision: z.boolean().optional(), + roleMapping: z.string().optional() +}); export type UpdateOrgIdpResponse = { idpId: number; diff --git a/server/private/routers/re-key/index.ts b/server/private/routers/re-key/index.ts index 41a1c967..9c1bccf8 100644 --- a/server/private/routers/re-key/index.ts +++ b/server/private/routers/re-key/index.ts @@ -13,4 +13,4 @@ export * from "./reGenerateClientSecret"; export * from "./reGenerateSiteSecret"; -export * from "./reGenerateExitNodeSecret"; \ No newline at end of file +export * from "./reGenerateExitNodeSecret"; diff --git a/server/private/routers/re-key/reGenerateClientSecret.ts b/server/private/routers/re-key/reGenerateClientSecret.ts index 310f2602..5478c690 100644 --- a/server/private/routers/re-key/reGenerateClientSecret.ts +++ b/server/private/routers/re-key/reGenerateClientSecret.ts @@ -123,7 +123,10 @@ export async function reGenerateClientSecret( }; // Don't await this to prevent blocking the response sendToClient(existingOlms[0].olmId, payload).catch((error) => { - logger.error("Failed to send termination message to olm:", error); + logger.error( + "Failed to send termination message to olm:", + error + ); }); disconnectClient(existingOlms[0].olmId).catch((error) => { @@ -133,7 +136,7 @@ export async function reGenerateClientSecret( return response(res, { data: { - olmId: existingOlms[0].olmId, + olmId: existingOlms[0].olmId }, success: true, error: false, diff --git a/server/private/routers/re-key/reGenerateExitNodeSecret.ts b/server/private/routers/re-key/reGenerateExitNodeSecret.ts index b642f102..021d2ce9 100644 --- a/server/private/routers/re-key/reGenerateExitNodeSecret.ts +++ b/server/private/routers/re-key/reGenerateExitNodeSecret.ts @@ -12,7 +12,14 @@ */ import { NextFunction, Request, Response } from "express"; -import { db, exitNodes, exitNodeOrgs, ExitNode, ExitNodeOrg, RemoteExitNode } from "@server/db"; +import { + db, + exitNodes, + exitNodeOrgs, + ExitNode, + ExitNodeOrg, + RemoteExitNode +} from "@server/db"; import HttpCode from "@server/types/HttpCode"; import { z } from "zod"; import { remoteExitNodes } from "@server/db"; @@ -91,14 +98,15 @@ export async function reGenerateExitNodeSecret( data: {} }; // Don't await this to prevent blocking the response - sendToClient(existingRemoteExitNode.remoteExitNodeId, payload).catch( - (error) => { - logger.error( - "Failed to send termination message to remote exit node:", - error - ); - } - ); + sendToClient( + existingRemoteExitNode.remoteExitNodeId, + payload + ).catch((error) => { + logger.error( + "Failed to send termination message to remote exit node:", + error + ); + }); disconnectClient(existingRemoteExitNode.remoteExitNodeId).catch( (error) => { diff --git a/server/private/routers/re-key/reGenerateSiteSecret.ts b/server/private/routers/re-key/reGenerateSiteSecret.ts index b427dcc2..09cf7599 100644 --- a/server/private/routers/re-key/reGenerateSiteSecret.ts +++ b/server/private/routers/re-key/reGenerateSiteSecret.ts @@ -80,7 +80,7 @@ export async function reGenerateSiteSecret( const secretHash = await hashPassword(secret); // get the newt to verify it exists - const existingNewts = await db + const existingNewts = await db .select() .from(newts) .where(eq(newts.siteId, siteId)); @@ -120,15 +120,20 @@ export async function reGenerateSiteSecret( data: {} }; // Don't await this to prevent blocking the response - sendToClient(existingNewts[0].newtId, payload).catch((error) => { - logger.error( - "Failed to send termination message to newt:", - error - ); - }); + sendToClient(existingNewts[0].newtId, payload).catch( + (error) => { + logger.error( + "Failed to send termination message to newt:", + error + ); + } + ); disconnectClient(existingNewts[0].newtId).catch((error) => { - logger.error("Failed to disconnect newt after re-key:", error); + logger.error( + "Failed to disconnect newt after re-key:", + error + ); }); } diff --git a/server/private/routers/remoteExitNode/createRemoteExitNode.ts b/server/private/routers/remoteExitNode/createRemoteExitNode.ts index 5afa82ef..f734813e 100644 --- a/server/private/routers/remoteExitNode/createRemoteExitNode.ts +++ b/server/private/routers/remoteExitNode/createRemoteExitNode.ts @@ -36,9 +36,9 @@ export const paramsSchema = z.object({ }); const bodySchema = z.strictObject({ - remoteExitNodeId: z.string().length(15), - secret: z.string().length(48) - }); + remoteExitNodeId: z.string().length(15), + secret: z.string().length(48) +}); export type CreateRemoteExitNodeBody = z.infer; diff --git a/server/private/routers/remoteExitNode/deleteRemoteExitNode.ts b/server/private/routers/remoteExitNode/deleteRemoteExitNode.ts index e293f421..a23363fc 100644 --- a/server/private/routers/remoteExitNode/deleteRemoteExitNode.ts +++ b/server/private/routers/remoteExitNode/deleteRemoteExitNode.ts @@ -25,9 +25,9 @@ import { usageService } from "@server/lib/billing/usageService"; import { FeatureId } from "@server/lib/billing"; const paramsSchema = z.strictObject({ - orgId: z.string().min(1), - remoteExitNodeId: z.string().min(1) - }); + orgId: z.string().min(1), + remoteExitNodeId: z.string().min(1) +}); export async function deleteRemoteExitNode( req: Request, diff --git a/server/private/routers/remoteExitNode/getRemoteExitNode.ts b/server/private/routers/remoteExitNode/getRemoteExitNode.ts index c7b98297..01ea080c 100644 --- a/server/private/routers/remoteExitNode/getRemoteExitNode.ts +++ b/server/private/routers/remoteExitNode/getRemoteExitNode.ts @@ -24,9 +24,9 @@ import { fromError } from "zod-validation-error"; import { GetRemoteExitNodeResponse } from "@server/routers/remoteExitNode/types"; const getRemoteExitNodeSchema = z.strictObject({ - orgId: z.string().min(1), - remoteExitNodeId: z.string().min(1) - }); + orgId: z.string().min(1), + remoteExitNodeId: z.string().min(1) +}); async function query(remoteExitNodeId: string) { const [remoteExitNode] = await db diff --git a/server/private/routers/remoteExitNode/getRemoteExitNodeToken.ts b/server/private/routers/remoteExitNode/getRemoteExitNodeToken.ts index 16ec4d5d..24f0de15 100644 --- a/server/private/routers/remoteExitNode/getRemoteExitNodeToken.ts +++ b/server/private/routers/remoteExitNode/getRemoteExitNodeToken.ts @@ -55,7 +55,8 @@ export async function getRemoteExitNodeToken( try { if (token) { - const { session, remoteExitNode } = await validateRemoteExitNodeSessionToken(token); + const { session, remoteExitNode } = + await validateRemoteExitNodeSessionToken(token); if (session) { if (config.getRawConfig().app.log_failed_attempts) { logger.info( @@ -103,7 +104,10 @@ export async function getRemoteExitNodeToken( } const resToken = generateSessionToken(); - await createRemoteExitNodeSession(resToken, existingRemoteExitNode.remoteExitNodeId); + await createRemoteExitNodeSession( + resToken, + existingRemoteExitNode.remoteExitNodeId + ); // logger.debug(`Created RemoteExitNode token response: ${JSON.stringify(resToken)}`); diff --git a/server/private/routers/remoteExitNode/handleRemoteExitNodePingMessage.ts b/server/private/routers/remoteExitNode/handleRemoteExitNodePingMessage.ts index 78492714..dafc1412 100644 --- a/server/private/routers/remoteExitNode/handleRemoteExitNodePingMessage.ts +++ b/server/private/routers/remoteExitNode/handleRemoteExitNodePingMessage.ts @@ -33,7 +33,9 @@ export const startRemoteExitNodeOfflineChecker = (): void => { offlineCheckerInterval = setInterval(async () => { try { - const twoMinutesAgo = Math.floor((Date.now() - OFFLINE_THRESHOLD_MS) / 1000); + const twoMinutesAgo = Math.floor( + (Date.now() - OFFLINE_THRESHOLD_MS) / 1000 + ); // Find clients that haven't pinged in the last 2 minutes and mark them as offline const newlyOfflineNodes = await db @@ -48,11 +50,13 @@ export const startRemoteExitNodeOfflineChecker = (): void => { isNull(exitNodes.lastPing) ) ) - ).returning(); - + ) + .returning(); // Update the sites to offline if they have not pinged either - const exitNodeIds = newlyOfflineNodes.map(node => node.exitNodeId); + const exitNodeIds = newlyOfflineNodes.map( + (node) => node.exitNodeId + ); const sitesOnNode = await db .select() @@ -77,7 +81,6 @@ export const startRemoteExitNodeOfflineChecker = (): void => { .where(eq(sites.siteId, site.siteId)); } } - } catch (error) { logger.error("Error in offline checker interval", { error }); } @@ -100,7 +103,9 @@ export const stopRemoteExitNodeOfflineChecker = (): void => { /** * Handles ping messages from clients and responds with pong */ -export const handleRemoteExitNodePingMessage: MessageHandler = async (context) => { +export const handleRemoteExitNodePingMessage: MessageHandler = async ( + context +) => { const { message, client: c, sendToClient } = context; const remoteExitNode = c as RemoteExitNode; @@ -120,7 +125,7 @@ export const handleRemoteExitNodePingMessage: MessageHandler = async (context) = .update(exitNodes) .set({ lastPing: Math.floor(Date.now() / 1000), - online: true, + online: true }) .where(eq(exitNodes.exitNodeId, remoteExitNode.exitNodeId)); } catch (error) { @@ -131,7 +136,7 @@ export const handleRemoteExitNodePingMessage: MessageHandler = async (context) = message: { type: "pong", data: { - timestamp: new Date().toISOString(), + timestamp: new Date().toISOString() } }, broadcast: false, diff --git a/server/private/routers/remoteExitNode/handleRemoteExitNodeRegisterMessage.ts b/server/private/routers/remoteExitNode/handleRemoteExitNodeRegisterMessage.ts index a733db7d..5ad37edc 100644 --- a/server/private/routers/remoteExitNode/handleRemoteExitNodeRegisterMessage.ts +++ b/server/private/routers/remoteExitNode/handleRemoteExitNodeRegisterMessage.ts @@ -29,7 +29,8 @@ export const handleRemoteExitNodeRegisterMessage: MessageHandler = async ( return; } - const { remoteExitNodeVersion, remoteExitNodeSecondaryVersion } = message.data; + const { remoteExitNodeVersion, remoteExitNodeSecondaryVersion } = + message.data; if (!remoteExitNodeVersion) { logger.warn("Remote exit node version not found"); @@ -39,7 +40,10 @@ export const handleRemoteExitNodeRegisterMessage: MessageHandler = async ( // update the version await db .update(remoteExitNodes) - .set({ version: remoteExitNodeVersion, secondaryVersion: remoteExitNodeSecondaryVersion }) + .set({ + version: remoteExitNodeVersion, + secondaryVersion: remoteExitNodeSecondaryVersion + }) .where( eq( remoteExitNodes.remoteExitNodeId, diff --git a/server/private/routers/remoteExitNode/listRemoteExitNodes.ts b/server/private/routers/remoteExitNode/listRemoteExitNodes.ts index a13a05cd..e6548600 100644 --- a/server/private/routers/remoteExitNode/listRemoteExitNodes.ts +++ b/server/private/routers/remoteExitNode/listRemoteExitNodes.ts @@ -24,8 +24,8 @@ import { fromError } from "zod-validation-error"; import { ListRemoteExitNodesResponse } from "@server/routers/remoteExitNode/types"; const listRemoteExitNodesParamsSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); const listRemoteExitNodesSchema = z.object({ limit: z diff --git a/server/private/routers/remoteExitNode/pickRemoteExitNodeDefaults.ts b/server/private/routers/remoteExitNode/pickRemoteExitNodeDefaults.ts index bb7c89d5..5dcd545e 100644 --- a/server/private/routers/remoteExitNode/pickRemoteExitNodeDefaults.ts +++ b/server/private/routers/remoteExitNode/pickRemoteExitNodeDefaults.ts @@ -22,8 +22,8 @@ import { z } from "zod"; import { PickRemoteExitNodeDefaultsResponse } from "@server/routers/remoteExitNode/types"; const paramsSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); export async function pickRemoteExitNodeDefaults( req: Request, diff --git a/server/private/routers/remoteExitNode/quickStartRemoteExitNode.ts b/server/private/routers/remoteExitNode/quickStartRemoteExitNode.ts index 4d368152..ebe365d1 100644 --- a/server/private/routers/remoteExitNode/quickStartRemoteExitNode.ts +++ b/server/private/routers/remoteExitNode/quickStartRemoteExitNode.ts @@ -38,7 +38,9 @@ export async function quickStartRemoteExitNode( next: NextFunction ): Promise { try { - const parsedBody = quickStartRemoteExitNodeBodySchema.safeParse(req.body); + const parsedBody = quickStartRemoteExitNodeBodySchema.safeParse( + req.body + ); if (!parsedBody.success) { return next( createHttpError( diff --git a/server/private/routers/ws/index.ts b/server/private/routers/ws/index.ts index 4d803a3a..3a8db537 100644 --- a/server/private/routers/ws/index.ts +++ b/server/private/routers/ws/index.ts @@ -11,4 +11,4 @@ * This file is not licensed under the AGPLv3. */ -export * from "./ws"; \ No newline at end of file +export * from "./ws"; diff --git a/server/private/routers/ws/messageHandlers.ts b/server/private/routers/ws/messageHandlers.ts index 71c2b253..5a6c85cf 100644 --- a/server/private/routers/ws/messageHandlers.ts +++ b/server/private/routers/ws/messageHandlers.ts @@ -23,4 +23,4 @@ export const messageHandlers: Record = { "remoteExitNode/ping": handleRemoteExitNodePingMessage }; -startRemoteExitNodeOfflineChecker(); // this is to handle the offline check for remote exit nodes \ No newline at end of file +startRemoteExitNodeOfflineChecker(); // this is to handle the offline check for remote exit nodes diff --git a/server/private/routers/ws/ws.ts b/server/private/routers/ws/ws.ts index f3f4c8fd..784c3d51 100644 --- a/server/private/routers/ws/ws.ts +++ b/server/private/routers/ws/ws.ts @@ -37,7 +37,14 @@ import { validateRemoteExitNodeSessionToken } from "#private/auth/sessions/remot import { rateLimitService } from "#private/lib/rateLimit"; import { messageHandlers } from "@server/routers/ws/messageHandlers"; import { messageHandlers as privateMessageHandlers } from "#private/routers/ws/messageHandlers"; -import { AuthenticatedWebSocket, ClientType, WSMessage, TokenPayload, WebSocketRequest, RedisMessage } from "@server/routers/ws"; +import { + AuthenticatedWebSocket, + ClientType, + WSMessage, + TokenPayload, + WebSocketRequest, + RedisMessage +} from "@server/routers/ws"; import { validateSessionToken } from "@server/auth/sessions/app"; // Merge public and private message handlers @@ -216,7 +223,7 @@ const initializeRedisSubscription = async (): Promise => { // Each node is responsible for restoring its own connection state to Redis // This approach is more efficient than cross-node coordination because: // 1. Each node knows its own connections (source of truth) -// 2. No network overhead from broadcasting state between nodes +// 2. No network overhead from broadcasting state between nodes // 3. No race conditions from simultaneous updates // 4. Redis becomes eventually consistent as each node restores independently // 5. Simpler logic with better fault tolerance @@ -233,8 +240,10 @@ const recoverConnectionState = async (): Promise => { // Each node simply restores its own local connections to Redis // This is the source of truth - no need for cross-node coordination await restoreLocalConnectionsToRedis(); - - logger.info("Redis connection state recovery completed - restored local state"); + + logger.info( + "Redis connection state recovery completed - restored local state" + ); } catch (error) { logger.error("Error during Redis recovery:", error); } finally { @@ -251,8 +260,10 @@ const restoreLocalConnectionsToRedis = async (): Promise => { try { // Restore all current local connections to Redis for (const [clientId, clients] of connectedClients.entries()) { - const validClients = clients.filter(client => client.readyState === WebSocket.OPEN); - + const validClients = clients.filter( + (client) => client.readyState === WebSocket.OPEN + ); + if (validClients.length > 0) { // Add this node to the client's connection list await redisManager.sadd(getConnectionsKey(clientId), NODE_ID); @@ -303,7 +314,10 @@ const addClient = async ( Date.now().toString() ); } catch (error) { - logger.error("Failed to add client to Redis tracking (connection still functional locally):", error); + logger.error( + "Failed to add client to Redis tracking (connection still functional locally):", + error + ); } } @@ -326,9 +340,14 @@ const removeClient = async ( if (redisManager.isRedisEnabled()) { try { await redisManager.srem(getConnectionsKey(clientId), NODE_ID); - await redisManager.del(getNodeConnectionsKey(NODE_ID, clientId)); + await redisManager.del( + getNodeConnectionsKey(NODE_ID, clientId) + ); } catch (error) { - logger.error("Failed to remove client from Redis tracking (cleanup will occur on recovery):", error); + logger.error( + "Failed to remove client from Redis tracking (cleanup will occur on recovery):", + error + ); } } @@ -345,7 +364,10 @@ const removeClient = async ( ws.connectionId ); } catch (error) { - logger.error("Failed to remove specific connection from Redis tracking:", error); + logger.error( + "Failed to remove specific connection from Redis tracking:", + error + ); } } @@ -372,7 +394,9 @@ const sendToClientLocal = async ( } }); - logger.debug(`sendToClient: Message type ${message.type} sent to clientId ${clientId}`); + logger.debug( + `sendToClient: Message type ${message.type} sent to clientId ${clientId}` + ); return true; }; @@ -411,14 +435,22 @@ const sendToClient = async ( fromNodeId: NODE_ID }; - await redisManager.publish(REDIS_CHANNEL, JSON.stringify(redisMessage)); + await redisManager.publish( + REDIS_CHANNEL, + JSON.stringify(redisMessage) + ); } catch (error) { - logger.error("Failed to send message via Redis, message may be lost:", error); + logger.error( + "Failed to send message via Redis, message may be lost:", + error + ); // Continue execution - local delivery already attempted } } else if (!localSent && !redisManager.isRedisEnabled()) { // Redis is disabled or unavailable - log that we couldn't deliver to remote nodes - logger.debug(`Could not deliver message to ${clientId} - not connected locally and Redis unavailable`); + logger.debug( + `Could not deliver message to ${clientId} - not connected locally and Redis unavailable` + ); } return localSent; @@ -441,13 +473,21 @@ const broadcastToAllExcept = async ( fromNodeId: NODE_ID }; - await redisManager.publish(REDIS_CHANNEL, JSON.stringify(redisMessage)); + await redisManager.publish( + REDIS_CHANNEL, + JSON.stringify(redisMessage) + ); } catch (error) { - logger.error("Failed to broadcast message via Redis, remote nodes may not receive it:", error); + logger.error( + "Failed to broadcast message via Redis, remote nodes may not receive it:", + error + ); // Continue execution - local broadcast already completed } } else { - logger.debug("Redis unavailable - broadcast limited to local node only"); + logger.debug( + "Redis unavailable - broadcast limited to local node only" + ); } }; @@ -512,8 +552,10 @@ const verifyToken = async ( return null; } - if (olm.userId) { // this is a user device and we need to check the user token - const { session: userSession, user } = await validateSessionToken(userToken); + if (olm.userId) { + // this is a user device and we need to check the user token + const { session: userSession, user } = + await validateSessionToken(userToken); if (!userSession || !user) { return null; } @@ -668,7 +710,7 @@ const handleWSUpgrade = (server: HttpServer): void => { url.searchParams.get("token") || request.headers["sec-websocket-protocol"] || ""; - const userToken = url.searchParams.get('userToken') || ''; + const userToken = url.searchParams.get("userToken") || ""; let clientType = url.searchParams.get( "clientType" ) as ClientType; @@ -690,7 +732,11 @@ const handleWSUpgrade = (server: HttpServer): void => { return; } - const tokenPayload = await verifyToken(token, clientType, userToken); + const tokenPayload = await verifyToken( + token, + clientType, + userToken + ); if (!tokenPayload) { logger.debug( "Unauthorized connection attempt: invalid token..." @@ -724,50 +770,68 @@ const handleWSUpgrade = (server: HttpServer): void => { // Add periodic connection state sync to handle Redis disconnections/reconnections const startPeriodicStateSync = (): void => { // Lightweight sync every 5 minutes - just restore our own state - setInterval(async () => { - if (redisManager.isRedisEnabled() && !isRedisRecoveryInProgress) { - try { - await restoreLocalConnectionsToRedis(); - logger.debug("Periodic connection state sync completed"); - } catch (error) { - logger.error("Error during periodic connection state sync:", error); + setInterval( + async () => { + if (redisManager.isRedisEnabled() && !isRedisRecoveryInProgress) { + try { + await restoreLocalConnectionsToRedis(); + logger.debug("Periodic connection state sync completed"); + } catch (error) { + logger.error( + "Error during periodic connection state sync:", + error + ); + } } - } - }, 5 * 60 * 1000); // 5 minutes + }, + 5 * 60 * 1000 + ); // 5 minutes // Cleanup stale connections every 15 minutes - setInterval(async () => { - if (redisManager.isRedisEnabled()) { - try { - await cleanupStaleConnections(); - logger.debug("Periodic connection cleanup completed"); - } catch (error) { - logger.error("Error during periodic connection cleanup:", error); + setInterval( + async () => { + if (redisManager.isRedisEnabled()) { + try { + await cleanupStaleConnections(); + logger.debug("Periodic connection cleanup completed"); + } catch (error) { + logger.error( + "Error during periodic connection cleanup:", + error + ); + } } - } - }, 15 * 60 * 1000); // 15 minutes + }, + 15 * 60 * 1000 + ); // 15 minutes }; const cleanupStaleConnections = async (): Promise => { if (!redisManager.isRedisEnabled()) return; try { - const nodeKeys = await redisManager.getClient()?.keys(`ws:node:${NODE_ID}:*`) || []; - + const nodeKeys = + (await redisManager.getClient()?.keys(`ws:node:${NODE_ID}:*`)) || + []; + for (const nodeKey of nodeKeys) { const connections = await redisManager.hgetall(nodeKey); - const clientId = nodeKey.replace(`ws:node:${NODE_ID}:`, ''); + const clientId = nodeKey.replace(`ws:node:${NODE_ID}:`, ""); const localClients = connectedClients.get(clientId) || []; const localConnectionIds = localClients - .filter(client => client.readyState === WebSocket.OPEN) - .map(client => client.connectionId) + .filter((client) => client.readyState === WebSocket.OPEN) + .map((client) => client.connectionId) .filter(Boolean); // Remove Redis entries for connections that no longer exist locally - for (const [connectionId, timestamp] of Object.entries(connections)) { + for (const [connectionId, timestamp] of Object.entries( + connections + )) { if (!localConnectionIds.includes(connectionId)) { await redisManager.hdel(nodeKey, connectionId); - logger.debug(`Cleaned up stale connection: ${connectionId} for client: ${clientId}`); + logger.debug( + `Cleaned up stale connection: ${connectionId} for client: ${clientId}` + ); } } @@ -776,7 +840,9 @@ const cleanupStaleConnections = async (): Promise => { if (Object.keys(remainingConnections).length === 0) { await redisManager.srem(getConnectionsKey(clientId), NODE_ID); await redisManager.del(nodeKey); - logger.debug(`Cleaned up empty connection tracking for client: ${clientId}`); + logger.debug( + `Cleaned up empty connection tracking for client: ${clientId}` + ); } } } catch (error) { @@ -789,38 +855,38 @@ if (redisManager.isRedisEnabled()) { initializeRedisSubscription().catch((error) => { logger.error("Failed to initialize Redis subscription:", error); }); - + // Register recovery callback with Redis manager // When Redis reconnects, each node simply restores its own local state redisManager.onReconnection(async () => { logger.info("Redis reconnected, starting WebSocket state recovery..."); await recoverConnectionState(); }); - + // Start periodic state synchronization startPeriodicStateSync(); - + logger.info( `WebSocket handler initialized with Redis support - Node ID: ${NODE_ID}` ); } else { - logger.debug( - "WebSocket handler initialized in local mode" - ); + logger.debug("WebSocket handler initialized in local mode"); } // Disconnect a specific client and force them to reconnect const disconnectClient = async (clientId: string): Promise => { const mapKey = getClientMapKey(clientId); const clients = connectedClients.get(mapKey); - + if (!clients || clients.length === 0) { logger.debug(`No connections found for client ID: ${clientId}`); return false; } - logger.info(`Disconnecting client ID: ${clientId} (${clients.length} connection(s))`); - + logger.info( + `Disconnecting client ID: ${clientId} (${clients.length} connection(s))` + ); + // Close all connections for this client clients.forEach((client) => { if (client.readyState === WebSocket.OPEN) { diff --git a/server/routers/accessToken/deleteAccessToken.ts b/server/routers/accessToken/deleteAccessToken.ts index 5de4df9b..4e18ddeb 100644 --- a/server/routers/accessToken/deleteAccessToken.ts +++ b/server/routers/accessToken/deleteAccessToken.ts @@ -11,8 +11,8 @@ import { db } from "@server/db"; import { OpenAPITags, registry } from "@server/openApi"; const deleteAccessTokenParamsSchema = z.strictObject({ - accessTokenId: z.string() - }); + accessTokenId: z.string() +}); registry.registerPath({ method: "delete", diff --git a/server/routers/accessToken/generateAccessToken.ts b/server/routers/accessToken/generateAccessToken.ts index 36a20268..35da6add 100644 --- a/server/routers/accessToken/generateAccessToken.ts +++ b/server/routers/accessToken/generateAccessToken.ts @@ -25,17 +25,14 @@ import { sha256 } from "@oslojs/crypto/sha2"; import { OpenAPITags, registry } from "@server/openApi"; export const generateAccessTokenBodySchema = z.strictObject({ - validForSeconds: z.int().positive().optional(), // seconds - title: z.string().optional(), - description: z.string().optional() - }); + validForSeconds: z.int().positive().optional(), // seconds + title: z.string().optional(), + description: z.string().optional() +}); export const generateAccssTokenParamsSchema = z.strictObject({ - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); export type GenerateAccessTokenResponse = Omit< ResourceAccessToken, diff --git a/server/routers/accessToken/listAccessTokens.ts b/server/routers/accessToken/listAccessTokens.ts index 476c858b..2f929fc6 100644 --- a/server/routers/accessToken/listAccessTokens.ts +++ b/server/routers/accessToken/listAccessTokens.ts @@ -17,7 +17,8 @@ import stoi from "@server/lib/stoi"; import { fromZodError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; -const listAccessTokensParamsSchema = z.strictObject({ +const listAccessTokensParamsSchema = z + .strictObject({ resourceId: z .string() .optional() diff --git a/server/routers/apiKeys/createRootApiKey.ts b/server/routers/apiKeys/createRootApiKey.ts index 8e9e571d..fc076623 100644 --- a/server/routers/apiKeys/createRootApiKey.ts +++ b/server/routers/apiKeys/createRootApiKey.ts @@ -15,8 +15,8 @@ import logger from "@server/logger"; import { hashPassword } from "@server/auth/password"; const bodySchema = z.strictObject({ - name: z.string().min(1).max(255) - }); + name: z.string().min(1).max(255) +}); export type CreateRootApiKeyBody = z.infer; diff --git a/server/routers/apiKeys/listApiKeyActions.ts b/server/routers/apiKeys/listApiKeyActions.ts index 7432d175..073a7583 100644 --- a/server/routers/apiKeys/listApiKeyActions.ts +++ b/server/routers/apiKeys/listApiKeyActions.ts @@ -47,8 +47,7 @@ export type ListApiKeyActionsResponse = { registry.registerPath({ method: "get", path: "/org/{orgId}/api-key/{apiKeyId}/actions", - description: - "List all actions set for an API key.", + description: "List all actions set for an API key.", tags: [OpenAPITags.Org, OpenAPITags.ApiKey], request: { params: paramsSchema, diff --git a/server/routers/apiKeys/setApiKeyActions.ts b/server/routers/apiKeys/setApiKeyActions.ts index fe8cc4f1..62967388 100644 --- a/server/routers/apiKeys/setApiKeyActions.ts +++ b/server/routers/apiKeys/setApiKeyActions.ts @@ -11,9 +11,10 @@ import { eq, and, inArray } from "drizzle-orm"; import { OpenAPITags, registry } from "@server/openApi"; const bodySchema = z.strictObject({ - actionIds: z.tuple([z.string()], z.string()) - .transform((v) => Array.from(new Set(v))) - }); + actionIds: z + .tuple([z.string()], z.string()) + .transform((v) => Array.from(new Set(v))) +}); const paramsSchema = z.object({ apiKeyId: z.string().nonempty() diff --git a/server/routers/apiKeys/setApiKeyOrgs.ts b/server/routers/apiKeys/setApiKeyOrgs.ts index d60aad73..51d0f043 100644 --- a/server/routers/apiKeys/setApiKeyOrgs.ts +++ b/server/routers/apiKeys/setApiKeyOrgs.ts @@ -10,9 +10,10 @@ import { fromError } from "zod-validation-error"; import { eq, and, inArray } from "drizzle-orm"; const bodySchema = z.strictObject({ - orgIds: z.tuple([z.string()], z.string()) - .transform((v) => Array.from(new Set(v))) - }); + orgIds: z + .tuple([z.string()], z.string()) + .transform((v) => Array.from(new Set(v))) +}); const paramsSchema = z.object({ apiKeyId: z.string().nonempty() diff --git a/server/routers/auditLogs/generateCSV.ts b/server/routers/auditLogs/generateCSV.ts index 8a067069..ea0da29f 100644 --- a/server/routers/auditLogs/generateCSV.ts +++ b/server/routers/auditLogs/generateCSV.ts @@ -2,15 +2,17 @@ export function generateCSV(data: any[]): string { if (data.length === 0) { return "orgId,action,actorType,timestamp,actor\n"; } - + const headers = Object.keys(data[0]).join(","); - const rows = data.map(row => - Object.values(row).map(value => - typeof value === 'string' && value.includes(',') - ? `"${value.replace(/"/g, '""')}"` - : value - ).join(",") + const rows = data.map((row) => + Object.values(row) + .map((value) => + typeof value === "string" && value.includes(",") + ? `"${value.replace(/"/g, '""')}"` + : value + ) + .join(",") ); - + return [headers, ...rows].join("\n"); -} \ No newline at end of file +} diff --git a/server/routers/auditLogs/types.ts b/server/routers/auditLogs/types.ts index 81cef733..474aa926 100644 --- a/server/routers/auditLogs/types.ts +++ b/server/routers/auditLogs/types.ts @@ -90,4 +90,4 @@ export type QueryAccessAuditLogResponse = { }[]; locations: string[]; }; -}; \ No newline at end of file +}; diff --git a/server/routers/auth/changePassword.ts b/server/routers/auth/changePassword.ts index fa007d37..1a26b911 100644 --- a/server/routers/auth/changePassword.ts +++ b/server/routers/auth/changePassword.ts @@ -6,10 +6,7 @@ import { z } from "zod"; import { db } from "@server/db"; import { User, users } from "@server/db"; import { response } from "@server/lib/response"; -import { - hashPassword, - verifyPassword -} from "@server/auth/password"; +import { hashPassword, verifyPassword } from "@server/auth/password"; import { verifyTotpCode } from "@server/auth/totp"; import logger from "@server/logger"; import { unauthorized } from "@server/auth/unauthorizedResponse"; @@ -23,10 +20,10 @@ import ConfirmPasswordReset from "@server/emails/templates/NotifyResetPassword"; import config from "@server/lib/config"; export const changePasswordBody = z.strictObject({ - oldPassword: z.string(), - newPassword: passwordSchema, - code: z.string().optional() - }); + oldPassword: z.string(), + newPassword: passwordSchema, + code: z.string().optional() +}); export type ChangePasswordBody = z.infer; @@ -62,12 +59,14 @@ async function invalidateAllSessionsExceptCurrent( } // Delete the user sessions (except current) - await trx.delete(sessions).where( - and( - eq(sessions.userId, userId), - ne(sessions.sessionId, currentSessionId) - ) - ); + await trx + .delete(sessions) + .where( + and( + eq(sessions.userId, userId), + ne(sessions.sessionId, currentSessionId) + ) + ); }); } catch (e) { logger.error("Failed to invalidate user sessions except current", e); @@ -157,7 +156,10 @@ export async function changePassword( .where(eq(users.userId, user.userId)); // Invalidate all sessions except the current one - await invalidateAllSessionsExceptCurrent(user.userId, req.session.sessionId); + await invalidateAllSessionsExceptCurrent( + user.userId, + req.session.sessionId + ); try { const email = user.email!; diff --git a/server/routers/auth/checkResourceSession.ts b/server/routers/auth/checkResourceSession.ts index 39466400..74a94a84 100644 --- a/server/routers/auth/checkResourceSession.ts +++ b/server/routers/auth/checkResourceSession.ts @@ -9,7 +9,7 @@ import logger from "@server/logger"; export const params = z.strictObject({ token: z.string(), - resourceId: z.string().transform(Number).pipe(z.int().positive()), + resourceId: z.string().transform(Number).pipe(z.int().positive()) }); export type CheckResourceSessionParams = z.infer; @@ -21,7 +21,7 @@ export type CheckResourceSessionResponse = { export async function checkResourceSession( req: Request, res: Response, - next: NextFunction, + next: NextFunction ): Promise { const parsedParams = params.safeParse(req.params); @@ -29,8 +29,8 @@ export async function checkResourceSession( return next( createHttpError( HttpCode.BAD_REQUEST, - fromError(parsedParams.error).toString(), - ), + fromError(parsedParams.error).toString() + ) ); } @@ -39,7 +39,7 @@ export async function checkResourceSession( try { const { resourceSession } = await validateResourceSessionToken( token, - resourceId, + resourceId ); let valid = false; @@ -52,15 +52,15 @@ export async function checkResourceSession( success: true, error: false, message: "Checked validity", - status: HttpCode.OK, + status: HttpCode.OK }); } catch (e) { logger.error(e); return next( createHttpError( HttpCode.INTERNAL_SERVER_ERROR, - "Failed to reset password", - ), + "Failed to reset password" + ) ); } } diff --git a/server/routers/auth/disable2fa.ts b/server/routers/auth/disable2fa.ts index ebf6ab52..254d6ccd 100644 --- a/server/routers/auth/disable2fa.ts +++ b/server/routers/auth/disable2fa.ts @@ -17,9 +17,9 @@ import { unauthorized } from "@server/auth/unauthorizedResponse"; import { UserType } from "@server/types/UserTypes"; export const disable2faBody = z.strictObject({ - password: z.string(), - code: z.string().optional() - }); + password: z.string(), + code: z.string().optional() +}); export type Disable2faBody = z.infer; @@ -56,7 +56,10 @@ export async function disable2fa( } try { - const validPassword = await verifyPassword(password, user.passwordHash!); + const validPassword = await verifyPassword( + password, + user.passwordHash! + ); if (!validPassword) { return next(unauthorized()); } diff --git a/server/routers/auth/index.ts b/server/routers/auth/index.ts index 4600a4cc..22040614 100644 --- a/server/routers/auth/index.ts +++ b/server/routers/auth/index.ts @@ -16,4 +16,4 @@ export * from "./checkResourceSession"; export * from "./securityKey"; export * from "./startDeviceWebAuth"; export * from "./verifyDeviceWebAuth"; -export * from "./pollDeviceWebAuth"; \ No newline at end of file +export * from "./pollDeviceWebAuth"; diff --git a/server/routers/auth/pollDeviceWebAuth.ts b/server/routers/auth/pollDeviceWebAuth.ts index 9949ab42..a5c71362 100644 --- a/server/routers/auth/pollDeviceWebAuth.ts +++ b/server/routers/auth/pollDeviceWebAuth.ts @@ -7,10 +7,7 @@ import logger from "@server/logger"; import { response } from "@server/lib/response"; import { db, deviceWebAuthCodes } from "@server/db"; import { eq, and, gt } from "drizzle-orm"; -import { - createSession, - generateSessionToken -} from "@server/auth/sessions/app"; +import { createSession, generateSessionToken } from "@server/auth/sessions/app"; import { encodeHexLowerCase } from "@oslojs/encoding"; import { sha256 } from "@oslojs/crypto/sha2"; @@ -22,9 +19,7 @@ export type PollDeviceWebAuthParams = z.infer; // Helper function to hash device code before querying database function hashDeviceCode(code: string): string { - return encodeHexLowerCase( - sha256(new TextEncoder().encode(code)) - ); + return encodeHexLowerCase(sha256(new TextEncoder().encode(code))); } export type PollDeviceWebAuthResponse = { @@ -127,7 +122,9 @@ export async function pollDeviceWebAuth( // Check if userId is set (should be set when verified) if (!deviceCode.userId) { - logger.error("Device code is verified but userId is missing", { codeId: deviceCode.codeId }); + logger.error("Device code is verified but userId is missing", { + codeId: deviceCode.codeId + }); return next( createHttpError( HttpCode.INTERNAL_SERVER_ERROR, @@ -165,4 +162,3 @@ export async function pollDeviceWebAuth( ); } } - diff --git a/server/routers/auth/requestPasswordReset.ts b/server/routers/auth/requestPasswordReset.ts index 0f9953e8..42b53d24 100644 --- a/server/routers/auth/requestPasswordReset.ts +++ b/server/routers/auth/requestPasswordReset.ts @@ -18,8 +18,8 @@ import { hashPassword } from "@server/auth/password"; import { UserType } from "@server/types/UserTypes"; export const requestPasswordResetBody = z.strictObject({ - email: z.email().toLowerCase() - }); + email: z.email().toLowerCase() +}); export type RequestPasswordResetBody = z.infer; diff --git a/server/routers/auth/requestTotpSecret.ts b/server/routers/auth/requestTotpSecret.ts index 53d80147..bc032ecd 100644 --- a/server/routers/auth/requestTotpSecret.ts +++ b/server/routers/auth/requestTotpSecret.ts @@ -17,9 +17,9 @@ import { verifySession } from "@server/auth/sessions/verifySession"; import config from "@server/lib/config"; export const requestTotpSecretBody = z.strictObject({ - password: z.string(), - email: z.email().optional() - }); + password: z.string(), + email: z.email().optional() +}); export type RequestTotpSecretBody = z.infer; @@ -46,7 +46,8 @@ export async function requestTotpSecret( const { password, email } = parsedBody.data; - const { user: sessionUser, session: existingSession } = await verifySession(req); + const { user: sessionUser, session: existingSession } = + await verifySession(req); let user: User | null = sessionUser; if (!existingSession) { @@ -112,11 +113,7 @@ export async function requestTotpSecret( const hex = crypto.getRandomValues(new Uint8Array(20)); const secret = encodeHex(hex); - const uri = createTOTPKeyURI( - appName, - user.email!, - hex - ); + const uri = createTOTPKeyURI(appName, user.email!, hex); await db .update(users) diff --git a/server/routers/auth/resetPassword.ts b/server/routers/auth/resetPassword.ts index aeb85558..6e616346 100644 --- a/server/routers/auth/resetPassword.ts +++ b/server/routers/auth/resetPassword.ts @@ -18,11 +18,11 @@ import { sendEmail } from "@server/emails"; import { passwordSchema } from "@server/auth/passwordSchema"; export const resetPasswordBody = z.strictObject({ - email: z.email().toLowerCase(), - token: z.string(), // reset secret code - newPassword: passwordSchema, - code: z.string().optional() // 2fa code - }); + email: z.email().toLowerCase(), + token: z.string(), // reset secret code + newPassword: passwordSchema, + code: z.string().optional() // 2fa code +}); export type ResetPasswordBody = z.infer; diff --git a/server/routers/auth/securityKey.ts b/server/routers/auth/securityKey.ts index eed2328d..9a1ee2cd 100644 --- a/server/routers/auth/securityKey.ts +++ b/server/routers/auth/securityKey.ts @@ -19,9 +19,7 @@ import type { GenerateAuthenticationOptionsOpts, AuthenticatorTransportFuture } from "@simplewebauthn/server"; -import { - isoBase64URL -} from '@simplewebauthn/server/helpers'; +import { isoBase64URL } from "@simplewebauthn/server/helpers"; import config from "@server/lib/config"; import { UserType } from "@server/types/UserTypes"; import { verifyPassword } from "@server/auth/password"; @@ -30,10 +28,12 @@ import { verifyTotpCode } from "@server/auth/totp"; // The RP ID is the domain name of your application const rpID = (() => { - const url = config.getRawConfig().app.dashboard_url ? new URL(config.getRawConfig().app.dashboard_url!) : undefined; + const url = config.getRawConfig().app.dashboard_url + ? new URL(config.getRawConfig().app.dashboard_url!) + : undefined; // For localhost, we must use 'localhost' without port - if (url?.hostname === 'localhost' || !url) { - return 'localhost'; + if (url?.hostname === "localhost" || !url) { + return "localhost"; } return url.hostname; })(); @@ -46,25 +46,38 @@ const origin = config.getRawConfig().app.dashboard_url || "localhost"; // This supports clustered deployments and persists across server restarts // Clean up expired challenges every 5 minutes -setInterval(async () => { - try { - const now = Date.now(); - await db - .delete(webauthnChallenge) - .where(lt(webauthnChallenge.expiresAt, now)); - // logger.debug("Cleaned up expired security key challenges"); - } catch (error) { - logger.error("Failed to clean up expired security key challenges", error); - } -}, 5 * 60 * 1000); +setInterval( + async () => { + try { + const now = Date.now(); + await db + .delete(webauthnChallenge) + .where(lt(webauthnChallenge.expiresAt, now)); + // logger.debug("Cleaned up expired security key challenges"); + } catch (error) { + logger.error( + "Failed to clean up expired security key challenges", + error + ); + } + }, + 5 * 60 * 1000 +); // Helper functions for challenge management -async function storeChallenge(sessionId: string, challenge: string, securityKeyName?: string, userId?: string) { - const expiresAt = Date.now() + (5 * 60 * 1000); // 5 minutes - +async function storeChallenge( + sessionId: string, + challenge: string, + securityKeyName?: string, + userId?: string +) { + const expiresAt = Date.now() + 5 * 60 * 1000; // 5 minutes + // Delete any existing challenge for this session - await db.delete(webauthnChallenge).where(eq(webauthnChallenge.sessionId, sessionId)); - + await db + .delete(webauthnChallenge) + .where(eq(webauthnChallenge.sessionId, sessionId)); + // Insert new challenge await db.insert(webauthnChallenge).values({ sessionId, @@ -88,7 +101,9 @@ async function getChallenge(sessionId: string) { // Check if expired if (challengeData.expiresAt < Date.now()) { - await db.delete(webauthnChallenge).where(eq(webauthnChallenge.sessionId, sessionId)); + await db + .delete(webauthnChallenge) + .where(eq(webauthnChallenge.sessionId, sessionId)); return null; } @@ -96,7 +111,9 @@ async function getChallenge(sessionId: string) { } async function clearChallenge(sessionId: string) { - await db.delete(webauthnChallenge).where(eq(webauthnChallenge.sessionId, sessionId)); + await db + .delete(webauthnChallenge) + .where(eq(webauthnChallenge.sessionId, sessionId)); } export const registerSecurityKeyBody = z.strictObject({ @@ -153,7 +170,10 @@ export async function startRegistration( try { // Verify password - const validPassword = await verifyPassword(password, user.passwordHash!); + const validPassword = await verifyPassword( + password, + user.passwordHash! + ); if (!validPassword) { return next(unauthorized()); } @@ -197,9 +217,11 @@ export async function startRegistration( .from(securityKeys) .where(eq(securityKeys.userId, user.userId)); - const excludeCredentials = existingSecurityKeys.map(key => ({ + const excludeCredentials = existingSecurityKeys.map((key) => ({ id: key.credentialId, - transports: key.transports ? JSON.parse(key.transports) as AuthenticatorTransportFuture[] : undefined + transports: key.transports + ? (JSON.parse(key.transports) as AuthenticatorTransportFuture[]) + : undefined })); const options: GenerateRegistrationOptionsOpts = { @@ -207,18 +229,23 @@ export async function startRegistration( rpID, userID: isoBase64URL.toBuffer(user.userId), userName: user.email || user.username, - attestationType: 'none', + attestationType: "none", excludeCredentials, authenticatorSelection: { - residentKey: 'preferred', - userVerification: 'preferred', + residentKey: "preferred", + userVerification: "preferred" } }; const registrationOptions = await generateRegistrationOptions(options); // Store challenge in database - await storeChallenge(req.session.sessionId, registrationOptions.challenge, name, user.userId); + await storeChallenge( + req.session.sessionId, + registrationOptions.challenge, + name, + user.userId + ); return response(res, { data: registrationOptions, @@ -270,7 +297,7 @@ export async function verifyRegistration( try { // Get challenge from database const challengeData = await getChallenge(req.session.sessionId); - + if (!challengeData) { return next( createHttpError( @@ -292,10 +319,7 @@ export async function verifyRegistration( if (!verified || !registrationInfo) { return next( - createHttpError( - HttpCode.BAD_REQUEST, - "Verification failed" - ) + createHttpError(HttpCode.BAD_REQUEST, "Verification failed") ); } @@ -303,9 +327,13 @@ export async function verifyRegistration( await db.insert(securityKeys).values({ credentialId: registrationInfo.credential.id, userId: user.userId, - publicKey: isoBase64URL.fromBuffer(registrationInfo.credential.publicKey), + publicKey: isoBase64URL.fromBuffer( + registrationInfo.credential.publicKey + ), signCount: registrationInfo.credential.counter || 0, - transports: registrationInfo.credential.transports ? JSON.stringify(registrationInfo.credential.transports) : null, + transports: registrationInfo.credential.transports + ? JSON.stringify(registrationInfo.credential.transports) + : null, name: challengeData.securityKeyName, lastUsed: new Date().toISOString(), dateCreated: new Date().toISOString() @@ -407,7 +435,10 @@ export async function deleteSecurityKey( try { // Verify password - const validPassword = await verifyPassword(password, user.passwordHash!); + const validPassword = await verifyPassword( + password, + user.passwordHash! + ); if (!validPassword) { return next(unauthorized()); } @@ -447,10 +478,12 @@ export async function deleteSecurityKey( await db .delete(securityKeys) - .where(and( - eq(securityKeys.credentialId, credentialId), - eq(securityKeys.userId, user.userId) - )); + .where( + and( + eq(securityKeys.credentialId, credentialId), + eq(securityKeys.userId, user.userId) + ) + ); return response(res, { data: null, @@ -502,10 +535,7 @@ export async function startAuthentication( if (!user || user.type !== UserType.Internal) { return next( - createHttpError( - HttpCode.BAD_REQUEST, - "Invalid credentials" - ) + createHttpError(HttpCode.BAD_REQUEST, "Invalid credentials") ); } @@ -525,25 +555,37 @@ export async function startAuthentication( ); } - allowCredentials = userSecurityKeys.map(key => ({ + allowCredentials = userSecurityKeys.map((key) => ({ id: key.credentialId, - transports: key.transports ? JSON.parse(key.transports) as AuthenticatorTransportFuture[] : undefined + transports: key.transports + ? (JSON.parse( + key.transports + ) as AuthenticatorTransportFuture[]) + : undefined })); } const options: GenerateAuthenticationOptionsOpts = { rpID, allowCredentials, - userVerification: 'preferred', + userVerification: "preferred" }; - const authenticationOptions = await generateAuthenticationOptions(options); + const authenticationOptions = + await generateAuthenticationOptions(options); // Generate a temporary session ID for unauthenticated users - const tempSessionId = email ? `temp_${email}_${Date.now()}` : `temp_${Date.now()}`; + const tempSessionId = email + ? `temp_${email}_${Date.now()}` + : `temp_${Date.now()}`; // Store challenge in database - await storeChallenge(tempSessionId, authenticationOptions.challenge, undefined, userId); + await storeChallenge( + tempSessionId, + authenticationOptions.challenge, + undefined, + userId + ); return response(res, { data: { ...authenticationOptions, tempSessionId }, @@ -580,7 +622,7 @@ export async function verifyAuthentication( } const { credential } = parsedBody.data; - const tempSessionId = req.headers['x-temp-session-id'] as string; + const tempSessionId = req.headers["x-temp-session-id"] as string; if (!tempSessionId) { return next( @@ -594,7 +636,7 @@ export async function verifyAuthentication( try { // Get challenge from database const challengeData = await getChallenge(tempSessionId); - + if (!challengeData) { return next( createHttpError( @@ -646,7 +688,11 @@ export async function verifyAuthentication( id: securityKey.credentialId, publicKey: isoBase64URL.toBuffer(securityKey.publicKey), counter: securityKey.signCount, - transports: securityKey.transports ? JSON.parse(securityKey.transports) as AuthenticatorTransportFuture[] : undefined + transports: securityKey.transports + ? (JSON.parse( + securityKey.transports + ) as AuthenticatorTransportFuture[]) + : undefined }, requireUserVerification: false }); @@ -672,7 +718,8 @@ export async function verifyAuthentication( .where(eq(securityKeys.credentialId, credentialId)); // Create session for the user - const { createSession, generateSessionToken, serializeSessionCookie } = await import("@server/auth/sessions/app"); + const { createSession, generateSessionToken, serializeSessionCookie } = + await import("@server/auth/sessions/app"); const token = generateSessionToken(); const session = await createSession(token, user.userId); const isSecure = req.protocol === "https"; @@ -703,4 +750,4 @@ export async function verifyAuthentication( ) ); } -} \ No newline at end of file +} diff --git a/server/routers/auth/signup.ts b/server/routers/auth/signup.ts index 842214cf..2605a026 100644 --- a/server/routers/auth/signup.ts +++ b/server/routers/auth/signup.ts @@ -56,8 +56,14 @@ export async function signup( ); } - const { email, password, inviteToken, inviteId, termsAcceptedTimestamp, marketingEmailConsent } = - parsedBody.data; + const { + email, + password, + inviteToken, + inviteId, + termsAcceptedTimestamp, + marketingEmailConsent + } = parsedBody.data; const passwordHash = await hashPassword(password); const userId = generateId(15); @@ -222,7 +228,9 @@ export async function signup( ); res.appendHeader("Set-Cookie", cookie); if (build == "saas" && marketingEmailConsent) { - logger.debug(`User ${email} opted in to marketing emails during signup.`); + logger.debug( + `User ${email} opted in to marketing emails during signup.` + ); moveEmailToAudience(email, AudienceIds.SignUps); } diff --git a/server/routers/auth/types.ts b/server/routers/auth/types.ts index bb5a1b4e..023b2d8e 100644 --- a/server/routers/auth/types.ts +++ b/server/routers/auth/types.ts @@ -5,4 +5,4 @@ export type TransferSessionResponse = { export type GetSessionTransferTokenRenponse = { token: string; -}; \ No newline at end of file +}; diff --git a/server/routers/auth/validateSetupToken.ts b/server/routers/auth/validateSetupToken.ts index 1a4725b6..26043f2d 100644 --- a/server/routers/auth/validateSetupToken.ts +++ b/server/routers/auth/validateSetupToken.ts @@ -9,8 +9,8 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; const validateSetupTokenSchema = z.strictObject({ - token: z.string().min(1, "Token is required") - }); + token: z.string().min(1, "Token is required") +}); export type ValidateSetupTokenResponse = { valid: boolean; @@ -41,10 +41,7 @@ export async function validateSetupToken( .select() .from(setupTokens) .where( - and( - eq(setupTokens.token, token), - eq(setupTokens.used, false) - ) + and(eq(setupTokens.token, token), eq(setupTokens.used, false)) ); if (!setupToken) { @@ -79,4 +76,4 @@ export async function validateSetupToken( ) ); } -} \ No newline at end of file +} diff --git a/server/routers/auth/verifyEmail.ts b/server/routers/auth/verifyEmail.ts index 8d31eb45..31c5166d 100644 --- a/server/routers/auth/verifyEmail.ts +++ b/server/routers/auth/verifyEmail.ts @@ -14,8 +14,8 @@ import { freeLimitSet, limitsService } from "@server/lib/billing"; import { build } from "@server/build"; export const verifyEmailBody = z.strictObject({ - code: z.string() - }); + code: z.string() +}); export type VerifyEmailBody = z.infer; diff --git a/server/routers/auth/verifyTotp.ts b/server/routers/auth/verifyTotp.ts index 9243c9f9..207287ea 100644 --- a/server/routers/auth/verifyTotp.ts +++ b/server/routers/auth/verifyTotp.ts @@ -19,10 +19,10 @@ import { verifySession } from "@server/auth/sessions/verifySession"; import { unauthorized } from "@server/auth/unauthorizedResponse"; export const verifyTotpBody = z.strictObject({ - email: z.email().optional(), - password: z.string().optional(), - code: z.string() - }); + email: z.email().optional(), + password: z.string().optional(), + code: z.string() +}); export type VerifyTotpBody = z.infer; diff --git a/server/routers/badger/exchangeSession.ts b/server/routers/badger/exchangeSession.ts index b4b2deea..b8d01c11 100644 --- a/server/routers/badger/exchangeSession.ts +++ b/server/routers/badger/exchangeSession.ts @@ -12,7 +12,10 @@ import { serializeResourceSessionCookie, validateResourceSessionToken } from "@server/auth/sessions/resource"; -import { generateSessionToken, SESSION_COOKIE_EXPIRES } from "@server/auth/sessions/app"; +import { + generateSessionToken, + SESSION_COOKIE_EXPIRES +} from "@server/auth/sessions/app"; import { SESSION_COOKIE_EXPIRES as RESOURCE_SESSION_COOKIE_EXPIRES } from "@server/auth/sessions/resource"; import config from "@server/lib/config"; import { response } from "@server/lib/response"; @@ -55,8 +58,8 @@ export async function exchangeSession( let cleanHost = host; // if the host ends with :port if (cleanHost.match(/:[0-9]{1,5}$/)) { - const matched = ''+cleanHost.match(/:[0-9]{1,5}$/); - cleanHost = cleanHost.slice(0, -1*matched.length); + const matched = "" + cleanHost.match(/:[0-9]{1,5}$/); + cleanHost = cleanHost.slice(0, -1 * matched.length); } const clientIp = requestIp?.split(":")[0]; @@ -153,8 +156,8 @@ export async function exchangeSession( } } else { const expires = new Date( - Date.now() + SESSION_COOKIE_EXPIRES - ).getTime(); + Date.now() + SESSION_COOKIE_EXPIRES + ).getTime(); await createResourceSession({ token, resourceId: resource.resourceId, diff --git a/server/routers/badger/verifySession.test.ts b/server/routers/badger/verifySession.test.ts index b0ad9873..7c967ace 100644 --- a/server/routers/badger/verifySession.test.ts +++ b/server/routers/badger/verifySession.test.ts @@ -1,13 +1,11 @@ -import { assertEquals } from '@test/assert'; +import { assertEquals } from "@test/assert"; function isPathAllowed(pattern: string, path: string): boolean { - // Normalize and split paths into segments const normalize = (p: string) => p.split("/").filter(Boolean); const patternParts = normalize(pattern); const pathParts = normalize(path); - // Recursive function to try different wildcard matches function matchSegments(patternIndex: number, pathIndex: number): boolean { const indent = " ".repeat(pathIndex); // Indent based on recursion depth @@ -30,7 +28,6 @@ function isPathAllowed(pattern: string, path: string): boolean { // For full segment wildcards, try consuming different numbers of path segments if (currentPatternPart === "*") { - // Try consuming 0 segments (skip the wildcard) if (matchSegments(patternIndex + 1, pathIndex)) { return true; @@ -74,69 +71,213 @@ function isPathAllowed(pattern: string, path: string): boolean { } function runTests() { - console.log('Running path matching tests...'); + console.log("Running path matching tests..."); // Test exact matching - assertEquals(isPathAllowed('foo', 'foo'), true, 'Exact match should be allowed'); - assertEquals(isPathAllowed('foo', 'bar'), false, 'Different segments should not match'); - assertEquals(isPathAllowed('foo/bar', 'foo/bar'), true, 'Exact multi-segment match should be allowed'); - assertEquals(isPathAllowed('foo/bar', 'foo/baz'), false, 'Partial multi-segment match should not be allowed'); + assertEquals( + isPathAllowed("foo", "foo"), + true, + "Exact match should be allowed" + ); + assertEquals( + isPathAllowed("foo", "bar"), + false, + "Different segments should not match" + ); + assertEquals( + isPathAllowed("foo/bar", "foo/bar"), + true, + "Exact multi-segment match should be allowed" + ); + assertEquals( + isPathAllowed("foo/bar", "foo/baz"), + false, + "Partial multi-segment match should not be allowed" + ); // Test with leading and trailing slashes - assertEquals(isPathAllowed('/foo', 'foo'), true, 'Pattern with leading slash should match'); - assertEquals(isPathAllowed('foo/', 'foo'), true, 'Pattern with trailing slash should match'); - assertEquals(isPathAllowed('/foo/', 'foo'), true, 'Pattern with both leading and trailing slashes should match'); - assertEquals(isPathAllowed('foo', '/foo/'), true, 'Path with leading and trailing slashes should match'); + assertEquals( + isPathAllowed("/foo", "foo"), + true, + "Pattern with leading slash should match" + ); + assertEquals( + isPathAllowed("foo/", "foo"), + true, + "Pattern with trailing slash should match" + ); + assertEquals( + isPathAllowed("/foo/", "foo"), + true, + "Pattern with both leading and trailing slashes should match" + ); + assertEquals( + isPathAllowed("foo", "/foo/"), + true, + "Path with leading and trailing slashes should match" + ); // Test simple wildcard matching - assertEquals(isPathAllowed('*', 'foo'), true, 'Single wildcard should match any single segment'); - assertEquals(isPathAllowed('*', 'foo/bar'), true, 'Single wildcard should match multiple segments'); - assertEquals(isPathAllowed('*/bar', 'foo/bar'), true, 'Wildcard prefix should match'); - assertEquals(isPathAllowed('foo/*', 'foo/bar'), true, 'Wildcard suffix should match'); - assertEquals(isPathAllowed('foo/*/baz', 'foo/bar/baz'), true, 'Wildcard in middle should match'); + assertEquals( + isPathAllowed("*", "foo"), + true, + "Single wildcard should match any single segment" + ); + assertEquals( + isPathAllowed("*", "foo/bar"), + true, + "Single wildcard should match multiple segments" + ); + assertEquals( + isPathAllowed("*/bar", "foo/bar"), + true, + "Wildcard prefix should match" + ); + assertEquals( + isPathAllowed("foo/*", "foo/bar"), + true, + "Wildcard suffix should match" + ); + assertEquals( + isPathAllowed("foo/*/baz", "foo/bar/baz"), + true, + "Wildcard in middle should match" + ); // Test multiple wildcards - assertEquals(isPathAllowed('*/*', 'foo/bar'), true, 'Multiple wildcards should match corresponding segments'); - assertEquals(isPathAllowed('*/*/*', 'foo/bar/baz'), true, 'Three wildcards should match three segments'); - assertEquals(isPathAllowed('foo/*/*', 'foo/bar/baz'), true, 'Specific prefix with wildcards should match'); - assertEquals(isPathAllowed('*/*/baz', 'foo/bar/baz'), true, 'Wildcards with specific suffix should match'); + assertEquals( + isPathAllowed("*/*", "foo/bar"), + true, + "Multiple wildcards should match corresponding segments" + ); + assertEquals( + isPathAllowed("*/*/*", "foo/bar/baz"), + true, + "Three wildcards should match three segments" + ); + assertEquals( + isPathAllowed("foo/*/*", "foo/bar/baz"), + true, + "Specific prefix with wildcards should match" + ); + assertEquals( + isPathAllowed("*/*/baz", "foo/bar/baz"), + true, + "Wildcards with specific suffix should match" + ); // Test wildcard consumption behavior - assertEquals(isPathAllowed('*', ''), true, 'Wildcard should optionally consume segments'); - assertEquals(isPathAllowed('foo/*', 'foo'), true, 'Trailing wildcard should be optional'); - assertEquals(isPathAllowed('*/*', 'foo'), true, 'Multiple wildcards can match fewer segments'); - assertEquals(isPathAllowed('*/*/*', 'foo/bar'), true, 'Extra wildcards can be skipped'); + assertEquals( + isPathAllowed("*", ""), + true, + "Wildcard should optionally consume segments" + ); + assertEquals( + isPathAllowed("foo/*", "foo"), + true, + "Trailing wildcard should be optional" + ); + assertEquals( + isPathAllowed("*/*", "foo"), + true, + "Multiple wildcards can match fewer segments" + ); + assertEquals( + isPathAllowed("*/*/*", "foo/bar"), + true, + "Extra wildcards can be skipped" + ); // Test complex nested paths - assertEquals(isPathAllowed('api/*/users', 'api/v1/users'), true, 'API versioning pattern should match'); - assertEquals(isPathAllowed('api/*/users/*', 'api/v1/users/123'), true, 'API resource pattern should match'); - assertEquals(isPathAllowed('api/*/users/*/profile', 'api/v1/users/123/profile'), true, 'Nested API pattern should match'); + assertEquals( + isPathAllowed("api/*/users", "api/v1/users"), + true, + "API versioning pattern should match" + ); + assertEquals( + isPathAllowed("api/*/users/*", "api/v1/users/123"), + true, + "API resource pattern should match" + ); + assertEquals( + isPathAllowed("api/*/users/*/profile", "api/v1/users/123/profile"), + true, + "Nested API pattern should match" + ); // Test for the requested padbootstrap* pattern - assertEquals(isPathAllowed('padbootstrap*', 'padbootstrap'), true, 'padbootstrap* should match padbootstrap'); - assertEquals(isPathAllowed('padbootstrap*', 'padbootstrapv1'), true, 'padbootstrap* should match padbootstrapv1'); - assertEquals(isPathAllowed('padbootstrap*', 'padbootstrap/files'), false, 'padbootstrap* should not match padbootstrap/files'); - assertEquals(isPathAllowed('padbootstrap*/*', 'padbootstrap/files'), true, 'padbootstrap*/* should match padbootstrap/files'); - assertEquals(isPathAllowed('padbootstrap*/files', 'padbootstrapv1/files'), true, 'padbootstrap*/files should not match padbootstrapv1/files (wildcard is segment-based, not partial)'); + assertEquals( + isPathAllowed("padbootstrap*", "padbootstrap"), + true, + "padbootstrap* should match padbootstrap" + ); + assertEquals( + isPathAllowed("padbootstrap*", "padbootstrapv1"), + true, + "padbootstrap* should match padbootstrapv1" + ); + assertEquals( + isPathAllowed("padbootstrap*", "padbootstrap/files"), + false, + "padbootstrap* should not match padbootstrap/files" + ); + assertEquals( + isPathAllowed("padbootstrap*/*", "padbootstrap/files"), + true, + "padbootstrap*/* should match padbootstrap/files" + ); + assertEquals( + isPathAllowed("padbootstrap*/files", "padbootstrapv1/files"), + true, + "padbootstrap*/files should not match padbootstrapv1/files (wildcard is segment-based, not partial)" + ); // Test wildcard edge cases - assertEquals(isPathAllowed('*/*/*/*/*/*', 'a/b'), true, 'Many wildcards can match few segments'); - assertEquals(isPathAllowed('a/*/b/*/c', 'a/anything/b/something/c'), true, 'Multiple wildcards in pattern should match corresponding segments'); + assertEquals( + isPathAllowed("*/*/*/*/*/*", "a/b"), + true, + "Many wildcards can match few segments" + ); + assertEquals( + isPathAllowed("a/*/b/*/c", "a/anything/b/something/c"), + true, + "Multiple wildcards in pattern should match corresponding segments" + ); // Test patterns with partial segment matches - assertEquals(isPathAllowed('padbootstrap*', 'padbootstrap-123'), true, 'Wildcards in isPathAllowed should be segment-based, not character-based'); - assertEquals(isPathAllowed('test*', 'testuser'), true, 'Asterisk as part of segment name is treated as a literal, not a wildcard'); - assertEquals(isPathAllowed('my*app', 'myapp'), true, 'Asterisk in middle of segment name is treated as a literal, not a wildcard'); + assertEquals( + isPathAllowed("padbootstrap*", "padbootstrap-123"), + true, + "Wildcards in isPathAllowed should be segment-based, not character-based" + ); + assertEquals( + isPathAllowed("test*", "testuser"), + true, + "Asterisk as part of segment name is treated as a literal, not a wildcard" + ); + assertEquals( + isPathAllowed("my*app", "myapp"), + true, + "Asterisk in middle of segment name is treated as a literal, not a wildcard" + ); - assertEquals(isPathAllowed('/', '/'), true, 'Root path should match root path'); - assertEquals(isPathAllowed('/', '/test'), false, 'Root path should not match non-root path'); + assertEquals( + isPathAllowed("/", "/"), + true, + "Root path should match root path" + ); + assertEquals( + isPathAllowed("/", "/test"), + false, + "Root path should not match non-root path" + ); - console.log('All tests passed!'); + console.log("All tests passed!"); } // Run all tests try { runTests(); } catch (error) { - console.error('Test failed:', error); + console.error("Test failed:", error); } diff --git a/server/routers/billing/types.ts b/server/routers/billing/types.ts index 2ec5a1b1..4e0aab52 100644 --- a/server/routers/billing/types.ts +++ b/server/routers/billing/types.ts @@ -14,4 +14,3 @@ export type GetOrgTierResponse = { tier: string | null; active: boolean; }; - diff --git a/server/routers/billing/webhooks.ts b/server/routers/billing/webhooks.ts index 0ca38a8a..53eda78c 100644 --- a/server/routers/billing/webhooks.ts +++ b/server/routers/billing/webhooks.ts @@ -11,4 +11,4 @@ export async function billingWebhookHandler( return next( createHttpError(HttpCode.NOT_FOUND, "This endpoint is not in use") ); -} \ No newline at end of file +} diff --git a/server/routers/blueprints/applyJSONBlueprint.ts b/server/routers/blueprints/applyJSONBlueprint.ts index f8c9caec..7eee15bf 100644 --- a/server/routers/blueprints/applyJSONBlueprint.ts +++ b/server/routers/blueprints/applyJSONBlueprint.ts @@ -9,12 +9,12 @@ import { OpenAPITags, registry } from "@server/openApi"; import { applyBlueprint } from "@server/lib/blueprints/applyBlueprint"; const applyBlueprintSchema = z.strictObject({ - blueprint: z.string() - }); + blueprint: z.string() +}); const applyBlueprintParamsSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); registry.registerPath({ method: "put", diff --git a/server/routers/blueprints/getBlueprint.ts b/server/routers/blueprints/getBlueprint.ts index 45c36af7..915e0481 100644 --- a/server/routers/blueprints/getBlueprint.ts +++ b/server/routers/blueprints/getBlueprint.ts @@ -13,12 +13,9 @@ import { OpenAPITags, registry } from "@server/openApi"; import { BlueprintData } from "./types"; const getBlueprintSchema = z.strictObject({ - blueprintId: z - .string() - .transform(stoi) - .pipe(z.int().positive()), - orgId: z.string() - }); + blueprintId: z.string().transform(stoi).pipe(z.int().positive()), + orgId: z.string() +}); async function query(blueprintId: number, orgId: string) { // Get the client diff --git a/server/routers/blueprints/listBlueprints.ts b/server/routers/blueprints/listBlueprints.ts index 315abfed..2ece9e53 100644 --- a/server/routers/blueprints/listBlueprints.ts +++ b/server/routers/blueprints/listBlueprints.ts @@ -11,23 +11,23 @@ import { OpenAPITags, registry } from "@server/openApi"; import { BlueprintData } from "./types"; const listBluePrintsParamsSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); const listBluePrintsSchema = z.strictObject({ - limit: z - .string() - .optional() - .default("1000") - .transform(Number) - .pipe(z.int().nonnegative()), - offset: z - .string() - .optional() - .default("0") - .transform(Number) - .pipe(z.int().nonnegative()) - }); + limit: z + .string() + .optional() + .default("1000") + .transform(Number) + .pipe(z.int().nonnegative()), + offset: z + .string() + .optional() + .default("0") + .transform(Number) + .pipe(z.int().nonnegative()) +}); async function queryBlueprints(orgId: string, limit: number, offset: number) { const res = await db diff --git a/server/routers/certificates/createCertificate.ts b/server/routers/certificates/createCertificate.ts index e160e644..e858e5cd 100644 --- a/server/routers/certificates/createCertificate.ts +++ b/server/routers/certificates/createCertificate.ts @@ -1,5 +1,9 @@ import { db, Transaction } from "@server/db"; -export async function createCertificate(domainId: string, domain: string, trx: Transaction | typeof db) { +export async function createCertificate( + domainId: string, + domain: string, + trx: Transaction | typeof db +) { return; -} \ No newline at end of file +} diff --git a/server/routers/certificates/types.ts b/server/routers/certificates/types.ts index 80136de8..3ec90857 100644 --- a/server/routers/certificates/types.ts +++ b/server/routers/certificates/types.ts @@ -10,4 +10,4 @@ export type GetCertificateResponse = { updatedAt: string; errorMessage?: string | null; renewalCount: number; -} \ No newline at end of file +}; diff --git a/server/routers/client/listClients.ts b/server/routers/client/listClients.ts index 68cd9aa0..42e47efe 100644 --- a/server/routers/client/listClients.ts +++ b/server/routers/client/listClients.ts @@ -10,7 +10,16 @@ import { import logger from "@server/logger"; import HttpCode from "@server/types/HttpCode"; import response from "@server/lib/response"; -import { and, count, eq, inArray, isNotNull, isNull, or, sql } from "drizzle-orm"; +import { + and, + count, + eq, + inArray, + isNotNull, + isNull, + or, + sql +} from "drizzle-orm"; import { NextFunction, Request, Response } from "express"; import createHttpError from "http-errors"; import { z } from "zod"; @@ -60,13 +69,9 @@ async function getLatestOlmVersion(): Promise { return latestVersion; } catch (error: any) { if (error.name === "AbortError") { - logger.warn( - "Request to fetch latest Olm version timed out (1.5s)" - ); + logger.warn("Request to fetch latest Olm version timed out (1.5s)"); } else if (error.cause?.code === "UND_ERR_CONNECT_TIMEOUT") { - logger.warn( - "Connection timeout while fetching latest Olm version" - ); + logger.warn("Connection timeout while fetching latest Olm version"); } else { logger.warn( "Error fetching latest Olm version:", @@ -77,10 +82,9 @@ async function getLatestOlmVersion(): Promise { } } - const listClientsParamsSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); const listClientsSchema = z.object({ limit: z @@ -95,12 +99,14 @@ const listClientsSchema = z.object({ .default("0") .transform(Number) .pipe(z.int().nonnegative()), - filter: z - .enum(["user", "machine"]) - .optional() + filter: z.enum(["user", "machine"]).optional() }); -function queryClients(orgId: string, accessibleClientIds: number[], filter?: "user" | "machine") { +function queryClients( + orgId: string, + accessibleClientIds: number[], + filter?: "user" | "machine" +) { const conditions = [ inArray(clients.clientId, accessibleClientIds), eq(clients.orgId, orgId) @@ -158,16 +164,17 @@ type OlmWithUpdateAvailable = Awaited>[0] & { olmUpdateAvailable?: boolean; }; - export type ListClientsResponse = { - clients: Array>[0] & { - sites: Array<{ - siteId: number; - siteName: string | null; - siteNiceId: string | null; - }> - olmUpdateAvailable?: boolean; - }>; + clients: Array< + Awaited>[0] & { + sites: Array<{ + siteId: number; + siteName: string | null; + siteNiceId: string | null; + }>; + olmUpdateAvailable?: boolean; + } + >; pagination: { total: number; limit: number; offset: number }; }; @@ -271,28 +278,34 @@ export async function listClients( const totalCount = totalCountResult[0].count; // Get associated sites for all clients - const clientIds = clientsList.map(client => client.clientId); + const clientIds = clientsList.map((client) => client.clientId); const siteAssociations = await getSiteAssociations(clientIds); // Group site associations by client ID - const sitesByClient = siteAssociations.reduce((acc, association) => { - if (!acc[association.clientId]) { - acc[association.clientId] = []; - } - acc[association.clientId].push({ - siteId: association.siteId, - siteName: association.siteName, - siteNiceId: association.siteNiceId - }); - return acc; - }, {} as Record>); + const sitesByClient = siteAssociations.reduce( + (acc, association) => { + if (!acc[association.clientId]) { + acc[association.clientId] = []; + } + acc[association.clientId].push({ + siteId: association.siteId, + siteName: association.siteName, + siteNiceId: association.siteNiceId + }); + return acc; + }, + {} as Record< + number, + Array<{ + siteId: number; + siteName: string | null; + siteNiceId: string | null; + }> + > + ); // Merge clients with their site associations - const clientsWithSites = clientsList.map(client => ({ + const clientsWithSites = clientsList.map((client) => ({ ...client, sites: sitesByClient[client.clientId] || [] })); @@ -322,7 +335,6 @@ export async function listClients( } catch (error) { client.olmUpdateAvailable = false; } - }); } } catch (error) { @@ -333,7 +345,6 @@ export async function listClients( ); } - return response(res, { data: { clients: clientsWithSites, diff --git a/server/routers/client/pickClientDefaults.ts b/server/routers/client/pickClientDefaults.ts index 3d447ecd..fd31da12 100644 --- a/server/routers/client/pickClientDefaults.ts +++ b/server/routers/client/pickClientDefaults.ts @@ -16,8 +16,8 @@ export type PickClientDefaultsResponse = { }; const pickClientDefaultsSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); registry.registerPath({ method: "get", diff --git a/server/routers/client/targets.ts b/server/routers/client/targets.ts index 9887a454..b7b91925 100644 --- a/server/routers/client/targets.ts +++ b/server/routers/client/targets.ts @@ -101,14 +101,18 @@ export async function removePeerData( export async function updatePeerData( clientId: number, siteId: number, - remoteSubnets: { - oldRemoteSubnets: string[]; - newRemoteSubnets: string[]; - } | undefined, - aliases: { - oldAliases: Alias[]; - newAliases: Alias[]; - } | undefined, + remoteSubnets: + | { + oldRemoteSubnets: string[]; + newRemoteSubnets: string[]; + } + | undefined, + aliases: + | { + oldAliases: Alias[]; + newAliases: Alias[]; + } + | undefined, olmId?: string ) { if (!olmId) { diff --git a/server/routers/client/terminate.ts b/server/routers/client/terminate.ts index dc49ef05..1cfdc709 100644 --- a/server/routers/client/terminate.ts +++ b/server/routers/client/terminate.ts @@ -2,7 +2,10 @@ import { sendToClient } from "#dynamic/routers/ws"; import { db, olms } from "@server/db"; import { eq } from "drizzle-orm"; -export async function sendTerminateClient(clientId: number, olmId?: string | null) { +export async function sendTerminateClient( + clientId: number, + olmId?: string | null +) { if (!olmId) { const [olm] = await db .select() diff --git a/server/routers/domain/createOrgDomain.ts b/server/routers/domain/createOrgDomain.ts index 3f223bce..6558d748 100644 --- a/server/routers/domain/createOrgDomain.ts +++ b/server/routers/domain/createOrgDomain.ts @@ -1,6 +1,13 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; -import { db, Domain, domains, OrgDomains, orgDomains, dnsRecords } from "@server/db"; +import { + db, + Domain, + domains, + OrgDomains, + orgDomains, + dnsRecords +} from "@server/db"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; @@ -16,16 +23,15 @@ import { build } from "@server/build"; import config from "@server/lib/config"; const paramsSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); const bodySchema = z.strictObject({ - type: z.enum(["ns", "cname", "wildcard"]), - baseDomain: subdomainSchema, - certResolver: z.string().optional().nullable(), - preferWildcardCert: z.boolean().optional().nullable() // optional, only for wildcard - }); - + type: z.enum(["ns", "cname", "wildcard"]), + baseDomain: subdomainSchema, + certResolver: z.string().optional().nullable(), + preferWildcardCert: z.boolean().optional().nullable() // optional, only for wildcard +}); export type CreateDomainResponse = { domainId: string; @@ -72,7 +78,8 @@ export async function createOrgDomain( } const { orgId } = parsedParams.data; - const { type, baseDomain, certResolver, preferWildcardCert } = parsedBody.data; + const { type, baseDomain, certResolver, preferWildcardCert } = + parsedBody.data; if (build == "oss") { if (type !== "wildcard") { @@ -278,7 +285,7 @@ export async function createOrgDomain( // TODO: This needs to be cross region and not hardcoded if (type === "ns") { nsRecords = config.getRawConfig().dns.nameservers as string[]; - + // Save NS records to database for (const nsValue of nsRecords) { recordsToInsert.push({ @@ -300,7 +307,7 @@ export async function createOrgDomain( baseDomain: `_acme-challenge.${baseDomain}` } ]; - + // Save CNAME records to database for (const cnameRecord of cnameRecords) { recordsToInsert.push({ @@ -322,7 +329,7 @@ export async function createOrgDomain( baseDomain: `${baseDomain}` } ]; - + // Save A records to database for (const aRecord of aRecords) { recordsToInsert.push({ diff --git a/server/routers/domain/deleteOrgDomain.ts b/server/routers/domain/deleteOrgDomain.ts index fe4a4805..fa916beb 100644 --- a/server/routers/domain/deleteOrgDomain.ts +++ b/server/routers/domain/deleteOrgDomain.ts @@ -11,9 +11,9 @@ import { usageService } from "@server/lib/billing/usageService"; import { FeatureId } from "@server/lib/billing"; const paramsSchema = z.strictObject({ - domainId: z.string(), - orgId: z.string() - }); + domainId: z.string(), + orgId: z.string() +}); export type DeleteAccountDomainResponse = { success: boolean; @@ -48,10 +48,7 @@ export async function deleteAccountDomain( eq(orgDomains.domainId, domainId) ) ) - .innerJoin( - domains, - eq(orgDomains.domainId, domains.domainId) - ); + .innerJoin(domains, eq(orgDomains.domainId, domains.domainId)); if (!existing) { return next( diff --git a/server/routers/domain/getDNSRecords.ts b/server/routers/domain/getDNSRecords.ts index 239cc455..5a373a11 100644 --- a/server/routers/domain/getDNSRecords.ts +++ b/server/routers/domain/getDNSRecords.ts @@ -11,16 +11,16 @@ import { OpenAPITags, registry } from "@server/openApi"; import { getServerIp } from "@server/lib/serverIpService"; // your in-memory IP module const getDNSRecordsSchema = z.strictObject({ - domainId: z.string(), - orgId: z.string() - }); + domainId: z.string(), + orgId: z.string() +}); async function query(domainId: string) { const records = await db .select() .from(dnsRecords) .where(eq(dnsRecords.domainId, domainId)); - + return records; } @@ -72,8 +72,11 @@ export async function getDNSRecords( const serverIp = getServerIp(); // Override value for type A or wildcard records - const updatedRecords = records.map(record => { - if ((record.recordType === "A" || record.baseDomain === "*") && serverIp) { + const updatedRecords = records.map((record) => { + if ( + (record.recordType === "A" || record.baseDomain === "*") && + serverIp + ) { return { ...record, value: serverIp }; } return record; @@ -92,4 +95,4 @@ export async function getDNSRecords( createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") ); } -} \ No newline at end of file +} diff --git a/server/routers/domain/getDomain.ts b/server/routers/domain/getDomain.ts index 408cf37d..3e5565f9 100644 --- a/server/routers/domain/getDomain.ts +++ b/server/routers/domain/getDomain.ts @@ -11,11 +11,9 @@ import { OpenAPITags, registry } from "@server/openApi"; import { domain } from "zod/v4/core/regexes"; const getDomainSchema = z.strictObject({ - domainId: z - .string() - .optional(), - orgId: z.string().optional() - }); + domainId: z.string().optional(), + orgId: z.string().optional() +}); async function query(domainId?: string, orgId?: string) { if (domainId) { @@ -65,7 +63,9 @@ export async function getDomain( const domain = await query(domainId, orgId); if (!domain) { - return next(createHttpError(HttpCode.NOT_FOUND, "Domain not found")); + return next( + createHttpError(HttpCode.NOT_FOUND, "Domain not found") + ); } return response(res, { diff --git a/server/routers/domain/index.ts b/server/routers/domain/index.ts index e7e0b555..73b28fea 100644 --- a/server/routers/domain/index.ts +++ b/server/routers/domain/index.ts @@ -4,4 +4,4 @@ export * from "./deleteOrgDomain"; export * from "./restartOrgDomain"; export * from "./getDomain"; export * from "./getDNSRecords"; -export * from "./updateDomain"; \ No newline at end of file +export * from "./updateDomain"; diff --git a/server/routers/domain/listDomains.ts b/server/routers/domain/listDomains.ts index 48f22c6c..20b23634 100644 --- a/server/routers/domain/listDomains.ts +++ b/server/routers/domain/listDomains.ts @@ -11,23 +11,23 @@ import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; const listDomainsParamsSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); const listDomainsSchema = z.strictObject({ - limit: z - .string() - .optional() - .default("1000") - .transform(Number) - .pipe(z.int().nonnegative()), - offset: z - .string() - .optional() - .default("0") - .transform(Number) - .pipe(z.int().nonnegative()) - }); + limit: z + .string() + .optional() + .default("1000") + .transform(Number) + .pipe(z.int().nonnegative()), + offset: z + .string() + .optional() + .default("0") + .transform(Number) + .pipe(z.int().nonnegative()) +}); async function queryDomains(orgId: string, limit: number, offset: number) { const res = await db diff --git a/server/routers/domain/restartOrgDomain.ts b/server/routers/domain/restartOrgDomain.ts index f2bf7c39..1039d2fb 100644 --- a/server/routers/domain/restartOrgDomain.ts +++ b/server/routers/domain/restartOrgDomain.ts @@ -9,9 +9,9 @@ import { fromError } from "zod-validation-error"; import { and, eq } from "drizzle-orm"; const paramsSchema = z.strictObject({ - domainId: z.string(), - orgId: z.string() - }); + domainId: z.string(), + orgId: z.string() +}); export type RestartOrgDomainResponse = { success: boolean; diff --git a/server/routers/domain/types.ts b/server/routers/domain/types.ts index 4ae48fb1..ececc2db 100644 --- a/server/routers/domain/types.ts +++ b/server/routers/domain/types.ts @@ -5,4 +5,4 @@ export type CheckDomainAvailabilityResponse = { domainId: string; fullDomain: string; }[]; -}; \ No newline at end of file +}; diff --git a/server/routers/domain/updateDomain.ts b/server/routers/domain/updateDomain.ts index 08301189..64e78641 100644 --- a/server/routers/domain/updateDomain.ts +++ b/server/routers/domain/updateDomain.ts @@ -10,14 +10,14 @@ import { eq, and } from "drizzle-orm"; import { OpenAPITags, registry } from "@server/openApi"; const paramsSchema = z.strictObject({ - orgId: z.string(), - domainId: z.string() - }); + orgId: z.string(), + domainId: z.string() +}); const bodySchema = z.strictObject({ - certResolver: z.string().optional().nullable(), - preferWildcardCert: z.boolean().optional().nullable() - }); + certResolver: z.string().optional().nullable(), + preferWildcardCert: z.boolean().optional().nullable() +}); export type UpdateDomainResponse = { domainId: string; @@ -25,7 +25,6 @@ export type UpdateDomainResponse = { preferWildcardCert: boolean | null; }; - registry.registerPath({ method: "patch", path: "/org/{orgId}/domain/{domainId}", @@ -88,7 +87,6 @@ export async function updateOrgDomain( ); } - const [existingDomain] = await db .select() .from(domains) @@ -154,4 +152,4 @@ export async function updateOrgDomain( createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") ); } -} \ No newline at end of file +} diff --git a/server/routers/external.ts b/server/routers/external.ts index 54e84e2e..54b48c6e 100644 --- a/server/routers/external.ts +++ b/server/routers/external.ts @@ -318,7 +318,7 @@ authenticated.post( verifyRoleAccess, verifyUserHasAction(ActionsEnum.setResourceRoles), logActionAudit(ActionsEnum.setResourceRoles), - siteResource.setSiteResourceRoles, + siteResource.setSiteResourceRoles ); authenticated.post( @@ -327,7 +327,7 @@ authenticated.post( verifySetResourceUsers, verifyUserHasAction(ActionsEnum.setResourceUsers), logActionAudit(ActionsEnum.setResourceUsers), - siteResource.setSiteResourceUsers, + siteResource.setSiteResourceUsers ); authenticated.post( @@ -336,7 +336,7 @@ authenticated.post( verifySetResourceClients, verifyUserHasAction(ActionsEnum.setResourceUsers), logActionAudit(ActionsEnum.setResourceUsers), - siteResource.setSiteResourceClients, + siteResource.setSiteResourceClients ); authenticated.post( @@ -345,7 +345,7 @@ authenticated.post( verifySetResourceClients, verifyUserHasAction(ActionsEnum.setResourceUsers), logActionAudit(ActionsEnum.setResourceUsers), - siteResource.addClientToSiteResource, + siteResource.addClientToSiteResource ); authenticated.post( @@ -354,7 +354,7 @@ authenticated.post( verifySetResourceClients, verifyUserHasAction(ActionsEnum.setResourceUsers), logActionAudit(ActionsEnum.setResourceUsers), - siteResource.removeClientFromSiteResource, + siteResource.removeClientFromSiteResource ); authenticated.put( @@ -812,17 +812,9 @@ authenticated.delete( // createNewt // ); -authenticated.put( - "/user/:userId/olm", - verifyIsLoggedInUser, - olm.createUserOlm -); +authenticated.put("/user/:userId/olm", verifyIsLoggedInUser, olm.createUserOlm); -authenticated.get( - "/user/:userId/olms", - verifyIsLoggedInUser, - olm.listUserOlms -); +authenticated.get("/user/:userId/olms", verifyIsLoggedInUser, olm.listUserOlms); authenticated.delete( "/user/:userId/olm/:olmId", diff --git a/server/routers/generatedLicense/types.ts b/server/routers/generatedLicense/types.ts index 4c5efed7..76e86265 100644 --- a/server/routers/generatedLicense/types.ts +++ b/server/routers/generatedLicense/types.ts @@ -27,4 +27,4 @@ export type NewLicenseKey = { }; }; -export type GenerateNewLicenseResponse = NewLicenseKey; \ No newline at end of file +export type GenerateNewLicenseResponse = NewLicenseKey; diff --git a/server/routers/gerbil/createExitNode.ts b/server/routers/gerbil/createExitNode.ts index 8148ed75..bc965036 100644 --- a/server/routers/gerbil/createExitNode.ts +++ b/server/routers/gerbil/createExitNode.ts @@ -5,7 +5,10 @@ import { getNextAvailableSubnet } from "@server/lib/exitNodes"; import logger from "@server/logger"; import { eq } from "drizzle-orm"; -export async function createExitNode(publicKey: string, reachableAt: string | undefined) { +export async function createExitNode( + publicKey: string, + reachableAt: string | undefined +) { // Fetch exit node const [exitNodeQuery] = await db.select().from(exitNodes).limit(1); let exitNode: ExitNode; diff --git a/server/routers/gerbil/getConfig.ts b/server/routers/gerbil/getConfig.ts index 56ebd744..ba3ab7ad 100644 --- a/server/routers/gerbil/getConfig.ts +++ b/server/routers/gerbil/getConfig.ts @@ -117,4 +117,4 @@ export async function generateGerbilConfig(exitNode: ExitNode) { }; return configResponse; -} \ No newline at end of file +} diff --git a/server/routers/gerbil/index.ts b/server/routers/gerbil/index.ts index bff57d05..aa957d3a 100644 --- a/server/routers/gerbil/index.ts +++ b/server/routers/gerbil/index.ts @@ -2,4 +2,4 @@ export * from "./getConfig"; export * from "./receiveBandwidth"; export * from "./updateHolePunch"; export * from "./getAllRelays"; -export * from "./getResolvedHostname"; \ No newline at end of file +export * from "./getResolvedHostname"; diff --git a/server/routers/hybrid.ts b/server/routers/hybrid.ts index 235961f1..398abdb8 100644 --- a/server/routers/hybrid.ts +++ b/server/routers/hybrid.ts @@ -1,4 +1,4 @@ import { Router } from "express"; // Root routes -export const hybridRouter = Router(); \ No newline at end of file +export const hybridRouter = Router(); diff --git a/server/routers/idp/createIdpOrgPolicy.ts b/server/routers/idp/createIdpOrgPolicy.ts index b8c947b0..b9a0098b 100644 --- a/server/routers/idp/createIdpOrgPolicy.ts +++ b/server/routers/idp/createIdpOrgPolicy.ts @@ -12,14 +12,14 @@ import { eq, and } from "drizzle-orm"; import { idp, idpOrg } from "@server/db"; const paramsSchema = z.strictObject({ - idpId: z.coerce.number(), - orgId: z.string() - }); + idpId: z.coerce.number(), + orgId: z.string() +}); const bodySchema = z.strictObject({ - roleMapping: z.string().optional(), - orgMapping: z.string().optional() - }); + roleMapping: z.string().optional(), + orgMapping: z.string().optional() +}); export type CreateIdpOrgPolicyResponse = {}; diff --git a/server/routers/idp/createOidcIdp.ts b/server/routers/idp/createOidcIdp.ts index 2548cb04..c7eeaf30 100644 --- a/server/routers/idp/createOidcIdp.ts +++ b/server/routers/idp/createOidcIdp.ts @@ -15,17 +15,17 @@ import config from "@server/lib/config"; const paramsSchema = z.strictObject({}); const bodySchema = z.strictObject({ - name: z.string().nonempty(), - clientId: z.string().nonempty(), - clientSecret: z.string().nonempty(), - authUrl: z.url(), - tokenUrl: z.url(), - identifierPath: z.string().nonempty(), - emailPath: z.string().optional(), - namePath: z.string().optional(), - scopes: z.string().nonempty(), - autoProvision: z.boolean().optional() - }); + name: z.string().nonempty(), + clientId: z.string().nonempty(), + clientSecret: z.string().nonempty(), + authUrl: z.url(), + tokenUrl: z.url(), + identifierPath: z.string().nonempty(), + emailPath: z.string().optional(), + namePath: z.string().optional(), + scopes: z.string().nonempty(), + autoProvision: z.boolean().optional() +}); export type CreateIdpResponse = { idpId: number; diff --git a/server/routers/idp/deleteIdp.ts b/server/routers/idp/deleteIdp.ts index 56c0ca98..f2b55099 100644 --- a/server/routers/idp/deleteIdp.ts +++ b/server/routers/idp/deleteIdp.ts @@ -53,12 +53,7 @@ export async function deleteIdp( .where(eq(idp.idpId, idpId)); if (!existingIdp) { - return next( - createHttpError( - HttpCode.NOT_FOUND, - "IdP not found" - ) - ); + return next(createHttpError(HttpCode.NOT_FOUND, "IdP not found")); } // Delete the IDP and its related records in a transaction @@ -69,14 +64,10 @@ export async function deleteIdp( .where(eq(idpOidcConfig.idpId, idpId)); // Delete IDP-org mappings - await trx - .delete(idpOrg) - .where(eq(idpOrg.idpId, idpId)); + await trx.delete(idpOrg).where(eq(idpOrg.idpId, idpId)); // Delete the IDP itself - await trx - .delete(idp) - .where(eq(idp.idpId, idpId)); + await trx.delete(idp).where(eq(idp.idpId, idpId)); }); return response(res, { diff --git a/server/routers/idp/deleteIdpOrgPolicy.ts b/server/routers/idp/deleteIdpOrgPolicy.ts index c5f18282..b52a37df 100644 --- a/server/routers/idp/deleteIdpOrgPolicy.ts +++ b/server/routers/idp/deleteIdpOrgPolicy.ts @@ -11,9 +11,9 @@ import { eq, and } from "drizzle-orm"; import { OpenAPITags, registry } from "@server/openApi"; const paramsSchema = z.strictObject({ - idpId: z.coerce.number(), - orgId: z.string() - }); + idpId: z.coerce.number(), + orgId: z.string() +}); registry.registerPath({ method: "delete", diff --git a/server/routers/idp/generateOidcUrl.ts b/server/routers/idp/generateOidcUrl.ts index 2db8783f..50b63ee5 100644 --- a/server/routers/idp/generateOidcUrl.ts +++ b/server/routers/idp/generateOidcUrl.ts @@ -24,8 +24,8 @@ const paramsSchema = z .strict(); const bodySchema = z.strictObject({ - redirectUrl: z.string() - }); + redirectUrl: z.string() +}); const querySchema = z.object({ orgId: z.string().optional() // check what actuall calls it diff --git a/server/routers/idp/getIdp.ts b/server/routers/idp/getIdp.ts index e8651c84..07253751 100644 --- a/server/routers/idp/getIdp.ts +++ b/server/routers/idp/getIdp.ts @@ -71,14 +71,8 @@ export async function getIdp( const clientSecret = idpRes.idpOidcConfig!.clientSecret; const clientId = idpRes.idpOidcConfig!.clientId; - idpRes.idpOidcConfig!.clientSecret = decrypt( - clientSecret, - key - ); - idpRes.idpOidcConfig!.clientId = decrypt( - clientId, - key - ); + idpRes.idpOidcConfig!.clientSecret = decrypt(clientSecret, key); + idpRes.idpOidcConfig!.clientId = decrypt(clientId, key); } return response(res, { diff --git a/server/routers/idp/index.ts b/server/routers/idp/index.ts index 81cec8d1..f0dcf02e 100644 --- a/server/routers/idp/index.ts +++ b/server/routers/idp/index.ts @@ -8,4 +8,4 @@ export * from "./getIdp"; export * from "./createIdpOrgPolicy"; export * from "./deleteIdpOrgPolicy"; export * from "./listIdpOrgPolicies"; -export * from "./updateIdpOrgPolicy"; \ No newline at end of file +export * from "./updateIdpOrgPolicy"; diff --git a/server/routers/idp/listIdpOrgPolicies.ts b/server/routers/idp/listIdpOrgPolicies.ts index 087b52f8..9f7cdb42 100644 --- a/server/routers/idp/listIdpOrgPolicies.ts +++ b/server/routers/idp/listIdpOrgPolicies.ts @@ -15,19 +15,19 @@ const paramsSchema = z.object({ }); const querySchema = z.strictObject({ - limit: z - .string() - .optional() - .default("1000") - .transform(Number) - .pipe(z.int().nonnegative()), - offset: z - .string() - .optional() - .default("0") - .transform(Number) - .pipe(z.int().nonnegative()) - }); + limit: z + .string() + .optional() + .default("1000") + .transform(Number) + .pipe(z.int().nonnegative()), + offset: z + .string() + .optional() + .default("0") + .transform(Number) + .pipe(z.int().nonnegative()) +}); async function query(idpId: number, limit: number, offset: number) { const res = await db diff --git a/server/routers/idp/listIdps.ts b/server/routers/idp/listIdps.ts index 8ce2ab78..20d1899e 100644 --- a/server/routers/idp/listIdps.ts +++ b/server/routers/idp/listIdps.ts @@ -11,19 +11,19 @@ import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; const querySchema = z.strictObject({ - limit: z - .string() - .optional() - .default("1000") - .transform(Number) - .pipe(z.int().nonnegative()), - offset: z - .string() - .optional() - .default("0") - .transform(Number) - .pipe(z.int().nonnegative()) - }); + limit: z + .string() + .optional() + .default("1000") + .transform(Number) + .pipe(z.int().nonnegative()), + offset: z + .string() + .optional() + .default("0") + .transform(Number) + .pipe(z.int().nonnegative()) +}); async function query(limit: number, offset: number) { const res = await db diff --git a/server/routers/idp/updateIdpOrgPolicy.ts b/server/routers/idp/updateIdpOrgPolicy.ts index 82d3b5f2..6432faf6 100644 --- a/server/routers/idp/updateIdpOrgPolicy.ts +++ b/server/routers/idp/updateIdpOrgPolicy.ts @@ -11,14 +11,14 @@ import { eq, and } from "drizzle-orm"; import { idp, idpOrg } from "@server/db"; const paramsSchema = z.strictObject({ - idpId: z.coerce.number(), - orgId: z.string() - }); + idpId: z.coerce.number(), + orgId: z.string() +}); const bodySchema = z.strictObject({ - roleMapping: z.string().optional(), - orgMapping: z.string().optional() - }); + roleMapping: z.string().optional(), + orgMapping: z.string().optional() +}); export type UpdateIdpOrgPolicyResponse = {}; diff --git a/server/routers/idp/updateOidcIdp.ts b/server/routers/idp/updateOidcIdp.ts index 1dbdd00a..a4d55187 100644 --- a/server/routers/idp/updateOidcIdp.ts +++ b/server/routers/idp/updateOidcIdp.ts @@ -19,19 +19,19 @@ const paramsSchema = z .strict(); const bodySchema = z.strictObject({ - name: z.string().optional(), - clientId: z.string().optional(), - clientSecret: z.string().optional(), - authUrl: z.string().optional(), - tokenUrl: z.string().optional(), - identifierPath: z.string().optional(), - emailPath: z.string().optional(), - namePath: z.string().optional(), - scopes: z.string().optional(), - autoProvision: z.boolean().optional(), - defaultRoleMapping: z.string().optional(), - defaultOrgMapping: z.string().optional() - }); + name: z.string().optional(), + clientId: z.string().optional(), + clientSecret: z.string().optional(), + authUrl: z.string().optional(), + tokenUrl: z.string().optional(), + identifierPath: z.string().optional(), + emailPath: z.string().optional(), + namePath: z.string().optional(), + scopes: z.string().optional(), + autoProvision: z.boolean().optional(), + defaultRoleMapping: z.string().optional(), + defaultOrgMapping: z.string().optional() +}); export type UpdateIdpResponse = { idpId: number; diff --git a/server/routers/license/types.ts b/server/routers/license/types.ts index 945bd368..a78a287f 100644 --- a/server/routers/license/types.ts +++ b/server/routers/license/types.ts @@ -8,4 +8,4 @@ export type GetLicenseStatusResponse = LicenseStatus; export type ListLicenseKeysResponse = LicenseKeyCache[]; -export type RecheckStatusResponse = LicenseStatus; \ No newline at end of file +export type RecheckStatusResponse = LicenseStatus; diff --git a/server/routers/loginPage/types.ts b/server/routers/loginPage/types.ts index 26f59cab..a68dd7d4 100644 --- a/server/routers/loginPage/types.ts +++ b/server/routers/loginPage/types.ts @@ -8,4 +8,4 @@ export type GetLoginPageResponse = LoginPage; export type UpdateLoginPageResponse = LoginPage; -export type LoadLoginPageResponse = LoginPage & { orgId: string }; \ No newline at end of file +export type LoadLoginPageResponse = LoginPage & { orgId: string }; diff --git a/server/routers/newt/createNewt.ts b/server/routers/newt/createNewt.ts index 930c04be..b5da405e 100644 --- a/server/routers/newt/createNewt.ts +++ b/server/routers/newt/createNewt.ts @@ -24,9 +24,9 @@ export type CreateNewtResponse = { }; const createNewtSchema = z.strictObject({ - newtId: z.string(), - secret: z.string() - }); + newtId: z.string(), + secret: z.string() +}); export async function createNewt( req: Request, @@ -34,7 +34,6 @@ export async function createNewt( next: NextFunction ): Promise { try { - const parsedBody = createNewtSchema.safeParse(req.body); if (!parsedBody.success) { return next( @@ -58,7 +57,7 @@ export async function createNewt( await db.insert(newts).values({ newtId: newtId, secretHash, - dateCreated: moment().toISOString(), + dateCreated: moment().toISOString() }); // give the newt their default permissions: @@ -75,12 +74,12 @@ export async function createNewt( data: { newtId, secret, - token, + token }, success: true, error: false, message: "Newt created successfully", - status: HttpCode.OK, + status: HttpCode.OK }); } catch (e) { if (e instanceof SqliteError && e.code === "SQLITE_CONSTRAINT_UNIQUE") { diff --git a/server/routers/newt/handleNewtPingRequestMessage.ts b/server/routers/newt/handleNewtPingRequestMessage.ts index fea157fd..b75ddd5e 100644 --- a/server/routers/newt/handleNewtPingRequestMessage.ts +++ b/server/routers/newt/handleNewtPingRequestMessage.ts @@ -35,7 +35,11 @@ export const handleNewtPingRequestMessage: MessageHandler = async (context) => { const { noCloud } = message.data; - const exitNodesList = await listExitNodes(site.orgId, true, noCloud || false); // filter for only the online ones + const exitNodesList = await listExitNodes( + site.orgId, + true, + noCloud || false + ); // filter for only the online ones let lastExitNodeId = null; if (newt.siteId) { diff --git a/server/routers/newt/handleNewtRegisterMessage.ts b/server/routers/newt/handleNewtRegisterMessage.ts index f4d963a1..77e49a20 100644 --- a/server/routers/newt/handleNewtRegisterMessage.ts +++ b/server/routers/newt/handleNewtRegisterMessage.ts @@ -255,7 +255,7 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => { hcTimeout: targetHealthCheck.hcTimeout, hcHeaders: targetHealthCheck.hcHeaders, hcMethod: targetHealthCheck.hcMethod, - hcTlsServerName: targetHealthCheck.hcTlsServerName, + hcTlsServerName: targetHealthCheck.hcTlsServerName }) .from(targets) .innerJoin(resources, eq(targets.resourceId, resources.resourceId)) @@ -328,7 +328,7 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => { hcTimeout: target.hcTimeout, // in seconds hcHeaders: hcHeadersSend, hcMethod: target.hcMethod, - hcTlsServerName: target.hcTlsServerName, + hcTlsServerName: target.hcTlsServerName }; }); @@ -366,7 +366,7 @@ async function getUniqueSubnetForSite( trx: Transaction | typeof db = db ): Promise { const lockKey = `subnet-allocation:${exitNode.exitNodeId}`; - + return await lockManager.withLock( lockKey, async () => { @@ -382,7 +382,8 @@ async function getUniqueSubnetForSite( .map((site) => site.subnet) .filter( (subnet) => - subnet && /^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/.test(subnet) + subnet && + /^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/.test(subnet) ) .filter((subnet) => subnet !== null); subnets.push(exitNode.address.replace(/\/\d+$/, `/${blockSize}`)); diff --git a/server/routers/newt/handleReceiveBandwidthMessage.ts b/server/routers/newt/handleReceiveBandwidthMessage.ts index f5170feb..3d060a0c 100644 --- a/server/routers/newt/handleReceiveBandwidthMessage.ts +++ b/server/routers/newt/handleReceiveBandwidthMessage.ts @@ -10,7 +10,9 @@ interface PeerBandwidth { bytesOut: number; } -export const handleReceiveBandwidthMessage: MessageHandler = async (context) => { +export const handleReceiveBandwidthMessage: MessageHandler = async ( + context +) => { const { message, client, sendToClient } = context; if (!message.data.bandwidthData) { @@ -44,7 +46,7 @@ export const handleReceiveBandwidthMessage: MessageHandler = async (context) => .set({ megabytesOut: (client.megabytesIn || 0) + bytesIn, megabytesIn: (client.megabytesOut || 0) + bytesOut, - lastBandwidthUpdate: new Date().toISOString(), + lastBandwidthUpdate: new Date().toISOString() }) .where(eq(clients.clientId, client.clientId)); } diff --git a/server/routers/newt/handleSocketMessages.ts b/server/routers/newt/handleSocketMessages.ts index 09a473b9..f26f69c9 100644 --- a/server/routers/newt/handleSocketMessages.ts +++ b/server/routers/newt/handleSocketMessages.ts @@ -64,9 +64,5 @@ export const handleDockerContainersMessage: MessageHandler = async ( return; } - await applyNewtDockerBlueprint( - newt.siteId, - newt.newtId, - containers - ); + await applyNewtDockerBlueprint(newt.siteId, newt.newtId, containers); }; diff --git a/server/routers/newt/index.ts b/server/routers/newt/index.ts index 9642a637..6b17f324 100644 --- a/server/routers/newt/index.ts +++ b/server/routers/newt/index.ts @@ -5,4 +5,4 @@ export * from "./handleReceiveBandwidthMessage"; export * from "./handleGetConfigMessage"; export * from "./handleSocketMessages"; export * from "./handleNewtPingRequestMessage"; -export * from "./handleApplyBlueprintMessage"; \ No newline at end of file +export * from "./handleApplyBlueprintMessage"; diff --git a/server/routers/newt/peers.ts b/server/routers/newt/peers.ts index 694f0c0f..c7546ff0 100644 --- a/server/routers/newt/peers.ts +++ b/server/routers/newt/peers.ts @@ -48,7 +48,11 @@ export async function addPeer( return site; } -export async function deletePeer(siteId: number, publicKey: string, newtId?: string) { +export async function deletePeer( + siteId: number, + publicKey: string, + newtId?: string +) { let site: Site | null = null; if (!newtId) { [site] = await db diff --git a/server/routers/newt/targets.ts b/server/routers/newt/targets.ts index a5883f30..e97aed35 100644 --- a/server/routers/newt/targets.ts +++ b/server/routers/newt/targets.ts @@ -26,22 +26,32 @@ export async function addTargets( // Create a map for quick lookup const healthCheckMap = new Map(); - healthCheckData.forEach(hc => { + healthCheckData.forEach((hc) => { healthCheckMap.set(hc.targetId, hc); }); const healthCheckTargets = targets.map((target) => { const hc = healthCheckMap.get(target.targetId); - + // If no health check data found, skip this target if (!hc) { - logger.warn(`No health check configuration found for target ${target.targetId}`); + logger.warn( + `No health check configuration found for target ${target.targetId}` + ); return null; } // Ensure all necessary fields are present - if (!hc.hcPath || !hc.hcHostname || !hc.hcPort || !hc.hcInterval || !hc.hcMethod) { - logger.debug(`Skipping target ${target.targetId} due to missing health check fields`); + if ( + !hc.hcPath || + !hc.hcHostname || + !hc.hcPort || + !hc.hcInterval || + !hc.hcMethod + ) { + logger.debug( + `Skipping target ${target.targetId} due to missing health check fields` + ); return null; // Skip targets with missing health check fields } @@ -49,9 +59,11 @@ export async function addTargets( const hcHeadersSend: { [key: string]: string } = {}; if (hcHeadersParse) { // transform - hcHeadersParse.forEach((header: { name: string; value: string }) => { - hcHeadersSend[header.name] = header.value; - }); + hcHeadersParse.forEach( + (header: { name: string; value: string }) => { + hcHeadersSend[header.name] = header.value; + } + ); } // try to parse the hcStatus into a int and if not possible set to undefined @@ -77,12 +89,14 @@ export async function addTargets( hcHeaders: hcHeadersSend, hcMethod: hc.hcMethod, hcStatus: hcStatus, - hcTlsServerName: hc.hcTlsServerName, + hcTlsServerName: hc.hcTlsServerName }; }); // Filter out any null values from health check targets - const validHealthCheckTargets = healthCheckTargets.filter((target) => target !== null); + const validHealthCheckTargets = healthCheckTargets.filter( + (target) => target !== null + ); await sendToClient(newtId, { type: `newt/healthcheck/add`, diff --git a/server/routers/olm/createOlm.ts b/server/routers/olm/createOlm.ts index 930c04be..b5da405e 100644 --- a/server/routers/olm/createOlm.ts +++ b/server/routers/olm/createOlm.ts @@ -24,9 +24,9 @@ export type CreateNewtResponse = { }; const createNewtSchema = z.strictObject({ - newtId: z.string(), - secret: z.string() - }); + newtId: z.string(), + secret: z.string() +}); export async function createNewt( req: Request, @@ -34,7 +34,6 @@ export async function createNewt( next: NextFunction ): Promise { try { - const parsedBody = createNewtSchema.safeParse(req.body); if (!parsedBody.success) { return next( @@ -58,7 +57,7 @@ export async function createNewt( await db.insert(newts).values({ newtId: newtId, secretHash, - dateCreated: moment().toISOString(), + dateCreated: moment().toISOString() }); // give the newt their default permissions: @@ -75,12 +74,12 @@ export async function createNewt( data: { newtId, secret, - token, + token }, success: true, error: false, message: "Newt created successfully", - status: HttpCode.OK, + status: HttpCode.OK }); } catch (e) { if (e instanceof SqliteError && e.code === "SQLITE_CONSTRAINT_UNIQUE") { diff --git a/server/routers/olm/handleOlmPingMessage.ts b/server/routers/olm/handleOlmPingMessage.ts index 35d704c7..0fa490c8 100644 --- a/server/routers/olm/handleOlmPingMessage.ts +++ b/server/routers/olm/handleOlmPingMessage.ts @@ -61,9 +61,12 @@ export const startOlmOfflineChecker = (): void => { // Send a disconnect message to the client if connected try { - await sendTerminateClient(offlineClient.clientId, offlineClient.olmId); // terminate first + await sendTerminateClient( + offlineClient.clientId, + offlineClient.olmId + ); // terminate first // wait a moment to ensure the message is sent - await new Promise(resolve => setTimeout(resolve, 1000)); + await new Promise((resolve) => setTimeout(resolve, 1000)); await disconnectClient(offlineClient.olmId); } catch (error) { logger.error( diff --git a/server/routers/olm/handleOlmServerPeerAddMessage.ts b/server/routers/olm/handleOlmServerPeerAddMessage.ts index c0556b0e..53f3474c 100644 --- a/server/routers/olm/handleOlmServerPeerAddMessage.ts +++ b/server/routers/olm/handleOlmServerPeerAddMessage.ts @@ -113,14 +113,14 @@ export const handleOlmServerPeerAddMessage: MessageHandler = async ( .select() .from(clientSitesAssociationsCache) .where( - and( + and( eq(clientSitesAssociationsCache.clientId, client.clientId), isNotNull(clientSitesAssociationsCache.endpoint), eq(clientSitesAssociationsCache.publicKey, client.pubKey) // limit it to the current session its connected with otherwise the endpoint could be stale ) ); - // pick an endpoint + // pick an endpoint for (const assoc of currentSessionSiteAssociationCaches) { if (assoc.endpoint) { endpoint = assoc.endpoint; diff --git a/server/routers/olm/index.ts b/server/routers/olm/index.ts index e671dd42..594ef9cb 100644 --- a/server/routers/olm/index.ts +++ b/server/routers/olm/index.ts @@ -8,4 +8,4 @@ export * from "./listUserOlms"; export * from "./deleteUserOlm"; export * from "./getUserOlm"; export * from "./handleOlmServerPeerAddMessage"; -export * from "./handleOlmUnRelayMessage"; \ No newline at end of file +export * from "./handleOlmUnRelayMessage"; diff --git a/server/routers/org/checkId.ts b/server/routers/org/checkId.ts index 2a898c30..f11809d2 100644 --- a/server/routers/org/checkId.ts +++ b/server/routers/org/checkId.ts @@ -10,8 +10,8 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; const getOrgSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); export async function checkId( req: Request, diff --git a/server/routers/org/getOrg.ts b/server/routers/org/getOrg.ts index 38a1c6ba..a30dcc1c 100644 --- a/server/routers/org/getOrg.ts +++ b/server/routers/org/getOrg.ts @@ -11,8 +11,8 @@ import { fromZodError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; const getOrgSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); export type GetOrgResponse = { org: Org; diff --git a/server/routers/org/getOrgOverview.ts b/server/routers/org/getOrgOverview.ts index dc704d6a..d368d1b3 100644 --- a/server/routers/org/getOrgOverview.ts +++ b/server/routers/org/getOrgOverview.ts @@ -19,8 +19,8 @@ import logger from "@server/logger"; import { fromZodError } from "zod-validation-error"; const getOrgParamsSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); export type GetOrgOverviewResponse = { orgName: string; diff --git a/server/routers/org/updateOrg.ts b/server/routers/org/updateOrg.ts index 6e7a9b35..aa9e2151 100644 --- a/server/routers/org/updateOrg.ts +++ b/server/routers/org/updateOrg.ts @@ -16,10 +16,11 @@ import { TierId } from "@server/lib/billing/tiers"; import { cache } from "@server/lib/cache"; const updateOrgParamsSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); -const updateOrgBodySchema = z.strictObject({ +const updateOrgBodySchema = z + .strictObject({ name: z.string().min(1).max(255).optional(), requireTwoFactor: z.boolean().optional(), maxSessionLengthHours: z.number().nullable().optional(), diff --git a/server/routers/orgIdp/types.ts b/server/routers/orgIdp/types.ts index a8e205cc..f6f581ee 100644 --- a/server/routers/orgIdp/types.ts +++ b/server/routers/orgIdp/types.ts @@ -6,10 +6,10 @@ export type CreateOrgIdpResponse = { }; export type GetOrgIdpResponse = { - idp: Idp, - idpOidcConfig: IdpOidcConfig | null, - redirectUrl: string -} + idp: Idp; + idpOidcConfig: IdpOidcConfig | null; + redirectUrl: string; +}; export type ListOrgIdpsResponse = { idps: { @@ -18,7 +18,7 @@ export type ListOrgIdpsResponse = { name: string; type: string; variant: string; - }[], + }[]; pagination: { total: number; limit: number; diff --git a/server/routers/remoteExitNode/types.ts b/server/routers/remoteExitNode/types.ts index 55d0a286..25a7d6c5 100644 --- a/server/routers/remoteExitNode/types.ts +++ b/server/routers/remoteExitNode/types.ts @@ -31,4 +31,14 @@ export type ListRemoteExitNodesResponse = { pagination: { total: number; limit: number; offset: number }; }; -export type GetRemoteExitNodeResponse = { remoteExitNodeId: string; dateCreated: string; version: string | null; exitNodeId: number | null; name: string; address: string; endpoint: string; online: boolean; type: string | null; } \ No newline at end of file +export type GetRemoteExitNodeResponse = { + remoteExitNodeId: string; + dateCreated: string; + version: string | null; + exitNodeId: number | null; + name: string; + address: string; + endpoint: string; + online: boolean; + type: string | null; +}; diff --git a/server/routers/resource/addEmailToResourceWhitelist.ts b/server/routers/resource/addEmailToResourceWhitelist.ts index f9cee838..53828b44 100644 --- a/server/routers/resource/addEmailToResourceWhitelist.ts +++ b/server/routers/resource/addEmailToResourceWhitelist.ts @@ -11,21 +11,19 @@ import { and, eq } from "drizzle-orm"; import { OpenAPITags, registry } from "@server/openApi"; const addEmailToResourceWhitelistBodySchema = z.strictObject({ - email: z.email() - .or( - z.string().regex(/^\*@[\w.-]+\.[a-zA-Z]{2,}$/, { - error: "Invalid email address. Wildcard (*) must be the entire local part." - }) - ) - .transform((v) => v.toLowerCase()) - }); + email: z + .email() + .or( + z.string().regex(/^\*@[\w.-]+\.[a-zA-Z]{2,}$/, { + error: "Invalid email address. Wildcard (*) must be the entire local part." + }) + ) + .transform((v) => v.toLowerCase()) +}); const addEmailToResourceWhitelistParamsSchema = z.strictObject({ - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); registry.registerPath({ method: "post", diff --git a/server/routers/resource/addRoleToResource.ts b/server/routers/resource/addRoleToResource.ts index c29f2757..ba344c6c 100644 --- a/server/routers/resource/addRoleToResource.ts +++ b/server/routers/resource/addRoleToResource.ts @@ -93,10 +93,7 @@ export async function addRoleToResource( .select() .from(roles) .where( - and( - eq(roles.roleId, roleId), - eq(roles.orgId, resource.orgId) - ) + and(eq(roles.roleId, roleId), eq(roles.orgId, resource.orgId)) ) .limit(1); @@ -158,4 +155,3 @@ export async function addRoleToResource( ); } } - diff --git a/server/routers/resource/addUserToResource.ts b/server/routers/resource/addUserToResource.ts index 6dbfe086..ee6081ff 100644 --- a/server/routers/resource/addUserToResource.ts +++ b/server/routers/resource/addUserToResource.ts @@ -127,4 +127,3 @@ export async function addUserToResource( ); } } - diff --git a/server/routers/resource/authWithAccessToken.ts b/server/routers/resource/authWithAccessToken.ts index 81ca7fbc..53f72cb2 100644 --- a/server/routers/resource/authWithAccessToken.ts +++ b/server/routers/resource/authWithAccessToken.ts @@ -16,17 +16,17 @@ import stoi from "@server/lib/stoi"; import { logAccessAudit } from "#dynamic/lib/logAccessAudit"; const authWithAccessTokenBodySchema = z.strictObject({ - accessToken: z.string(), - accessTokenId: z.string().optional() - }); + accessToken: z.string(), + accessTokenId: z.string().optional() +}); const authWithAccessTokenParamsSchema = z.strictObject({ - resourceId: z - .string() - .optional() - .transform(stoi) - .pipe(z.int().positive().optional()) - }); + resourceId: z + .string() + .optional() + .transform(stoi) + .pipe(z.int().positive().optional()) +}); export type AuthWithAccessTokenResponse = { session?: string; diff --git a/server/routers/resource/authWithPassword.ts b/server/routers/resource/authWithPassword.ts index 4c1f2058..ecf61896 100644 --- a/server/routers/resource/authWithPassword.ts +++ b/server/routers/resource/authWithPassword.ts @@ -16,15 +16,12 @@ import config from "@server/lib/config"; import { logAccessAudit } from "#dynamic/lib/logAccessAudit"; export const authWithPasswordBodySchema = z.strictObject({ - password: z.string() - }); + password: z.string() +}); export const authWithPasswordParamsSchema = z.strictObject({ - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); export type AuthWithPasswordResponse = { session?: string; diff --git a/server/routers/resource/authWithPincode.ts b/server/routers/resource/authWithPincode.ts index 59f80ee0..78e132d2 100644 --- a/server/routers/resource/authWithPincode.ts +++ b/server/routers/resource/authWithPincode.ts @@ -15,15 +15,12 @@ import config from "@server/lib/config"; import { logAccessAudit } from "#dynamic/lib/logAccessAudit"; export const authWithPincodeBodySchema = z.strictObject({ - pincode: z.string() - }); + pincode: z.string() +}); export const authWithPincodeParamsSchema = z.strictObject({ - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); export type AuthWithPincodeResponse = { session?: string; diff --git a/server/routers/resource/authWithWhitelist.ts b/server/routers/resource/authWithWhitelist.ts index 11f84043..6a2b7ee7 100644 --- a/server/routers/resource/authWithWhitelist.ts +++ b/server/routers/resource/authWithWhitelist.ts @@ -15,16 +15,13 @@ import config from "@server/lib/config"; import { logAccessAudit } from "#dynamic/lib/logAccessAudit"; const authWithWhitelistBodySchema = z.strictObject({ - email: z.email().toLowerCase(), - otp: z.string().optional() - }); + email: z.email().toLowerCase(), + otp: z.string().optional() +}); const authWithWhitelistParamsSchema = z.strictObject({ - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); export type AuthWithWhitelistResponse = { otpSent?: boolean; diff --git a/server/routers/resource/createResource.ts b/server/routers/resource/createResource.ts index b9ab3ce5..ba1fdba2 100644 --- a/server/routers/resource/createResource.ts +++ b/server/routers/resource/createResource.ts @@ -26,16 +26,17 @@ import { getUniqueResourceName } from "@server/db/names"; import { validateAndConstructDomain } from "@server/lib/domainUtils"; const createResourceParamsSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); -const createHttpResourceSchema = z.strictObject({ +const createHttpResourceSchema = z + .strictObject({ name: z.string().min(1).max(255), subdomain: z.string().nullable().optional(), http: z.boolean(), protocol: z.enum(["tcp", "udp"]), domainId: z.string(), - stickySession: z.boolean().optional(), + stickySession: z.boolean().optional() }) .refine( (data) => { @@ -49,7 +50,8 @@ const createHttpResourceSchema = z.strictObject({ } ); -const createRawResourceSchema = z.strictObject({ +const createRawResourceSchema = z + .strictObject({ name: z.string().min(1).max(255), http: z.boolean(), protocol: z.enum(["tcp", "udp"]), @@ -188,7 +190,7 @@ async function createHttpResource( const { name, domainId } = parsedBody.data; const subdomain = parsedBody.data.subdomain; - const stickySession=parsedBody.data.stickySession; + const stickySession = parsedBody.data.stickySession; // Validate domain and construct full domain const domainResult = await validateAndConstructDomain( diff --git a/server/routers/resource/createResourceRule.ts b/server/routers/resource/createResourceRule.ts index c3e086b0..3f86665b 100644 --- a/server/routers/resource/createResourceRule.ts +++ b/server/routers/resource/createResourceRule.ts @@ -16,19 +16,16 @@ import { import { OpenAPITags, registry } from "@server/openApi"; const createResourceRuleSchema = z.strictObject({ - action: z.enum(["ACCEPT", "DROP", "PASS"]), - match: z.enum(["CIDR", "IP", "PATH", "COUNTRY"]), - value: z.string().min(1), - priority: z.int(), - enabled: z.boolean().optional() - }); + action: z.enum(["ACCEPT", "DROP", "PASS"]), + match: z.enum(["CIDR", "IP", "PATH", "COUNTRY"]), + value: z.string().min(1), + priority: z.int(), + enabled: z.boolean().optional() +}); const createResourceRuleParamsSchema = z.strictObject({ - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); registry.registerPath({ method: "put", diff --git a/server/routers/resource/deleteResource.ts b/server/routers/resource/deleteResource.ts index a81208a5..d8891d75 100644 --- a/server/routers/resource/deleteResource.ts +++ b/server/routers/resource/deleteResource.ts @@ -15,11 +15,8 @@ import { OpenAPITags, registry } from "@server/openApi"; // Define Zod schema for request parameters validation const deleteResourceSchema = z.strictObject({ - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); registry.registerPath({ method: "delete", diff --git a/server/routers/resource/deleteResourceRule.ts b/server/routers/resource/deleteResourceRule.ts index 58cb7b48..638f2e1d 100644 --- a/server/routers/resource/deleteResourceRule.ts +++ b/server/routers/resource/deleteResourceRule.ts @@ -11,12 +11,9 @@ import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; const deleteResourceRuleSchema = z.strictObject({ - ruleId: z.string().transform(Number).pipe(z.int().positive()), - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + ruleId: z.string().transform(Number).pipe(z.int().positive()), + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); registry.registerPath({ method: "delete", diff --git a/server/routers/resource/getExchangeToken.ts b/server/routers/resource/getExchangeToken.ts index 8a0276a0..b0af4b7f 100644 --- a/server/routers/resource/getExchangeToken.ts +++ b/server/routers/resource/getExchangeToken.ts @@ -17,11 +17,8 @@ import { checkOrgAccessPolicy } from "#dynamic/lib/checkOrgAccessPolicy"; import { logAccessAudit } from "#dynamic/lib/logAccessAudit"; const getExchangeTokenParams = z.strictObject({ - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); export type GetExchangeTokenResponse = { requestToken: string; diff --git a/server/routers/resource/getResource.ts b/server/routers/resource/getResource.ts index f2ce559e..7f3e8a0e 100644 --- a/server/routers/resource/getResource.ts +++ b/server/routers/resource/getResource.ts @@ -12,15 +12,15 @@ import stoi from "@server/lib/stoi"; import { OpenAPITags, registry } from "@server/openApi"; const getResourceSchema = z.strictObject({ - resourceId: z - .string() - .optional() - .transform(stoi) - .pipe(z.int().positive().optional()) - .optional(), - niceId: z.string().optional(), - orgId: z.string().optional() - }); + resourceId: z + .string() + .optional() + .transform(stoi) + .pipe(z.int().positive().optional()) + .optional(), + niceId: z.string().optional(), + orgId: z.string().optional() +}); async function query(resourceId?: number, niceId?: string, orgId?: string) { if (resourceId) { @@ -34,13 +34,18 @@ async function query(resourceId?: number, niceId?: string, orgId?: string) { const [res] = await db .select() .from(resources) - .where(and(eq(resources.niceId, niceId), eq(resources.orgId, orgId))) + .where( + and(eq(resources.niceId, niceId), eq(resources.orgId, orgId)) + ) .limit(1); return res; } } -export type GetResourceResponse = Omit>>, 'headers'> & { +export type GetResourceResponse = Omit< + NonNullable>>, + "headers" +> & { headers: { name: string; value: string }[] | null; }; @@ -101,7 +106,9 @@ export async function getResource( return response(res, { data: { ...resource, - headers: resource.headers ? JSON.parse(resource.headers) : resource.headers + headers: resource.headers + ? JSON.parse(resource.headers) + : resource.headers }, success: true, error: false, diff --git a/server/routers/resource/getResourceAuthInfo.ts b/server/routers/resource/getResourceAuthInfo.ts index 60f8e586..fe0a38c8 100644 --- a/server/routers/resource/getResourceAuthInfo.ts +++ b/server/routers/resource/getResourceAuthInfo.ts @@ -16,8 +16,8 @@ import logger from "@server/logger"; import { build } from "@server/build"; const getResourceAuthInfoSchema = z.strictObject({ - resourceGuid: z.string() - }); + resourceGuid: z.string() +}); export type GetResourceAuthInfoResponse = { resourceId: number; diff --git a/server/routers/resource/getResourceWhitelist.ts b/server/routers/resource/getResourceWhitelist.ts index 3171352a..52cff0c7 100644 --- a/server/routers/resource/getResourceWhitelist.ts +++ b/server/routers/resource/getResourceWhitelist.ts @@ -11,11 +11,8 @@ import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; const getResourceWhitelistSchema = z.strictObject({ - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); async function queryWhitelist(resourceId: number) { return await db diff --git a/server/routers/resource/listResourceRoles.ts b/server/routers/resource/listResourceRoles.ts index 3dbb8c0d..68dc58a2 100644 --- a/server/routers/resource/listResourceRoles.ts +++ b/server/routers/resource/listResourceRoles.ts @@ -11,11 +11,8 @@ import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; const listResourceRolesSchema = z.strictObject({ - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); async function query(resourceId: number) { return await db diff --git a/server/routers/resource/listResourceRules.ts b/server/routers/resource/listResourceRules.ts index bc2516a0..dae7922d 100644 --- a/server/routers/resource/listResourceRules.ts +++ b/server/routers/resource/listResourceRules.ts @@ -11,11 +11,8 @@ import logger from "@server/logger"; import { OpenAPITags, registry } from "@server/openApi"; const listResourceRulesParamsSchema = z.strictObject({ - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); const listResourceRulesSchema = z.object({ limit: z diff --git a/server/routers/resource/listResourceUsers.ts b/server/routers/resource/listResourceUsers.ts index b07bcf0a..e7f73287 100644 --- a/server/routers/resource/listResourceUsers.ts +++ b/server/routers/resource/listResourceUsers.ts @@ -11,11 +11,8 @@ import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; const listResourceUsersSchema = z.strictObject({ - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); async function queryUsers(resourceId: number) { return await db diff --git a/server/routers/resource/removeEmailFromResourceWhitelist.ts b/server/routers/resource/removeEmailFromResourceWhitelist.ts index c2cac2de..d60133b8 100644 --- a/server/routers/resource/removeEmailFromResourceWhitelist.ts +++ b/server/routers/resource/removeEmailFromResourceWhitelist.ts @@ -11,21 +11,19 @@ import { and, eq } from "drizzle-orm"; import { OpenAPITags, registry } from "@server/openApi"; const removeEmailFromResourceWhitelistBodySchema = z.strictObject({ - email: z.email() - .or( - z.string().regex(/^\*@[\w.-]+\.[a-zA-Z]{2,}$/, { - error: "Invalid email address. Wildcard (*) must be the entire local part." - }) - ) - .transform((v) => v.toLowerCase()) - }); + email: z + .email() + .or( + z.string().regex(/^\*@[\w.-]+\.[a-zA-Z]{2,}$/, { + error: "Invalid email address. Wildcard (*) must be the entire local part." + }) + ) + .transform((v) => v.toLowerCase()) +}); const removeEmailFromResourceWhitelistParamsSchema = z.strictObject({ - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); registry.registerPath({ method: "post", diff --git a/server/routers/resource/removeRoleFromResource.ts b/server/routers/resource/removeRoleFromResource.ts index cb44ac4a..eab7660c 100644 --- a/server/routers/resource/removeRoleFromResource.ts +++ b/server/routers/resource/removeRoleFromResource.ts @@ -49,9 +49,7 @@ export async function removeRoleFromResource( next: NextFunction ): Promise { try { - const parsedBody = removeRoleFromResourceBodySchema.safeParse( - req.body - ); + const parsedBody = removeRoleFromResourceBodySchema.safeParse(req.body); if (!parsedBody.success) { return next( createHttpError( @@ -95,10 +93,7 @@ export async function removeRoleFromResource( .select() .from(roles) .where( - and( - eq(roles.roleId, roleId), - eq(roles.orgId, resource.orgId) - ) + and(eq(roles.roleId, roleId), eq(roles.orgId, resource.orgId)) ) .limit(1); @@ -163,4 +158,3 @@ export async function removeRoleFromResource( ); } } - diff --git a/server/routers/resource/removeUserFromResource.ts b/server/routers/resource/removeUserFromResource.ts index 8dce7e48..9da96d3c 100644 --- a/server/routers/resource/removeUserFromResource.ts +++ b/server/routers/resource/removeUserFromResource.ts @@ -49,9 +49,7 @@ export async function removeUserFromResource( next: NextFunction ): Promise { try { - const parsedBody = removeUserFromResourceBodySchema.safeParse( - req.body - ); + const parsedBody = removeUserFromResourceBodySchema.safeParse(req.body); if (!parsedBody.success) { return next( createHttpError( @@ -133,4 +131,3 @@ export async function removeUserFromResource( ); } } - diff --git a/server/routers/resource/setResourceHeaderAuth.ts b/server/routers/resource/setResourceHeaderAuth.ts index 87ffbacd..b89179ae 100644 --- a/server/routers/resource/setResourceHeaderAuth.ts +++ b/server/routers/resource/setResourceHeaderAuth.ts @@ -15,9 +15,9 @@ const setResourceAuthMethodsParamsSchema = z.object({ }); const setResourceAuthMethodsBodySchema = z.strictObject({ - user: z.string().min(4).max(100).nullable(), - password: z.string().min(4).max(100).nullable() - }); + user: z.string().min(4).max(100).nullable(), + password: z.string().min(4).max(100).nullable() +}); registry.registerPath({ method: "post", @@ -75,7 +75,9 @@ export async function setResourceHeaderAuth( .where(eq(resourceHeaderAuth.resourceId, resourceId)); if (user && password) { - const headerAuthHash = await hashPassword(Buffer.from(`${user}:${password}`).toString("base64")); + const headerAuthHash = await hashPassword( + Buffer.from(`${user}:${password}`).toString("base64") + ); await trx .insert(resourceHeaderAuth) diff --git a/server/routers/resource/setResourcePassword.ts b/server/routers/resource/setResourcePassword.ts index 3f9ce9f1..9bd845a4 100644 --- a/server/routers/resource/setResourcePassword.ts +++ b/server/routers/resource/setResourcePassword.ts @@ -17,8 +17,8 @@ const setResourceAuthMethodsParamsSchema = z.object({ }); const setResourceAuthMethodsBodySchema = z.strictObject({ - password: z.string().min(4).max(100).nullable() - }); + password: z.string().min(4).max(100).nullable() +}); registry.registerPath({ method: "post", diff --git a/server/routers/resource/setResourcePincode.ts b/server/routers/resource/setResourcePincode.ts index 6a88a279..0d527273 100644 --- a/server/routers/resource/setResourcePincode.ts +++ b/server/routers/resource/setResourcePincode.ts @@ -18,11 +18,11 @@ const setResourceAuthMethodsParamsSchema = z.object({ }); const setResourceAuthMethodsBodySchema = z.strictObject({ - pincode: z - .string() - .regex(/^\d{6}$/) - .or(z.null()) - }); + pincode: z + .string() + .regex(/^\d{6}$/) + .or(z.null()) +}); registry.registerPath({ method: "post", diff --git a/server/routers/resource/setResourceRoles.ts b/server/routers/resource/setResourceRoles.ts index 5064c7e0..751fe4f9 100644 --- a/server/routers/resource/setResourceRoles.ts +++ b/server/routers/resource/setResourceRoles.ts @@ -11,15 +11,12 @@ import { eq, and, ne, inArray } from "drizzle-orm"; import { OpenAPITags, registry } from "@server/openApi"; const setResourceRolesBodySchema = z.strictObject({ - roleIds: z.array(z.int().positive()) - }); + roleIds: z.array(z.int().positive()) +}); const setResourceRolesParamsSchema = z.strictObject({ - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); registry.registerPath({ method: "post", @@ -113,10 +110,7 @@ export async function setResourceRoles( .select() .from(roles) .where( - and( - eq(roles.isAdmin, true), - eq(roles.orgId, resource.orgId) - ) + and(eq(roles.isAdmin, true), eq(roles.orgId, resource.orgId)) ); const adminRoleIds = adminRoles.map((role) => role.roleId); @@ -129,9 +123,9 @@ export async function setResourceRoles( ) ); } else { - await trx.delete(roleResources).where( - eq(roleResources.resourceId, resourceId) - ); + await trx + .delete(roleResources) + .where(eq(roleResources.resourceId, resourceId)); } const newRoleResources = await Promise.all( @@ -158,4 +152,3 @@ export async function setResourceRoles( ); } } - diff --git a/server/routers/resource/setResourceUsers.ts b/server/routers/resource/setResourceUsers.ts index b5eca17c..5ddceb8f 100644 --- a/server/routers/resource/setResourceUsers.ts +++ b/server/routers/resource/setResourceUsers.ts @@ -11,15 +11,12 @@ import { eq } from "drizzle-orm"; import { OpenAPITags, registry } from "@server/openApi"; const setUserResourcesBodySchema = z.strictObject({ - userIds: z.array(z.string()) - }); + userIds: z.array(z.string()) +}); const setUserResourcesParamsSchema = z.strictObject({ - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); registry.registerPath({ method: "post", diff --git a/server/routers/resource/setResourceWhitelist.ts b/server/routers/resource/setResourceWhitelist.ts index 417ef6d9..18f612f2 100644 --- a/server/routers/resource/setResourceWhitelist.ts +++ b/server/routers/resource/setResourceWhitelist.ts @@ -11,25 +11,21 @@ import { and, eq } from "drizzle-orm"; import { OpenAPITags, registry } from "@server/openApi"; const setResourceWhitelistBodySchema = z.strictObject({ - emails: z - .array( - z.email() - .or( - z.string().regex(/^\*@[\w.-]+\.[a-zA-Z]{2,}$/, { - error: "Invalid email address. Wildcard (*) must be the entire local part." - }) - ) + emails: z + .array( + z.email().or( + z.string().regex(/^\*@[\w.-]+\.[a-zA-Z]{2,}$/, { + error: "Invalid email address. Wildcard (*) must be the entire local part." + }) ) - .max(50) - .transform((v) => v.map((e) => e.toLowerCase())) - }); + ) + .max(50) + .transform((v) => v.map((e) => e.toLowerCase())) +}); const setResourceWhitelistParamsSchema = z.strictObject({ - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); registry.registerPath({ method: "post", diff --git a/server/routers/resource/updateResource.ts b/server/routers/resource/updateResource.ts index f3792e28..1dff9757 100644 --- a/server/routers/resource/updateResource.ts +++ b/server/routers/resource/updateResource.ts @@ -26,13 +26,11 @@ import { validateHeaders } from "@server/lib/validators"; import { build } from "@server/build"; const updateResourceParamsSchema = z.strictObject({ - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); -const updateHttpResourceBodySchema = z.strictObject({ +const updateHttpResourceBodySchema = z + .strictObject({ name: z.string().min(1).max(255).optional(), niceId: z.string().min(1).max(255).optional(), subdomain: subdomainSchema.nullable().optional(), @@ -91,7 +89,8 @@ const updateHttpResourceBodySchema = z.strictObject({ export type UpdateResourceResponse = Resource; -const updateRawResourceBodySchema = z.strictObject({ +const updateRawResourceBodySchema = z + .strictObject({ name: z.string().min(1).max(255).optional(), niceId: z.string().min(1).max(255).optional(), proxyPort: z.int().min(1).max(65535).optional(), @@ -239,11 +238,11 @@ async function updateHttpResource( .select() .from(resources) .where( - and( - eq(resources.niceId, updateData.niceId), - eq(resources.orgId, resource.orgId) - ) - ); + and( + eq(resources.niceId, updateData.niceId), + eq(resources.orgId, resource.orgId) + ) + ); if ( existingResource && @@ -391,11 +390,11 @@ async function updateRawResource( .select() .from(resources) .where( - and( - eq(resources.niceId, updateData.niceId), - eq(resources.orgId, resource.orgId) - ) - ); + and( + eq(resources.niceId, updateData.niceId), + eq(resources.orgId, resource.orgId) + ) + ); if ( existingResource && diff --git a/server/routers/resource/updateResourceRule.ts b/server/routers/resource/updateResourceRule.ts index b92c3d07..cae3f16e 100644 --- a/server/routers/resource/updateResourceRule.ts +++ b/server/routers/resource/updateResourceRule.ts @@ -17,15 +17,13 @@ import { OpenAPITags, registry } from "@server/openApi"; // Define Zod schema for request parameters validation const updateResourceRuleParamsSchema = z.strictObject({ - ruleId: z.string().transform(Number).pipe(z.int().positive()), - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + ruleId: z.string().transform(Number).pipe(z.int().positive()), + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); // Define Zod schema for request body validation -const updateResourceRuleSchema = z.strictObject({ +const updateResourceRuleSchema = z + .strictObject({ action: z.enum(["ACCEPT", "DROP", "PASS"]).optional(), match: z.enum(["CIDR", "IP", "PATH", "COUNTRY"]).optional(), value: z.string().min(1).optional(), diff --git a/server/routers/role/addRoleAction.ts b/server/routers/role/addRoleAction.ts index 74540b78..5c258de7 100644 --- a/server/routers/role/addRoleAction.ts +++ b/server/routers/role/addRoleAction.ts @@ -10,12 +10,12 @@ import { eq } from "drizzle-orm"; import { fromError } from "zod-validation-error"; const addRoleActionParamSchema = z.strictObject({ - roleId: z.string().transform(Number).pipe(z.int().positive()) - }); + roleId: z.string().transform(Number).pipe(z.int().positive()) +}); const addRoleActionSchema = z.strictObject({ - actionId: z.string() - }); + actionId: z.string() +}); export async function addRoleAction( req: Request, diff --git a/server/routers/role/addRoleSite.ts b/server/routers/role/addRoleSite.ts index d33c733d..ddd1f07e 100644 --- a/server/routers/role/addRoleSite.ts +++ b/server/routers/role/addRoleSite.ts @@ -10,12 +10,12 @@ import { eq } from "drizzle-orm"; import { fromError } from "zod-validation-error"; const addRoleSiteParamsSchema = z.strictObject({ - roleId: z.string().transform(Number).pipe(z.int().positive()) - }); + roleId: z.string().transform(Number).pipe(z.int().positive()) +}); const addRoleSiteSchema = z.strictObject({ - siteId: z.string().transform(Number).pipe(z.int().positive()) - }); + siteId: z.string().transform(Number).pipe(z.int().positive()) +}); export async function addRoleSite( req: Request, diff --git a/server/routers/role/createRole.ts b/server/routers/role/createRole.ts index 26573c6c..16696af4 100644 --- a/server/routers/role/createRole.ts +++ b/server/routers/role/createRole.ts @@ -12,13 +12,13 @@ import { eq, and } from "drizzle-orm"; import { OpenAPITags, registry } from "@server/openApi"; const createRoleParamsSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); const createRoleSchema = z.strictObject({ - name: z.string().min(1).max(255), - description: z.string().optional() - }); + name: z.string().min(1).max(255), + description: z.string().optional() +}); export const defaultRoleAllowedActions: ActionsEnum[] = [ ActionsEnum.getOrg, diff --git a/server/routers/role/deleteRole.ts b/server/routers/role/deleteRole.ts index e4d89b2f..490fe91c 100644 --- a/server/routers/role/deleteRole.ts +++ b/server/routers/role/deleteRole.ts @@ -11,12 +11,12 @@ import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; const deleteRoleSchema = z.strictObject({ - roleId: z.string().transform(Number).pipe(z.int().positive()) - }); + roleId: z.string().transform(Number).pipe(z.int().positive()) +}); const deelteRoleBodySchema = z.strictObject({ - roleId: z.string().transform(Number).pipe(z.int().positive()) - }); + roleId: z.string().transform(Number).pipe(z.int().positive()) +}); registry.registerPath({ method: "delete", diff --git a/server/routers/role/getRole.ts b/server/routers/role/getRole.ts index afd6e83a..a5c45996 100644 --- a/server/routers/role/getRole.ts +++ b/server/routers/role/getRole.ts @@ -11,8 +11,8 @@ import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; const getRoleSchema = z.strictObject({ - roleId: z.string().transform(Number).pipe(z.int().positive()) - }); + roleId: z.string().transform(Number).pipe(z.int().positive()) +}); registry.registerPath({ method: "get", diff --git a/server/routers/role/listRoleActions.ts b/server/routers/role/listRoleActions.ts index 8392c296..31ef6604 100644 --- a/server/routers/role/listRoleActions.ts +++ b/server/routers/role/listRoleActions.ts @@ -10,8 +10,8 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; const listRoleActionsSchema = z.strictObject({ - roleId: z.string().transform(Number).pipe(z.int().positive()) - }); + roleId: z.string().transform(Number).pipe(z.int().positive()) +}); export async function listRoleActions( req: Request, diff --git a/server/routers/role/listRoleResources.ts b/server/routers/role/listRoleResources.ts index 57a84c5c..7ba1fdab 100644 --- a/server/routers/role/listRoleResources.ts +++ b/server/routers/role/listRoleResources.ts @@ -10,8 +10,8 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; const listRoleResourcesSchema = z.strictObject({ - roleId: z.string().transform(Number).pipe(z.int().positive()) - }); + roleId: z.string().transform(Number).pipe(z.int().positive()) +}); export async function listRoleResources( req: Request, diff --git a/server/routers/role/listRoleSites.ts b/server/routers/role/listRoleSites.ts index f35e367c..1c9dcdbe 100644 --- a/server/routers/role/listRoleSites.ts +++ b/server/routers/role/listRoleSites.ts @@ -10,8 +10,8 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; const listRoleSitesSchema = z.strictObject({ - roleId: z.string().transform(Number).pipe(z.int().positive()) - }); + roleId: z.string().transform(Number).pipe(z.int().positive()) +}); export async function listRoleSites( req: Request, diff --git a/server/routers/role/listRoles.ts b/server/routers/role/listRoles.ts index 14a5c2d1..288a540d 100644 --- a/server/routers/role/listRoles.ts +++ b/server/routers/role/listRoles.ts @@ -12,8 +12,8 @@ import stoi from "@server/lib/stoi"; import { OpenAPITags, registry } from "@server/openApi"; const listRolesParamsSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); const listRolesSchema = z.object({ limit: z diff --git a/server/routers/role/removeRoleAction.ts b/server/routers/role/removeRoleAction.ts index 25fbaa29..3c2ee788 100644 --- a/server/routers/role/removeRoleAction.ts +++ b/server/routers/role/removeRoleAction.ts @@ -10,12 +10,12 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; const removeRoleActionParamsSchema = z.strictObject({ - roleId: z.string().transform(Number).pipe(z.int().positive()) - }); + roleId: z.string().transform(Number).pipe(z.int().positive()) +}); const removeRoleActionSchema = z.strictObject({ - actionId: z.string() - }); + actionId: z.string() +}); export async function removeRoleAction( req: Request, diff --git a/server/routers/role/removeRoleResource.ts b/server/routers/role/removeRoleResource.ts index d2c7cae9..fac1c941 100644 --- a/server/routers/role/removeRoleResource.ts +++ b/server/routers/role/removeRoleResource.ts @@ -10,15 +10,12 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; const removeRoleResourceParamsSchema = z.strictObject({ - roleId: z.string().transform(Number).pipe(z.int().positive()) - }); + roleId: z.string().transform(Number).pipe(z.int().positive()) +}); const removeRoleResourceSchema = z.strictObject({ - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); export async function removeRoleResource( req: Request, diff --git a/server/routers/role/removeRoleSite.ts b/server/routers/role/removeRoleSite.ts index 8092eed1..6c64820e 100644 --- a/server/routers/role/removeRoleSite.ts +++ b/server/routers/role/removeRoleSite.ts @@ -10,12 +10,12 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; const removeRoleSiteParamsSchema = z.strictObject({ - roleId: z.string().transform(Number).pipe(z.int().positive()) - }); + roleId: z.string().transform(Number).pipe(z.int().positive()) +}); const removeRoleSiteSchema = z.strictObject({ - siteId: z.string().transform(Number).pipe(z.int().positive()) - }); + siteId: z.string().transform(Number).pipe(z.int().positive()) +}); export async function removeRoleSite( req: Request, diff --git a/server/routers/role/updateRole.ts b/server/routers/role/updateRole.ts index 136ca389..c9f63a7b 100644 --- a/server/routers/role/updateRole.ts +++ b/server/routers/role/updateRole.ts @@ -10,10 +10,11 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; const updateRoleParamsSchema = z.strictObject({ - roleId: z.string().transform(Number).pipe(z.int().positive()) - }); + roleId: z.string().transform(Number).pipe(z.int().positive()) +}); -const updateRoleBodySchema = z.strictObject({ +const updateRoleBodySchema = z + .strictObject({ name: z.string().min(1).max(255).optional(), description: z.string().optional() }) diff --git a/server/routers/site/createSite.ts b/server/routers/site/createSite.ts index 2ec8d3dc..c798ea30 100644 --- a/server/routers/site/createSite.ts +++ b/server/routers/site/createSite.ts @@ -20,25 +20,25 @@ import { verifyExitNodeOrgAccess } from "#dynamic/lib/exitNodes"; import { build } from "@server/build"; const createSiteParamsSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); const createSiteSchema = z.strictObject({ - name: z.string().min(1).max(255), - exitNodeId: z.int().positive().optional(), - // subdomain: z - // .string() - // .min(1) - // .max(255) - // .transform((val) => val.toLowerCase()) - // .optional(), - pubKey: z.string().optional(), - subnet: z.string().optional(), - newtId: z.string().optional(), - secret: z.string().optional(), - address: z.string().optional(), - type: z.enum(["newt", "wireguard", "local"]) - }); + name: z.string().min(1).max(255), + exitNodeId: z.int().positive().optional(), + // subdomain: z + // .string() + // .min(1) + // .max(255) + // .transform((val) => val.toLowerCase()) + // .optional(), + pubKey: z.string().optional(), + subnet: z.string().optional(), + newtId: z.string().optional(), + secret: z.string().optional(), + address: z.string().optional(), + type: z.enum(["newt", "wireguard", "local"]) +}); // .refine((data) => { // if (data.type === "local") { // return !config.getRawConfig().flags?.disable_local_sites; diff --git a/server/routers/site/deleteSite.ts b/server/routers/site/deleteSite.ts index a086e143..09750c31 100644 --- a/server/routers/site/deleteSite.ts +++ b/server/routers/site/deleteSite.ts @@ -13,8 +13,8 @@ import { sendToClient } from "#dynamic/routers/ws"; import { OpenAPITags, registry } from "@server/openApi"; const deleteSiteSchema = z.strictObject({ - siteId: z.string().transform(Number).pipe(z.int().positive()) - }); + siteId: z.string().transform(Number).pipe(z.int().positive()) +}); registry.registerPath({ method: "delete", @@ -93,8 +93,11 @@ export async function deleteSite( data: {} }; // Don't await this to prevent blocking the response - sendToClient(deletedNewtId, payload).catch(error => { - logger.error("Failed to send termination message to newt:", error); + sendToClient(deletedNewtId, payload).catch((error) => { + logger.error( + "Failed to send termination message to newt:", + error + ); }); } diff --git a/server/routers/site/index.ts b/server/routers/site/index.ts index b97557a8..3edf67c1 100644 --- a/server/routers/site/index.ts +++ b/server/routers/site/index.ts @@ -5,4 +5,4 @@ export * from "./updateSite"; export * from "./listSites"; export * from "./listSiteRoles"; export * from "./pickSiteDefaults"; -export * from "./socketIntegration"; \ No newline at end of file +export * from "./socketIntegration"; diff --git a/server/routers/site/listSiteRoles.ts b/server/routers/site/listSiteRoles.ts index ec66d3c5..a2cacf1d 100644 --- a/server/routers/site/listSiteRoles.ts +++ b/server/routers/site/listSiteRoles.ts @@ -10,8 +10,8 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; const listSiteRolesSchema = z.strictObject({ - siteId: z.string().transform(Number).pipe(z.int().positive()) - }); + siteId: z.string().transform(Number).pipe(z.int().positive()) +}); export async function listSiteRoles( req: Request, diff --git a/server/routers/site/listSites.ts b/server/routers/site/listSites.ts index f0854764..37ca8fe4 100644 --- a/server/routers/site/listSites.ts +++ b/server/routers/site/listSites.ts @@ -69,8 +69,8 @@ async function getLatestNewtVersion(): Promise { } const listSitesParamsSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); const listSitesSchema = z.object({ limit: z diff --git a/server/routers/site/pickSiteDefaults.ts b/server/routers/site/pickSiteDefaults.ts index 029ae322..69ed7688 100644 --- a/server/routers/site/pickSiteDefaults.ts +++ b/server/routers/site/pickSiteDefaults.ts @@ -45,8 +45,8 @@ registry.registerPath({ }); const pickSiteDefaultsSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); export async function pickSiteDefaults( req: Request, @@ -74,7 +74,10 @@ export async function pickSiteDefaults( if (!randomExitNode) { return next( - createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "No available exit node") + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "No available exit node" + ) ); } @@ -90,7 +93,10 @@ export async function pickSiteDefaults( // TODO: we need to lock this subnet for some time so someone else does not take it const subnets = sitesQuery .map((site) => site.subnet) - .filter((subnet) => subnet && /^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/.test(subnet)) + .filter( + (subnet) => + subnet && /^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/.test(subnet) + ) .filter((subnet) => subnet !== null); // exclude the exit node address by replacing after the / with a site block size subnets.push( diff --git a/server/routers/site/socketIntegration.ts b/server/routers/site/socketIntegration.ts index 33893000..e0ad09d1 100644 --- a/server/routers/site/socketIntegration.ts +++ b/server/routers/site/socketIntegration.ts @@ -10,10 +10,7 @@ import { z } from "zod"; import { fromError } from "zod-validation-error"; import stoi from "@server/lib/stoi"; import { sendToClient } from "#dynamic/routers/ws"; -import { - fetchContainers, - dockerSocket -} from "../newt/dockerSocket"; +import { fetchContainers, dockerSocket } from "../newt/dockerSocket"; import cache from "@server/lib/cache"; export interface ContainerNetwork { @@ -47,13 +44,13 @@ export interface Container { } const siteIdParamsSchema = z.strictObject({ - siteId: z.string().transform(stoi).pipe(z.int().positive()) - }); + siteId: z.string().transform(stoi).pipe(z.int().positive()) +}); const DockerStatusSchema = z.strictObject({ - isAvailable: z.boolean(), - socketPath: z.string().optional() - }); + isAvailable: z.boolean(), + socketPath: z.string().optional() +}); function validateSiteIdParams(params: any) { const parsedParams = siteIdParamsSchema.safeParse(params); @@ -161,9 +158,7 @@ async function triggerFetch(siteId: number) { async function queryContainers(siteId: number) { const { newt } = await getSiteAndNewt(siteId); - const result = cache.get( - `${newt.newtId}:dockerContainers` - ) as Container[]; + const result = cache.get(`${newt.newtId}:dockerContainers`) as Container[]; if (!result) { throw createHttpError( HttpCode.TOO_EARLY, diff --git a/server/routers/site/updateSite.ts b/server/routers/site/updateSite.ts index 4c25d4c5..44764362 100644 --- a/server/routers/site/updateSite.ts +++ b/server/routers/site/updateSite.ts @@ -12,16 +12,15 @@ import { OpenAPITags, registry } from "@server/openApi"; import { isValidCIDR } from "@server/lib/validators"; const updateSiteParamsSchema = z.strictObject({ - siteId: z.string().transform(Number).pipe(z.int().positive()) - }); + siteId: z.string().transform(Number).pipe(z.int().positive()) +}); -const updateSiteBodySchema = z.strictObject({ +const updateSiteBodySchema = z + .strictObject({ name: z.string().min(1).max(255).optional(), niceId: z.string().min(1).max(255).optional(), dockerSocketEnabled: z.boolean().optional(), - remoteSubnets: z - .string() - .optional() + remoteSubnets: z.string().optional() // subdomain: z // .string() // .min(1) @@ -41,8 +40,7 @@ const updateSiteBodySchema = z.strictObject({ registry.registerPath({ method: "post", path: "/site/{siteId}", - description: - "Update a site.", + description: "Update a site.", tags: [OpenAPITags.Site], request: { params: updateSiteParamsSchema, @@ -111,7 +109,9 @@ export async function updateSite( // if remoteSubnets is provided, ensure it's a valid comma-separated list of cidrs if (updateData.remoteSubnets) { - const subnets = updateData.remoteSubnets.split(",").map((s) => s.trim()); + const subnets = updateData.remoteSubnets + .split(",") + .map((s) => s.trim()); for (const subnet of subnets) { if (!isValidCIDR(subnet)) { return next( diff --git a/server/routers/siteResource/addClientToSiteResource.ts b/server/routers/siteResource/addClientToSiteResource.ts index 587294e5..27d7f057 100644 --- a/server/routers/siteResource/addClientToSiteResource.ts +++ b/server/routers/siteResource/addClientToSiteResource.ts @@ -28,7 +28,8 @@ const addClientToSiteResourceParamsSchema = z registry.registerPath({ method: "post", path: "/site-resource/{siteResourceId}/clients/add", - description: "Add a single client to a site resource. Clients with a userId cannot be added.", + description: + "Add a single client to a site resource. Clients with a userId cannot be added.", tags: [OpenAPITags.Resource, OpenAPITags.Client], request: { params: addClientToSiteResourceParamsSchema, @@ -49,7 +50,9 @@ export async function addClientToSiteResource( next: NextFunction ): Promise { try { - const parsedBody = addClientToSiteResourceBodySchema.safeParse(req.body); + const parsedBody = addClientToSiteResourceBodySchema.safeParse( + req.body + ); if (!parsedBody.success) { return next( createHttpError( @@ -153,4 +156,3 @@ export async function addClientToSiteResource( ); } } - diff --git a/server/routers/siteResource/addRoleToSiteResource.ts b/server/routers/siteResource/addRoleToSiteResource.ts index 542ca535..abc2d221 100644 --- a/server/routers/siteResource/addRoleToSiteResource.ts +++ b/server/routers/siteResource/addRoleToSiteResource.ts @@ -163,4 +163,3 @@ export async function addRoleToSiteResource( ); } } - diff --git a/server/routers/siteResource/addUserToSiteResource.ts b/server/routers/siteResource/addUserToSiteResource.ts index c9d1f30a..4edf741c 100644 --- a/server/routers/siteResource/addUserToSiteResource.ts +++ b/server/routers/siteResource/addUserToSiteResource.ts @@ -132,4 +132,3 @@ export async function addUserToSiteResource( ); } } - diff --git a/server/routers/siteResource/deleteSiteResource.ts b/server/routers/siteResource/deleteSiteResource.ts index a7175608..3d1e70cc 100644 --- a/server/routers/siteResource/deleteSiteResource.ts +++ b/server/routers/siteResource/deleteSiteResource.ts @@ -106,7 +106,10 @@ export async function deleteSiteResource( ); } - await rebuildClientAssociationsFromSiteResource(removedSiteResource, trx); + await rebuildClientAssociationsFromSiteResource( + removedSiteResource, + trx + ); }); logger.info( diff --git a/server/routers/siteResource/getSiteResource.ts b/server/routers/siteResource/getSiteResource.ts index 48f10b8b..7cb9e620 100644 --- a/server/routers/siteResource/getSiteResource.ts +++ b/server/routers/siteResource/getSiteResource.ts @@ -11,44 +11,55 @@ import logger from "@server/logger"; import { OpenAPITags, registry } from "@server/openApi"; const getSiteResourceParamsSchema = z.strictObject({ - siteResourceId: z - .string() - .optional() - .transform((val) => val ? Number(val) : undefined) - .pipe(z.int().positive().optional()) - .optional(), - siteId: z.string().transform(Number).pipe(z.int().positive()), - niceId: z.string().optional(), - orgId: z.string() - }); + siteResourceId: z + .string() + .optional() + .transform((val) => (val ? Number(val) : undefined)) + .pipe(z.int().positive().optional()) + .optional(), + siteId: z.string().transform(Number).pipe(z.int().positive()), + niceId: z.string().optional(), + orgId: z.string() +}); -async function query(siteResourceId?: number, siteId?: number, niceId?: string, orgId?: string) { +async function query( + siteResourceId?: number, + siteId?: number, + niceId?: string, + orgId?: string +) { if (siteResourceId && siteId && orgId) { const [siteResource] = await db .select() .from(siteResources) - .where(and( - eq(siteResources.siteResourceId, siteResourceId), - eq(siteResources.siteId, siteId), - eq(siteResources.orgId, orgId) - )) + .where( + and( + eq(siteResources.siteResourceId, siteResourceId), + eq(siteResources.siteId, siteId), + eq(siteResources.orgId, orgId) + ) + ) .limit(1); return siteResource; } else if (niceId && siteId && orgId) { const [siteResource] = await db .select() .from(siteResources) - .where(and( - eq(siteResources.niceId, niceId), - eq(siteResources.siteId, siteId), - eq(siteResources.orgId, orgId) - )) + .where( + and( + eq(siteResources.niceId, niceId), + eq(siteResources.siteId, siteId), + eq(siteResources.orgId, orgId) + ) + ) .limit(1); return siteResource; } } -export type GetSiteResourceResponse = NonNullable>>; +export type GetSiteResourceResponse = NonNullable< + Awaited> +>; registry.registerPath({ method: "get", @@ -103,10 +114,7 @@ export async function getSiteResource( if (!siteResource) { return next( - createHttpError( - HttpCode.NOT_FOUND, - "Site resource not found" - ) + createHttpError(HttpCode.NOT_FOUND, "Site resource not found") ); } @@ -119,6 +127,11 @@ export async function getSiteResource( }); } catch (error) { logger.error("Error getting site resource:", error); - return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "Failed to get site resource")); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to get site resource" + ) + ); } } diff --git a/server/routers/siteResource/listAllSiteResourcesByOrg.ts b/server/routers/siteResource/listAllSiteResourcesByOrg.ts index 5de66505..f6975cd2 100644 --- a/server/routers/siteResource/listAllSiteResourcesByOrg.ts +++ b/server/routers/siteResource/listAllSiteResourcesByOrg.ts @@ -11,8 +11,8 @@ import logger from "@server/logger"; import { OpenAPITags, registry } from "@server/openApi"; const listAllSiteResourcesByOrgParamsSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); const listAllSiteResourcesByOrgQuerySchema = z.object({ limit: z @@ -30,7 +30,11 @@ const listAllSiteResourcesByOrgQuerySchema = z.object({ }); export type ListAllSiteResourcesByOrgResponse = { - siteResources: (SiteResource & { siteName: string, siteNiceId: string, siteAddress: string | null })[]; + siteResources: (SiteResource & { + siteName: string; + siteNiceId: string; + siteAddress: string | null; + })[]; }; registry.registerPath({ @@ -51,7 +55,9 @@ export async function listAllSiteResourcesByOrg( next: NextFunction ): Promise { try { - const parsedParams = listAllSiteResourcesByOrgParamsSchema.safeParse(req.params); + const parsedParams = listAllSiteResourcesByOrgParamsSchema.safeParse( + req.params + ); if (!parsedParams.success) { return next( createHttpError( @@ -61,7 +67,9 @@ export async function listAllSiteResourcesByOrg( ); } - const parsedQuery = listAllSiteResourcesByOrgQuerySchema.safeParse(req.query); + const parsedQuery = listAllSiteResourcesByOrgQuerySchema.safeParse( + req.query + ); if (!parsedQuery.success) { return next( createHttpError( @@ -108,6 +116,11 @@ export async function listAllSiteResourcesByOrg( }); } catch (error) { logger.error("Error listing all site resources by org:", error); - return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "Failed to list site resources")); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to list site resources" + ) + ); } } diff --git a/server/routers/siteResource/listSiteResourceClients.ts b/server/routers/siteResource/listSiteResourceClients.ts index 9b04ac32..772750d1 100644 --- a/server/routers/siteResource/listSiteResourceClients.ts +++ b/server/routers/siteResource/listSiteResourceClients.ts @@ -52,7 +52,9 @@ export async function listSiteResourceClients( next: NextFunction ): Promise { try { - const parsedParams = listSiteResourceClientsSchema.safeParse(req.params); + const parsedParams = listSiteResourceClientsSchema.safeParse( + req.params + ); if (!parsedParams.success) { return next( createHttpError( @@ -82,4 +84,3 @@ export async function listSiteResourceClients( ); } } - diff --git a/server/routers/siteResource/listSiteResourceRoles.ts b/server/routers/siteResource/listSiteResourceRoles.ts index 5504c003..0dc5913b 100644 --- a/server/routers/siteResource/listSiteResourceRoles.ts +++ b/server/routers/siteResource/listSiteResourceRoles.ts @@ -83,4 +83,3 @@ export async function listSiteResourceRoles( ); } } - diff --git a/server/routers/siteResource/listSiteResourceUsers.ts b/server/routers/siteResource/listSiteResourceUsers.ts index 6cc19557..daf75480 100644 --- a/server/routers/siteResource/listSiteResourceUsers.ts +++ b/server/routers/siteResource/listSiteResourceUsers.ts @@ -86,4 +86,3 @@ export async function listSiteResourceUsers( ); } } - diff --git a/server/routers/siteResource/listSiteResources.ts b/server/routers/siteResource/listSiteResources.ts index e530952d..6ecda7c4 100644 --- a/server/routers/siteResource/listSiteResources.ts +++ b/server/routers/siteResource/listSiteResources.ts @@ -11,9 +11,9 @@ import logger from "@server/logger"; import { OpenAPITags, registry } from "@server/openApi"; const listSiteResourcesParamsSchema = z.strictObject({ - siteId: z.string().transform(Number).pipe(z.int().positive()), - orgId: z.string() - }); + siteId: z.string().transform(Number).pipe(z.int().positive()), + orgId: z.string() +}); const listSiteResourcesQuerySchema = z.object({ limit: z @@ -52,7 +52,9 @@ export async function listSiteResources( next: NextFunction ): Promise { try { - const parsedParams = listSiteResourcesParamsSchema.safeParse(req.params); + const parsedParams = listSiteResourcesParamsSchema.safeParse( + req.params + ); if (!parsedParams.success) { return next( createHttpError( @@ -83,22 +85,19 @@ export async function listSiteResources( .limit(1); if (site.length === 0) { - return next( - createHttpError( - HttpCode.NOT_FOUND, - "Site not found" - ) - ); + return next(createHttpError(HttpCode.NOT_FOUND, "Site not found")); } // Get site resources const siteResourcesList = await db .select() .from(siteResources) - .where(and( - eq(siteResources.siteId, siteId), - eq(siteResources.orgId, orgId) - )) + .where( + and( + eq(siteResources.siteId, siteId), + eq(siteResources.orgId, orgId) + ) + ) .limit(limit) .offset(offset); @@ -111,6 +110,11 @@ export async function listSiteResources( }); } catch (error) { logger.error("Error listing site resources:", error); - return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "Failed to list site resources")); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to list site resources" + ) + ); } } diff --git a/server/routers/siteResource/removeClientFromSiteResource.ts b/server/routers/siteResource/removeClientFromSiteResource.ts index c6a5dfe8..351128d1 100644 --- a/server/routers/siteResource/removeClientFromSiteResource.ts +++ b/server/routers/siteResource/removeClientFromSiteResource.ts @@ -28,7 +28,8 @@ const removeClientFromSiteResourceParamsSchema = z registry.registerPath({ method: "post", path: "/site-resource/{siteResourceId}/clients/remove", - description: "Remove a single client from a site resource. Clients with a userId cannot be removed.", + description: + "Remove a single client from a site resource. Clients with a userId cannot be removed.", tags: [OpenAPITags.Resource, OpenAPITags.Client], request: { params: removeClientFromSiteResourceParamsSchema, @@ -159,4 +160,3 @@ export async function removeClientFromSiteResource( ); } } - diff --git a/server/routers/siteResource/removeRoleFromSiteResource.ts b/server/routers/siteResource/removeRoleFromSiteResource.ts index 0041ed83..c9857e84 100644 --- a/server/routers/siteResource/removeRoleFromSiteResource.ts +++ b/server/routers/siteResource/removeRoleFromSiteResource.ts @@ -151,7 +151,7 @@ export async function removeRoleFromSiteResource( ) ); - await rebuildClientAssociationsFromSiteResource(siteResource, trx); + await rebuildClientAssociationsFromSiteResource(siteResource, trx); }); return response(res, { @@ -168,4 +168,3 @@ export async function removeRoleFromSiteResource( ); } } - diff --git a/server/routers/siteResource/removeUserFromSiteResource.ts b/server/routers/siteResource/removeUserFromSiteResource.ts index 280a01f2..84347b2f 100644 --- a/server/routers/siteResource/removeUserFromSiteResource.ts +++ b/server/routers/siteResource/removeUserFromSiteResource.ts @@ -138,4 +138,3 @@ export async function removeUserFromSiteResource( ); } } - diff --git a/server/routers/siteResource/setSiteResourceClients.ts b/server/routers/siteResource/setSiteResourceClients.ts index 0a25b7e9..5a8acbcf 100644 --- a/server/routers/siteResource/setSiteResourceClients.ts +++ b/server/routers/siteResource/setSiteResourceClients.ts @@ -62,7 +62,9 @@ export async function setSiteResourceClients( const { clientIds } = parsedBody.data; - const parsedParams = setSiteResourceClientsParamsSchema.safeParse(req.params); + const parsedParams = setSiteResourceClientsParamsSchema.safeParse( + req.params + ); if (!parsedParams.success) { return next( createHttpError( @@ -95,9 +97,7 @@ export async function setSiteResourceClients( const clientsWithUsers = await db .select() .from(clients) - .where( - inArray(clients.clientId, clientIds) - ); + .where(inArray(clients.clientId, clientIds)); const clientsWithUserId = clientsWithUsers.filter( (client) => client.userId !== null @@ -119,9 +119,12 @@ export async function setSiteResourceClients( .where(eq(clientSiteResources.siteResourceId, siteResourceId)); if (clientIds.length > 0) { - await trx - .insert(clientSiteResources) - .values(clientIds.map((clientId) => ({ clientId, siteResourceId }))); + await trx.insert(clientSiteResources).values( + clientIds.map((clientId) => ({ + clientId, + siteResourceId + })) + ); } await rebuildClientAssociationsFromSiteResource(siteResource, trx); @@ -141,4 +144,3 @@ export async function setSiteResourceClients( ); } } - diff --git a/server/routers/siteResource/setSiteResourceRoles.ts b/server/routers/siteResource/setSiteResourceRoles.ts index 7aa07de1..bb71a16b 100644 --- a/server/routers/siteResource/setSiteResourceRoles.ts +++ b/server/routers/siteResource/setSiteResourceRoles.ts @@ -136,15 +136,19 @@ export async function setSiteResourceRoles( ) ); } else { - await trx.delete(roleSiteResources).where( - eq(roleSiteResources.siteResourceId, siteResourceId) - ); + await trx + .delete(roleSiteResources) + .where( + eq(roleSiteResources.siteResourceId, siteResourceId) + ); } if (roleIds.length > 0) { await trx .insert(roleSiteResources) - .values(roleIds.map((roleId) => ({ roleId, siteResourceId }))); + .values( + roleIds.map((roleId) => ({ roleId, siteResourceId })) + ); } await rebuildClientAssociationsFromSiteResource(siteResource, trx); diff --git a/server/routers/siteResource/setSiteResourceUsers.ts b/server/routers/siteResource/setSiteResourceUsers.ts index 4dae0ada..eacd826c 100644 --- a/server/routers/siteResource/setSiteResourceUsers.ts +++ b/server/routers/siteResource/setSiteResourceUsers.ts @@ -63,7 +63,9 @@ export async function setSiteResourceUsers( const { userIds } = parsedBody.data; - const parsedParams = setSiteResourceUsersParamsSchema.safeParse(req.params); + const parsedParams = setSiteResourceUsersParamsSchema.safeParse( + req.params + ); if (!parsedParams.success) { return next( createHttpError( @@ -99,7 +101,9 @@ export async function setSiteResourceUsers( if (userIds.length > 0) { await trx .insert(userSiteResources) - .values(userIds.map((userId) => ({ userId, siteResourceId }))); + .values( + userIds.map((userId) => ({ userId, siteResourceId })) + ); } await rebuildClientAssociationsFromSiteResource(siteResource, trx); @@ -119,4 +123,3 @@ export async function setSiteResourceUsers( ); } } - diff --git a/server/routers/supporterKey/validateSupporterKey.ts b/server/routers/supporterKey/validateSupporterKey.ts index d8b16421..9ac3c473 100644 --- a/server/routers/supporterKey/validateSupporterKey.ts +++ b/server/routers/supporterKey/validateSupporterKey.ts @@ -10,9 +10,9 @@ import { db } from "@server/db"; import config from "@server/lib/config"; const validateSupporterKeySchema = z.strictObject({ - githubUsername: z.string().nonempty(), - key: z.string().nonempty() - }); + githubUsername: z.string().nonempty(), + key: z.string().nonempty() +}); export type ValidateSupporterKeyResponse = { valid: boolean; diff --git a/server/routers/target/createTarget.ts b/server/routers/target/createTarget.ts index 2c09b5a6..5d37f617 100644 --- a/server/routers/target/createTarget.ts +++ b/server/routers/target/createTarget.ts @@ -16,51 +16,41 @@ import { isTargetValid } from "@server/lib/validators"; import { OpenAPITags, registry } from "@server/openApi"; const createTargetParamsSchema = z.strictObject({ - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); const createTargetSchema = z.strictObject({ - siteId: z.int().positive(), - ip: z.string().refine(isTargetValid), - method: z.string().optional().nullable(), - port: z.int().min(1).max(65535), - enabled: z.boolean().default(true), - hcEnabled: z.boolean().optional(), - hcPath: z.string().min(1).optional().nullable(), - hcScheme: z.string().optional().nullable(), - hcMode: z.string().optional().nullable(), - hcHostname: z.string().optional().nullable(), - hcPort: z.int().positive().optional().nullable(), - hcInterval: z.int().positive().min(5).optional().nullable(), - hcUnhealthyInterval: z.int() - .positive() - .min(5) - .optional() - .nullable(), - hcTimeout: z.int().positive().min(1).optional().nullable(), - hcHeaders: z - .array(z.strictObject({ name: z.string(), value: z.string() })) - .nullable() - .optional(), - hcFollowRedirects: z.boolean().optional().nullable(), - hcMethod: z.string().min(1).optional().nullable(), - hcStatus: z.int().optional().nullable(), - hcTlsServerName: z.string().optional().nullable(), - path: z.string().optional().nullable(), - pathMatchType: z - .enum(["exact", "prefix", "regex"]) - .optional() - .nullable(), - rewritePath: z.string().optional().nullable(), - rewritePathType: z - .enum(["exact", "prefix", "regex", "stripPrefix"]) - .optional() - .nullable(), - priority: z.int().min(1).max(1000).optional().nullable() - }); + siteId: z.int().positive(), + ip: z.string().refine(isTargetValid), + method: z.string().optional().nullable(), + port: z.int().min(1).max(65535), + enabled: z.boolean().default(true), + hcEnabled: z.boolean().optional(), + hcPath: z.string().min(1).optional().nullable(), + hcScheme: z.string().optional().nullable(), + hcMode: z.string().optional().nullable(), + hcHostname: z.string().optional().nullable(), + hcPort: z.int().positive().optional().nullable(), + hcInterval: z.int().positive().min(5).optional().nullable(), + hcUnhealthyInterval: z.int().positive().min(5).optional().nullable(), + hcTimeout: z.int().positive().min(1).optional().nullable(), + hcHeaders: z + .array(z.strictObject({ name: z.string(), value: z.string() })) + .nullable() + .optional(), + hcFollowRedirects: z.boolean().optional().nullable(), + hcMethod: z.string().min(1).optional().nullable(), + hcStatus: z.int().optional().nullable(), + hcTlsServerName: z.string().optional().nullable(), + path: z.string().optional().nullable(), + pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable(), + rewritePath: z.string().optional().nullable(), + rewritePathType: z + .enum(["exact", "prefix", "regex", "stripPrefix"]) + .optional() + .nullable(), + priority: z.int().min(1).max(1000).optional().nullable() +}); export type CreateTargetResponse = Target & TargetHealthCheck; @@ -159,7 +149,9 @@ export async function createTarget( if (existingTarget) { // log a warning - logger.warn(`Target with IP ${targetData.ip}, port ${targetData.port}, method ${targetData.method} already exists for resource ID ${resourceId}`); + logger.warn( + `Target with IP ${targetData.ip}, port ${targetData.port}, method ${targetData.method} already exists for resource ID ${resourceId}` + ); } let newTarget: Target[] = []; diff --git a/server/routers/target/deleteTarget.ts b/server/routers/target/deleteTarget.ts index a70b2a1e..606d8635 100644 --- a/server/routers/target/deleteTarget.ts +++ b/server/routers/target/deleteTarget.ts @@ -14,8 +14,8 @@ import { getAllowedIps } from "./helpers"; import { OpenAPITags, registry } from "@server/openApi"; const deleteTargetSchema = z.strictObject({ - targetId: z.string().transform(Number).pipe(z.int().positive()) - }); + targetId: z.string().transform(Number).pipe(z.int().positive()) +}); registry.registerPath({ method: "delete", diff --git a/server/routers/target/getTarget.ts b/server/routers/target/getTarget.ts index 7fe2e062..749e1399 100644 --- a/server/routers/target/getTarget.ts +++ b/server/routers/target/getTarget.ts @@ -11,12 +11,13 @@ import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; const getTargetSchema = z.strictObject({ - targetId: z.string().transform(Number).pipe(z.int().positive()) - }); + targetId: z.string().transform(Number).pipe(z.int().positive()) +}); -type GetTargetResponse = Target & Omit & { - hcHeaders: { name: string; value: string; }[] | null; -}; +type GetTargetResponse = Target & + Omit & { + hcHeaders: { name: string; value: string }[] | null; + }; registry.registerPath({ method: "get", diff --git a/server/routers/target/handleHealthcheckStatusMessage.ts b/server/routers/target/handleHealthcheckStatusMessage.ts index ee4e7950..2bfcff19 100644 --- a/server/routers/target/handleHealthcheckStatusMessage.ts +++ b/server/routers/target/handleHealthcheckStatusMessage.ts @@ -30,7 +30,9 @@ interface HealthcheckStatusMessage { targets: Record; } -export const handleHealthcheckStatusMessage: MessageHandler = async (context) => { +export const handleHealthcheckStatusMessage: MessageHandler = async ( + context +) => { const { message, client: c } = context; const newt = c as Newt; @@ -59,7 +61,9 @@ export const handleHealthcheckStatusMessage: MessageHandler = async (context) => // Process each target status update for (const [targetId, healthStatus] of Object.entries(data.targets)) { - logger.debug(`Processing health status for target ${targetId}: ${healthStatus.status}${healthStatus.lastError ? ` (${healthStatus.lastError})` : ''}`); + logger.debug( + `Processing health status for target ${targetId}: ${healthStatus.status}${healthStatus.lastError ? ` (${healthStatus.lastError})` : ""}` + ); // Verify the target belongs to this newt's site before updating // This prevents unauthorized updates to targets from other sites @@ -76,7 +80,10 @@ export const handleHealthcheckStatusMessage: MessageHandler = async (context) => siteId: targets.siteId }) .from(targets) - .innerJoin(resources, eq(targets.resourceId, resources.resourceId)) + .innerJoin( + resources, + eq(targets.resourceId, resources.resourceId) + ) .innerJoin(sites, eq(targets.siteId, sites.siteId)) .where( and( @@ -87,7 +94,9 @@ export const handleHealthcheckStatusMessage: MessageHandler = async (context) => .limit(1); if (!targetCheck) { - logger.warn(`Target ${targetId} not found or does not belong to site ${newt.siteId}`); + logger.warn( + `Target ${targetId} not found or does not belong to site ${newt.siteId}` + ); errorCount++; continue; } @@ -101,11 +110,15 @@ export const handleHealthcheckStatusMessage: MessageHandler = async (context) => .where(eq(targetHealthCheck.targetId, targetIdNum)) .execute(); - logger.debug(`Updated health status for target ${targetId} to ${healthStatus.status}`); + logger.debug( + `Updated health status for target ${targetId} to ${healthStatus.status}` + ); successCount++; } - logger.debug(`Health status update complete: ${successCount} successful, ${errorCount} errors out of ${Object.keys(data.targets).length} targets`); + logger.debug( + `Health status update complete: ${successCount} successful, ${errorCount} errors out of ${Object.keys(data.targets).length} targets` + ); } catch (error) { logger.error("Error processing healthcheck status message:", error); } diff --git a/server/routers/target/helpers.ts b/server/routers/target/helpers.ts index 13b2ee46..fe76bd13 100644 --- a/server/routers/target/helpers.ts +++ b/server/routers/target/helpers.ts @@ -4,7 +4,10 @@ import { eq } from "drizzle-orm"; const currentBannedPorts: number[] = []; -export async function pickPort(siteId: number, trx: Transaction | typeof db): Promise<{ +export async function pickPort( + siteId: number, + trx: Transaction | typeof db +): Promise<{ internalPort: number; targetIps: string[]; }> { diff --git a/server/routers/target/listTargets.ts b/server/routers/target/listTargets.ts index 356276cb..11a23f02 100644 --- a/server/routers/target/listTargets.ts +++ b/server/routers/target/listTargets.ts @@ -11,11 +11,8 @@ import logger from "@server/logger"; import { OpenAPITags, registry } from "@server/openApi"; const listTargetsParamsSchema = z.strictObject({ - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); const listTargetsSchema = z.object({ limit: z @@ -62,7 +59,7 @@ function queryTargets(resourceId: number) { pathMatchType: targets.pathMatchType, rewritePath: targets.rewritePath, rewritePathType: targets.rewritePathType, - priority: targets.priority, + priority: targets.priority }) .from(targets) .leftJoin(sites, eq(sites.siteId, targets.siteId)) @@ -75,8 +72,11 @@ function queryTargets(resourceId: number) { return baseQuery; } -type TargetWithParsedHeaders = Omit>[0], 'hcHeaders'> & { - hcHeaders: { name: string; value: string; }[] | null; +type TargetWithParsedHeaders = Omit< + Awaited>[0], + "hcHeaders" +> & { + hcHeaders: { name: string; value: string }[] | null; }; export type ListTargetsResponse = { @@ -136,7 +136,7 @@ export async function listTargets( const totalCount = totalCountResult[0].count; // Parse hcHeaders from JSON string back to array for each target - const parsedTargetsList = targetsList.map(target => { + const parsedTargetsList = targetsList.map((target) => { let parsedHcHeaders = null; if (target.hcHeaders) { try { diff --git a/server/routers/target/updateTarget.ts b/server/routers/target/updateTarget.ts index f4a59858..b00340ee 100644 --- a/server/routers/target/updateTarget.ts +++ b/server/routers/target/updateTarget.ts @@ -16,10 +16,11 @@ import { OpenAPITags, registry } from "@server/openApi"; import { vs } from "@react-email/components"; const updateTargetParamsSchema = z.strictObject({ - targetId: z.string().transform(Number).pipe(z.int().positive()) - }); + targetId: z.string().transform(Number).pipe(z.int().positive()) +}); -const updateTargetBodySchema = z.strictObject({ +const updateTargetBodySchema = z + .strictObject({ siteId: z.int().positive(), ip: z.string().refine(isTargetValid), method: z.string().min(1).max(10).optional().nullable(), @@ -32,22 +33,27 @@ const updateTargetBodySchema = z.strictObject({ hcHostname: z.string().optional().nullable(), hcPort: z.int().positive().optional().nullable(), hcInterval: z.int().positive().min(5).optional().nullable(), - hcUnhealthyInterval: z.int() - .positive() - .min(5) - .optional() - .nullable(), + hcUnhealthyInterval: z.int().positive().min(5).optional().nullable(), hcTimeout: z.int().positive().min(1).optional().nullable(), - hcHeaders: z.array(z.strictObject({ name: z.string(), value: z.string() })).nullable().optional(), + hcHeaders: z + .array(z.strictObject({ name: z.string(), value: z.string() })) + .nullable() + .optional(), hcFollowRedirects: z.boolean().optional().nullable(), hcMethod: z.string().min(1).optional().nullable(), hcStatus: z.int().optional().nullable(), hcTlsServerName: z.string().optional().nullable(), path: z.string().optional().nullable(), - pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable(), + pathMatchType: z + .enum(["exact", "prefix", "regex"]) + .optional() + .nullable(), rewritePath: z.string().optional().nullable(), - rewritePathType: z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable(), - priority: z.int().min(1).max(1000).optional(), + rewritePathType: z + .enum(["exact", "prefix", "regex", "stripPrefix"]) + .optional() + .nullable(), + priority: z.int().min(1).max(1000).optional() }) .refine((data) => Object.keys(data).length > 0, { error: "At least one field must be provided for update" @@ -166,7 +172,9 @@ export async function updateTarget( if (foundTarget) { // log a warning - logger.warn(`Target with IP ${targetData.ip}, port ${targetData.port}, method ${targetData.method} already exists for resource ID ${target.resourceId}`); + logger.warn( + `Target with IP ${targetData.ip}, port ${targetData.port}, method ${targetData.method} already exists for resource ID ${target.resourceId}` + ); } const { internalPort, targetIps } = await pickPort(site.siteId!, db); @@ -205,9 +213,11 @@ export async function updateTarget( // When health check is disabled, reset hcHealth to "unknown" // to prevent previously unhealthy targets from being excluded - const hcHealthValue = (parsedBody.data.hcEnabled === false || parsedBody.data.hcEnabled === null) - ? "unknown" - : undefined; + const hcHealthValue = + parsedBody.data.hcEnabled === false || + parsedBody.data.hcEnabled === null + ? "unknown" + : undefined; const [updatedHc] = await db .update(targetHealthCheck) diff --git a/server/routers/traefik/index.ts b/server/routers/traefik/index.ts index 6f5bd4f0..195f0087 100644 --- a/server/routers/traefik/index.ts +++ b/server/routers/traefik/index.ts @@ -1 +1 @@ -export * from "./traefikConfigProvider"; \ No newline at end of file +export * from "./traefikConfigProvider"; diff --git a/server/routers/traefik/traefikConfigProvider.ts b/server/routers/traefik/traefikConfigProvider.ts index 9b12ed8a..e8ac1621 100644 --- a/server/routers/traefik/traefikConfigProvider.ts +++ b/server/routers/traefik/traefikConfigProvider.ts @@ -59,4 +59,4 @@ export async function traefikConfigProvider( error: "Failed to build Traefik config" }); } -} \ No newline at end of file +} diff --git a/server/routers/user/acceptInvite.ts b/server/routers/user/acceptInvite.ts index 3e94d96c..d64ccfb5 100644 --- a/server/routers/user/acceptInvite.ts +++ b/server/routers/user/acceptInvite.ts @@ -15,9 +15,9 @@ import { FeatureId } from "@server/lib/billing"; import { calculateUserClientsForOrgs } from "@server/lib/calculateUserClientsForOrgs"; const acceptInviteBodySchema = z.strictObject({ - token: z.string(), - inviteId: z.string() - }); + token: z.string(), + inviteId: z.string() +}); export type AcceptInviteResponse = { accepted: boolean; diff --git a/server/routers/user/addUserAction.ts b/server/routers/user/addUserAction.ts index f75d5005..ddbae6b0 100644 --- a/server/routers/user/addUserAction.ts +++ b/server/routers/user/addUserAction.ts @@ -10,10 +10,10 @@ import { eq } from "drizzle-orm"; import { fromError } from "zod-validation-error"; const addUserActionSchema = z.strictObject({ - userId: z.string(), - actionId: z.string(), - orgId: z.string() - }); + userId: z.string(), + actionId: z.string(), + orgId: z.string() +}); export async function addUserAction( req: Request, diff --git a/server/routers/user/addUserSite.ts b/server/routers/user/addUserSite.ts index 38ef264c..ffb9f1ba 100644 --- a/server/routers/user/addUserSite.ts +++ b/server/routers/user/addUserSite.ts @@ -10,9 +10,9 @@ import { eq } from "drizzle-orm"; import { fromError } from "zod-validation-error"; const addUserSiteSchema = z.strictObject({ - userId: z.string(), - siteId: z.string().transform(Number).pipe(z.int().positive()) - }); + userId: z.string(), + siteId: z.string().transform(Number).pipe(z.int().positive()) +}); export async function addUserSite( req: Request, @@ -61,7 +61,6 @@ export async function addUserSite( status: HttpCode.CREATED }); }); - } catch (error) { logger.error(error); return next( diff --git a/server/routers/user/adminGeneratePasswordResetCode.ts b/server/routers/user/adminGeneratePasswordResetCode.ts index 5d283c5c..562a459e 100644 --- a/server/routers/user/adminGeneratePasswordResetCode.ts +++ b/server/routers/user/adminGeneratePasswordResetCode.ts @@ -19,7 +19,9 @@ const adminGeneratePasswordResetCodeSchema = z.strictObject({ userId: z.string().min(1) }); -export type AdminGeneratePasswordResetCodeBody = z.infer; +export type AdminGeneratePasswordResetCodeBody = z.infer< + typeof adminGeneratePasswordResetCodeSchema +>; export type AdminGeneratePasswordResetCodeResponse = { token: string; @@ -32,7 +34,9 @@ export async function adminGeneratePasswordResetCode( res: Response, next: NextFunction ): Promise { - const parsedParams = adminGeneratePasswordResetCodeSchema.safeParse(req.params); + const parsedParams = adminGeneratePasswordResetCodeSchema.safeParse( + req.params + ); if (!parsedParams.success) { return next( @@ -52,12 +56,7 @@ export async function adminGeneratePasswordResetCode( .where(eq(users.userId, userId)); if (!existingUser || !existingUser.length) { - return next( - createHttpError( - HttpCode.NOT_FOUND, - "User not found" - ) - ); + return next(createHttpError(HttpCode.NOT_FOUND, "User not found")); } if (existingUser[0].type !== UserType.Internal) { @@ -122,4 +121,3 @@ export async function adminGeneratePasswordResetCode( ); } } - diff --git a/server/routers/user/adminGetUser.ts b/server/routers/user/adminGetUser.ts index bda14476..06045c77 100644 --- a/server/routers/user/adminGetUser.ts +++ b/server/routers/user/adminGetUser.ts @@ -10,8 +10,8 @@ import logger from "@server/logger"; import { OpenAPITags, registry } from "@server/openApi"; const adminGetUserSchema = z.strictObject({ - userId: z.string().min(1) - }); + userId: z.string().min(1) +}); registry.registerPath({ method: "get", diff --git a/server/routers/user/adminListUsers.ts b/server/routers/user/adminListUsers.ts index a3ad9cdd..3a965259 100644 --- a/server/routers/user/adminListUsers.ts +++ b/server/routers/user/adminListUsers.ts @@ -10,19 +10,19 @@ import { idp, users } from "@server/db"; import { fromZodError } from "zod-validation-error"; const listUsersSchema = z.strictObject({ - limit: z - .string() - .optional() - .default("1000") - .transform(Number) - .pipe(z.int().nonnegative()), - offset: z - .string() - .optional() - .default("0") - .transform(Number) - .pipe(z.int().nonnegative()) - }); + limit: z + .string() + .optional() + .default("1000") + .transform(Number) + .pipe(z.int().nonnegative()), + offset: z + .string() + .optional() + .default("0") + .transform(Number) + .pipe(z.int().nonnegative()) +}); async function queryUsers(limit: number, offset: number) { return await db diff --git a/server/routers/user/adminUpdateUser2FA.ts b/server/routers/user/adminUpdateUser2FA.ts index 4bb2486a..7fb37d01 100644 --- a/server/routers/user/adminUpdateUser2FA.ts +++ b/server/routers/user/adminUpdateUser2FA.ts @@ -11,12 +11,12 @@ import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; const updateUser2FAParamsSchema = z.strictObject({ - userId: z.string() - }); + userId: z.string() +}); const updateUser2FABodySchema = z.strictObject({ - twoFactorSetupRequested: z.boolean() - }); + twoFactorSetupRequested: z.boolean() +}); export type UpdateUser2FAResponse = { userId: string; @@ -90,13 +90,15 @@ export async function updateUser2FA( ); } - logger.debug(`Updating 2FA for user ${userId} to ${twoFactorSetupRequested}`); + logger.debug( + `Updating 2FA for user ${userId} to ${twoFactorSetupRequested}` + ); if (twoFactorSetupRequested) { await db .update(users) .set({ - twoFactorSetupRequested: true, + twoFactorSetupRequested: true }) .where(eq(users.userId, userId)); } else { diff --git a/server/routers/user/createOrgUser.ts b/server/routers/user/createOrgUser.ts index 99a2258c..e1902477 100644 --- a/server/routers/user/createOrgUser.ts +++ b/server/routers/user/createOrgUser.ts @@ -18,25 +18,26 @@ import { TierId } from "@server/lib/billing/tiers"; import { calculateUserClientsForOrgs } from "@server/lib/calculateUserClientsForOrgs"; const paramsSchema = z.strictObject({ - orgId: z.string().nonempty() - }); + orgId: z.string().nonempty() +}); const bodySchema = z.strictObject({ - email: z.email() - .toLowerCase() - .optional() - .refine((data) => { - if (data) { - return z.email().safeParse(data).success; - } - return true; - }), - username: z.string().nonempty().toLowerCase(), - name: z.string().optional(), - type: z.enum(["internal", "oidc"]).optional(), - idpId: z.number().optional(), - roleId: z.number() - }); + email: z + .email() + .toLowerCase() + .optional() + .refine((data) => { + if (data) { + return z.email().safeParse(data).success; + } + return true; + }), + username: z.string().nonempty().toLowerCase(), + name: z.string().optional(), + type: z.enum(["internal", "oidc"]).optional(), + idpId: z.number().optional(), + roleId: z.number() +}); export type CreateOrgUserResponse = {}; diff --git a/server/routers/user/getOrgUser.ts b/server/routers/user/getOrgUser.ts index 4e09afd6..f22a29d3 100644 --- a/server/routers/user/getOrgUser.ts +++ b/server/routers/user/getOrgUser.ts @@ -47,9 +47,9 @@ export type GetOrgUserResponse = NonNullable< >; const getOrgUserParamsSchema = z.strictObject({ - userId: z.string(), - orgId: z.string() - }); + userId: z.string(), + orgId: z.string() +}); registry.registerPath({ method: "get", diff --git a/server/routers/user/inviteUser.ts b/server/routers/user/inviteUser.ts index f43ebeb8..6a778868 100644 --- a/server/routers/user/inviteUser.ts +++ b/server/routers/user/inviteUser.ts @@ -22,16 +22,16 @@ import { build } from "@server/build"; import cache from "@server/lib/cache"; const inviteUserParamsSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); const inviteUserBodySchema = z.strictObject({ - email: z.email().toLowerCase(), - roleId: z.number(), - validHours: z.number().gt(0).lte(168), - sendEmail: z.boolean().optional(), - regenerate: z.boolean().optional() - }); + email: z.email().toLowerCase(), + roleId: z.number(), + validHours: z.number().gt(0).lte(168), + sendEmail: z.boolean().optional(), + regenerate: z.boolean().optional() +}); export type InviteUserBody = z.infer; @@ -109,12 +109,7 @@ export async function inviteUser( const [role] = await db .select() .from(roles) - .where( - and( - eq(roles.roleId, roleId), - eq(roles.orgId, orgId) - ) - ) + .where(and(eq(roles.roleId, roleId), eq(roles.orgId, orgId))) .limit(1); if (!role) { diff --git a/server/routers/user/listInvitations.ts b/server/routers/user/listInvitations.ts index a61e2372..4289b877 100644 --- a/server/routers/user/listInvitations.ts +++ b/server/routers/user/listInvitations.ts @@ -11,23 +11,23 @@ import { fromZodError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; const listInvitationsParamsSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); const listInvitationsQuerySchema = z.strictObject({ - limit: z - .string() - .optional() - .default("1000") - .transform(Number) - .pipe(z.int().nonnegative()), - offset: z - .string() - .optional() - .default("0") - .transform(Number) - .pipe(z.int().nonnegative()) - }); + limit: z + .string() + .optional() + .default("1000") + .transform(Number) + .pipe(z.int().nonnegative()), + offset: z + .string() + .optional() + .default("0") + .transform(Number) + .pipe(z.int().nonnegative()) +}); async function queryInvitations(orgId: string, limit: number, offset: number) { return await db diff --git a/server/routers/user/listUsers.ts b/server/routers/user/listUsers.ts index aa70874e..401dcf58 100644 --- a/server/routers/user/listUsers.ts +++ b/server/routers/user/listUsers.ts @@ -12,23 +12,23 @@ import { OpenAPITags, registry } from "@server/openApi"; import { eq } from "drizzle-orm"; const listUsersParamsSchema = z.strictObject({ - orgId: z.string() - }); + orgId: z.string() +}); const listUsersSchema = z.strictObject({ - limit: z - .string() - .optional() - .default("1000") - .transform(Number) - .pipe(z.int().nonnegative()), - offset: z - .string() - .optional() - .default("0") - .transform(Number) - .pipe(z.int().nonnegative()) - }); + limit: z + .string() + .optional() + .default("1000") + .transform(Number) + .pipe(z.int().nonnegative()), + offset: z + .string() + .optional() + .default("0") + .transform(Number) + .pipe(z.int().nonnegative()) +}); async function queryUsers(orgId: string, limit: number, offset: number) { return await db @@ -48,7 +48,7 @@ async function queryUsers(orgId: string, limit: number, offset: number) { idpId: users.idpId, idpType: idp.type, idpVariant: idpOidcConfig.variant, - twoFactorEnabled: users.twoFactorEnabled, + twoFactorEnabled: users.twoFactorEnabled }) .from(users) .leftJoin(userOrgs, eq(users.userId, userOrgs.userId)) diff --git a/server/routers/user/removeInvitation.ts b/server/routers/user/removeInvitation.ts index 44ec8c23..6a000afc 100644 --- a/server/routers/user/removeInvitation.ts +++ b/server/routers/user/removeInvitation.ts @@ -10,9 +10,9 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; const removeInvitationParamsSchema = z.strictObject({ - orgId: z.string(), - inviteId: z.string() - }); + orgId: z.string(), + inviteId: z.string() +}); export async function removeInvitation( req: Request, diff --git a/server/routers/user/removeUserAction.ts b/server/routers/user/removeUserAction.ts index 6e4c1a66..b9dc8cc0 100644 --- a/server/routers/user/removeUserAction.ts +++ b/server/routers/user/removeUserAction.ts @@ -10,13 +10,13 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; const removeUserActionParamsSchema = z.strictObject({ - userId: z.string() - }); + userId: z.string() +}); const removeUserActionSchema = z.strictObject({ - actionId: z.string(), - orgId: z.string() - }); + actionId: z.string(), + orgId: z.string() +}); export async function removeUserAction( req: Request, diff --git a/server/routers/user/removeUserOrg.ts b/server/routers/user/removeUserOrg.ts index cbbb4495..97045e92 100644 --- a/server/routers/user/removeUserOrg.ts +++ b/server/routers/user/removeUserOrg.ts @@ -16,9 +16,9 @@ import { UserType } from "@server/types/UserTypes"; import { calculateUserClientsForOrgs } from "@server/lib/calculateUserClientsForOrgs"; const removeUserSchema = z.strictObject({ - userId: z.string(), - orgId: z.string() - }); + userId: z.string(), + orgId: z.string() +}); registry.registerPath({ method: "delete", diff --git a/server/routers/user/removeUserResource.ts b/server/routers/user/removeUserResource.ts index 14dbb540..bdb0cda3 100644 --- a/server/routers/user/removeUserResource.ts +++ b/server/routers/user/removeUserResource.ts @@ -10,12 +10,9 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; const removeUserResourceSchema = z.strictObject({ - userId: z.string(), - resourceId: z - .string() - .transform(Number) - .pipe(z.int().positive()) - }); + userId: z.string(), + resourceId: z.string().transform(Number).pipe(z.int().positive()) +}); export async function removeUserResource( req: Request, diff --git a/server/routers/user/removeUserSite.ts b/server/routers/user/removeUserSite.ts index 6ed2288a..a531f02c 100644 --- a/server/routers/user/removeUserSite.ts +++ b/server/routers/user/removeUserSite.ts @@ -10,12 +10,12 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; const removeUserSiteParamsSchema = z.strictObject({ - userId: z.string() - }); + userId: z.string() +}); const removeUserSiteSchema = z.strictObject({ - siteId: z.int().positive() - }); + siteId: z.int().positive() +}); export async function removeUserSite( req: Request, diff --git a/server/routers/user/updateOrgUser.ts b/server/routers/user/updateOrgUser.ts index e1000063..97bedb5f 100644 --- a/server/routers/user/updateOrgUser.ts +++ b/server/routers/user/updateOrgUser.ts @@ -10,11 +10,12 @@ import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; const paramsSchema = z.strictObject({ - userId: z.string(), - orgId: z.string() - }); + userId: z.string(), + orgId: z.string() +}); -const bodySchema = z.strictObject({ +const bodySchema = z + .strictObject({ autoProvisioned: z.boolean().optional() }) .refine((data) => Object.keys(data).length > 0, { diff --git a/server/routers/ws/index.ts b/server/routers/ws/index.ts index 16440ec9..b580b369 100644 --- a/server/routers/ws/index.ts +++ b/server/routers/ws/index.ts @@ -1,2 +1,2 @@ export * from "./ws"; -export * from "./types"; \ No newline at end of file +export * from "./types"; diff --git a/server/routers/ws/types.ts b/server/routers/ws/types.ts index 7063bc87..b4ec690b 100644 --- a/server/routers/ws/types.ts +++ b/server/routers/ws/types.ts @@ -58,7 +58,9 @@ export interface HandlerContext { connectedClients: Map; } -export type MessageHandler = (context: HandlerContext) => Promise; +export type MessageHandler = ( + context: HandlerContext +) => Promise; // Redis message type for cross-node communication export interface RedisMessage { @@ -67,4 +69,4 @@ export interface RedisMessage { excludeClientId?: string; message: WSMessage; fromNodeId: string; -} \ No newline at end of file +} diff --git a/server/routers/ws/ws.ts b/server/routers/ws/ws.ts index abbec880..0544af9d 100644 --- a/server/routers/ws/ws.ts +++ b/server/routers/ws/ws.ts @@ -10,7 +10,13 @@ import { validateOlmSessionToken } from "@server/auth/sessions/olm"; import { messageHandlers } from "./messageHandlers"; import logger from "@server/logger"; import { v4 as uuidv4 } from "uuid"; -import { ClientType, TokenPayload, WebSocketRequest, WSMessage, AuthenticatedWebSocket } from "./types"; +import { + ClientType, + TokenPayload, + WebSocketRequest, + WSMessage, + AuthenticatedWebSocket +} from "./types"; import { validateSessionToken } from "@server/auth/sessions/app"; // Subset of TokenPayload for public ws.ts (newt and olm only) @@ -32,7 +38,11 @@ const connectedClients: Map = new Map(); const getClientMapKey = (clientId: string) => clientId; // Helper functions for client management -const addClient = async (clientType: ClientType, clientId: string, ws: AuthenticatedWebSocket): Promise => { +const addClient = async ( + clientType: ClientType, + clientId: string, + ws: AuthenticatedWebSocket +): Promise => { // Generate unique connection ID const connectionId = uuidv4(); ws.connectionId = connectionId; @@ -43,33 +53,46 @@ const addClient = async (clientType: ClientType, clientId: string, ws: Authentic existingClients.push(ws); connectedClients.set(mapKey, existingClients); - logger.info(`Client added to tracking - ${clientType.toUpperCase()} ID: ${clientId}, Connection ID: ${connectionId}, Total connections: ${existingClients.length}`); + logger.info( + `Client added to tracking - ${clientType.toUpperCase()} ID: ${clientId}, Connection ID: ${connectionId}, Total connections: ${existingClients.length}` + ); }; -const removeClient = async (clientType: ClientType, clientId: string, ws: AuthenticatedWebSocket): Promise => { +const removeClient = async ( + clientType: ClientType, + clientId: string, + ws: AuthenticatedWebSocket +): Promise => { const mapKey = getClientMapKey(clientId); const existingClients = connectedClients.get(mapKey) || []; - const updatedClients = existingClients.filter(client => client !== ws); + const updatedClients = existingClients.filter((client) => client !== ws); if (updatedClients.length === 0) { connectedClients.delete(mapKey); - logger.info(`All connections removed for ${clientType.toUpperCase()} ID: ${clientId}`); + logger.info( + `All connections removed for ${clientType.toUpperCase()} ID: ${clientId}` + ); } else { connectedClients.set(mapKey, updatedClients); - logger.info(`Connection removed - ${clientType.toUpperCase()} ID: ${clientId}, Remaining connections: ${updatedClients.length}`); + logger.info( + `Connection removed - ${clientType.toUpperCase()} ID: ${clientId}, Remaining connections: ${updatedClients.length}` + ); } }; // Local message sending (within this node) -const sendToClientLocal = async (clientId: string, message: WSMessage): Promise => { +const sendToClientLocal = async ( + clientId: string, + message: WSMessage +): Promise => { const mapKey = getClientMapKey(clientId); const clients = connectedClients.get(mapKey); if (!clients || clients.length === 0) { return false; } const messageString = JSON.stringify(message); - clients.forEach(client => { + clients.forEach((client) => { if (client.readyState === WebSocket.OPEN) { client.send(messageString); } @@ -77,11 +100,14 @@ const sendToClientLocal = async (clientId: string, message: WSMessage): Promise< return true; }; -const broadcastToAllExceptLocal = async (message: WSMessage, excludeClientId?: string): Promise => { +const broadcastToAllExceptLocal = async ( + message: WSMessage, + excludeClientId?: string +): Promise => { connectedClients.forEach((clients, mapKey) => { const [type, id] = mapKey.split(":"); if (!(excludeClientId && id === excludeClientId)) { - clients.forEach(client => { + clients.forEach((client) => { if (client.readyState === WebSocket.OPEN) { client.send(JSON.stringify(message)); } @@ -91,39 +117,53 @@ const broadcastToAllExceptLocal = async (message: WSMessage, excludeClientId?: s }; // Cross-node message sending -const sendToClient = async (clientId: string, message: WSMessage): Promise => { +const sendToClient = async ( + clientId: string, + message: WSMessage +): Promise => { // Try to send locally first const localSent = await sendToClientLocal(clientId, message); - logger.debug(`sendToClient: Message type ${message.type} sent to clientId ${clientId}`); + logger.debug( + `sendToClient: Message type ${message.type} sent to clientId ${clientId}` + ); return localSent; }; -const broadcastToAllExcept = async (message: WSMessage, excludeClientId?: string): Promise => { +const broadcastToAllExcept = async ( + message: WSMessage, + excludeClientId?: string +): Promise => { // Broadcast locally await broadcastToAllExceptLocal(message, excludeClientId); }; // Check if a client has active connections across all nodes const hasActiveConnections = async (clientId: string): Promise => { - const mapKey = getClientMapKey(clientId); - const clients = connectedClients.get(mapKey); - return !!(clients && clients.length > 0); + const mapKey = getClientMapKey(clientId); + const clients = connectedClients.get(mapKey); + return !!(clients && clients.length > 0); }; // Get all active nodes for a client -const getActiveNodes = async (clientType: ClientType, clientId: string): Promise => { - const mapKey = getClientMapKey(clientId); - const clients = connectedClients.get(mapKey); - return (clients && clients.length > 0) ? [NODE_ID] : []; +const getActiveNodes = async ( + clientType: ClientType, + clientId: string +): Promise => { + const mapKey = getClientMapKey(clientId); + const clients = connectedClients.get(mapKey); + return clients && clients.length > 0 ? [NODE_ID] : []; }; // Token verification middleware -const verifyToken = async (token: string, clientType: ClientType, userToken: string): Promise => { - -try { - if (clientType === 'newt') { +const verifyToken = async ( + token: string, + clientType: ClientType, + userToken: string +): Promise => { + try { + if (clientType === "newt") { const { session, newt } = await validateNewtSessionToken(token); if (!session || !newt) { return null; @@ -136,7 +176,7 @@ try { return null; } return { client: existingNewt[0], session, clientType }; - } else if (clientType === 'olm') { + } else if (clientType === "olm") { const { session, olm } = await validateOlmSessionToken(token); if (!session || !olm) { return null; @@ -149,8 +189,10 @@ try { return null; } - if (olm.userId) { // this is a user device and we need to check the user token - const { session: userSession, user } = await validateSessionToken(userToken); + if (olm.userId) { + // this is a user device and we need to check the user token + const { session: userSession, user } = + await validateSessionToken(userToken); if (!userSession || !user) { return null; } @@ -161,7 +203,7 @@ try { return { client: existingOlm[0], session, clientType }; } - + return null; } catch (error) { logger.error("Token verification failed:", error); @@ -169,7 +211,11 @@ try { } }; -const setupConnection = async (ws: AuthenticatedWebSocket, client: Newt | Olm, clientType: "newt" | "olm"): Promise => { +const setupConnection = async ( + ws: AuthenticatedWebSocket, + client: Newt | Olm, + clientType: "newt" | "olm" +): Promise => { logger.info("Establishing websocket connection"); if (!client) { logger.error("Connection attempt without client"); @@ -180,7 +226,8 @@ const setupConnection = async (ws: AuthenticatedWebSocket, client: Newt | Olm, c ws.clientType = clientType; // Add client to tracking - const clientId = clientType === 'newt' ? (client as Newt).newtId : (client as Olm).olmId; + const clientId = + clientType === "newt" ? (client as Newt).newtId : (client as Olm).olmId; await addClient(clientType, clientId, ws); ws.on("message", async (data) => { @@ -188,7 +235,9 @@ const setupConnection = async (ws: AuthenticatedWebSocket, client: Newt | Olm, c const message: WSMessage = JSON.parse(data.toString()); if (!message.type || typeof message.type !== "string") { - throw new Error("Invalid message format: missing or invalid type"); + throw new Error( + "Invalid message format: missing or invalid type" + ); } const handler = messageHandlers[message.type]; @@ -213,33 +262,48 @@ const setupConnection = async (ws: AuthenticatedWebSocket, client: Newt | Olm, c response.excludeSender ? clientId : undefined ); } else if (response.targetClientId) { - await sendToClient(response.targetClientId, response.message); + await sendToClient( + response.targetClientId, + response.message + ); } else { ws.send(JSON.stringify(response.message)); } } } catch (error) { logger.error("Message handling error:", error); - ws.send(JSON.stringify({ - type: "error", - data: { - message: error instanceof Error ? error.message : "Unknown error occurred", - originalMessage: data.toString() - } - })); + ws.send( + JSON.stringify({ + type: "error", + data: { + message: + error instanceof Error + ? error.message + : "Unknown error occurred", + originalMessage: data.toString() + } + }) + ); } }); ws.on("close", () => { removeClient(clientType, clientId, ws); - logger.info(`Client disconnected - ${clientType.toUpperCase()} ID: ${clientId}`); + logger.info( + `Client disconnected - ${clientType.toUpperCase()} ID: ${clientId}` + ); }); ws.on("error", (error: Error) => { - logger.error(`WebSocket error for ${clientType.toUpperCase()} ID ${clientId}:`, error); + logger.error( + `WebSocket error for ${clientType.toUpperCase()} ID ${clientId}:`, + error + ); }); - logger.info(`WebSocket connection established - ${clientType.toUpperCase()} ID: ${clientId}`); + logger.info( + `WebSocket connection established - ${clientType.toUpperCase()} ID: ${clientId}` + ); }; // Router endpoint @@ -249,55 +313,89 @@ router.get("/ws", (req: Request, res: Response) => { // WebSocket upgrade handler const handleWSUpgrade = (server: HttpServer): void => { - server.on("upgrade", async (request: WebSocketRequest, socket: Socket, head: Buffer) => { - try { - const url = new URL(request.url || '', `http://${request.headers.host}`); - const token = url.searchParams.get('token') || request.headers["sec-websocket-protocol"] || ''; - const userToken = url.searchParams.get('userToken') || ''; - let clientType = url.searchParams.get('clientType') as ClientType; + server.on( + "upgrade", + async (request: WebSocketRequest, socket: Socket, head: Buffer) => { + try { + const url = new URL( + request.url || "", + `http://${request.headers.host}` + ); + const token = + url.searchParams.get("token") || + request.headers["sec-websocket-protocol"] || + ""; + const userToken = url.searchParams.get("userToken") || ""; + let clientType = url.searchParams.get( + "clientType" + ) as ClientType; - if (!clientType) { - clientType = "newt"; - } + if (!clientType) { + clientType = "newt"; + } - if (!token || !clientType || !['newt', 'olm'].includes(clientType)) { - logger.warn("Unauthorized connection attempt: invalid token or client type..."); - socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n"); + if ( + !token || + !clientType || + !["newt", "olm"].includes(clientType) + ) { + logger.warn( + "Unauthorized connection attempt: invalid token or client type..." + ); + socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n"); + socket.destroy(); + return; + } + + const tokenPayload = await verifyToken( + token, + clientType, + userToken + ); + if (!tokenPayload) { + logger.warn( + "Unauthorized connection attempt: invalid token..." + ); + socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n"); + socket.destroy(); + return; + } + + wss.handleUpgrade( + request, + socket, + head, + (ws: AuthenticatedWebSocket) => { + setupConnection( + ws, + tokenPayload.client, + tokenPayload.clientType + ); + } + ); + } catch (error) { + logger.error("WebSocket upgrade error:", error); + socket.write("HTTP/1.1 500 Internal Server Error\r\n\r\n"); socket.destroy(); - return; } - - const tokenPayload = await verifyToken(token, clientType, userToken); - if (!tokenPayload) { - logger.warn("Unauthorized connection attempt: invalid token..."); - socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n"); - socket.destroy(); - return; - } - - wss.handleUpgrade(request, socket, head, (ws: AuthenticatedWebSocket) => { - setupConnection(ws, tokenPayload.client, tokenPayload.clientType); - }); - } catch (error) { - logger.error("WebSocket upgrade error:", error); - socket.write("HTTP/1.1 500 Internal Server Error\r\n\r\n"); - socket.destroy(); } - }); + ); }; // Disconnect a specific client and force them to reconnect const disconnectClient = async (clientId: string): Promise => { const mapKey = getClientMapKey(clientId); const clients = connectedClients.get(mapKey); - + if (!clients || clients.length === 0) { logger.debug(`No connections found for client ID: ${clientId}`); return false; } - logger.info(`Disconnecting client ID: ${clientId} (${clients.length} connection(s))`); - + logger.info( + `Disconnecting client ID: ${clientId} (${clients.length} connection(s))` + ); + // Close all connections for this client clients.forEach((client) => { if (client.readyState === WebSocket.OPEN) { @@ -313,16 +411,16 @@ const cleanup = async (): Promise => { try { // Close all WebSocket connections connectedClients.forEach((clients) => { - clients.forEach(client => { + clients.forEach((client) => { if (client.readyState === WebSocket.OPEN) { client.terminate(); } }); }); - logger.info('WebSocket cleanup completed'); + logger.info("WebSocket cleanup completed"); } catch (error) { - logger.error('Error during WebSocket cleanup:', error); + logger.error("Error during WebSocket cleanup:", error); } }; diff --git a/server/setup/ensureSetupToken.ts b/server/setup/ensureSetupToken.ts index 46a62ca5..64298029 100644 --- a/server/setup/ensureSetupToken.ts +++ b/server/setup/ensureSetupToken.ts @@ -31,7 +31,9 @@ export async function ensureSetupToken() { // If admin exists, no need for setup token if (existingAdmin) { - logger.debug("Server admin exists. Setup token generation skipped."); + logger.debug( + "Server admin exists. Setup token generation skipped." + ); return; } diff --git a/server/setup/migrationsPg.ts b/server/setup/migrationsPg.ts index c778cca3..0fc42f9d 100644 --- a/server/setup/migrationsPg.ts +++ b/server/setup/migrationsPg.ts @@ -30,7 +30,7 @@ const migrations = [ { version: "1.11.0", run: m7 }, { version: "1.11.1", run: m8 }, { version: "1.12.0", run: m9 }, - { version: "1.13.0", run: m10 }, + { version: "1.13.0", run: m10 } // Add new migrations here as they are created ] as { version: string; diff --git a/server/setup/scriptsPg/1.12.0.ts b/server/setup/scriptsPg/1.12.0.ts index 38cdaf43..d3c257e3 100644 --- a/server/setup/scriptsPg/1.12.0.ts +++ b/server/setup/scriptsPg/1.12.0.ts @@ -9,7 +9,9 @@ export default async function migration() { try { await db.execute(sql`BEGIN`); - await db.execute(sql`UPDATE "resourceRules" SET "match" = 'COUNTRY' WHERE "match" = 'GEOIP'`); + await db.execute( + sql`UPDATE "resourceRules" SET "match" = 'COUNTRY' WHERE "match" = 'GEOIP'` + ); await db.execute(sql` CREATE TABLE "accessAuditLog" ( @@ -92,40 +94,97 @@ export default async function migration() { ); `); - await db.execute(sql`ALTER TABLE "blueprints" ADD CONSTRAINT "blueprints_orgId_orgs_orgId_fk" FOREIGN KEY ("orgId") REFERENCES "public"."orgs"("orgId") ON DELETE cascade ON UPDATE no action;`); + await db.execute( + sql`ALTER TABLE "blueprints" ADD CONSTRAINT "blueprints_orgId_orgs_orgId_fk" FOREIGN KEY ("orgId") REFERENCES "public"."orgs"("orgId") ON DELETE cascade ON UPDATE no action;` + ); - await db.execute(sql`ALTER TABLE "remoteExitNode" ADD COLUMN "secondaryVersion" varchar;`); - await db.execute(sql`ALTER TABLE "resources" DROP CONSTRAINT "resources_skipToIdpId_idp_idpId_fk";`); - await db.execute(sql`ALTER TABLE "domains" ADD COLUMN "certResolver" varchar;`); - await db.execute(sql`ALTER TABLE "domains" ADD COLUMN "customCertResolver" varchar;`); - await db.execute(sql`ALTER TABLE "domains" ADD COLUMN "preferWildcardCert" boolean;`); - await db.execute(sql`ALTER TABLE "orgs" ADD COLUMN "requireTwoFactor" boolean;`); - await db.execute(sql`ALTER TABLE "orgs" ADD COLUMN "maxSessionLengthHours" integer;`); - await db.execute(sql`ALTER TABLE "orgs" ADD COLUMN "passwordExpiryDays" integer;`); - await db.execute(sql`ALTER TABLE "orgs" ADD COLUMN "settingsLogRetentionDaysRequest" integer DEFAULT 7 NOT NULL;`); - await db.execute(sql`ALTER TABLE "orgs" ADD COLUMN "settingsLogRetentionDaysAccess" integer DEFAULT 0 NOT NULL;`); - await db.execute(sql`ALTER TABLE "orgs" ADD COLUMN "settingsLogRetentionDaysAction" integer DEFAULT 0 NOT NULL;`); - await db.execute(sql`ALTER TABLE "resourceSessions" ADD COLUMN "issuedAt" bigint;`); - await db.execute(sql`ALTER TABLE "resources" ADD COLUMN "proxyProtocol" boolean DEFAULT false NOT NULL;`); - await db.execute(sql`ALTER TABLE "resources" ADD COLUMN "proxyProtocolVersion" integer DEFAULT 1;`); - await db.execute(sql`ALTER TABLE "session" ADD COLUMN "issuedAt" bigint;`); - await db.execute(sql`ALTER TABLE "user" ADD COLUMN "lastPasswordChange" bigint;`); - await db.execute(sql`ALTER TABLE "accessAuditLog" ADD CONSTRAINT "accessAuditLog_orgId_orgs_orgId_fk" FOREIGN KEY ("orgId") REFERENCES "public"."orgs"("orgId") ON DELETE cascade ON UPDATE no action;`); - await db.execute(sql`ALTER TABLE "actionAuditLog" ADD CONSTRAINT "actionAuditLog_orgId_orgs_orgId_fk" FOREIGN KEY ("orgId") REFERENCES "public"."orgs"("orgId") ON DELETE cascade ON UPDATE no action;`); - await db.execute(sql`ALTER TABLE "dnsRecords" ADD CONSTRAINT "dnsRecords_domainId_domains_domainId_fk" FOREIGN KEY ("domainId") REFERENCES "public"."domains"("domainId") ON DELETE cascade ON UPDATE no action;`); - await db.execute(sql`ALTER TABLE "requestAuditLog" ADD CONSTRAINT "requestAuditLog_orgId_orgs_orgId_fk" FOREIGN KEY ("orgId") REFERENCES "public"."orgs"("orgId") ON DELETE cascade ON UPDATE no action;`); - await db.execute(sql`CREATE INDEX "idx_identityAuditLog_timestamp" ON "accessAuditLog" USING btree ("timestamp");`); - await db.execute(sql`CREATE INDEX "idx_identityAuditLog_org_timestamp" ON "accessAuditLog" USING btree ("orgId","timestamp");`); - await db.execute(sql`CREATE INDEX "idx_actionAuditLog_timestamp" ON "actionAuditLog" USING btree ("timestamp");`); - await db.execute(sql`CREATE INDEX "idx_actionAuditLog_org_timestamp" ON "actionAuditLog" USING btree ("orgId","timestamp");`); - await db.execute(sql`CREATE INDEX "idx_requestAuditLog_timestamp" ON "requestAuditLog" USING btree ("timestamp");`); - await db.execute(sql`CREATE INDEX "idx_requestAuditLog_org_timestamp" ON "requestAuditLog" USING btree ("orgId","timestamp");`); - await db.execute(sql`ALTER TABLE "resources" ADD CONSTRAINT "resources_skipToIdpId_idp_idpId_fk" FOREIGN KEY ("skipToIdpId") REFERENCES "public"."idp"("idpId") ON DELETE set null ON UPDATE no action;`); + await db.execute( + sql`ALTER TABLE "remoteExitNode" ADD COLUMN "secondaryVersion" varchar;` + ); + await db.execute( + sql`ALTER TABLE "resources" DROP CONSTRAINT "resources_skipToIdpId_idp_idpId_fk";` + ); + await db.execute( + sql`ALTER TABLE "domains" ADD COLUMN "certResolver" varchar;` + ); + await db.execute( + sql`ALTER TABLE "domains" ADD COLUMN "customCertResolver" varchar;` + ); + await db.execute( + sql`ALTER TABLE "domains" ADD COLUMN "preferWildcardCert" boolean;` + ); + await db.execute( + sql`ALTER TABLE "orgs" ADD COLUMN "requireTwoFactor" boolean;` + ); + await db.execute( + sql`ALTER TABLE "orgs" ADD COLUMN "maxSessionLengthHours" integer;` + ); + await db.execute( + sql`ALTER TABLE "orgs" ADD COLUMN "passwordExpiryDays" integer;` + ); + await db.execute( + sql`ALTER TABLE "orgs" ADD COLUMN "settingsLogRetentionDaysRequest" integer DEFAULT 7 NOT NULL;` + ); + await db.execute( + sql`ALTER TABLE "orgs" ADD COLUMN "settingsLogRetentionDaysAccess" integer DEFAULT 0 NOT NULL;` + ); + await db.execute( + sql`ALTER TABLE "orgs" ADD COLUMN "settingsLogRetentionDaysAction" integer DEFAULT 0 NOT NULL;` + ); + await db.execute( + sql`ALTER TABLE "resourceSessions" ADD COLUMN "issuedAt" bigint;` + ); + await db.execute( + sql`ALTER TABLE "resources" ADD COLUMN "proxyProtocol" boolean DEFAULT false NOT NULL;` + ); + await db.execute( + sql`ALTER TABLE "resources" ADD COLUMN "proxyProtocolVersion" integer DEFAULT 1;` + ); + await db.execute( + sql`ALTER TABLE "session" ADD COLUMN "issuedAt" bigint;` + ); + await db.execute( + sql`ALTER TABLE "user" ADD COLUMN "lastPasswordChange" bigint;` + ); + await db.execute( + sql`ALTER TABLE "accessAuditLog" ADD CONSTRAINT "accessAuditLog_orgId_orgs_orgId_fk" FOREIGN KEY ("orgId") REFERENCES "public"."orgs"("orgId") ON DELETE cascade ON UPDATE no action;` + ); + await db.execute( + sql`ALTER TABLE "actionAuditLog" ADD CONSTRAINT "actionAuditLog_orgId_orgs_orgId_fk" FOREIGN KEY ("orgId") REFERENCES "public"."orgs"("orgId") ON DELETE cascade ON UPDATE no action;` + ); + await db.execute( + sql`ALTER TABLE "dnsRecords" ADD CONSTRAINT "dnsRecords_domainId_domains_domainId_fk" FOREIGN KEY ("domainId") REFERENCES "public"."domains"("domainId") ON DELETE cascade ON UPDATE no action;` + ); + await db.execute( + sql`ALTER TABLE "requestAuditLog" ADD CONSTRAINT "requestAuditLog_orgId_orgs_orgId_fk" FOREIGN KEY ("orgId") REFERENCES "public"."orgs"("orgId") ON DELETE cascade ON UPDATE no action;` + ); + await db.execute( + sql`CREATE INDEX "idx_identityAuditLog_timestamp" ON "accessAuditLog" USING btree ("timestamp");` + ); + await db.execute( + sql`CREATE INDEX "idx_identityAuditLog_org_timestamp" ON "accessAuditLog" USING btree ("orgId","timestamp");` + ); + await db.execute( + sql`CREATE INDEX "idx_actionAuditLog_timestamp" ON "actionAuditLog" USING btree ("timestamp");` + ); + await db.execute( + sql`CREATE INDEX "idx_actionAuditLog_org_timestamp" ON "actionAuditLog" USING btree ("orgId","timestamp");` + ); + await db.execute( + sql`CREATE INDEX "idx_requestAuditLog_timestamp" ON "requestAuditLog" USING btree ("timestamp");` + ); + await db.execute( + sql`CREATE INDEX "idx_requestAuditLog_org_timestamp" ON "requestAuditLog" USING btree ("orgId","timestamp");` + ); + await db.execute( + sql`ALTER TABLE "resources" ADD CONSTRAINT "resources_skipToIdpId_idp_idpId_fk" FOREIGN KEY ("skipToIdpId") REFERENCES "public"."idp"("idpId") ON DELETE set null ON UPDATE no action;` + ); await db.execute(sql`ALTER TABLE "orgs" DROP COLUMN "settings";`); - // get all of the domains - const domainsQuery = await db.execute(sql`SELECT "domainId", "baseDomain" FROM "domains"`); + const domainsQuery = await db.execute( + sql`SELECT "domainId", "baseDomain" FROM "domains"` + ); const domains = domainsQuery.rows as { domainId: string; baseDomain: string; @@ -135,11 +194,11 @@ export default async function migration() { // insert two records into the dnsRecords table for each domain await db.execute(sql` INSERT INTO "dnsRecords" ("domainId", "recordType", "baseDomain", "value", "verified") - VALUES (${domain.domainId}, 'A', ${`*.${domain.baseDomain}`}, ${'Server IP Address'}, true) + VALUES (${domain.domainId}, 'A', ${`*.${domain.baseDomain}`}, ${"Server IP Address"}, true) `); await db.execute(sql` INSERT INTO "dnsRecords" ("domainId", "recordType", "baseDomain", "value", "verified") - VALUES (${domain.domainId}, 'A', ${domain.baseDomain}, ${'Server IP Address'}, true) + VALUES (${domain.domainId}, 'A', ${domain.baseDomain}, ${"Server IP Address"}, true) `); } diff --git a/server/setup/scriptsPg/1.13.0.ts b/server/setup/scriptsPg/1.13.0.ts index e13276df..9a56706c 100644 --- a/server/setup/scriptsPg/1.13.0.ts +++ b/server/setup/scriptsPg/1.13.0.ts @@ -255,7 +255,9 @@ export default async function migration() { const siteDataQuery = await db.execute(sql` SELECT "orgId" FROM "sites" WHERE "siteId" = ${site.siteId} `); - const siteData = siteDataQuery.rows[0] as { orgId: string } | undefined; + const siteData = siteDataQuery.rows[0] as + | { orgId: string } + | undefined; if (!siteData) continue; const subnets = site.remoteSubnets.split(","); diff --git a/server/setup/scriptsPg/1.7.0.ts b/server/setup/scriptsPg/1.7.0.ts index 3cb799e0..aa740ecb 100644 --- a/server/setup/scriptsPg/1.7.0.ts +++ b/server/setup/scriptsPg/1.7.0.ts @@ -121,7 +121,7 @@ export default async function migration() { try { await db.execute(sql`BEGIN`); - + // Update all existing orgs to have the default subnet await db.execute(sql`UPDATE "orgs" SET "subnet" = '100.90.128.0/24'`); diff --git a/server/setup/scriptsPg/1.9.0.ts b/server/setup/scriptsPg/1.9.0.ts index fdbf3ae9..eac7ade9 100644 --- a/server/setup/scriptsPg/1.9.0.ts +++ b/server/setup/scriptsPg/1.9.0.ts @@ -11,7 +11,9 @@ export default async function migration() { try { // Get the first siteId to use as default - const firstSite = await db.execute(sql`SELECT "siteId" FROM "sites" LIMIT 1`); + const firstSite = await db.execute( + sql`SELECT "siteId" FROM "sites" LIMIT 1` + ); if (firstSite.rows.length > 0) { firstSiteId = firstSite.rows[0].siteId as number; } @@ -52,33 +54,59 @@ export default async function migration() { "enabled" boolean DEFAULT true NOT NULL );`); - await db.execute(sql`ALTER TABLE "resources" DROP CONSTRAINT "resources_siteId_sites_siteId_fk";`); + await db.execute( + sql`ALTER TABLE "resources" DROP CONSTRAINT "resources_siteId_sites_siteId_fk";` + ); - await db.execute(sql`ALTER TABLE "clients" ALTER COLUMN "lastPing" TYPE integer USING NULL;`); + await db.execute( + sql`ALTER TABLE "clients" ALTER COLUMN "lastPing" TYPE integer USING NULL;` + ); - await db.execute(sql`ALTER TABLE "clientSites" ADD COLUMN "endpoint" varchar;`); + await db.execute( + sql`ALTER TABLE "clientSites" ADD COLUMN "endpoint" varchar;` + ); - await db.execute(sql`ALTER TABLE "exitNodes" ADD COLUMN "online" boolean DEFAULT false NOT NULL;`); + await db.execute( + sql`ALTER TABLE "exitNodes" ADD COLUMN "online" boolean DEFAULT false NOT NULL;` + ); - await db.execute(sql`ALTER TABLE "exitNodes" ADD COLUMN "lastPing" integer;`); + await db.execute( + sql`ALTER TABLE "exitNodes" ADD COLUMN "lastPing" integer;` + ); - await db.execute(sql`ALTER TABLE "exitNodes" ADD COLUMN "type" text DEFAULT 'gerbil';`); + await db.execute( + sql`ALTER TABLE "exitNodes" ADD COLUMN "type" text DEFAULT 'gerbil';` + ); await db.execute(sql`ALTER TABLE "olms" ADD COLUMN "version" text;`); await db.execute(sql`ALTER TABLE "orgs" ADD COLUMN "createdAt" text;`); - await db.execute(sql`ALTER TABLE "resources" ADD COLUMN "skipToIdpId" integer;`); + await db.execute( + sql`ALTER TABLE "resources" ADD COLUMN "skipToIdpId" integer;` + ); - await db.execute(sql.raw(`ALTER TABLE "targets" ADD COLUMN "siteId" integer NOT NULL DEFAULT ${firstSiteId || 1};`)); + await db.execute( + sql.raw( + `ALTER TABLE "targets" ADD COLUMN "siteId" integer NOT NULL DEFAULT ${firstSiteId || 1};` + ) + ); - await db.execute(sql`ALTER TABLE "siteResources" ADD CONSTRAINT "siteResources_siteId_sites_siteId_fk" FOREIGN KEY ("siteId") REFERENCES "public"."sites"("siteId") ON DELETE cascade ON UPDATE no action;`); + await db.execute( + sql`ALTER TABLE "siteResources" ADD CONSTRAINT "siteResources_siteId_sites_siteId_fk" FOREIGN KEY ("siteId") REFERENCES "public"."sites"("siteId") ON DELETE cascade ON UPDATE no action;` + ); - await db.execute(sql`ALTER TABLE "siteResources" ADD CONSTRAINT "siteResources_orgId_orgs_orgId_fk" FOREIGN KEY ("orgId") REFERENCES "public"."orgs"("orgId") ON DELETE cascade ON UPDATE no action;`); + await db.execute( + sql`ALTER TABLE "siteResources" ADD CONSTRAINT "siteResources_orgId_orgs_orgId_fk" FOREIGN KEY ("orgId") REFERENCES "public"."orgs"("orgId") ON DELETE cascade ON UPDATE no action;` + ); - await db.execute(sql`ALTER TABLE "resources" ADD CONSTRAINT "resources_skipToIdpId_idp_idpId_fk" FOREIGN KEY ("skipToIdpId") REFERENCES "public"."idp"("idpId") ON DELETE cascade ON UPDATE no action;`); + await db.execute( + sql`ALTER TABLE "resources" ADD CONSTRAINT "resources_skipToIdpId_idp_idpId_fk" FOREIGN KEY ("skipToIdpId") REFERENCES "public"."idp"("idpId") ON DELETE cascade ON UPDATE no action;` + ); - await db.execute(sql`ALTER TABLE "targets" ADD CONSTRAINT "targets_siteId_sites_siteId_fk" FOREIGN KEY ("siteId") REFERENCES "public"."sites"("siteId") ON DELETE cascade ON UPDATE no action;`); + await db.execute( + sql`ALTER TABLE "targets" ADD CONSTRAINT "targets_siteId_sites_siteId_fk" FOREIGN KEY ("siteId") REFERENCES "public"."sites"("siteId") ON DELETE cascade ON UPDATE no action;` + ); await db.execute(sql`ALTER TABLE "clients" DROP COLUMN "endpoint";`); diff --git a/server/setup/scriptsSqlite/1.0.0-beta13.ts b/server/setup/scriptsSqlite/1.0.0-beta13.ts index 9ced727f..9986b06f 100644 --- a/server/setup/scriptsSqlite/1.0.0-beta13.ts +++ b/server/setup/scriptsSqlite/1.0.0-beta13.ts @@ -25,7 +25,9 @@ export default async function migration() { console.log(`Added new table and column: resourceRules, applyRules`); } catch (e) { - console.log("Unable to add new table and column: resourceRules, applyRules"); + console.log( + "Unable to add new table and column: resourceRules, applyRules" + ); throw e; } diff --git a/server/setup/scriptsSqlite/1.0.0-beta3.ts b/server/setup/scriptsSqlite/1.0.0-beta3.ts index fccfeb88..5d69af6b 100644 --- a/server/setup/scriptsSqlite/1.0.0-beta3.ts +++ b/server/setup/scriptsSqlite/1.0.0-beta3.ts @@ -38,4 +38,4 @@ export default async function migration() { fs.writeFileSync(filePath, updatedYaml, "utf8"); console.log("Done."); -} \ No newline at end of file +} diff --git a/server/setup/scriptsSqlite/1.0.0-beta6.ts b/server/setup/scriptsSqlite/1.0.0-beta6.ts index 89129678..a13a7e31 100644 --- a/server/setup/scriptsSqlite/1.0.0-beta6.ts +++ b/server/setup/scriptsSqlite/1.0.0-beta6.ts @@ -43,7 +43,9 @@ export default async function migration() { const updatedYaml = yaml.dump(rawConfig); fs.writeFileSync(filePath, updatedYaml, "utf8"); } catch (error) { - console.log("We were unable to add CORS to your config file. Please add it manually."); + console.log( + "We were unable to add CORS to your config file. Please add it manually." + ); console.error(error); } diff --git a/server/setup/scriptsSqlite/1.0.0-beta9.ts b/server/setup/scriptsSqlite/1.0.0-beta9.ts index 7cce1c2d..6d48ed39 100644 --- a/server/setup/scriptsSqlite/1.0.0-beta9.ts +++ b/server/setup/scriptsSqlite/1.0.0-beta9.ts @@ -182,12 +182,15 @@ export default async function migration() { if (parsedConfig.success) { // delete permanent from redirect-to-https middleware - delete traefikConfig.http.middlewares["redirect-to-https"].redirectScheme.permanent; + delete traefikConfig.http.middlewares["redirect-to-https"] + .redirectScheme.permanent; const updatedTraefikYaml = yaml.dump(traefikConfig); fs.writeFileSync(traefikPath, updatedTraefikYaml, "utf8"); - console.log("Deleted permanent from redirect-to-https middleware."); + console.log( + "Deleted permanent from redirect-to-https middleware." + ); } else { console.log(fromZodError(parsedConfig.error)); console.log( diff --git a/server/setup/scriptsSqlite/1.10.0.ts b/server/setup/scriptsSqlite/1.10.0.ts index 3065a664..03cf24dc 100644 --- a/server/setup/scriptsSqlite/1.10.0.ts +++ b/server/setup/scriptsSqlite/1.10.0.ts @@ -13,15 +13,11 @@ export default async function migration() { try { const resources = db - .prepare( - "SELECT resourceId FROM resources" - ) + .prepare("SELECT resourceId FROM resources") .all() as Array<{ resourceId: number }>; const siteResources = db - .prepare( - "SELECT siteResourceId FROM siteResources" - ) + .prepare("SELECT siteResourceId FROM siteResources") .all() as Array<{ siteResourceId: number }>; db.transaction(() => { @@ -82,17 +78,13 @@ export default async function migration() { // Handle auto-provisioned users for identity providers const autoProvisionIdps = db - .prepare( - "SELECT idpId FROM idp WHERE autoProvision = 1" - ) + .prepare("SELECT idpId FROM idp WHERE autoProvision = 1") .all() as Array<{ idpId: number }>; for (const idp of autoProvisionIdps) { // Get all users with this identity provider const usersWithIdp = db - .prepare( - "SELECT id FROM user WHERE idpId = ?" - ) + .prepare("SELECT id FROM user WHERE idpId = ?") .all(idp.idpId) as Array<{ id: string }>; // Update userOrgs to set autoProvisioned to true for these users diff --git a/server/setup/scriptsSqlite/1.10.1.ts b/server/setup/scriptsSqlite/1.10.1.ts index f6f9894e..24181558 100644 --- a/server/setup/scriptsSqlite/1.10.1.ts +++ b/server/setup/scriptsSqlite/1.10.1.ts @@ -5,16 +5,16 @@ import path from "path"; const version = "1.10.1"; export default async function migration() { - console.log(`Running setup script ${version}...`); + console.log(`Running setup script ${version}...`); - const location = path.join(APP_PATH, "db", "db.sqlite"); - const db = new Database(location); + const location = path.join(APP_PATH, "db", "db.sqlite"); + const db = new Database(location); - try { - db.pragma("foreign_keys = OFF"); + try { + db.pragma("foreign_keys = OFF"); - db.transaction(() => { - db.exec(`ALTER TABLE "targets" RENAME TO "targets_old"; + db.transaction(() => { + db.exec(`ALTER TABLE "targets" RENAME TO "targets_old"; --> statement-breakpoint CREATE TABLE "targets" ( "targetId" INTEGER PRIMARY KEY AUTOINCREMENT, @@ -57,13 +57,13 @@ SELECT FROM "targets_old"; --> statement-breakpoint DROP TABLE "targets_old";`); - })(); + })(); - db.pragma("foreign_keys = ON"); + db.pragma("foreign_keys = ON"); - console.log(`Migrated database`); - } catch (e) { - console.log("Failed to migrate db:", e); - throw e; - } -} \ No newline at end of file + console.log(`Migrated database`); + } catch (e) { + console.log("Failed to migrate db:", e); + throw e; + } +} diff --git a/server/setup/scriptsSqlite/1.11.0.ts b/server/setup/scriptsSqlite/1.11.0.ts index c79cfdb4..41d68563 100644 --- a/server/setup/scriptsSqlite/1.11.0.ts +++ b/server/setup/scriptsSqlite/1.11.0.ts @@ -13,25 +13,29 @@ export default async function migration() { const db = new Database(location); db.transaction(() => { - - db.prepare(` + db.prepare( + ` CREATE TABLE 'account' ( 'accountId' integer PRIMARY KEY AUTOINCREMENT NOT NULL, 'userId' text NOT NULL, FOREIGN KEY ('userId') REFERENCES 'user'('id') ON UPDATE no action ON DELETE cascade ); - `).run(); + ` + ).run(); - db.prepare(` + db.prepare( + ` CREATE TABLE 'accountDomains' ( 'accountId' integer NOT NULL, 'domainId' text NOT NULL, FOREIGN KEY ('accountId') REFERENCES 'account'('accountId') ON UPDATE no action ON DELETE cascade, FOREIGN KEY ('domainId') REFERENCES 'domains'('domainId') ON UPDATE no action ON DELETE cascade ); - `).run(); + ` + ).run(); - db.prepare(` + db.prepare( + ` CREATE TABLE 'certificates' ( 'certId' integer PRIMARY KEY AUTOINCREMENT NOT NULL, 'domain' text NOT NULL, @@ -49,11 +53,15 @@ export default async function migration() { 'keyFile' text, FOREIGN KEY ('domainId') REFERENCES 'domains'('domainId') ON UPDATE no action ON DELETE cascade ); - `).run(); + ` + ).run(); - db.prepare(`CREATE UNIQUE INDEX 'certificates_domain_unique' ON 'certificates' ('domain');`).run(); + db.prepare( + `CREATE UNIQUE INDEX 'certificates_domain_unique' ON 'certificates' ('domain');` + ).run(); - db.prepare(` + db.prepare( + ` CREATE TABLE 'customers' ( 'customerId' text PRIMARY KEY NOT NULL, 'orgId' text NOT NULL, @@ -65,9 +73,11 @@ export default async function migration() { 'updatedAt' integer NOT NULL, FOREIGN KEY ('orgId') REFERENCES 'orgs'('orgId') ON UPDATE no action ON DELETE cascade ); - `).run(); + ` + ).run(); - db.prepare(` + db.prepare( + ` CREATE TABLE 'dnsChallenges' ( 'dnsChallengeId' integer PRIMARY KEY AUTOINCREMENT NOT NULL, 'domain' text NOT NULL, @@ -77,26 +87,32 @@ export default async function migration() { 'expiresAt' integer NOT NULL, 'completed' integer DEFAULT false ); - `).run(); + ` + ).run(); - db.prepare(` + db.prepare( + ` CREATE TABLE 'domainNamespaces' ( 'domainNamespaceId' text PRIMARY KEY NOT NULL, 'domainId' text NOT NULL, FOREIGN KEY ('domainId') REFERENCES 'domains'('domainId') ON UPDATE no action ON DELETE set null ); - `).run(); + ` + ).run(); - db.prepare(` + db.prepare( + ` CREATE TABLE 'exitNodeOrgs' ( 'exitNodeId' integer NOT NULL, 'orgId' text NOT NULL, FOREIGN KEY ('exitNodeId') REFERENCES 'exitNodes'('exitNodeId') ON UPDATE no action ON DELETE cascade, FOREIGN KEY ('orgId') REFERENCES 'orgs'('orgId') ON UPDATE no action ON DELETE cascade ); - `).run(); + ` + ).run(); - db.prepare(` + db.prepare( + ` CREATE TABLE 'loginPage' ( 'loginPageId' integer PRIMARY KEY AUTOINCREMENT NOT NULL, 'subdomain' text, @@ -106,27 +122,33 @@ export default async function migration() { FOREIGN KEY ('exitNodeId') REFERENCES 'exitNodes'('exitNodeId') ON UPDATE no action ON DELETE set null, FOREIGN KEY ('domainId') REFERENCES 'domains'('domainId') ON UPDATE no action ON DELETE set null ); - `).run(); + ` + ).run(); - db.prepare(` + db.prepare( + ` CREATE TABLE 'loginPageOrg' ( 'loginPageId' integer NOT NULL, 'orgId' text NOT NULL, FOREIGN KEY ('loginPageId') REFERENCES 'loginPage'('loginPageId') ON UPDATE no action ON DELETE cascade, FOREIGN KEY ('orgId') REFERENCES 'orgs'('orgId') ON UPDATE no action ON DELETE cascade ); - `).run(); + ` + ).run(); - db.prepare(` + db.prepare( + ` CREATE TABLE 'remoteExitNodeSession' ( 'id' text PRIMARY KEY NOT NULL, 'remoteExitNodeId' text NOT NULL, 'expiresAt' integer NOT NULL, FOREIGN KEY ('remoteExitNodeId') REFERENCES 'remoteExitNode'('id') ON UPDATE no action ON DELETE cascade ); - `).run(); + ` + ).run(); - db.prepare(` + db.prepare( + ` CREATE TABLE 'remoteExitNode' ( 'id' text PRIMARY KEY NOT NULL, 'secretHash' text NOT NULL, @@ -135,9 +157,11 @@ export default async function migration() { 'exitNodeId' integer, FOREIGN KEY ('exitNodeId') REFERENCES 'exitNodes'('exitNodeId') ON UPDATE no action ON DELETE cascade ); - `).run(); + ` + ).run(); - db.prepare(` + db.prepare( + ` CREATE TABLE 'sessionTransferToken' ( 'token' text PRIMARY KEY NOT NULL, 'sessionId' text NOT NULL, @@ -145,9 +169,11 @@ export default async function migration() { 'expiresAt' integer NOT NULL, FOREIGN KEY ('sessionId') REFERENCES 'session'('id') ON UPDATE no action ON DELETE cascade ); - `).run(); + ` + ).run(); - db.prepare(` + db.prepare( + ` CREATE TABLE 'subscriptionItems' ( 'subscriptionItemId' integer PRIMARY KEY AUTOINCREMENT NOT NULL, 'subscriptionId' text NOT NULL, @@ -162,9 +188,11 @@ export default async function migration() { 'name' text, FOREIGN KEY ('subscriptionId') REFERENCES 'subscriptions'('subscriptionId') ON UPDATE no action ON DELETE cascade ); - `).run(); + ` + ).run(); - db.prepare(` + db.prepare( + ` CREATE TABLE 'subscriptions' ( 'subscriptionId' text PRIMARY KEY NOT NULL, 'customerId' text NOT NULL, @@ -175,9 +203,11 @@ export default async function migration() { 'billingCycleAnchor' integer, FOREIGN KEY ('customerId') REFERENCES 'customers'('customerId') ON UPDATE no action ON DELETE cascade ); - `).run(); + ` + ).run(); - db.prepare(` + db.prepare( + ` CREATE TABLE 'usage' ( 'usageId' text PRIMARY KEY NOT NULL, 'featureId' text NOT NULL, @@ -191,9 +221,11 @@ export default async function migration() { 'nextRolloverAt' integer, FOREIGN KEY ('orgId') REFERENCES 'orgs'('orgId') ON UPDATE no action ON DELETE cascade ); - `).run(); + ` + ).run(); - db.prepare(` + db.prepare( + ` CREATE TABLE 'usageNotifications' ( 'notificationId' integer PRIMARY KEY AUTOINCREMENT NOT NULL, 'orgId' text NOT NULL, @@ -203,18 +235,22 @@ export default async function migration() { 'sentAt' integer NOT NULL, FOREIGN KEY ('orgId') REFERENCES 'orgs'('orgId') ON UPDATE no action ON DELETE cascade ); - `).run(); + ` + ).run(); - db.prepare(` + db.prepare( + ` CREATE TABLE 'resourceHeaderAuth' ( 'headerAuthId' integer PRIMARY KEY AUTOINCREMENT NOT NULL, 'resourceId' integer NOT NULL, 'headerAuthHash' text NOT NULL, FOREIGN KEY ('resourceId') REFERENCES 'resources'('resourceId') ON UPDATE no action ON DELETE cascade ); - `).run(); + ` + ).run(); - db.prepare(` + db.prepare( + ` CREATE TABLE 'targetHealthCheck' ( 'targetHealthCheckId' integer PRIMARY KEY AUTOINCREMENT NOT NULL, 'targetId' integer NOT NULL, @@ -234,11 +270,13 @@ export default async function migration() { 'hcHealth' text DEFAULT 'unknown', FOREIGN KEY ('targetId') REFERENCES 'targets'('targetId') ON UPDATE no action ON DELETE cascade ); - `).run(); + ` + ).run(); db.prepare(`DROP TABLE 'limits';`).run(); - db.prepare(` + db.prepare( + ` CREATE TABLE 'limits' ( 'limitId' text PRIMARY KEY NOT NULL, 'featureId' text NOT NULL, @@ -247,12 +285,15 @@ export default async function migration() { 'description' text, FOREIGN KEY ('orgId') REFERENCES 'orgs'('orgId') ON UPDATE no action ON DELETE cascade ); - `).run(); + ` + ).run(); db.prepare(`ALTER TABLE 'orgs' ADD 'settings' text;`).run(); db.prepare(`ALTER TABLE 'targets' ADD 'rewritePath' text;`).run(); db.prepare(`ALTER TABLE 'targets' ADD 'rewritePathType' text;`).run(); - db.prepare(`ALTER TABLE 'targets' ADD 'priority' integer DEFAULT 100 NOT NULL;`).run(); + db.prepare( + `ALTER TABLE 'targets' ADD 'priority' integer DEFAULT 100 NOT NULL;` + ).run(); const webauthnCredentials = db .prepare( @@ -269,7 +310,7 @@ export default async function migration() { dateCreated: string; }[]; - db.prepare(`DELETE FROM 'webauthnCredentials';`).run(); + db.prepare(`DELETE FROM 'webauthnCredentials';`).run(); for (const webauthnCredential of webauthnCredentials) { const newCredentialId = isoBase64URL.fromBuffer( @@ -304,7 +345,9 @@ export default async function migration() { ).run(); // 2. Select all rows - const resources = db.prepare(`SELECT resourceId FROM resources`).all() as { + const resources = db + .prepare(`SELECT resourceId FROM resources`) + .all() as { resourceId: number; }[]; diff --git a/server/setup/scriptsSqlite/1.12.0.ts b/server/setup/scriptsSqlite/1.12.0.ts index bb357c81..292f1f05 100644 --- a/server/setup/scriptsSqlite/1.12.0.ts +++ b/server/setup/scriptsSqlite/1.12.0.ts @@ -112,7 +112,6 @@ export default async function migration() { ` ).run(); - db.prepare( ` CREATE TABLE 'blueprints' ( @@ -212,10 +211,14 @@ export default async function migration() { db.prepare( `ALTER TABLE 'user' ADD 'lastPasswordChange' integer;` ).run(); - db.prepare(`ALTER TABLE 'remoteExitNode' ADD 'secondaryVersion' text;`).run(); + db.prepare( + `ALTER TABLE 'remoteExitNode' ADD 'secondaryVersion' text;` + ).run(); // get all of the domains - const domains = db.prepare(`SELECT domainId, baseDomain from domains`).all() as { + const domains = db + .prepare(`SELECT domainId, baseDomain from domains`) + .all() as { domainId: number; baseDomain: string; }[]; diff --git a/server/setup/scriptsSqlite/1.13.0.ts b/server/setup/scriptsSqlite/1.13.0.ts index 5b2bcf01..df8d7344 100644 --- a/server/setup/scriptsSqlite/1.13.0.ts +++ b/server/setup/scriptsSqlite/1.13.0.ts @@ -287,7 +287,10 @@ export default async function migration() { let aliasIpOctet = 8; for (const siteResource of siteResourcesForAlias) { const aliasAddress = `100.96.128.${aliasIpOctet}`; - updateAliasAddress.run(aliasAddress, siteResource.siteResourceId); + updateAliasAddress.run( + aliasAddress, + siteResource.siteResourceId + ); aliasIpOctet++; } @@ -303,7 +306,12 @@ export default async function migration() { for (const subnet of subnets) { // Generate a unique niceId for each new site resource let niceId = generateName(); - insertCidrResource.run(site.siteId, subnet.trim(), niceId, site.siteId); + insertCidrResource.run( + site.siteId, + subnet.trim(), + niceId, + site.siteId + ); } } } diff --git a/server/setup/scriptsSqlite/1.5.0.ts b/server/setup/scriptsSqlite/1.5.0.ts index 46e9ccca..10c12294 100644 --- a/server/setup/scriptsSqlite/1.5.0.ts +++ b/server/setup/scriptsSqlite/1.5.0.ts @@ -48,9 +48,7 @@ export default async function migration() { const rawConfig = yaml.load(fileContents) as any; if (rawConfig.cors?.headers) { - const headers = JSON.parse( - JSON.stringify(rawConfig.cors.headers) - ); + const headers = JSON.parse(JSON.stringify(rawConfig.cors.headers)); rawConfig.cors.allowed_headers = headers; delete rawConfig.cors.headers; } @@ -61,9 +59,7 @@ export default async function migration() { console.log(`Migrated CORS headers to allowed_headers`); } catch (e) { - console.log( - `Unable to migrate config file. Error: ${e}` - ); + console.log(`Unable to migrate config file. Error: ${e}`); } console.log(`${version} migration complete`); diff --git a/server/setup/scriptsSqlite/1.6.0.ts b/server/setup/scriptsSqlite/1.6.0.ts index adab2697..45abe693 100644 --- a/server/setup/scriptsSqlite/1.6.0.ts +++ b/server/setup/scriptsSqlite/1.6.0.ts @@ -58,7 +58,9 @@ export default async function migration() { console.log(`Set trust_proxy to 1 in config file`); } catch (e) { - console.log(`Unable to migrate config file. Please do it manually. Error: ${e}`); + console.log( + `Unable to migrate config file. Please do it manually. Error: ${e}` + ); } console.log(`${version} migration complete`); diff --git a/server/setup/scriptsSqlite/1.9.0.ts b/server/setup/scriptsSqlite/1.9.0.ts index 5f247ea5..89d7b595 100644 --- a/server/setup/scriptsSqlite/1.9.0.ts +++ b/server/setup/scriptsSqlite/1.9.0.ts @@ -11,26 +11,28 @@ export default async function migration() { const db = new Database(location); const resourceSiteMap = new Map(); - let firstSiteId: number = 1; + let firstSiteId: number = 1; - try { - // Get the first siteId to use as default - const firstSite = db.prepare("SELECT siteId FROM sites LIMIT 1").get() as { siteId: number } | undefined; - if (firstSite) { - firstSiteId = firstSite.siteId; - } + try { + // Get the first siteId to use as default + const firstSite = db + .prepare("SELECT siteId FROM sites LIMIT 1") + .get() as { siteId: number } | undefined; + if (firstSite) { + firstSiteId = firstSite.siteId; + } - const resources = db - .prepare( - "SELECT resourceId, siteId FROM resources WHERE siteId IS NOT NULL" - ) - .all() as Array<{ resourceId: number; siteId: number }>; - for (const resource of resources) { - resourceSiteMap.set(resource.resourceId, resource.siteId); - } - } catch (e) { - console.log("Error getting resources:", e); - } + const resources = db + .prepare( + "SELECT resourceId, siteId FROM resources WHERE siteId IS NOT NULL" + ) + .all() as Array<{ resourceId: number; siteId: number }>; + for (const resource of resources) { + resourceSiteMap.set(resource.resourceId, resource.siteId); + } + } catch (e) { + console.log("Error getting resources:", e); + } try { db.pragma("foreign_keys = OFF"); diff --git a/server/types/HttpCode.ts b/server/types/HttpCode.ts index 70f21053..a20c8577 100644 --- a/server/types/HttpCode.ts +++ b/server/types/HttpCode.ts @@ -59,7 +59,7 @@ export enum HttpCode { INSUFFICIENT_STORAGE = 507, LOOP_DETECTED = 508, NOT_EXTENDED = 510, - NETWORK_AUTHENTICATION_REQUIRED = 511, + NETWORK_AUTHENTICATION_REQUIRED = 511 } export default HttpCode; diff --git a/src/app/[orgId]/settings/(private)/billing/layout.tsx b/src/app/[orgId]/settings/(private)/billing/layout.tsx index 538c7fde..e52f19ed 100644 --- a/src/app/[orgId]/settings/(private)/billing/layout.tsx +++ b/src/app/[orgId]/settings/(private)/billing/layout.tsx @@ -10,7 +10,7 @@ import { GetOrgUserResponse } from "@server/routers/user"; import { AxiosResponse } from "axios"; import { redirect } from "next/navigation"; import { cache } from "react"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; type BillingSettingsProps = { children: React.ReactNode; @@ -19,7 +19,7 @@ type BillingSettingsProps = { export default async function BillingSettingsPage({ children, - params, + params }: BillingSettingsProps) { const { orgId } = await params; @@ -35,8 +35,8 @@ export default async function BillingSettingsPage({ const getOrgUser = cache(async () => internal.get>( `/org/${orgId}/user/${user.userId}`, - await authCookieHeader(), - ), + await authCookieHeader() + ) ); const res = await getOrgUser(); orgUser = res.data.data; @@ -49,8 +49,8 @@ export default async function BillingSettingsPage({ const getOrg = cache(async () => internal.get>( `/org/${orgId}`, - await authCookieHeader(), - ), + await authCookieHeader() + ) ); const res = await getOrg(); org = res.data.data; @@ -65,11 +65,11 @@ export default async function BillingSettingsPage({ - {children} + {children} diff --git a/src/app/[orgId]/settings/(private)/idp/create/page.tsx b/src/app/[orgId]/settings/(private)/idp/create/page.tsx index 8667abda..a899a2aa 100644 --- a/src/app/[orgId]/settings/(private)/idp/create/page.tsx +++ b/src/app/[orgId]/settings/(private)/idp/create/page.tsx @@ -64,10 +64,8 @@ export default function Page() { clientSecret: z .string() .min(1, { message: t("idpClientSecretRequired") }), - authUrl: z.url({ message: t("idpErrorAuthUrlInvalid") }) - .optional(), - tokenUrl: z.url({ message: t("idpErrorTokenUrlInvalid") }) - .optional(), + authUrl: z.url({ message: t("idpErrorAuthUrlInvalid") }).optional(), + tokenUrl: z.url({ message: t("idpErrorTokenUrlInvalid") }).optional(), identifierPath: z .string() .min(1, { message: t("idpPathRequired") }) @@ -379,9 +377,11 @@ export default function Page() { > { form.setValue( "autoProvision", diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesDataTable.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesDataTable.tsx index a1bb69c0..c12aa9ba 100644 --- a/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesDataTable.tsx +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesDataTable.tsx @@ -19,18 +19,17 @@ export function ExitNodesDataTable({ onRefresh, isRefreshing }: DataTableProps) { - const t = useTranslations(); return ( { const remoteExitNodeId = searchParams.get("remoteExitNodeId"); const remoteExitNodeSecret = searchParams.get("remoteExitNodeSecret"); - + if (remoteExitNodeId && remoteExitNodeSecret) { setStrategy("adopt"); form.setValue("remoteExitNodeId", remoteExitNodeId); diff --git a/src/app/[orgId]/settings/access/invitations/page.tsx b/src/app/[orgId]/settings/access/invitations/page.tsx index d7fee322..b6ee1448 100644 --- a/src/app/[orgId]/settings/access/invitations/page.tsx +++ b/src/app/[orgId]/settings/access/invitations/page.tsx @@ -1,7 +1,9 @@ import { internal } from "@app/lib/api"; import { authCookieHeader } from "@app/lib/api/cookies"; import { AxiosResponse } from "axios"; -import InvitationsTable, { InvitationRow } from "../../../../../components/InvitationsTable"; +import InvitationsTable, { + InvitationRow +} from "../../../../../components/InvitationsTable"; import { GetOrgResponse } from "@server/routers/org"; import { cache } from "react"; import OrgProvider from "@app/providers/OrgProvider"; @@ -9,7 +11,7 @@ import UserProvider from "@app/providers/UserProvider"; import { verifySession } from "@app/lib/auth/verifySession"; import AccessPageHeaderAndNav from "../../../../../components/AccessPageHeaderAndNav"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; type InvitationsPageProps = { params: Promise<{ orgId: string }>; @@ -68,7 +70,7 @@ export default async function InvitationsPage(props: InvitationsPageProps) { id: invite.inviteId, email: invite.email, expiresAt: new Date(Number(invite.expiresAt)).toISOString(), - role: invite.roleName || t('accessRoleUnknown'), + role: invite.roleName || t("accessRoleUnknown"), roleId: invite.roleId }; }); @@ -76,8 +78,8 @@ export default async function InvitationsPage(props: InvitationsPageProps) { return ( <> diff --git a/src/app/[orgId]/settings/access/roles/page.tsx b/src/app/[orgId]/settings/access/roles/page.tsx index cffe4ed9..c4818abe 100644 --- a/src/app/[orgId]/settings/access/roles/page.tsx +++ b/src/app/[orgId]/settings/access/roles/page.tsx @@ -7,7 +7,7 @@ import OrgProvider from "@app/providers/OrgProvider"; import { ListRolesResponse } from "@server/routers/role"; import RolesTable, { RoleRow } from "../../../../../components/RolesTable"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; type RolesPageProps = { params: Promise<{ orgId: string }>; @@ -66,8 +66,8 @@ export default async function RolesPage(props: RolesPageProps) { return ( <> diff --git a/src/app/[orgId]/settings/access/users/create/page.tsx b/src/app/[orgId]/settings/access/users/create/page.tsx index 9417282d..3199d817 100644 --- a/src/app/[orgId]/settings/access/users/create/page.tsx +++ b/src/app/[orgId]/settings/access/users/create/page.tsx @@ -106,7 +106,8 @@ export default function Page() { const genericOidcFormSchema = z.object({ username: z.string().min(1, { message: t("usernameRequired") }), - email: z.email({ message: t("emailInvalid") }) + email: z + .email({ message: t("emailInvalid") }) .optional() .or(z.literal("")), name: z.string().optional(), diff --git a/src/app/[orgId]/settings/access/users/page.tsx b/src/app/[orgId]/settings/access/users/page.tsx index 453aca2f..662ada60 100644 --- a/src/app/[orgId]/settings/access/users/page.tsx +++ b/src/app/[orgId]/settings/access/users/page.tsx @@ -10,7 +10,7 @@ import UserProvider from "@app/providers/UserProvider"; import { verifySession } from "@app/lib/auth/verifySession"; import AccessPageHeaderAndNav from "../../../../../components/AccessPageHeaderAndNav"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; type UsersPageProps = { params: Promise<{ orgId: string }>; @@ -79,9 +79,11 @@ export default async function UsersPage(props: UsersPageProps) { type: user.type, idpVariant: user.idpVariant, idpId: user.idpId, - idpName: user.idpName || t('idpNameInternal'), - status: t('userConfirmed'), - role: user.isOwner ? t('accessRoleOwner') : user.roleName || t('accessRoleMember'), + idpName: user.idpName || t("idpNameInternal"), + status: t("userConfirmed"), + role: user.isOwner + ? t("accessRoleOwner") + : user.roleName || t("accessRoleMember"), isOwner: user.isOwner || false }; }); @@ -89,8 +91,8 @@ export default async function UsersPage(props: UsersPageProps) { return ( <> diff --git a/src/app/[orgId]/settings/api-keys/[apiKeyId]/layout.tsx b/src/app/[orgId]/settings/api-keys/[apiKeyId]/layout.tsx index e012f332..19b695ca 100644 --- a/src/app/[orgId]/settings/api-keys/[apiKeyId]/layout.tsx +++ b/src/app/[orgId]/settings/api-keys/[apiKeyId]/layout.tsx @@ -6,7 +6,7 @@ import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; import { GetApiKeyResponse } from "@server/routers/apiKeys"; import ApiKeyProvider from "@app/providers/ApiKeyProvider"; import { HorizontalTabs } from "@app/components/HorizontalTabs"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; interface SettingsLayoutProps { children: React.ReactNode; @@ -33,14 +33,16 @@ export default async function SettingsLayout(props: SettingsLayoutProps) { const navItems = [ { - title: t('apiKeysPermissionsTitle'), + title: t("apiKeysPermissionsTitle"), href: "/{orgId}/settings/api-keys/{apiKeyId}/permissions" } ]; return ( <> - + {children} diff --git a/src/app/[orgId]/settings/api-keys/[apiKeyId]/page.tsx b/src/app/[orgId]/settings/api-keys/[apiKeyId]/page.tsx index e54f442d..518db250 100644 --- a/src/app/[orgId]/settings/api-keys/[apiKeyId]/page.tsx +++ b/src/app/[orgId]/settings/api-keys/[apiKeyId]/page.tsx @@ -4,5 +4,7 @@ export default async function ApiKeysPage(props: { params: Promise<{ orgId: string; apiKeyId: string }>; }) { const params = await props.params; - redirect(`/${params.orgId}/settings/api-keys/${params.apiKeyId}/permissions`); + redirect( + `/${params.orgId}/settings/api-keys/${params.apiKeyId}/permissions` + ); } diff --git a/src/app/[orgId]/settings/api-keys/[apiKeyId]/permissions/page.tsx b/src/app/[orgId]/settings/api-keys/[apiKeyId]/permissions/page.tsx index 121d9523..da63c9c5 100644 --- a/src/app/[orgId]/settings/api-keys/[apiKeyId]/permissions/page.tsx +++ b/src/app/[orgId]/settings/api-keys/[apiKeyId]/permissions/page.tsx @@ -45,10 +45,10 @@ export default function Page() { .catch((e) => { toast({ variant: "destructive", - title: t('apiKeysPermissionsErrorLoadingActions'), + title: t("apiKeysPermissionsErrorLoadingActions"), description: formatAxiosError( e, - t('apiKeysPermissionsErrorLoadingActions') + t("apiKeysPermissionsErrorLoadingActions") ) }); }); @@ -79,18 +79,18 @@ export default function Page() { ) }) .catch((e) => { - console.error(t('apiKeysErrorSetPermission'), e); + console.error(t("apiKeysErrorSetPermission"), e); toast({ variant: "destructive", - title: t('apiKeysErrorSetPermission'), + title: t("apiKeysErrorSetPermission"), description: formatAxiosError(e) }); }); if (actionsRes && actionsRes.status === 200) { toast({ - title: t('apiKeysPermissionsUpdated'), - description: t('apiKeysPermissionsUpdatedDescription') + title: t("apiKeysPermissionsUpdated"), + description: t("apiKeysPermissionsUpdatedDescription") }); } @@ -104,10 +104,12 @@ export default function Page() { - {t('apiKeysPermissionsGeneralSettings')} + {t("apiKeysPermissionsGeneralSettings")} - {t('apiKeysPermissionsGeneralSettingsDescription')} + {t( + "apiKeysPermissionsGeneralSettingsDescription" + )} @@ -124,7 +126,7 @@ export default function Page() { loading={loadingSavePermissions} disabled={loadingSavePermissions} > - {t('apiKeysPermissionsSave')} + {t("apiKeysPermissionsSave")} diff --git a/src/app/[orgId]/settings/api-keys/create/page.tsx b/src/app/[orgId]/settings/api-keys/create/page.tsx index b62c2628..fa062ba1 100644 --- a/src/app/[orgId]/settings/api-keys/create/page.tsx +++ b/src/app/[orgId]/settings/api-keys/create/page.tsx @@ -66,10 +66,10 @@ export default function Page() { name: z .string() .min(2, { - message: t('nameMin', {len: 2}) + message: t("nameMin", { len: 2 }) }) .max(255, { - message: t('nameMax', {len: 255}) + message: t("nameMax", { len: 255 }) }) }); @@ -84,7 +84,7 @@ export default function Page() { return data.copied; }, { - message: t('apiKeysConfirmCopy2'), + message: t("apiKeysConfirmCopy2"), path: ["copied"] } ); @@ -119,7 +119,7 @@ export default function Page() { .catch((e) => { toast({ variant: "destructive", - title: t('apiKeysErrorCreate'), + title: t("apiKeysErrorCreate"), description: formatAxiosError(e) }); }); @@ -140,10 +140,10 @@ export default function Page() { ) }) .catch((e) => { - console.error(t('apiKeysErrorSetPermission'), e); + console.error(t("apiKeysErrorSetPermission"), e); toast({ variant: "destructive", - title: t('apiKeysErrorSetPermission'), + title: t("apiKeysErrorSetPermission"), description: formatAxiosError(e) }); }); @@ -182,8 +182,8 @@ export default function Page() { <>
@@ -203,7 +203,7 @@ export default function Page() { - {t('apiKeysTitle')} + {t("apiKeysTitle")} @@ -224,7 +224,7 @@ export default function Page() { render={({ field }) => ( - {t('name')} + {t("name")} - {t('apiKeysGeneralSettings')} + {t("apiKeysGeneralSettings")} - {t('apiKeysGeneralSettingsDescription')} + {t( + "apiKeysGeneralSettingsDescription" + )} @@ -267,14 +269,14 @@ export default function Page() { - {t('apiKeysList')} + {t("apiKeysList")} - {t('name')} + {t("name")} - {t('created')} + {t("created")} {moment( @@ -297,10 +299,10 @@ export default function Page() { - {t('apiKeysSave')} + {t("apiKeysSave")} - {t('apiKeysSaveDescription')} + {t("apiKeysSaveDescription")} @@ -367,7 +369,7 @@ export default function Page() { router.push(`/${orgId}/settings/api-keys`); }} > - {t('cancel')} + {t("cancel")} )} {!apiKey && ( @@ -379,7 +381,7 @@ export default function Page() { form.handleSubmit(onSubmit)(); }} > - {t('generate')} + {t("generate")} )} @@ -390,7 +392,7 @@ export default function Page() { copiedForm.handleSubmit(onCopiedSubmit)(); }} > - {t('done')} + {t("done")} )} diff --git a/src/app/[orgId]/settings/api-keys/page.tsx b/src/app/[orgId]/settings/api-keys/page.tsx index ca526a7d..2973bb54 100644 --- a/src/app/[orgId]/settings/api-keys/page.tsx +++ b/src/app/[orgId]/settings/api-keys/page.tsx @@ -2,9 +2,11 @@ import { internal } from "@app/lib/api"; import { authCookieHeader } from "@app/lib/api/cookies"; import { AxiosResponse } from "axios"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; -import OrgApiKeysTable, { OrgApiKeyRow } from "../../../../components/OrgApiKeysTable"; +import OrgApiKeysTable, { + OrgApiKeyRow +} from "../../../../components/OrgApiKeysTable"; import { ListOrgApiKeysResponse } from "@server/routers/apiKeys"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; type ApiKeyPageProps = { params: Promise<{ orgId: string }>; @@ -37,8 +39,8 @@ export default async function ApiKeysPage(props: ApiKeyPageProps) { return ( <> diff --git a/src/app/[orgId]/settings/clients/machine/[niceId]/layout.tsx b/src/app/[orgId]/settings/clients/machine/[niceId]/layout.tsx index f8a9610e..145fb172 100644 --- a/src/app/[orgId]/settings/clients/machine/[niceId]/layout.tsx +++ b/src/app/[orgId]/settings/clients/machine/[niceId]/layout.tsx @@ -21,7 +21,10 @@ export default async function SettingsLayout(props: SettingsLayoutProps) { let client = null; try { - console.log("making request to ", `/org/${params.orgId}/client/${params.niceId}`); + console.log( + "making request to ", + `/org/${params.orgId}/client/${params.niceId}` + ); const res = await internal.get>( `/org/${params.orgId}/client/${params.niceId}`, await authCookieHeader() diff --git a/src/app/[orgId]/settings/general/layout.tsx b/src/app/[orgId]/settings/general/layout.tsx index 82b2c999..8c8efa59 100644 --- a/src/app/[orgId]/settings/general/layout.tsx +++ b/src/app/[orgId]/settings/general/layout.tsx @@ -10,7 +10,7 @@ import { GetOrgUserResponse } from "@server/routers/user"; import { AxiosResponse } from "axios"; import { redirect } from "next/navigation"; import { cache } from "react"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; type GeneralSettingsProps = { children: React.ReactNode; @@ -19,7 +19,7 @@ type GeneralSettingsProps = { export default async function GeneralSettingsPage({ children, - params, + params }: GeneralSettingsProps) { const { orgId } = await params; @@ -35,8 +35,8 @@ export default async function GeneralSettingsPage({ const getOrgUser = cache(async () => internal.get>( `/org/${orgId}/user/${user.userId}`, - await authCookieHeader(), - ), + await authCookieHeader() + ) ); const res = await getOrgUser(); orgUser = res.data.data; @@ -49,8 +49,8 @@ export default async function GeneralSettingsPage({ const getOrg = cache(async () => internal.get>( `/org/${orgId}`, - await authCookieHeader(), - ), + await authCookieHeader() + ) ); const res = await getOrg(); org = res.data.data; @@ -62,9 +62,9 @@ export default async function GeneralSettingsPage({ const navItems = [ { - title: t('general'), - href: `/{orgId}/settings/general`, - }, + title: t("general"), + href: `/{orgId}/settings/general` + } ]; return ( @@ -72,13 +72,11 @@ export default async function GeneralSettingsPage({ - - {children} - + {children} diff --git a/src/app/[orgId]/settings/logs/request/page.tsx b/src/app/[orgId]/settings/logs/request/page.tsx index b2f9bab4..42cfec57 100644 --- a/src/app/[orgId]/settings/logs/request/page.tsx +++ b/src/app/[orgId]/settings/logs/request/page.tsx @@ -6,7 +6,11 @@ import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { useParams, useRouter, useSearchParams } from "next/navigation"; import { useTranslations } from "next-intl"; -import { getStoredPageSize, LogDataTable, setStoredPageSize } from "@app/components/LogDataTable"; +import { + getStoredPageSize, + LogDataTable, + setStoredPageSize +} from "@app/components/LogDataTable"; import { ColumnDef } from "@tanstack/react-table"; import { DateTimeValue } from "@app/components/DateTimePicker"; import { Key, RouteOff, User, Lock, Unlock, ArrowUpRight } from "lucide-react"; @@ -757,8 +761,8 @@ export default function GeneralPage() { return ( <> {authInfo.headerAuth - ? t("resourceHeaderAuthProtectionEnabled") + ? t( + "resourceHeaderAuthProtectionEnabled" + ) : t( "resourceHeaderAuthProtectionDisabled" )} @@ -921,7 +923,8 @@ export default function ResourceAuthenticationPage() { validateTag={( tag ) => { - return z.email() + return z + .email() .or( z .string() diff --git a/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx b/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx index 3d9aa977..fa9a6976 100644 --- a/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx +++ b/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx @@ -104,7 +104,7 @@ export default function GeneralForm() { name: z.string().min(1).max(255), niceId: z.string().min(1).max(255).optional(), domainId: z.string().optional(), - proxyPort: z.int().min(1).max(65535).optional(), + proxyPort: z.int().min(1).max(65535).optional() // enableProxy: z.boolean().optional() }) .refine( @@ -134,7 +134,7 @@ export default function GeneralForm() { niceId: resource.niceId, subdomain: resource.subdomain ? resource.subdomain : undefined, domainId: resource.domainId || undefined, - proxyPort: resource.proxyPort || undefined, + proxyPort: resource.proxyPort || undefined // enableProxy: resource.enableProxy || false }, mode: "onChange" @@ -168,7 +168,7 @@ export default function GeneralForm() { const rawDomains = res.data.data.domains as DomainRow[]; const domains = rawDomains.map((domain) => ({ ...domain, - baseDomain: toUnicode(domain.baseDomain), + baseDomain: toUnicode(domain.baseDomain) })); setBaseDomains(domains); setFormKey((key) => key + 1); @@ -195,9 +195,11 @@ export default function GeneralForm() { enabled: data.enabled, name: data.name, niceId: data.niceId, - subdomain: data.subdomain ? toASCII(data.subdomain) : undefined, + subdomain: data.subdomain + ? toASCII(data.subdomain) + : undefined, domainId: data.domainId, - proxyPort: data.proxyPort, + proxyPort: data.proxyPort // ...(!resource.http && { // enableProxy: data.enableProxy // }) @@ -223,7 +225,7 @@ export default function GeneralForm() { niceId: data.niceId, subdomain: data.subdomain, fullDomain: resource.fullDomain, - proxyPort: data.proxyPort, + proxyPort: data.proxyPort // ...(!resource.http && { // enableProxy: data.enableProxy // }) @@ -235,7 +237,9 @@ export default function GeneralForm() { }); if (data.niceId && data.niceId !== resource?.niceId) { - router.replace(`/${updated.orgId}/settings/resources/proxy/${data.niceId}/general`); + router.replace( + `/${updated.orgId}/settings/resources/proxy/${data.niceId}/general` + ); } else { router.refresh(); } @@ -320,11 +324,15 @@ export default function GeneralForm() { name="niceId" render={({ field }) => ( - {t("identifier")} + + {t("identifier")} + @@ -360,10 +368,10 @@ export default function GeneralForm() { .target .value ? parseInt( - e - .target - .value - ) + e + .target + .value + ) : undefined ) } @@ -498,17 +506,29 @@ export default function GeneralForm() { ); @@ -440,15 +449,18 @@ export default function ResourceRules(props: { type="number" onClick={(e) => e.currentTarget.focus()} onBlur={(e) => { - const parsed = z.int() + const parsed = z + .int() .optional() .safeParse(e.target.value); if (!parsed.data) { toast({ variant: "destructive", - title: t('rulesErrorInvalidIpAddress'), // correct priority or IP? - description: t('rulesErrorInvalidPriorityDescription') + title: t("rulesErrorInvalidIpAddress"), // correct priority or IP? + description: t( + "rulesErrorInvalidPriorityDescription" + ) }); setLoading(false); return; @@ -463,7 +475,7 @@ export default function ResourceRules(props: { }, { accessorKey: "action", - header: () => ({t('rulesAction')}), + header: () => {t("rulesAction")}, cell: ({ row }) => ( - updateRule(row.original.ruleId, { match: value, value: value === "COUNTRY" ? "US" : row.original.value }) + onValueChange={( + value: "CIDR" | "IP" | "PATH" | "COUNTRY" + ) => + updateRule(row.original.ruleId, { + match: value, + value: + value === "COUNTRY" ? "US" : row.original.value + }) } > @@ -502,7 +520,9 @@ export default function ResourceRules(props: { {RuleMatch.IP} {RuleMatch.CIDR} {isMaxmindAvailable && ( - {RuleMatch.COUNTRY} + + {RuleMatch.COUNTRY} + )} @@ -510,8 +530,8 @@ export default function ResourceRules(props: { }, { accessorKey: "value", - header: () => ({t('value')}), - cell: ({ row }) => ( + header: () => {t("value")}, + cell: ({ row }) => row.original.match === "COUNTRY" ? ( @@ -521,29 +541,43 @@ export default function ResourceRules(props: { className="min-w-[200px] justify-between" > {row.original.value - ? COUNTRIES.find((country) => country.code === row.original.value)?.name + - " (" + row.original.value + ")" - : t('selectCountry')} + ? COUNTRIES.find( + (country) => + country.code === + row.original.value + )?.name + + " (" + + row.original.value + + ")" + : t("selectCountry")} - + - {t('noCountryFound')} + + {t("noCountryFound")} + {COUNTRIES.map((country) => ( { - updateRule(row.original.ruleId, { value: country.code }); + updateRule( + row.original.ruleId, + { value: country.code } + ); }} > ) - ) }, { accessorKey: "enabled", - header: () => ({t('enabled')}), + header: () => {t("enabled")}, cell: ({ row }) => ( ({t('actions')}), + header: () => {t("actions")}, cell: ({ row }) => (
) @@ -664,10 +697,10 @@ export default function ResourceRules(props: { - {t('rulesResource')} + {t("rulesResource")} - {t('rulesResourceDescription')} + {t("rulesResourceDescription")} @@ -675,7 +708,7 @@ export default function ResourceRules(props: {
setRulesEnabled(val)} /> @@ -692,7 +725,9 @@ export default function ResourceRules(props: { name="action" render={({ field }) => ( - {t('rulesAction')} + + {t("rulesAction")} + @@ -725,11 +766,15 @@ export default function ResourceRules(props: { name="match" render={({ field }) => ( - {t('rulesMatchType')} + + {t("rulesMatchType")} + diff --git a/src/app/[orgId]/settings/sites/[niceId]/wireguardConfig.ts b/src/app/[orgId]/settings/sites/[niceId]/wireguardConfig.ts index 01157a68..5e3d1281 100644 --- a/src/app/[orgId]/settings/sites/[niceId]/wireguardConfig.ts +++ b/src/app/[orgId]/settings/sites/[niceId]/wireguardConfig.ts @@ -6,16 +6,16 @@ function gf(init: number[] | undefined = undefined) { var r = new Float64Array(16); if (init) { - for (var i = 0; i < init.length; ++i) - r[i] = init[i]; + for (var i = 0; i < init.length; ++i) r[i] = init[i]; } return r; } function pack(o: Uint8Array, n: Float64Array) { - var b, m = gf(), t = gf(); - for (var i = 0; i < 16; ++i) - t[i] = n[i]; + var b, + m = gf(), + t = gf(); + for (var i = 0; i < 16; ++i) t[i] = n[i]; carry(t); carry(t); carry(t); @@ -45,7 +45,8 @@ function carry(o: Float64Array) { } function cswap(p: Float64Array, q: Float64Array, b: number) { - var t, c = ~(b - 1); + var t, + c = ~(b - 1); for (var i = 0; i < 16; ++i) { t = c & (p[i] ^ q[i]); p[i] ^= t; @@ -54,40 +55,32 @@ function cswap(p: Float64Array, q: Float64Array, b: number) { } function add(o: Float64Array, a: Float64Array, b: Float64Array) { - for (var i = 0; i < 16; ++i) - o[i] = (a[i] + b[i]) | 0; + for (var i = 0; i < 16; ++i) o[i] = (a[i] + b[i]) | 0; } function subtract(o: Float64Array, a: Float64Array, b: Float64Array) { - for (var i = 0; i < 16; ++i) - o[i] = (a[i] - b[i]) | 0; + for (var i = 0; i < 16; ++i) o[i] = (a[i] - b[i]) | 0; } function multmod(o: Float64Array, a: Float64Array, b: Float64Array) { var t = new Float64Array(31); for (var i = 0; i < 16; ++i) { - for (var j = 0; j < 16; ++j) - t[i + j] += a[i] * b[j]; + for (var j = 0; j < 16; ++j) t[i + j] += a[i] * b[j]; } - for (var i = 0; i < 15; ++i) - t[i] += 38 * t[i + 16]; - for (var i = 0; i < 16; ++i) - o[i] = t[i]; + for (var i = 0; i < 15; ++i) t[i] += 38 * t[i + 16]; + for (var i = 0; i < 16; ++i) o[i] = t[i]; carry(o); carry(o); } function invert(o: Float64Array, i: Float64Array) { var c = gf(); - for (var a = 0; a < 16; ++a) - c[a] = i[a]; + for (var a = 0; a < 16; ++a) c[a] = i[a]; for (var a = 253; a >= 0; --a) { multmod(c, c, c); - if (a !== 2 && a !== 4) - multmod(c, c, i); + if (a !== 2 && a !== 4) multmod(c, c, i); } - for (var a = 0; a < 16; ++a) - o[a] = c[a]; + for (var a = 0; a < 16; ++a) o[a] = c[a]; } function clamp(z: Uint8Array) { @@ -96,7 +89,8 @@ function clamp(z: Uint8Array) { } function generatePublicKey(privateKey: Uint8Array) { - var r, z = new Uint8Array(32); + var r, + z = new Uint8Array(32); var a = gf([1]), b = gf([9]), c = gf(), @@ -105,8 +99,7 @@ function generatePublicKey(privateKey: Uint8Array) { f = gf(), _121665 = gf([0xdb41, 1]), _9 = gf([9]); - for (var i = 0; i < 32; ++i) - z[i] = privateKey[i]; + for (var i = 0; i < 32; ++i) z[i] = privateKey[i]; clamp(z); for (var i = 254; i >= 0; --i) { r = (z[i >>> 3] >>> (i & 7)) & 1; @@ -152,9 +145,16 @@ function generatePrivateKey() { } function encodeBase64(dest: Uint8Array, src: Uint8Array) { - var input = Uint8Array.from([(src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63]); + var input = Uint8Array.from([ + (src[0] >> 2) & 63, + ((src[0] << 4) | (src[1] >> 4)) & 63, + ((src[1] << 2) | (src[2] >> 6)) & 63, + src[2] & 63 + ]); for (var i = 0; i < 4; ++i) - dest[i] = input[i] + 65 + + dest[i] = + input[i] + + 65 + (((25 - input[i]) >> 8) & 6) - (((51 - input[i]) >> 8) & 75) - (((61 - input[i]) >> 8) & 15) + @@ -162,10 +162,14 @@ function encodeBase64(dest: Uint8Array, src: Uint8Array) { } function keyToBase64(key: Uint8Array) { - var i, base64 = new Uint8Array(44); + var i, + base64 = new Uint8Array(44); for (i = 0; i < 32 / 3; ++i) encodeBase64(base64.subarray(i * 4), key.subarray(i * 3)); - encodeBase64(base64.subarray(i * 4), Uint8Array.from([key[i * 3 + 0], key[i * 3 + 1], 0])); + encodeBase64( + base64.subarray(i * 4), + Uint8Array.from([key[i * 3 + 0], key[i * 3 + 1], 0]) + ); base64[43] = 61; return String.fromCharCode.apply(null, base64 as any); } @@ -177,4 +181,4 @@ export function generateKeypair() { publicKey: keyToBase64(publicKey), privateKey: keyToBase64(privateKey) }; -} \ No newline at end of file +} diff --git a/src/app/[orgId]/settings/sites/create/page.tsx b/src/app/[orgId]/settings/sites/create/page.tsx index f4ea4f05..6395c265 100644 --- a/src/app/[orgId]/settings/sites/create/page.tsx +++ b/src/app/[orgId]/settings/sites/create/page.tsx @@ -215,7 +215,6 @@ export default function Page() { string | undefined >(); - const hydrateCommands = ( id: string, secret: string, @@ -753,7 +752,9 @@ WantedBy=default.target` {tunnelTypes.length > 1 && ( <>
- {t("type")} + + {t("type")} +
= 1024 * 1024) { - return t('terabytes', {count: (mb / (1024 * 1024)).toFixed(2)}); + return t("terabytes", { count: (mb / (1024 * 1024)).toFixed(2) }); } else if (mb >= 1024) { - return t('gigabytes', {count: (mb / 1024).toFixed(2)}); + return t("gigabytes", { count: (mb / 1024).toFixed(2) }); } else { - return t('megabytes', {count: mb.toFixed(2)}); + return t("megabytes", { count: mb.toFixed(2) }); } } @@ -53,7 +53,7 @@ export default async function SitesPage(props: SitesPageProps) { newtVersion: site.newtVersion || undefined, newtUpdateAvailable: site.newtUpdateAvailable || false, exitNodeName: site.exitNodeName || undefined, - exitNodeEndpoint: site.exitNodeEndpoint || undefined, + exitNodeEndpoint: site.exitNodeEndpoint || undefined }; }); @@ -62,8 +62,8 @@ export default async function SitesPage(props: SitesPageProps) { {/* */} diff --git a/src/app/admin/api-keys/[apiKeyId]/layout.tsx b/src/app/admin/api-keys/[apiKeyId]/layout.tsx index 7e9e579f..d547a0b0 100644 --- a/src/app/admin/api-keys/[apiKeyId]/layout.tsx +++ b/src/app/admin/api-keys/[apiKeyId]/layout.tsx @@ -34,14 +34,16 @@ export default async function SettingsLayout(props: SettingsLayoutProps) { const navItems = [ { - title: t('apiKeysPermissionsTitle'), + title: t("apiKeysPermissionsTitle"), href: "/admin/api-keys/{apiKeyId}/permissions" } ]; return ( <> - + {children} diff --git a/src/app/admin/api-keys/[apiKeyId]/permissions/page.tsx b/src/app/admin/api-keys/[apiKeyId]/permissions/page.tsx index e00ae425..fad36194 100644 --- a/src/app/admin/api-keys/[apiKeyId]/permissions/page.tsx +++ b/src/app/admin/api-keys/[apiKeyId]/permissions/page.tsx @@ -45,10 +45,10 @@ export default function Page() { .catch((e) => { toast({ variant: "destructive", - title: t('apiKeysPermissionsErrorLoadingActions'), + title: t("apiKeysPermissionsErrorLoadingActions"), description: formatAxiosError( e, - t('apiKeysPermissionsErrorLoadingActions') + t("apiKeysPermissionsErrorLoadingActions") ) }); }); @@ -79,18 +79,18 @@ export default function Page() { ) }) .catch((e) => { - console.error(t('apiKeysErrorSetPermission'), e); + console.error(t("apiKeysErrorSetPermission"), e); toast({ variant: "destructive", - title: t('apiKeysErrorSetPermission'), + title: t("apiKeysErrorSetPermission"), description: formatAxiosError(e) }); }); if (actionsRes && actionsRes.status === 200) { toast({ - title: t('apiKeysPermissionsUpdated'), - description: t('apiKeysPermissionsUpdatedDescription') + title: t("apiKeysPermissionsUpdated"), + description: t("apiKeysPermissionsUpdatedDescription") }); } @@ -104,10 +104,12 @@ export default function Page() { - {t('apiKeysPermissionsTitle')} + {t("apiKeysPermissionsTitle")} - {t('apiKeysPermissionsGeneralSettingsDescription')} + {t( + "apiKeysPermissionsGeneralSettingsDescription" + )} @@ -125,7 +127,7 @@ export default function Page() { loading={loadingSavePermissions} disabled={loadingSavePermissions} > - {t('apiKeysPermissionsSave')} + {t("apiKeysPermissionsSave")} diff --git a/src/app/admin/api-keys/create/page.tsx b/src/app/admin/api-keys/create/page.tsx index 65f8e46a..083ec89d 100644 --- a/src/app/admin/api-keys/create/page.tsx +++ b/src/app/admin/api-keys/create/page.tsx @@ -30,7 +30,7 @@ import { createApiClient, formatAxiosError } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { toast } from "@app/hooks/useToast"; import { AxiosResponse } from "axios"; -import { useRouter } from "next/navigation"; +import { useRouter } from "next/navigation"; import { CreateOrgApiKeyBody, CreateOrgApiKeyResponse @@ -64,10 +64,10 @@ export default function Page() { name: z .string() .min(2, { - message: t('nameMin', {len: 2}) + message: t("nameMin", { len: 2 }) }) .max(255, { - message: t('nameMax', {len: 255}) + message: t("nameMax", { len: 255 }) }) }); @@ -82,7 +82,7 @@ export default function Page() { return data.copied; }, { - message: t('apiKeysConfirmCopy2'), + message: t("apiKeysConfirmCopy2"), path: ["copied"] } ); @@ -115,7 +115,7 @@ export default function Page() { .catch((e) => { toast({ variant: "destructive", - title: t('apiKeysErrorCreate'), + title: t("apiKeysErrorCreate"), description: formatAxiosError(e) }); }); @@ -136,10 +136,10 @@ export default function Page() { ) }) .catch((e) => { - console.error(t('apiKeysErrorSetPermission'), e); + console.error(t("apiKeysErrorSetPermission"), e); toast({ variant: "destructive", - title: t('apiKeysErrorSetPermission'), + title: t("apiKeysErrorSetPermission"), description: formatAxiosError(e) }); }); @@ -172,8 +172,8 @@ export default function Page() { <>
@@ -193,7 +193,7 @@ export default function Page() { - {t('apiKeysTitle')} + {t("apiKeysTitle")} @@ -214,7 +214,7 @@ export default function Page() { render={({ field }) => ( - {t('name')} + {t("name")} - {t('apiKeysGeneralSettings')} + {t("apiKeysGeneralSettings")} - {t('apiKeysGeneralSettingsDescription')} + {t( + "apiKeysGeneralSettingsDescription" + )} @@ -258,14 +260,14 @@ export default function Page() { - {t('apiKeysList')} + {t("apiKeysList")} - {t('name')} + {t("name")} - {t('created')} + {t("created")} {moment( @@ -288,10 +290,10 @@ export default function Page() { - {t('apiKeysSave')} + {t("apiKeysSave")} - {t('apiKeysSaveDescription')} + {t("apiKeysSaveDescription")} @@ -358,7 +360,7 @@ export default function Page() { router.push(`/admin/api-keys`); }} > - {t('cancel')} + {t("cancel")} )} {!apiKey && ( @@ -370,7 +372,7 @@ export default function Page() { form.handleSubmit(onSubmit)(); }} > - {t('generate')} + {t("generate")} )} @@ -381,7 +383,7 @@ export default function Page() { copiedForm.handleSubmit(onCopiedSubmit)(); }} > - {t('done')} + {t("done")} )}
diff --git a/src/app/admin/api-keys/page.tsx b/src/app/admin/api-keys/page.tsx index e518911f..60195c4a 100644 --- a/src/app/admin/api-keys/page.tsx +++ b/src/app/admin/api-keys/page.tsx @@ -34,8 +34,8 @@ export default async function ApiKeysPage(props: ApiKeyPageProps) { return ( <> diff --git a/src/app/admin/idp/[idpId]/general/page.tsx b/src/app/admin/idp/[idpId]/general/page.tsx index 7eae6950..d431efa2 100644 --- a/src/app/admin/idp/[idpId]/general/page.tsx +++ b/src/app/admin/idp/[idpId]/general/page.tsx @@ -58,17 +58,17 @@ export default function GeneralPage() { const t = useTranslations(); const GeneralFormSchema = z.object({ - name: z.string().min(2, { message: t('nameMin', {len: 2}) }), - clientId: z.string().min(1, { message: t('idpClientIdRequired') }), - clientSecret: z.string().min(1, { message: t('idpClientSecretRequired') }), - authUrl: z.url({ message: t('idpErrorAuthUrlInvalid') }), - tokenUrl: z.url({ message: t('idpErrorTokenUrlInvalid') }), - identifierPath: z + name: z.string().min(2, { message: t("nameMin", { len: 2 }) }), + clientId: z.string().min(1, { message: t("idpClientIdRequired") }), + clientSecret: z .string() - .min(1, { message: t('idpPathRequired') }), + .min(1, { message: t("idpClientSecretRequired") }), + authUrl: z.url({ message: t("idpErrorAuthUrlInvalid") }), + tokenUrl: z.url({ message: t("idpErrorTokenUrlInvalid") }), + identifierPath: z.string().min(1, { message: t("idpPathRequired") }), emailPath: z.string().optional(), namePath: z.string().optional(), - scopes: z.string().min(1, { message: t('idpScopeRequired') }), + scopes: z.string().min(1, { message: t("idpScopeRequired") }), autoProvision: z.boolean().default(false) }); @@ -111,7 +111,7 @@ export default function GeneralPage() { } } catch (e) { toast({ - title: t('error'), + title: t("error"), description: formatAxiosError(e), variant: "destructive" }); @@ -145,14 +145,14 @@ export default function GeneralPage() { if (res.status === 200) { toast({ - title: t('success'), - description: t('idpUpdatedDescription') + title: t("success"), + description: t("idpUpdatedDescription") }); router.refresh(); } } catch (e) { toast({ - title: t('error'), + title: t("error"), description: formatAxiosError(e), variant: "destructive" }); @@ -171,17 +171,17 @@ export default function GeneralPage() { - {t('idpTitle')} + {t("idpTitle")} - {t('idpSettingsDescription')} + {t("idpSettingsDescription")} - {t('redirectUrl')} + {t("redirectUrl")} @@ -192,10 +192,10 @@ export default function GeneralPage() { - {t('redirectUrlAbout')} + {t("redirectUrlAbout")} - {t('redirectUrlAboutDescription')} + {t("redirectUrlAboutDescription")} @@ -210,12 +210,14 @@ export default function GeneralPage() { name="name" render={({ field }) => ( - {t('name')} + + {t("name")} + - {t('idpDisplayName')} + {t("idpDisplayName")} @@ -225,7 +227,7 @@ export default function GeneralPage() {
- {t('idpAutoProvisionUsersDescription')} + {t("idpAutoProvisionUsersDescription")} @@ -250,10 +252,10 @@ export default function GeneralPage() { - {t('idpOidcConfigure')} + {t("idpOidcConfigure")} - {t('idpOidcConfigureDescription')} + {t("idpOidcConfigureDescription")} @@ -270,13 +272,15 @@ export default function GeneralPage() { render={({ field }) => ( - {t('idpClientId')} + {t("idpClientId")} - {t('idpClientIdDescription')} + {t( + "idpClientIdDescription" + )} @@ -289,7 +293,7 @@ export default function GeneralPage() { render={({ field }) => ( - {t('idpClientSecret')} + {t("idpClientSecret")} - {t('idpClientSecretDescription')} + {t( + "idpClientSecretDescription" + )} @@ -311,13 +317,15 @@ export default function GeneralPage() { render={({ field }) => ( - {t('idpAuthUrl')} + {t("idpAuthUrl")} - {t('idpAuthUrlDescription')} + {t( + "idpAuthUrlDescription" + )} @@ -330,13 +338,15 @@ export default function GeneralPage() { render={({ field }) => ( - {t('idpTokenUrl')} + {t("idpTokenUrl")} - {t('idpTokenUrlDescription')} + {t( + "idpTokenUrlDescription" + )} @@ -351,10 +361,10 @@ export default function GeneralPage() { - {t('idpToken')} + {t("idpToken")} - {t('idpTokenDescription')} + {t("idpTokenDescription")} @@ -368,17 +378,21 @@ export default function GeneralPage() { - {t('idpJmespathAbout')} + {t("idpJmespathAbout")} - {t('idpJmespathAboutDescription')} + {t( + "idpJmespathAboutDescription" + )} - {t('idpJmespathAboutDescriptionLink')}{" "} + {t( + "idpJmespathAboutDescriptionLink" + )}{" "} @@ -390,13 +404,15 @@ export default function GeneralPage() { render={({ field }) => ( - {t('idpJmespathLabel')} + {t("idpJmespathLabel")} - {t('idpJmespathLabelDescription')} + {t( + "idpJmespathLabelDescription" + )} @@ -409,13 +425,17 @@ export default function GeneralPage() { render={({ field }) => ( - {t('idpJmespathEmailPathOptional')} + {t( + "idpJmespathEmailPathOptional" + )} - {t('idpJmespathEmailPathOptionalDescription')} + {t( + "idpJmespathEmailPathOptionalDescription" + )} @@ -428,13 +448,17 @@ export default function GeneralPage() { render={({ field }) => ( - {t('idpJmespathNamePathOptional')} + {t( + "idpJmespathNamePathOptional" + )} - {t('idpJmespathNamePathOptionalDescription')} + {t( + "idpJmespathNamePathOptionalDescription" + )} @@ -447,13 +471,17 @@ export default function GeneralPage() { render={({ field }) => ( - {t('idpOidcConfigureScopes')} + {t( + "idpOidcConfigureScopes" + )} - {t('idpOidcConfigureScopesDescription')} + {t( + "idpOidcConfigureScopesDescription" + )} @@ -474,7 +502,7 @@ export default function GeneralPage() { loading={loading} disabled={loading} > - {t('saveGeneralSettings')} + {t("saveGeneralSettings")} diff --git a/src/app/admin/idp/[idpId]/layout.tsx b/src/app/admin/idp/[idpId]/layout.tsx index af64e440..d3d9cb2e 100644 --- a/src/app/admin/idp/[idpId]/layout.tsx +++ b/src/app/admin/idp/[idpId]/layout.tsx @@ -30,11 +30,11 @@ export default async function SettingsLayout(props: SettingsLayoutProps) { const navItems: HorizontalTabs = [ { - title: t('general'), + title: t("general"), href: `/admin/idp/${params.idpId}/general` }, { - title: t('orgPolicies'), + title: t("orgPolicies"), href: `/admin/idp/${params.idpId}/policies` } ]; @@ -42,8 +42,8 @@ export default async function SettingsLayout(props: SettingsLayoutProps) { return ( <>
diff --git a/src/app/admin/idp/[idpId]/policies/page.tsx b/src/app/admin/idp/[idpId]/policies/page.tsx index e3b085b4..bf17abe9 100644 --- a/src/app/admin/idp/[idpId]/policies/page.tsx +++ b/src/app/admin/idp/[idpId]/policies/page.tsx @@ -89,7 +89,7 @@ export default function PoliciesPage() { const [editingPolicy, setEditingPolicy] = useState(null); const policyFormSchema = z.object({ - orgId: z.string().min(1, { message: t('orgRequired') }), + orgId: z.string().min(1, { message: t("orgRequired") }), roleMapping: z.string().optional(), orgMapping: z.string().optional() }); @@ -133,7 +133,7 @@ export default function PoliciesPage() { } } catch (e) { toast({ - title: t('error'), + title: t("error"), description: formatAxiosError(e), variant: "destructive" }); @@ -148,7 +148,7 @@ export default function PoliciesPage() { } } catch (e) { toast({ - title: t('error'), + title: t("error"), description: formatAxiosError(e), variant: "destructive" }); @@ -167,7 +167,7 @@ export default function PoliciesPage() { } } catch (e) { toast({ - title: t('error'), + title: t("error"), description: formatAxiosError(e), variant: "destructive" }); @@ -202,15 +202,15 @@ export default function PoliciesPage() { }; setPolicies([...policies, newPolicy]); toast({ - title: t('success'), - description: t('orgPolicyAddedDescription') + title: t("success"), + description: t("orgPolicyAddedDescription") }); setShowAddDialog(false); form.reset(); } } catch (e) { toast({ - title: t('error'), + title: t("error"), description: formatAxiosError(e), variant: "destructive" }); @@ -244,8 +244,8 @@ export default function PoliciesPage() { ) ); toast({ - title: t('success'), - description: t('orgPolicyUpdatedDescription') + title: t("success"), + description: t("orgPolicyUpdatedDescription") }); setShowAddDialog(false); setEditingPolicy(null); @@ -253,7 +253,7 @@ export default function PoliciesPage() { } } catch (e) { toast({ - title: t('error'), + title: t("error"), description: formatAxiosError(e), variant: "destructive" }); @@ -271,13 +271,13 @@ export default function PoliciesPage() { policies.filter((policy) => policy.orgId !== orgId) ); toast({ - title: t('success'), - description: t('orgPolicyDeletedDescription') + title: t("success"), + description: t("orgPolicyDeletedDescription") }); } } catch (e) { toast({ - title: t('error'), + title: t("error"), description: formatAxiosError(e), variant: "destructive" }); @@ -295,13 +295,13 @@ export default function PoliciesPage() { }); if (res.status === 200) { toast({ - title: t('success'), - description: t('defaultMappingsUpdatedDescription') + title: t("success"), + description: t("defaultMappingsUpdatedDescription") }); } } catch (e) { toast({ - title: t('error'), + title: t("error"), description: formatAxiosError(e), variant: "destructive" }); @@ -320,18 +320,18 @@ export default function PoliciesPage() { - {t('orgPoliciesAbout')} + {t("orgPoliciesAbout")} {/*TODO(vlalx): Validate replacing */} - {t('orgPoliciesAboutDescription')}{" "} + {t("orgPoliciesAboutDescription")}{" "} - {t('orgPoliciesAboutDescriptionLink')} + {t("orgPoliciesAboutDescriptionLink")} @@ -340,10 +340,10 @@ export default function PoliciesPage() { - {t('defaultMappingsOptional')} + {t("defaultMappingsOptional")} - {t('defaultMappingsOptionalDescription')} + {t("defaultMappingsOptionalDescription")} @@ -362,13 +362,15 @@ export default function PoliciesPage() { render={({ field }) => ( - {t('defaultMappingsRole')} + {t("defaultMappingsRole")} - {t('defaultMappingsRoleDescription')} + {t( + "defaultMappingsRoleDescription" + )} @@ -381,13 +383,15 @@ export default function PoliciesPage() { render={({ field }) => ( - {t('defaultMappingsOrg')} + {t("defaultMappingsOrg")} - {t('defaultMappingsOrgDescription')} + {t( + "defaultMappingsOrgDescription" + )} @@ -402,7 +406,7 @@ export default function PoliciesPage() { form="policy-default-mappings-form" loading={updateDefaultMappingsLoading} > - {t('defaultMappingsSubmit')} + {t("defaultMappingsSubmit")} @@ -445,11 +449,11 @@ export default function PoliciesPage() { {editingPolicy - ? t('orgPoliciesEdit') - : t('orgPoliciesAdd')} + ? t("orgPoliciesEdit") + : t("orgPoliciesAdd")} - {t('orgPolicyConfig')} + {t("orgPolicyConfig")} @@ -466,7 +470,7 @@ export default function PoliciesPage() { name="orgId" render={({ field }) => ( - {t('org')} + {t("org")} {editingPolicy ? ( ) : ( @@ -490,17 +494,25 @@ export default function PoliciesPage() { org.orgId === field.value )?.name - : t('orgSelect')} + : t( + "orgSelect" + )} - + - {t('orgNotFound')} + {t( + "orgNotFound" + )} {organizations.map( @@ -551,13 +563,15 @@ export default function PoliciesPage() { render={({ field }) => ( - {t('roleMappingPathOptional')} + {t("roleMappingPathOptional")} - {t('defaultMappingsRoleDescription')} + {t( + "defaultMappingsRoleDescription" + )} @@ -570,13 +584,15 @@ export default function PoliciesPage() { render={({ field }) => ( - {t('orgMappingPathOptional')} + {t("orgMappingPathOptional")} - {t('defaultMappingsOrgDescription')} + {t( + "defaultMappingsOrgDescription" + )} @@ -603,7 +619,9 @@ export default function PoliciesPage() { : addPolicyLoading } > - {editingPolicy ? t('orgPolicyUpdate') : t('orgPolicyAdd')} + {editingPolicy + ? t("orgPolicyUpdate") + : t("orgPolicyAdd")} diff --git a/src/app/admin/idp/create/page.tsx b/src/app/admin/idp/create/page.tsx index 73d605a1..dbcd5f00 100644 --- a/src/app/admin/idp/create/page.tsx +++ b/src/app/admin/idp/create/page.tsx @@ -48,18 +48,18 @@ export default function Page() { const t = useTranslations(); const createIdpFormSchema = z.object({ - name: z.string().min(2, { message: t('nameMin', {len: 2}) }), + name: z.string().min(2, { message: t("nameMin", { len: 2 }) }), type: z.enum(["oidc"]), - clientId: z.string().min(1, { message: t('idpClientIdRequired') }), - clientSecret: z.string().min(1, { message: t('idpClientSecretRequired') }), - authUrl: z.url({ message: t('idpErrorAuthUrlInvalid') }), - tokenUrl: z.url({ message: t('idpErrorTokenUrlInvalid') }), - identifierPath: z + clientId: z.string().min(1, { message: t("idpClientIdRequired") }), + clientSecret: z .string() - .min(1, { message: t('idpPathRequired') }), + .min(1, { message: t("idpClientSecretRequired") }), + authUrl: z.url({ message: t("idpErrorAuthUrlInvalid") }), + tokenUrl: z.url({ message: t("idpErrorTokenUrlInvalid") }), + identifierPath: z.string().min(1, { message: t("idpPathRequired") }), emailPath: z.string().optional(), namePath: z.string().optional(), - scopes: z.string().min(1, { message: t('idpScopeRequired') }), + scopes: z.string().min(1, { message: t("idpScopeRequired") }), autoProvision: z.boolean().default(false) }); @@ -75,7 +75,7 @@ export default function Page() { { id: "oidc", title: "OAuth2/OIDC", - description: t('idpOidcDescription') + description: t("idpOidcDescription") } ]; @@ -117,14 +117,14 @@ export default function Page() { if (res.status === 201) { toast({ - title: t('success'), - description: t('idpCreatedDescription') + title: t("success"), + description: t("idpCreatedDescription") }); router.push(`/admin/idp/${res.data.data.idpId}`); } } catch (e) { toast({ - title: t('error'), + title: t("error"), description: formatAxiosError(e), variant: "destructive" }); @@ -137,8 +137,8 @@ export default function Page() { <>
@@ -154,10 +154,10 @@ export default function Page() { - {t('idpTitle')} + {t("idpTitle")} - {t('idpCreateSettingsDescription')} + {t("idpCreateSettingsDescription")} @@ -173,12 +173,14 @@ export default function Page() { name="name" render={({ field }) => ( - {t('name')} + + {t("name")} + - {t('idpDisplayName')} + {t("idpDisplayName")} @@ -188,7 +190,7 @@ export default function Page() {
- {t('idpAutoProvisionUsersDescription')} + {t("idpAutoProvisionUsersDescription")} @@ -212,10 +214,10 @@ export default function Page() { - {t('idpType')} + {t("idpType")} - {t('idpTypeDescription')} + {t("idpTypeDescription")} @@ -235,10 +237,10 @@ export default function Page() { - {t('idpOidcConfigure')} + {t("idpOidcConfigure")} - {t('idpOidcConfigureDescription')} + {t("idpOidcConfigureDescription")} @@ -254,13 +256,15 @@ export default function Page() { render={({ field }) => ( - {t('idpClientId')} + {t("idpClientId")} - {t('idpClientIdDescription')} + {t( + "idpClientIdDescription" + )} @@ -273,7 +277,7 @@ export default function Page() { render={({ field }) => ( - {t('idpClientSecret')} + {t("idpClientSecret")} - {t('idpClientSecretDescription')} + {t( + "idpClientSecretDescription" + )} @@ -295,7 +301,7 @@ export default function Page() { render={({ field }) => ( - {t('idpAuthUrl')} + {t("idpAuthUrl")} - {t('idpAuthUrlDescription')} + {t( + "idpAuthUrlDescription" + )} @@ -317,7 +325,7 @@ export default function Page() { render={({ field }) => ( - {t('idpTokenUrl')} + {t("idpTokenUrl")} - {t('idpTokenUrlDescription')} + {t( + "idpTokenUrlDescription" + )} @@ -338,10 +348,10 @@ export default function Page() { - {t('idpOidcConfigureAlert')} + {t("idpOidcConfigureAlert")} - {t('idpOidcConfigureAlertDescription')} + {t("idpOidcConfigureAlertDescription")} @@ -350,10 +360,10 @@ export default function Page() { - {t('idpToken')} + {t("idpToken")} - {t('idpTokenDescription')} + {t("idpTokenDescription")} @@ -366,17 +376,21 @@ export default function Page() { - {t('idpJmespathAbout')} + {t("idpJmespathAbout")} - {t('idpJmespathAboutDescription')}{" "} + {t( + "idpJmespathAboutDescription" + )}{" "} - {t('idpJmespathAboutDescriptionLink')}{" "} + {t( + "idpJmespathAboutDescriptionLink" + )}{" "} @@ -388,13 +402,15 @@ export default function Page() { render={({ field }) => ( - {t('idpJmespathLabel')} + {t("idpJmespathLabel")} - {t('idpJmespathLabelDescription')} + {t( + "idpJmespathLabelDescription" + )} @@ -407,13 +423,17 @@ export default function Page() { render={({ field }) => ( - {t('idpJmespathEmailPathOptional')} + {t( + "idpJmespathEmailPathOptional" + )} - {t('idpJmespathEmailPathOptionalDescription')} + {t( + "idpJmespathEmailPathOptionalDescription" + )} @@ -426,13 +446,17 @@ export default function Page() { render={({ field }) => ( - {t('idpJmespathNamePathOptional')} + {t( + "idpJmespathNamePathOptional" + )} - {t('idpJmespathNamePathOptionalDescription')} + {t( + "idpJmespathNamePathOptionalDescription" + )} @@ -445,13 +469,17 @@ export default function Page() { render={({ field }) => ( - {t('idpOidcConfigureScopes')} + {t( + "idpOidcConfigureScopes" + )} - {t('idpOidcConfigureScopesDescription')} + {t( + "idpOidcConfigureScopesDescription" + )} @@ -473,7 +501,7 @@ export default function Page() { router.push("/admin/idp"); }} > - {t('cancel')} + {t("cancel")}
diff --git a/src/app/admin/idp/page.tsx b/src/app/admin/idp/page.tsx index fef0990c..a341c046 100644 --- a/src/app/admin/idp/page.tsx +++ b/src/app/admin/idp/page.tsx @@ -22,8 +22,8 @@ export default async function IdpPage() { return ( <> diff --git a/src/app/admin/license/layout.tsx b/src/app/admin/license/layout.tsx index 6c6e8baf..56813034 100644 --- a/src/app/admin/license/layout.tsx +++ b/src/app/admin/license/layout.tsx @@ -14,4 +14,3 @@ export default async function AdminLicenseLayout(props: LayoutProps) { return props.children; } - diff --git a/src/app/admin/license/page.tsx b/src/app/admin/license/page.tsx index d01b215a..ac6d3e67 100644 --- a/src/app/admin/license/page.tsx +++ b/src/app/admin/license/page.tsx @@ -316,9 +316,7 @@ export default function LicensePage() { }} dialog={
-

- {t("licenseQuestionRemove")} -

+

{t("licenseQuestionRemove")}

{t("licenseMessageRemove")}

diff --git a/src/app/admin/users/AdminUsersTable.tsx b/src/app/admin/users/AdminUsersTable.tsx index 94f3f0b9..efcf9484 100644 --- a/src/app/admin/users/AdminUsersTable.tsx +++ b/src/app/admin/users/AdminUsersTable.tsx @@ -188,7 +188,7 @@ export default function UsersTable({ users }: Props) { }, { id: "actions", - header: () => ({t("actions")}), + header: () => {t("actions")}, cell: ({ row }) => { const r = row.original; return ( diff --git a/src/app/admin/users/[userId]/layout.tsx b/src/app/admin/users/[userId]/layout.tsx index 062b40d8..0c8c50e6 100644 --- a/src/app/admin/users/[userId]/layout.tsx +++ b/src/app/admin/users/[userId]/layout.tsx @@ -6,7 +6,7 @@ import { AdminGetUserResponse } from "@server/routers/user/adminGetUser"; import { HorizontalTabs } from "@app/components/HorizontalTabs"; import { cache } from "react"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; interface UserLayoutProps { children: React.ReactNode; @@ -36,7 +36,7 @@ export default async function UserLayoutProps(props: UserLayoutProps) { const navItems = [ { - title: t('general'), + title: t("general"), href: "/admin/users/{userId}/general" } ]; @@ -45,11 +45,9 @@ export default async function UserLayoutProps(props: UserLayoutProps) { <> - - {children} - + {children} ); -} \ No newline at end of file +} diff --git a/src/app/admin/users/[userId]/page.tsx b/src/app/admin/users/[userId]/page.tsx index edf5aaed..07b39686 100644 --- a/src/app/admin/users/[userId]/page.tsx +++ b/src/app/admin/users/[userId]/page.tsx @@ -5,4 +5,4 @@ export default async function UserPage(props: { }) { const { userId } = await props.params; redirect(`/admin/users/${userId}/general`); -} \ No newline at end of file +} diff --git a/src/app/admin/users/page.tsx b/src/app/admin/users/page.tsx index bf6547a3..c84a077f 100644 --- a/src/app/admin/users/page.tsx +++ b/src/app/admin/users/page.tsx @@ -36,7 +36,7 @@ export default async function UsersPage(props: PageProps) { username: row.username, type: row.type, idpId: row.idpId, - idpName: row.idpName || t('idpNameInternal'), + idpName: row.idpName || t("idpNameInternal"), dateCreated: row.dateCreated, serverAdmin: row.serverAdmin, twoFactorEnabled: row.twoFactorEnabled, @@ -47,14 +47,16 @@ export default async function UsersPage(props: PageProps) { return ( <> - {t('userAbount')} + + {t("userAbount")} + - {t('userAbountDescription')} + {t("userAbountDescription")} diff --git a/src/app/auth/(private)/org/page.tsx b/src/app/auth/(private)/org/page.tsx index 36dfd175..5b68708a 100644 --- a/src/app/auth/(private)/org/page.tsx +++ b/src/app/auth/(private)/org/page.tsx @@ -9,9 +9,7 @@ import { LoginFormIDP } from "@app/components/LoginForm"; import { ListOrgIdpsResponse } from "@server/routers/orgIdp/types"; import { build } from "@server/build"; import { headers } from "next/headers"; -import { - LoadLoginPageResponse -} from "@server/routers/loginPage/types"; +import { LoadLoginPageResponse } from "@server/routers/loginPage/types"; import IdpLoginButtons from "@app/components/private/IdpLoginButtons"; import { Card, diff --git a/src/app/auth/2fa/setup/page.tsx b/src/app/auth/2fa/setup/page.tsx index 64a6cf57..944731b9 100644 --- a/src/app/auth/2fa/setup/page.tsx +++ b/src/app/auth/2fa/setup/page.tsx @@ -45,7 +45,9 @@ export default function Setup2FAPage() { {t("otpSetup")} - {t("adminEnabled2FaOnYourAccount", { email: email || "your account" })} + {t("adminEnabled2FaOnYourAccount", { + email: email || "your account" + })} diff --git a/src/app/auth/idp/[idpId]/oidc/callback/page.tsx b/src/app/auth/idp/[idpId]/oidc/callback/page.tsx index a2432e3e..3d646084 100644 --- a/src/app/auth/idp/[idpId]/oidc/callback/page.tsx +++ b/src/app/auth/idp/[idpId]/oidc/callback/page.tsx @@ -25,15 +25,17 @@ export default async function Page(props: { const allCookies = await cookies(); const stateCookie = allCookies.get("p_oidc_state")?.value; - const idpRes = await cache( - async () => await priv.get>(`/idp/${params.idpId}`) + async () => + await priv.get>( + `/idp/${params.idpId}` + ) )(); const foundIdp = idpRes.data?.data?.idp; if (!foundIdp) { - return
{t('idpErrorNotFound')}
; + return
{t("idpErrorNotFound")}
; } const allHeaders = await headers(); diff --git a/src/app/auth/login/device/page.tsx b/src/app/auth/login/device/page.tsx index 07c804fb..2fa5fac5 100644 --- a/src/app/auth/login/device/page.tsx +++ b/src/app/auth/login/device/page.tsx @@ -19,7 +19,9 @@ export default async function DeviceLoginPage({ searchParams }: Props) { const redirectDestination = code ? `/auth/login/device?code=${encodeURIComponent(code)}` : "/auth/login/device"; - redirect(`/auth/login?forceLogin=true&redirect=${encodeURIComponent(redirectDestination)}`); + redirect( + `/auth/login?forceLogin=true&redirect=${encodeURIComponent(redirectDestination)}` + ); } const userName = user?.name || user?.username || ""; diff --git a/src/app/auth/login/device/success/page.tsx b/src/app/auth/login/device/success/page.tsx index 1b79c600..81e62fd6 100644 --- a/src/app/auth/login/device/success/page.tsx +++ b/src/app/auth/login/device/success/page.tsx @@ -26,7 +26,9 @@ export default function DeviceAuthSuccessPage() {
-

{t("deviceActivation")}

+

+ {t("deviceActivation")} +

diff --git a/src/app/auth/login/page.tsx b/src/app/auth/login/page.tsx index 615f33cf..f632a88c 100644 --- a/src/app/auth/login/page.tsx +++ b/src/app/auth/login/page.tsx @@ -98,7 +98,11 @@ export default async function Page(props: { )} - + {(!signUpDisabled || isInvite) && (

diff --git a/src/app/auth/reset-password/ResetPasswordForm.tsx b/src/app/auth/reset-password/ResetPasswordForm.tsx index 986c52e4..b6afd6d8 100644 --- a/src/app/auth/reset-password/ResetPasswordForm.tsx +++ b/src/app/auth/reset-password/ResetPasswordForm.tsx @@ -88,18 +88,18 @@ export default function ResetPasswordForm({ const formSchema = z .object({ - email: z.email({ message: t('emailInvalid') }), - token: z.string().min(8, { message: t('tokenInvalid') }), + email: z.email({ message: t("emailInvalid") }), + token: z.string().min(8, { message: t("tokenInvalid") }), password: passwordSchema, confirmPassword: passwordSchema }) .refine((data) => data.password === data.confirmPassword, { path: ["confirmPassword"], - message: t('passwordNotMatch') + message: t("passwordNotMatch") }); const mfaSchema = z.object({ - code: z.string().length(6, { message: t('pincodeInvalid') }) + code: z.string().length(6, { message: t("pincodeInvalid") }) }); const form = useForm({ @@ -139,8 +139,8 @@ export default function ResetPasswordForm({ } as RequestPasswordResetBody ) .catch((e) => { - setError(formatAxiosError(e, t('errorOccurred'))); - console.error(t('passwordErrorRequestReset'), e); + setError(formatAxiosError(e, t("errorOccurred"))); + console.error(t("passwordErrorRequestReset"), e); setIsSubmitting(false); }); @@ -169,8 +169,8 @@ export default function ResetPasswordForm({ } as ResetPasswordBody ) .catch((e) => { - setError(formatAxiosError(e, t('errorOccurred'))); - console.error(t('passwordErrorReset'), e); + setError(formatAxiosError(e, t("errorOccurred"))); + console.error(t("passwordErrorReset"), e); setIsSubmitting(false); }); @@ -186,7 +186,11 @@ export default function ResetPasswordForm({ return; } - setSuccessMessage(quickstart ? t('accountSetupSuccess') : t('passwordResetSuccess')); + setSuccessMessage( + quickstart + ? t("accountSetupSuccess") + : t("passwordResetSuccess") + ); // Auto-login after successful password reset try { @@ -208,7 +212,10 @@ export default function ResetPasswordForm({ try { await api.post("/auth/verify-email/request"); } catch (verificationError) { - console.error("Failed to send verification code:", verificationError); + console.error( + "Failed to send verification code:", + verificationError + ); } if (redirect) { @@ -229,7 +236,6 @@ export default function ResetPasswordForm({ } setIsSubmitting(false); }, 1500); - } catch (loginError) { // Auto-login failed, but password reset was successful console.error("Auto-login failed:", loginError); @@ -251,13 +257,14 @@ export default function ResetPasswordForm({ - {quickstart ? t('completeAccountSetup') : t('passwordReset')} + {quickstart + ? t("completeAccountSetup") + : t("passwordReset")} {quickstart - ? t('completeAccountSetupDescription') - : t('passwordResetDescription') - } + ? t("completeAccountSetupDescription") + : t("passwordResetDescription")} @@ -276,16 +283,19 @@ export default function ResetPasswordForm({ name="email" render={({ field }) => ( - {t('email')} + + {t("email")} + {quickstart - ? t('accountSetupSent') - : t('passwordResetSent') - } + ? t("accountSetupSent") + : t( + "passwordResetSent" + )} )} @@ -306,7 +316,9 @@ export default function ResetPasswordForm({ name="email" render={({ field }) => ( - {t('email')} + + {t("email")} + {quickstart - ? t('accountSetupCode') - : t('passwordResetCode') - } + ? t( + "accountSetupCode" + ) + : t( + "passwordResetCode" + )} {quickstart - ? t('accountSetupCodeDescription') - : t('passwordResetCodeDescription') - } + ? t( + "accountSetupCodeDescription" + ) + : t( + "passwordResetCodeDescription" + )} )} @@ -355,9 +373,8 @@ export default function ResetPasswordForm({ {quickstart - ? t('passwordCreate') - : t('passwordNew') - } + ? t("passwordCreate") + : t("passwordNew")} {quickstart - ? t('passwordCreateConfirm') - : t('passwordNewConfirm') - } + ? t( + "passwordCreateConfirm" + ) + : t( + "passwordNewConfirm" + )} ( - {t('pincodeAuth')} + {t("pincodeAuth")}

@@ -475,8 +495,10 @@ export default function ResetPasswordForm({ )} {state === "reset" - ? (quickstart ? t('completeSetup') : t('passwordReset')) - : t('pincodeSubmit2')} + ? quickstart + ? t("completeSetup") + : t("passwordReset") + : t("pincodeSubmit2")} )} @@ -491,9 +513,8 @@ export default function ResetPasswordForm({ )} {quickstart - ? t('accountSetupSubmit') - : t('passwordResetSubmit') - } + ? t("accountSetupSubmit") + : t("passwordResetSubmit")} )} @@ -507,7 +528,7 @@ export default function ResetPasswordForm({ mfaForm.reset(); }} > - {t('passwordBack')} + {t("passwordBack")} )} @@ -521,7 +542,7 @@ export default function ResetPasswordForm({ form.reset(); }} > - {t('backToEmail')} + {t("backToEmail")} )}
diff --git a/src/app/auth/verify-email/page.tsx b/src/app/auth/verify-email/page.tsx index c549abf0..e4428370 100644 --- a/src/app/auth/verify-email/page.tsx +++ b/src/app/auth/verify-email/page.tsx @@ -35,10 +35,7 @@ export default async function Page(props: { return ( <> - + ); } diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx index 60c02bee..d3ca37cc 100644 --- a/src/app/not-found.tsx +++ b/src/app/not-found.tsx @@ -1,17 +1,16 @@ import { getTranslations } from "next-intl/server"; export default async function NotFound() { - const t = await getTranslations(); return (

404

- {t('pageNotFound')} + {t("pageNotFound")}

- {t('pageNotFoundDescription')} + {t("pageNotFoundDescription")}

); diff --git a/src/app/setup/page.tsx b/src/app/setup/page.tsx index 53072554..36853e5c 100644 --- a/src/app/setup/page.tsx +++ b/src/app/setup/page.tsx @@ -312,7 +312,9 @@ export default function StepperForm() {
- {t("setupSubnetDescription")} + {t( + "setupSubnetDescription" + )}
)} diff --git a/src/components/AccessPageHeaderAndNav.tsx b/src/components/AccessPageHeaderAndNav.tsx index 47690dc6..12a34f06 100644 --- a/src/components/AccessPageHeaderAndNav.tsx +++ b/src/components/AccessPageHeaderAndNav.tsx @@ -14,21 +14,21 @@ export default function AccessPageHeaderAndNav({ hasInvitations }: AccessPageHeaderAndNavProps) { const t = useTranslations(); - + const navItems = [ { - title: t('users'), + title: t("users"), href: `/{orgId}/settings/access/users` }, { - title: t('roles'), + title: t("roles"), href: `/{orgId}/settings/access/roles` } ]; if (hasInvitations) { navItems.push({ - title: t('invite'), + title: t("invite"), href: `/{orgId}/settings/access/invitations` }); } @@ -36,13 +36,11 @@ export default function AccessPageHeaderAndNav({ return ( <> - - {children} - + {children} ); } diff --git a/src/components/AccessToken.tsx b/src/components/AccessToken.tsx index 969a2d4e..54f92643 100644 --- a/src/components/AccessToken.tsx +++ b/src/components/AccessToken.tsx @@ -20,10 +20,7 @@ type AccessTokenProps = { resourceId?: number; }; -export default function AccessToken({ - token, - resourceId -}: AccessTokenProps) { +export default function AccessToken({ token, resourceId }: AccessTokenProps) { const [loading, setLoading] = useState(true); const [isValid, setIsValid] = useState(false); @@ -79,7 +76,7 @@ export default function AccessToken({ ); } } catch (e) { - console.error(t('accessTokenError'), e); + console.error(t("accessTokenError"), e); } finally { setLoading(false); } @@ -102,7 +99,7 @@ export default function AccessToken({ ); } } catch (e) { - console.error(t('accessTokenError'), e); + console.error(t("accessTokenError"), e); } finally { setLoading(false); } @@ -118,26 +115,22 @@ export default function AccessToken({ function renderTitle() { if (isValid) { - return t('accessGranted'); + return t("accessGranted"); } else { - return t('accessUrlInvalid'); + return t("accessUrlInvalid"); } } function renderContent() { if (isValid) { - return ( -
- {t('accessGrantedDescription')} -
- ); + return
{t("accessGrantedDescription")}
; } else { return (
- {t('accessUrlInvalidDescription')} + {t("accessUrlInvalidDescription")}
diff --git a/src/components/AccessTokenUsage.tsx b/src/components/AccessTokenUsage.tsx index c44f43b7..4b170371 100644 --- a/src/components/AccessTokenUsage.tsx +++ b/src/components/AccessTokenUsage.tsx @@ -44,31 +44,35 @@ export default function AccessTokenSection({ <>

- {t('shareTokenDescription')} + {t("shareTokenDescription")}

- {t('accessToken')} - {t('usageExamples')} + {t("accessToken")} + + {t("usageExamples")} +
-
{t('tokenId')}
+
{t("tokenId")}
-
{t('token')}
+
{t("token")}
-

{t('requestHeades')}

+

+ {t("requestHeades")} +

-

{t('queryParameter')}

+

+ {t("queryParameter")} +

@@ -85,17 +91,17 @@ ${env.server.resourceAccessTokenHeadersToken}: ${token}`} - {t('importantNote')} + {t("importantNote")} - {t('shareImportantDescription')} + {t("shareImportantDescription")}
- {t('shareTokenSecurety')} + {t("shareTokenSecurety")}
); diff --git a/src/components/AdminIdpDataTable.tsx b/src/components/AdminIdpDataTable.tsx index 26ec6b31..e11ed8fc 100644 --- a/src/components/AdminIdpDataTable.tsx +++ b/src/components/AdminIdpDataTable.tsx @@ -26,10 +26,10 @@ export function IdpDataTable({ columns={columns} data={data} persistPageSize="idp-table" - title={t('idp')} - searchPlaceholder={t('idpSearch')} + title={t("idp")} + searchPlaceholder={t("idpSearch")} searchColumn="name" - addButtonText={t('idpAdd')} + addButtonText={t("idpAdd")} onAdd={() => { router.push("/admin/idp/create"); }} diff --git a/src/components/AdminIdpTable.tsx b/src/components/AdminIdpTable.tsx index b0782fcb..76a0fdd7 100644 --- a/src/components/AdminIdpTable.tsx +++ b/src/components/AdminIdpTable.tsx @@ -175,9 +175,7 @@ export default function IdpTable({ idps }: Props) { - diff --git a/src/components/AdminUsersDataTable.tsx b/src/components/AdminUsersDataTable.tsx index 2eb3c2e2..afa473e8 100644 --- a/src/components/AdminUsersDataTable.tsx +++ b/src/components/AdminUsersDataTable.tsx @@ -1,8 +1,6 @@ "use client"; -import { - ColumnDef, -} from "@tanstack/react-table"; +import { ColumnDef } from "@tanstack/react-table"; import { DataTable } from "@app/components/ui/data-table"; import { useTranslations } from "next-intl"; @@ -19,7 +17,6 @@ export function UsersDataTable({ onRefresh, isRefreshing }: DataTableProps) { - const t = useTranslations(); return ( @@ -27,8 +24,8 @@ export function UsersDataTable({ columns={columns} data={data} persistPageSize="userServer-table" - title={t('userServer')} - searchPlaceholder={t('userSearch')} + title={t("userServer")} + searchPlaceholder={t("userSearch")} searchColumn="email" onRefresh={onRefresh} isRefreshing={isRefreshing} diff --git a/src/components/ApiKeysDataTable.tsx b/src/components/ApiKeysDataTable.tsx index c4f93136..8069b101 100644 --- a/src/components/ApiKeysDataTable.tsx +++ b/src/components/ApiKeysDataTable.tsx @@ -44,7 +44,6 @@ export function ApiKeysDataTable({ onRefresh, isRefreshing }: DataTableProps) { - const t = useTranslations(); return ( @@ -52,11 +51,11 @@ export function ApiKeysDataTable({ columns={columns} data={data} persistPageSize="apiKeys-table" - title={t('apiKeys')} - searchPlaceholder={t('searchApiKeys')} + title={t("apiKeys")} + searchPlaceholder={t("searchApiKeys")} searchColumn="name" onAdd={addApiKey} - addButtonText={t('apiKeysAdd')} + addButtonText={t("apiKeysAdd")} onRefresh={onRefresh} isRefreshing={isRefreshing} enableColumnVisibility={true} diff --git a/src/components/ApiKeysTable.tsx b/src/components/ApiKeysTable.tsx index 863d2bc6..c3202277 100644 --- a/src/components/ApiKeysTable.tsx +++ b/src/components/ApiKeysTable.tsx @@ -108,7 +108,7 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) { { accessorKey: "key", friendlyName: t("key"), - header: () => ({t("key")}), + header: () => {t("key")}, cell: ({ row }) => { const r = row.original; return {r.key}; @@ -117,7 +117,7 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) { { accessorKey: "createdAt", friendlyName: t("createdAt"), - header: () => ({t("createdAt")}), + header: () => {t("createdAt")}, cell: ({ row }) => { const r = row.original; return {moment(r.createdAt).format("lll")} ; @@ -161,9 +161,7 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) { - diff --git a/src/components/AutoLoginHandler.tsx b/src/components/AutoLoginHandler.tsx index 2391ece6..ee8f4126 100644 --- a/src/components/AutoLoginHandler.tsx +++ b/src/components/AutoLoginHandler.tsx @@ -67,7 +67,8 @@ export default function AutoLoginHandler({ console.error("Failed to generate OIDC URL:", e); setError( t("autoLoginErrorGeneratingUrl", { - defaultValue: "An unexpected error occurred. Please try again." + defaultValue: + "An unexpected error occurred. Please try again." }) ); } finally { diff --git a/src/components/BrandingLogo.tsx b/src/components/BrandingLogo.tsx index 540b8e0e..b4472365 100644 --- a/src/components/BrandingLogo.tsx +++ b/src/components/BrandingLogo.tsx @@ -38,7 +38,7 @@ export default function BrandingLogo(props: BrandingLogoProps) { if (isUnlocked() && env.branding.logo?.darkPath) { return env.branding.logo.darkPath; } - return "/logo/word_mark_white.png"; + return "/logo/word_mark_white.png"; } const path = getPath(); diff --git a/src/components/ChangePasswordDialog.tsx b/src/components/ChangePasswordDialog.tsx index 85a55ab2..3430f26a 100644 --- a/src/components/ChangePasswordDialog.tsx +++ b/src/components/ChangePasswordDialog.tsx @@ -20,7 +20,10 @@ type ChangePasswordDialogProps = { setOpen: (val: boolean) => void; }; -export default function ChangePasswordDialog({ open, setOpen }: ChangePasswordDialogProps) { +export default function ChangePasswordDialog({ + open, + setOpen +}: ChangePasswordDialogProps) { const t = useTranslations(); const [currentStep, setCurrentStep] = useState(1); const [loading, setLoading] = useState(false); @@ -47,18 +50,16 @@ export default function ChangePasswordDialog({ open, setOpen }: ChangePasswordDi > - - {t('changePassword')} - + {t("changePassword")} - {t('changePasswordDescription')} + {t("changePasswordDescription")} setOpen(false)} @@ -77,7 +78,7 @@ export default function ChangePasswordDialog({ open, setOpen }: ChangePasswordDi disabled={loading} onClick={handleSubmit} > - {t('submit')} + {t("submit")} )} diff --git a/src/components/ChangePasswordForm.tsx b/src/components/ChangePasswordForm.tsx index 5d1395bc..57015712 100644 --- a/src/components/ChangePasswordForm.tsx +++ b/src/components/ChangePasswordForm.tsx @@ -22,11 +22,7 @@ import { import { toast } from "@app/hooks/useToast"; import { formatAxiosError } from "@app/lib/api"; import { useTranslations } from "next-intl"; -import { - InputOTP, - InputOTPGroup, - InputOTPSlot -} from "./ui/input-otp"; +import { InputOTP, InputOTPGroup, InputOTPSlot } from "./ui/input-otp"; import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp"; import { ChangePasswordResponse } from "@server/routers/auth"; import { cn } from "@app/lib/cn"; @@ -114,14 +110,22 @@ const ChangePasswordForm = forwardRef< onLoadingChange?.(loading); }, [loading, onLoadingChange]); - const passwordSchema = z.object({ - oldPassword: z.string().min(1, { message: t("passwordRequired") }), - newPassword: z.string().min(8, { message: t("passwordRequirementsChars") }), - confirmPassword: z.string().min(1, { message: t("passwordRequired") }) - }).refine((data) => data.newPassword === data.confirmPassword, { - message: t("passwordsDoNotMatch"), - path: ["confirmPassword"], - }); + const passwordSchema = z + .object({ + oldPassword: z + .string() + .min(1, { message: t("passwordRequired") }), + newPassword: z + .string() + .min(8, { message: t("passwordRequirementsChars") }), + confirmPassword: z + .string() + .min(1, { message: t("passwordRequired") }) + }) + .refine((data) => data.newPassword === data.confirmPassword, { + message: t("passwordsDoNotMatch"), + path: ["confirmPassword"] + }); const mfaSchema = z.object({ code: z.string().length(6, { message: t("pincodeInvalid") }) @@ -143,11 +147,13 @@ const ChangePasswordForm = forwardRef< } }); - const changePassword = async (values: z.infer) => { + const changePassword = async ( + values: z.infer + ) => { setLoading(true); const endpoint = `/auth/change-password`; - const payload = { + const payload = { oldPassword: values.oldPassword, newPassword: values.newPassword }; @@ -181,7 +187,7 @@ const ChangePasswordForm = forwardRef< const endpoint = `/auth/change-password`; const passwordValues = passwordForm.getValues(); - const payload = { + const payload = { oldPassword: passwordValues.oldPassword, newPassword: passwordValues.newPassword, code: values.code @@ -303,7 +309,9 @@ const ChangePasswordForm = forwardRef<
- {t("passwordStrength")} + {t( + "passwordStrength" + )}
- {t("passwordRequirements")} + {t( + "passwordRequirements" + )}
@@ -505,13 +515,14 @@ const ChangePasswordForm = forwardRef< {confirmPasswordValue.length > 0 && !doPasswordsMatch && (

- {t("passwordsDoNotMatch")} + {t( + "passwordsDoNotMatch" + )}

)} {/* Only show FormMessage when field is empty */} - {confirmPasswordValue.length === 0 && ( - - )} + {confirmPasswordValue.length === + 0 && } )} /> @@ -523,7 +534,9 @@ const ChangePasswordForm = forwardRef< {step === 2 && (
-

{t("otpAuth")}

+

+ {t("otpAuth")} +

{t("otpAuthDescription")}

@@ -551,9 +564,12 @@ const ChangePasswordForm = forwardRef< onChange={( value: string ) => { - field.onChange(value); + field.onChange( + value + ); if ( - value.length === 6 + value.length === + 6 ) { mfaForm.handleSubmit( confirmMfa @@ -630,10 +646,7 @@ const ChangePasswordForm = forwardRef< )} {step === 3 && ( - )} @@ -644,4 +657,4 @@ const ChangePasswordForm = forwardRef< } ); -export default ChangePasswordForm; \ No newline at end of file +export default ChangePasswordForm; diff --git a/src/components/ClientsDataTable.tsx b/src/components/ClientsDataTable.tsx index cf42e7bc..8d38e597 100644 --- a/src/components/ClientsDataTable.tsx +++ b/src/components/ClientsDataTable.tsx @@ -1,8 +1,6 @@ "use client"; -import { - ColumnDef, -} from "@tanstack/react-table"; +import { ColumnDef } from "@tanstack/react-table"; import { DataTable } from "@app/components/ui/data-table"; type TabFilter = { diff --git a/src/components/ColumnFilter.tsx b/src/components/ColumnFilter.tsx index eee91ecc..a856984e 100644 --- a/src/components/ColumnFilter.tsx +++ b/src/components/ColumnFilter.tsx @@ -1,7 +1,18 @@ import { useState } from "react"; import { Button } from "@app/components/ui/button"; -import { Popover, PopoverContent, PopoverTrigger } from "@app/components/ui/popover"; -import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@app/components/ui/command"; +import { + Popover, + PopoverContent, + PopoverTrigger +} from "@app/components/ui/popover"; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList +} from "@app/components/ui/command"; import { CheckIcon, ChevronDownIcon, Filter } from "lucide-react"; import { cn } from "@app/lib/cn"; @@ -31,7 +42,9 @@ export function ColumnFilter({ }: ColumnFilterProps) { const [open, setOpen] = useState(false); - const selectedOption = options.find(option => option.value === selectedValue); + const selectedOption = options.find( + (option) => option.value === selectedValue + ); return ( @@ -49,7 +62,9 @@ export function ColumnFilter({
- {selectedOption ? selectedOption.label : placeholder} + {selectedOption + ? selectedOption.label + : placeholder}
@@ -79,7 +94,9 @@ export function ColumnFilter({ value={option.label} onSelect={() => { onValueChange( - selectedValue === option.value ? undefined : option.value + selectedValue === option.value + ? undefined + : option.value ); setOpen(false); }} @@ -101,4 +118,4 @@ export function ColumnFilter({
); -} \ No newline at end of file +} diff --git a/src/components/ContainersSelector.tsx b/src/components/ContainersSelector.tsx index 7a5cc74b..5a0f49f9 100644 --- a/src/components/ContainersSelector.tsx +++ b/src/components/ContainersSelector.tsx @@ -186,7 +186,7 @@ const DockerContainersTable: FC<{ { accessorKey: "name", friendlyName: t("containerName"), - header: () => ({t("containerName")}), + header: () => {t("containerName")}, cell: ({ row }) => (
{row.original.name}
) @@ -194,7 +194,7 @@ const DockerContainersTable: FC<{ { accessorKey: "image", friendlyName: t("containerImage"), - header: () => ({t("containerImage")}), + header: () => {t("containerImage")}, cell: ({ row }) => (
{row.original.image} @@ -204,7 +204,7 @@ const DockerContainersTable: FC<{ { accessorKey: "state", friendlyName: t("containerState"), - header: () => ({t("containerState")}), + header: () => {t("containerState")}, cell: ({ row }) => ( ({t("containerNetworks")}), + header: () => {t("containerNetworks")}, cell: ({ row }) => { const networks = Object.keys(row.original.networks); return ( @@ -239,7 +239,9 @@ const DockerContainersTable: FC<{ { accessorKey: "hostname", friendlyName: t("containerHostnameIp"), - header: () => ({t("containerHostnameIp")}), + header: () => ( + {t("containerHostnameIp")} + ), enableHiding: false, cell: ({ row }) => (
@@ -250,7 +252,7 @@ const DockerContainersTable: FC<{ { accessorKey: "labels", friendlyName: t("containerLabels"), - header: () => ({t("containerLabels")}), + header: () => {t("containerLabels")}, cell: ({ row }) => { const labels = row.original.labels || {}; const labelEntries = Object.entries(labels); @@ -302,7 +304,7 @@ const DockerContainersTable: FC<{ }, { accessorKey: "ports", - header: () => ({t("containerPorts")}), + header: () => {t("containerPorts")}, enableHiding: false, cell: ({ row }) => { const ports = getExposedPorts(row.original); @@ -360,7 +362,7 @@ const DockerContainersTable: FC<{ }, { id: "actions", - header: () => ({t("containerActions")}), + header: () => {t("containerActions")}, cell: ({ row }) => { const ports = getExposedPorts(row.original); return ( diff --git a/src/components/CopyTextBox.tsx b/src/components/CopyTextBox.tsx index 72a99c3f..e516bf0d 100644 --- a/src/components/CopyTextBox.tsx +++ b/src/components/CopyTextBox.tsx @@ -29,7 +29,7 @@ export default function CopyTextBox({ setIsCopied(true); setTimeout(() => setIsCopied(false), 2000); } catch (err) { - console.error(t('copyTextFailed'), err); + console.error(t("copyTextFailed"), err); } } }; @@ -54,7 +54,7 @@ export default function CopyTextBox({ type="button" className="absolute top-0.5 right-0 z-10 bg-card" onClick={copyToClipboard} - aria-label={t('copyTextClipboard')} + aria-label={t("copyTextClipboard")} > {isCopied ? ( diff --git a/src/components/CopyToClipboard.tsx b/src/components/CopyToClipboard.tsx index 5cdeed55..b755f9a5 100644 --- a/src/components/CopyToClipboard.tsx +++ b/src/components/CopyToClipboard.tsx @@ -9,7 +9,11 @@ type CopyToClipboardProps = { isLink?: boolean; }; -const CopyToClipboard = ({ text, displayText, isLink }: CopyToClipboardProps) => { +const CopyToClipboard = ({ + text, + displayText, + isLink +}: CopyToClipboardProps) => { const [copied, setCopied] = useState(false); const handleCopy = () => { @@ -60,7 +64,7 @@ const CopyToClipboard = ({ text, displayText, isLink }: CopyToClipboardProps) => ) : ( )} - {t('copyText')} + {t("copyText")}
); diff --git a/src/components/CreateDomainForm.tsx b/src/components/CreateDomainForm.tsx index 7d614376..9a187f1e 100644 --- a/src/components/CreateDomainForm.tsx +++ b/src/components/CreateDomainForm.tsx @@ -45,11 +45,16 @@ import { } from "@app/components/InfoSection"; import { useOrgContext } from "@app/hooks/useOrgContext"; import { build } from "@server/build"; -import { toASCII, toUnicode } from 'punycode'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select"; +import { toASCII, toUnicode } from "punycode"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue +} from "./ui/select"; import { useRouter } from "next/navigation"; - // Helper functions for Unicode domain handling function toPunycode(domain: string): string { try { @@ -76,9 +81,9 @@ function isValidDomainFormat(domain: string): boolean { return false; } - const parts = domain.split('.'); + const parts = domain.split("."); for (const part of parts) { - if (part.length === 0 || part.startsWith('-') || part.endsWith('-')) { + if (part.length === 0 || part.startsWith("-") || part.endsWith("-")) { return false; } if (part.length > 63) { @@ -137,7 +142,8 @@ export default function CreateDomainForm({ resolver: zodResolver(formSchema), defaultValues: { baseDomain: "", - type: build == "oss" || !env.flags.usePangolinDns ? "wildcard" : "ns", + type: + build == "oss" || !env.flags.usePangolinDns ? "wildcard" : "ns", certResolver: null, preferWildcardCert: false } @@ -172,7 +178,9 @@ export default function CreateDomainForm({ description: t("domainCreatedDescription") }); onCreated?.(domainData); - router.push(`/${org.org.orgId}/settings/domains/${domainData.domainId}`); + router.push( + `/${org.org.orgId}/settings/domains/${domainData.domainId}` + ); } catch (e) { toast({ title: t("error"), @@ -182,7 +190,7 @@ export default function CreateDomainForm({ } finally { setLoading(false); } - }; + } // Domain type options let domainOptions: any = []; @@ -225,145 +233,213 @@ export default function CreateDomainForm({ -
- - ( - - - - - )} - /> - ( - - {t("domain")} - - - - {punycodePreview && ( - - - - {t("internationaldomaindetected")} - -
-

{t("willbestoredas")} {punycodePreview}

-
-
-
-
- )} - -
- )} - /> - {domainType === "wildcard" && ( - <> - ( - - {t("certResolver")} - - - - - - )} + + + ( + + - {form.watch("certResolver") !== null && - form.watch("certResolver") !== "default" && ( - ( - - - field.onChange(e.target.value)} - /> - - - - )} - /> - )} + + + )} + /> + ( + + {t("domain")} + + + + {punycodePreview && ( + + + + + {t( + "internationaldomaindetected" + )} + + +
+

+ {t( + "willbestoredas" + )}{" "} + + { + punycodePreview + } + +

+
+
+
+
+ )} + +
+ )} + /> + {domainType === "wildcard" && ( + <> + ( + + + {t("certResolver")} + + + + + + + )} + /> + {form.watch("certResolver") !== null && + form.watch("certResolver") !== + "default" && ( + ( + + + + field.onChange( + e.target + .value + ) + } + /> + + + + )} + /> + )} - {form.watch("certResolver") !== null && - form.watch("certResolver") !== "default" && ( - ( - - - - - {/*
+ {form.watch("certResolver") !== null && + form.watch("certResolver") !== + "default" && ( + ( + + + + + {/*
{t("preferWildcardCert")}
*/} -
- )} - /> - )} - - )} - - - + + )} + /> + )} + + )} + + diff --git a/src/components/CreateInternalResourceDialog.tsx b/src/components/CreateInternalResourceDialog.tsx index 94238f9d..91ef26da 100644 --- a/src/components/CreateInternalResourceDialog.tsx +++ b/src/components/CreateInternalResourceDialog.tsx @@ -272,7 +272,7 @@ export default function CreateInternalResourceDialog({ // an alias is required if (data.mode === "host" && isHostname(data.destination)) { const currentAlias = data.alias?.trim() || ""; - + if (!currentAlias) { // Prefill alias based on destination let aliasValue = data.destination; @@ -281,7 +281,7 @@ export default function CreateInternalResourceDialog({ const cleanedName = cleanForFQDN(data.name); aliasValue = `${cleanedName}.internal`; } - + // Update the form with the prefilled alias form.setValue("alias", aliasValue); data.alias = aliasValue; diff --git a/src/components/CreateRoleForm.tsx b/src/components/CreateRoleForm.tsx index dca75f5a..b8df1f78 100644 --- a/src/components/CreateRoleForm.tsx +++ b/src/components/CreateRoleForm.tsx @@ -48,7 +48,7 @@ export default function CreateRoleForm({ const t = useTranslations(); const formSchema = z.object({ - name: z.string({ message: t('nameRequired') }).max(32), + name: z.string({ message: t("nameRequired") }).max(32), description: z.string().max(255).optional() }); @@ -78,10 +78,10 @@ export default function CreateRoleForm({ .catch((e) => { toast({ variant: "destructive", - title: t('accessRoleErrorCreate'), + title: t("accessRoleErrorCreate"), description: formatAxiosError( e, - t('accessRoleErrorCreateDescription') + t("accessRoleErrorCreateDescription") ) }); }); @@ -89,8 +89,8 @@ export default function CreateRoleForm({ if (res && res.status === 201) { toast({ variant: "default", - title: t('accessRoleCreated'), - description: t('accessRoleCreatedDescription') + title: t("accessRoleCreated"), + description: t("accessRoleCreatedDescription") }); if (open) { @@ -117,9 +117,9 @@ export default function CreateRoleForm({ > - {t('accessRoleCreate')} + {t("accessRoleCreate")} - {t('accessRoleCreateDescription')} + {t("accessRoleCreateDescription")} @@ -134,7 +134,9 @@ export default function CreateRoleForm({ name="name" render={({ field }) => ( - {t('accessRoleName')} + + {t("accessRoleName")} + @@ -147,7 +149,9 @@ export default function CreateRoleForm({ name="description" render={({ field }) => ( - {t('description')} + + {t("description")} + @@ -160,7 +164,7 @@ export default function CreateRoleForm({ - + diff --git a/src/components/CreateShareLinkForm.tsx b/src/components/CreateShareLinkForm.tsx index 3cc203f3..361bfe7d 100644 --- a/src/components/CreateShareLinkForm.tsx +++ b/src/components/CreateShareLinkForm.tsx @@ -67,7 +67,7 @@ import { } from "@app/components/ui/collapsible"; import AccessTokenSection from "@app/components/AccessTokenUsage"; import { useTranslations } from "next-intl"; -import { toUnicode } from 'punycode'; +import { toUnicode } from "punycode"; type FormProps = { open: boolean; @@ -104,7 +104,7 @@ export default function CreateShareLinkForm({ >([]); const formSchema = z.object({ - resourceId: z.number({ message: t('shareErrorSelectResource') }), + resourceId: z.number({ message: t("shareErrorSelectResource") }), resourceName: z.string(), resourceUrl: z.string(), timeUnit: z.string(), @@ -113,12 +113,12 @@ export default function CreateShareLinkForm({ }); const timeUnits = [ - { unit: "minutes", name: t('minutes') }, - { unit: "hours", name: t('hours') }, - { unit: "days", name: t('days') }, - { unit: "weeks", name: t('weeks') }, - { unit: "months", name: t('months') }, - { unit: "years", name: t('years') } + { unit: "minutes", name: t("minutes") }, + { unit: "hours", name: t("hours") }, + { unit: "days", name: t("days") }, + { unit: "weeks", name: t("weeks") }, + { unit: "months", name: t("months") }, + { unit: "years", name: t("years") } ]; const form = useForm>({ @@ -144,10 +144,10 @@ export default function CreateShareLinkForm({ console.error(e); toast({ variant: "destructive", - title: t('shareErrorFetchResource'), + title: t("shareErrorFetchResource"), description: formatAxiosError( e, - t('shareErrorFetchResourceDescription') + t("shareErrorFetchResourceDescription") ) }); }); @@ -204,17 +204,21 @@ export default function CreateShareLinkForm({ validForSeconds: neverExpire ? undefined : timeInSeconds, title: values.title || - t('shareLink', {resource: (values.resourceName || "Resource" + values.resourceId)}) + t("shareLink", { + resource: + values.resourceName || + "Resource" + values.resourceId + }) } ) .catch((e) => { console.error(e); toast({ variant: "destructive", - title: t('shareErrorCreate'), + title: t("shareErrorCreate"), description: formatAxiosError( e, - t('shareErrorCreateDescription') + t("shareErrorCreateDescription") ) }); }); @@ -263,9 +267,9 @@ export default function CreateShareLinkForm({ > - {t('shareCreate')} + {t("shareCreate")} - {t('shareCreateDescription')} + {t("shareCreateDescription")} @@ -283,7 +287,7 @@ export default function CreateShareLinkForm({ render={({ field }) => ( - {t('resource')} + {t("resource")} @@ -301,17 +305,25 @@ export default function CreateShareLinkForm({ ? getSelectedResourceName( field.value ) - : t('resourceSelect')} + : t( + "resourceSelect" + )} - + - {t('resourcesNotFound')} + {t( + "resourcesNotFound" + )} {resources.map( @@ -367,7 +379,9 @@ export default function CreateShareLinkForm({ render={({ field }) => ( - {t('shareTitleOptional')} + {t( + "shareTitleOptional" + )} @@ -379,7 +393,9 @@ export default function CreateShareLinkForm({
- {t('expireIn')} + + {t("expireIn")} +
- + @@ -458,12 +480,12 @@ export default function CreateShareLinkForm({ htmlFor="terms" className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" > - {t('neverExpire')} + {t("neverExpire")}

- {t('shareExpireDescription')} + {t("shareExpireDescription")}

@@ -471,16 +493,15 @@ export default function CreateShareLinkForm({ )} {link && (
-

- {t('shareSeeOnce')} -

-

- {t('shareAccessHint')} -

+

{t("shareSeeOnce")}

+

{t("shareAccessHint")}

- +
@@ -503,12 +524,12 @@ export default function CreateShareLinkForm({ className="p-0 flex items-center justify-between w-full" >

- {t('shareTokenUsage')} + {t("shareTokenUsage")}

- {t('toggle')} + {t("toggle")}
@@ -538,7 +559,7 @@ export default function CreateShareLinkForm({ - + diff --git a/src/components/Credenza.tsx b/src/components/Credenza.tsx index 6b80b3d3..32c09b49 100644 --- a/src/components/Credenza.tsx +++ b/src/components/Credenza.tsx @@ -78,7 +78,10 @@ const CredenzaClose = ({ className, children, ...props }: CredenzaProps) => { const CredenzaClose = isDesktop ? DialogClose : DrawerClose; return ( - + {children} ); @@ -155,7 +158,13 @@ const CredenzaBody = ({ className, children, ...props }: CredenzaProps) => { // ); return ( -
+
{children}
); diff --git a/src/components/DNSRecordTable.tsx b/src/components/DNSRecordTable.tsx index bc764eb9..6995301d 100644 --- a/src/components/DNSRecordTable.tsx +++ b/src/components/DNSRecordTable.tsx @@ -21,10 +21,7 @@ type Props = { type: string | null; }; -export default function DNSRecordsTable({ - records, - type -}: Props) { +export default function DNSRecordsTable({ records, type }: Props) { const t = useTranslations(); const env = useEnvContext(); @@ -114,11 +111,5 @@ export default function DNSRecordsTable({ ...(env.env.flags.usePangolinDns ? [statusColumn] : []) ]; - return ( - - ); + return ; } diff --git a/src/components/DNSRecordsDataTable.tsx b/src/components/DNSRecordsDataTable.tsx index 2a6b75fd..786b2d71 100644 --- a/src/components/DNSRecordsDataTable.tsx +++ b/src/components/DNSRecordsDataTable.tsx @@ -110,7 +110,11 @@ export function DNSRecordsDataTable({

{t("dnsRecord")}

{t("required")}
- +