@intlayer/backend 8.12.2 → 8.12.4-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/utils/AI/askDocQuestion/embeddings/docs/en/bundle_optimization.json +9954 -6953
- package/dist/assets/utils/AI/askDocQuestion/embeddings/docs/en/configuration.json +1 -1
- package/dist/esm/controllers/ai.controller.mjs.map +1 -1
- package/dist/esm/controllers/audit.controller.mjs.map +1 -1
- package/dist/esm/controllers/bitbucket.controller.mjs.map +1 -1
- package/dist/esm/controllers/cliSessionToken.controller.mjs.map +1 -1
- package/dist/esm/controllers/demo.controller.mjs +32 -25
- package/dist/esm/controllers/demo.controller.mjs.map +1 -1
- package/dist/esm/controllers/dictionary.controller.mjs +1 -0
- package/dist/esm/controllers/dictionary.controller.mjs.map +1 -1
- package/dist/esm/controllers/environment.controller.mjs.map +1 -1
- package/dist/esm/controllers/eventListener.controller.mjs.map +1 -1
- package/dist/esm/controllers/github.controller.mjs.map +1 -1
- package/dist/esm/controllers/gitlab.controller.mjs.map +1 -1
- package/dist/esm/controllers/newsletter.controller.mjs.map +1 -1
- package/dist/esm/controllers/oAuth2.controller.mjs.map +1 -1
- package/dist/esm/controllers/organization.controller.mjs.map +1 -1
- package/dist/esm/controllers/project.controller.mjs.map +1 -1
- package/dist/esm/controllers/projectAccessKey.controller.mjs.map +1 -1
- package/dist/esm/controllers/projectMemberAccess.controller.mjs.map +1 -1
- package/dist/esm/controllers/recursiveAudit.controller.mjs.map +1 -1
- package/dist/esm/controllers/reviewer.controller.mjs.map +1 -1
- package/dist/esm/controllers/searchDoc.controller.mjs.map +1 -1
- package/dist/esm/controllers/showcaseProject.controller.mjs.map +1 -1
- package/dist/esm/controllers/stripe.controller.mjs.map +1 -1
- package/dist/esm/controllers/tag.controller.mjs.map +1 -1
- package/dist/esm/controllers/translation.controller.mjs.map +1 -1
- package/dist/esm/controllers/user.controller.mjs.map +1 -1
- package/dist/esm/emails/AffiliateActivatedEmail.mjs.map +1 -1
- package/dist/esm/emails/AffiliateConversionEmail.mjs.map +1 -1
- package/dist/esm/emails/AffiliateInvitationEmail.mjs.map +1 -1
- package/dist/esm/emails/AffiliateWelcomeEmail.mjs.map +1 -1
- package/dist/esm/emails/InviteUserEmail.mjs.map +1 -1
- package/dist/esm/emails/MagicLinkEmail.mjs.map +1 -1
- package/dist/esm/emails/MissionRequestedClientEmail.mjs.map +1 -1
- package/dist/esm/emails/MissionRequestedReviewerEmail.mjs.map +1 -1
- package/dist/esm/emails/OAuthTokenCreatedEmail.mjs.map +1 -1
- package/dist/esm/emails/PasswordChangeConfirmation.mjs.map +1 -1
- package/dist/esm/emails/ResetUserPassword.mjs.map +1 -1
- package/dist/esm/emails/ReviewerApplicationEmail.mjs.map +1 -1
- package/dist/esm/emails/ReviewerApprovedEmail.mjs.map +1 -1
- package/dist/esm/emails/ReviewerContactEmail.mjs.map +1 -1
- package/dist/esm/emails/SubscriptionPaymentCancellation.mjs.map +1 -1
- package/dist/esm/emails/SubscriptionPaymentError.mjs.map +1 -1
- package/dist/esm/emails/SubscriptionPaymentSuccess.mjs.map +1 -1
- package/dist/esm/emails/ValidateUserEmail.mjs.map +1 -1
- package/dist/esm/emails/Welcome.mjs.map +1 -1
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/logger/index.mjs.map +1 -1
- package/dist/esm/middlewares/oAuth2.middleware.mjs.map +1 -1
- package/dist/esm/middlewares/sessionAuth.middleware.mjs.map +1 -1
- package/dist/esm/routes/ai.routes.mjs.map +1 -1
- package/dist/esm/routes/audit.routes.mjs.map +1 -1
- package/dist/esm/routes/bitbucket.routes.mjs.map +1 -1
- package/dist/esm/routes/demo.routes.mjs.map +1 -1
- package/dist/esm/routes/dictionary.routes.mjs.map +1 -1
- package/dist/esm/routes/environment.routes.mjs.map +1 -1
- package/dist/esm/routes/eventListener.routes.mjs.map +1 -1
- package/dist/esm/routes/github.routes.mjs.map +1 -1
- package/dist/esm/routes/gitlab.routes.mjs.map +1 -1
- package/dist/esm/routes/newsletter.routes.mjs.map +1 -1
- package/dist/esm/routes/organization.routes.mjs.map +1 -1
- package/dist/esm/routes/paramsSchemas.mjs.map +1 -1
- package/dist/esm/routes/project.routes.mjs.map +1 -1
- package/dist/esm/routes/reviewer.routes.mjs.map +1 -1
- package/dist/esm/routes/search.routes.mjs.map +1 -1
- package/dist/esm/routes/showcaseProject.routes.mjs.map +1 -1
- package/dist/esm/routes/stripe.routes.mjs.map +1 -1
- package/dist/esm/routes/tags.routes.mjs.map +1 -1
- package/dist/esm/routes/translate.routes.mjs.map +1 -1
- package/dist/esm/routes/user.routes.mjs.map +1 -1
- package/dist/esm/schemas/account.schema.mjs.map +1 -1
- package/dist/esm/schemas/affiliate.schema.mjs.map +1 -1
- package/dist/esm/schemas/affiliateInvitation.schema.mjs.map +1 -1
- package/dist/esm/schemas/audit.schema.mjs.map +1 -1
- package/dist/esm/schemas/auditJob.schema.mjs.map +1 -1
- package/dist/esm/schemas/auditPage.schema.mjs.map +1 -1
- package/dist/esm/schemas/cliSessionToken.schema.mjs.map +1 -1
- package/dist/esm/schemas/dictionary.schema.mjs.map +1 -1
- package/dist/esm/schemas/discussion.schema.mjs.map +1 -1
- package/dist/esm/schemas/oAuth2.schema.mjs.map +1 -1
- package/dist/esm/schemas/organization.schema.mjs.map +1 -1
- package/dist/esm/schemas/plans.schema.mjs.map +1 -1
- package/dist/esm/schemas/project.schema.mjs.map +1 -1
- package/dist/esm/schemas/promoCode.schema.mjs.map +1 -1
- package/dist/esm/schemas/reviewer.schema.mjs.map +1 -1
- package/dist/esm/schemas/session.schema.mjs.map +1 -1
- package/dist/esm/schemas/showcaseProject.schema.mjs.map +1 -1
- package/dist/esm/schemas/tag.schema.mjs.map +1 -1
- package/dist/esm/schemas/user.schema.mjs.map +1 -1
- package/dist/esm/services/affiliate.service.mjs.map +1 -1
- package/dist/esm/services/audit/analysis/analyzeBundleContent.mjs.map +1 -1
- package/dist/esm/services/audit/analysis/analyzeLinguisticStructure.mjs.map +1 -1
- package/dist/esm/services/audit/analysis/analyzeMetadata.mjs.map +1 -1
- package/dist/esm/services/audit/analysis/analyzeRobots.mjs.map +1 -1
- package/dist/esm/services/audit/analysis/analyzeSitemap.mjs.map +1 -1
- package/dist/esm/services/audit/analysis/analyzeUrlStructure.mjs.map +1 -1
- package/dist/esm/services/audit/analysis/calculateScore.mjs.map +1 -1
- package/dist/esm/services/audit/checkers/bundleChecker.mjs.map +1 -1
- package/dist/esm/services/audit/checkers/linguisticChecker.mjs.map +1 -1
- package/dist/esm/services/audit/checkers/metadataChecker.mjs.map +1 -1
- package/dist/esm/services/audit/checkers/pageChecker.mjs.map +1 -1
- package/dist/esm/services/audit/checkers/robotsChecker.mjs.map +1 -1
- package/dist/esm/services/audit/checkers/sitemapChecker.mjs.map +1 -1
- package/dist/esm/services/audit/checkers/urlChecker.mjs.map +1 -1
- package/dist/esm/services/audit/recursiveAudit.service.mjs.map +1 -1
- package/dist/esm/services/audit/seoAudit.service.mjs.map +1 -1
- package/dist/esm/services/bitbucket.service.mjs.map +1 -1
- package/dist/esm/services/ci.service.mjs.map +1 -1
- package/dist/esm/services/cliSessionToken.service.mjs.map +1 -1
- package/dist/esm/services/dictionary.service.mjs.map +1 -1
- package/dist/esm/services/email.service.mjs.map +1 -1
- package/dist/esm/services/environment.service.mjs.map +1 -1
- package/dist/esm/services/github.service.mjs.map +1 -1
- package/dist/esm/services/gitlab.service.mjs.map +1 -1
- package/dist/esm/services/oAuth2.service.mjs.map +1 -1
- package/dist/esm/services/organization.service.mjs.map +1 -1
- package/dist/esm/services/project/projectScreenshot.service.mjs.map +1 -1
- package/dist/esm/services/project.service.mjs.map +1 -1
- package/dist/esm/services/projectAccessKey.service.mjs.map +1 -1
- package/dist/esm/services/promoCode.service.mjs.map +1 -1
- package/dist/esm/services/reviewer/pictureUpload.service.mjs.map +1 -1
- package/dist/esm/services/reviewer.service.mjs.map +1 -1
- package/dist/esm/services/reviewerMessage.service.mjs.map +1 -1
- package/dist/esm/services/reviewerMission.service.mjs.map +1 -1
- package/dist/esm/services/session.service.mjs.map +1 -1
- package/dist/esm/services/showcase/showcaseProject.service.mjs.map +1 -1
- package/dist/esm/services/showcase/showcaseScan.service.mjs.map +1 -1
- package/dist/esm/services/showcase/showcaseUploadScreenshot.service.mjs.map +1 -1
- package/dist/esm/services/showcase/showcaseVerifyBundle.service.mjs.map +1 -1
- package/dist/esm/services/showcase/showcaseVerifyGithub.service.mjs.map +1 -1
- package/dist/esm/services/subscription.service.mjs.map +1 -1
- package/dist/esm/services/tag.service.mjs.map +1 -1
- package/dist/esm/services/translationQueue.service.mjs.map +1 -1
- package/dist/esm/services/translationWorker.service.mjs.map +1 -1
- package/dist/esm/services/user/avatarUpload.service.mjs.map +1 -1
- package/dist/esm/services/user.service.mjs.map +1 -1
- package/dist/esm/services/webhook.service.mjs.map +1 -1
- package/dist/esm/types/user.types.mjs.map +1 -1
- package/dist/esm/utils/AI/askDocQuestion/askDocQuestion.mjs.map +1 -1
- package/dist/esm/utils/AI/askDocQuestion/embeddings/docs/en/bundle_optimization.json +9954 -6953
- package/dist/esm/utils/AI/askDocQuestion/embeddings/docs/en/configuration.json +1 -1
- package/dist/esm/utils/AI/askDocQuestion/indexMarkdownFiles.mjs.map +1 -1
- package/dist/esm/utils/AI/auditDictionary/index.mjs.map +1 -1
- package/dist/esm/utils/AI/auditDictionaryField/index.mjs.map +1 -1
- package/dist/esm/utils/AI/auditDictionaryMetadata/index.mjs.map +1 -1
- package/dist/esm/utils/AI/auditTag/index.mjs.map +1 -1
- package/dist/esm/utils/AI/autocomplete/index.mjs.map +1 -1
- package/dist/esm/utils/AI/chat/index.mjs.map +1 -1
- package/dist/esm/utils/AI/chat/mcpInProcessTools.mjs.map +1 -1
- package/dist/esm/utils/AI/chat/sessionTools.mjs.map +1 -1
- package/dist/esm/utils/AI/customQuery/index.mjs.map +1 -1
- package/dist/esm/utils/AI/getProjectAIOptions.mjs.map +1 -1
- package/dist/esm/utils/AI/translateDictionaryDB.mjs.map +1 -1
- package/dist/esm/utils/AI/translateJSON/index.mjs.map +1 -1
- package/dist/esm/utils/accessControl.mjs.map +1 -1
- package/dist/esm/utils/auth/getAuth.mjs.map +1 -1
- package/dist/esm/utils/cors.mjs +2 -13
- package/dist/esm/utils/cors.mjs.map +1 -1
- package/dist/esm/utils/demoDictionaries.mjs.map +1 -1
- package/dist/esm/utils/ensureArrayQueryFilter.mjs.map +1 -1
- package/dist/esm/utils/ensureMongoDocumentToObject.mjs.map +1 -1
- package/dist/esm/utils/errors/ErrorHandler.mjs.map +1 -1
- package/dist/esm/utils/errors/ErrorsClass.mjs.map +1 -1
- package/dist/esm/utils/errors/errorCodes.mjs.map +1 -1
- package/dist/esm/utils/errors/index.mjs +1 -0
- package/dist/esm/utils/filtersAndPagination/getDictionaryFiltersAndPagination.mjs.map +1 -1
- package/dist/esm/utils/filtersAndPagination/getDiscussionFiltersAndPagination.mjs.map +1 -1
- package/dist/esm/utils/filtersAndPagination/getFiltersAndPaginationFromBody.mjs.map +1 -1
- package/dist/esm/utils/filtersAndPagination/getOrganizationFiltersAndPagination.mjs.map +1 -1
- package/dist/esm/utils/filtersAndPagination/getProjectFiltersAndPagination.mjs.map +1 -1
- package/dist/esm/utils/filtersAndPagination/getTagFiltersAndPagination.mjs.map +1 -1
- package/dist/esm/utils/filtersAndPagination/getUserFiltersAndPagination.mjs.map +1 -1
- package/dist/esm/utils/getFaviconUrl.mjs.map +1 -1
- package/dist/esm/utils/github/connectGithub.mjs.map +1 -1
- package/dist/esm/utils/httpStatusCodes.mjs.map +1 -1
- package/dist/esm/utils/image/resizeImage.mjs.map +1 -1
- package/dist/esm/utils/mapper/dictionary.mjs.map +1 -1
- package/dist/esm/utils/mapper/organization.mjs.map +1 -1
- package/dist/esm/utils/mapper/project.mjs.map +1 -1
- package/dist/esm/utils/mapper/session.mjs.map +1 -1
- package/dist/esm/utils/mapper/showcaseProject.mjs.map +1 -1
- package/dist/esm/utils/mapper/tag.mjs.map +1 -1
- package/dist/esm/utils/mapper/user.mjs.map +1 -1
- package/dist/esm/utils/mongoDB/connectDB.mjs.map +1 -1
- package/dist/esm/utils/oAuth2.mjs.map +1 -1
- package/dist/esm/utils/permissions.mjs.map +1 -1
- package/dist/esm/utils/plan.mjs.map +1 -1
- package/dist/esm/utils/puppeteer/launchBrowser.mjs.map +1 -1
- package/dist/esm/utils/rateLimiter.mjs.map +1 -1
- package/dist/esm/utils/redis/connectRedis.mjs.map +1 -1
- package/dist/esm/utils/removeObjectKeys.mjs.map +1 -1
- package/dist/esm/utils/responseData.mjs.map +1 -1
- package/dist/esm/utils/s3/s3Client.mjs.map +1 -1
- package/dist/esm/utils/validation/validateDictionary.mjs.map +1 -1
- package/dist/esm/utils/validation/validateOrganization.mjs.map +1 -1
- package/dist/esm/utils/validation/validateProject.mjs.map +1 -1
- package/dist/esm/utils/validation/validateTag.mjs.map +1 -1
- package/dist/esm/utils/validation/validateUser.mjs.map +1 -1
- package/dist/esm/webhooks/stripe.webhook.mjs.map +1 -1
- package/dist/types/controllers/ai.controller.d.ts.map +1 -1
- package/dist/types/controllers/bitbucket.controller.d.ts.map +1 -1
- package/dist/types/controllers/cliSessionToken.controller.d.ts.map +1 -1
- package/dist/types/controllers/demo.controller.d.ts.map +1 -1
- package/dist/types/controllers/dictionary.controller.d.ts.map +1 -1
- package/dist/types/controllers/environment.controller.d.ts.map +1 -1
- package/dist/types/controllers/eventListener.controller.d.ts.map +1 -1
- package/dist/types/controllers/github.controller.d.ts.map +1 -1
- package/dist/types/controllers/gitlab.controller.d.ts.map +1 -1
- package/dist/types/controllers/newsletter.controller.d.ts.map +1 -1
- package/dist/types/controllers/oAuth2.controller.d.ts.map +1 -1
- package/dist/types/controllers/organization.controller.d.ts.map +1 -1
- package/dist/types/controllers/project.controller.d.ts.map +1 -1
- package/dist/types/controllers/projectAccessKey.controller.d.ts.map +1 -1
- package/dist/types/controllers/projectMemberAccess.controller.d.ts.map +1 -1
- package/dist/types/controllers/recursiveAudit.controller.d.ts.map +1 -1
- package/dist/types/controllers/reviewer.controller.d.ts.map +1 -1
- package/dist/types/controllers/searchDoc.controller.d.ts.map +1 -1
- package/dist/types/controllers/showcaseProject.controller.d.ts.map +1 -1
- package/dist/types/controllers/stripe.controller.d.ts.map +1 -1
- package/dist/types/controllers/tag.controller.d.ts.map +1 -1
- package/dist/types/controllers/translation.controller.d.ts.map +1 -1
- package/dist/types/controllers/user.controller.d.ts.map +1 -1
- package/dist/types/emails/AffiliateActivatedEmail.d.ts +20 -18
- package/dist/types/emails/AffiliateActivatedEmail.d.ts.map +1 -1
- package/dist/types/emails/AffiliateConversionEmail.d.ts +21 -19
- package/dist/types/emails/AffiliateConversionEmail.d.ts.map +1 -1
- package/dist/types/emails/AffiliateInvitationEmail.d.ts +20 -18
- package/dist/types/emails/AffiliateInvitationEmail.d.ts.map +1 -1
- package/dist/types/emails/AffiliateWelcomeEmail.d.ts +20 -18
- package/dist/types/emails/AffiliateWelcomeEmail.d.ts.map +1 -1
- package/dist/types/emails/InviteUserEmail.d.ts +20 -18
- package/dist/types/emails/InviteUserEmail.d.ts.map +1 -1
- package/dist/types/emails/MagicLinkEmail.d.ts +20 -18
- package/dist/types/emails/MagicLinkEmail.d.ts.map +1 -1
- package/dist/types/emails/MissionRequestedClientEmail.d.ts +20 -18
- package/dist/types/emails/MissionRequestedClientEmail.d.ts.map +1 -1
- package/dist/types/emails/MissionRequestedReviewerEmail.d.ts +20 -18
- package/dist/types/emails/MissionRequestedReviewerEmail.d.ts.map +1 -1
- package/dist/types/emails/OAuthTokenCreatedEmail.d.ts +20 -18
- package/dist/types/emails/OAuthTokenCreatedEmail.d.ts.map +1 -1
- package/dist/types/emails/PasswordChangeConfirmation.d.ts +20 -18
- package/dist/types/emails/PasswordChangeConfirmation.d.ts.map +1 -1
- package/dist/types/emails/ResetUserPassword.d.ts +20 -18
- package/dist/types/emails/ResetUserPassword.d.ts.map +1 -1
- package/dist/types/emails/ReviewerApplicationEmail.d.ts +20 -18
- package/dist/types/emails/ReviewerApplicationEmail.d.ts.map +1 -1
- package/dist/types/emails/ReviewerApprovedEmail.d.ts +20 -18
- package/dist/types/emails/ReviewerApprovedEmail.d.ts.map +1 -1
- package/dist/types/emails/ReviewerContactEmail.d.ts +3 -1
- package/dist/types/emails/ReviewerContactEmail.d.ts.map +1 -1
- package/dist/types/emails/SubscriptionPaymentCancellation.d.ts +20 -18
- package/dist/types/emails/SubscriptionPaymentCancellation.d.ts.map +1 -1
- package/dist/types/emails/SubscriptionPaymentError.d.ts +20 -18
- package/dist/types/emails/SubscriptionPaymentError.d.ts.map +1 -1
- package/dist/types/emails/SubscriptionPaymentSuccess.d.ts +20 -18
- package/dist/types/emails/SubscriptionPaymentSuccess.d.ts.map +1 -1
- package/dist/types/emails/ValidateUserEmail.d.ts +20 -18
- package/dist/types/emails/ValidateUserEmail.d.ts.map +1 -1
- package/dist/types/emails/Welcome.d.ts +20 -18
- package/dist/types/emails/Welcome.d.ts.map +1 -1
- package/dist/types/export.d.ts +1 -1
- package/dist/types/logger/index.d.ts +3 -1
- package/dist/types/logger/index.d.ts.map +1 -1
- package/dist/types/middlewares/oAuth2.middleware.d.ts.map +1 -1
- package/dist/types/middlewares/sessionAuth.middleware.d.ts.map +1 -1
- package/dist/types/routes/demo.routes.d.ts.map +1 -1
- package/dist/types/schemas/account.schema.d.ts +35 -34
- package/dist/types/schemas/account.schema.d.ts.map +1 -1
- package/dist/types/schemas/affiliate.schema.d.ts +109 -108
- package/dist/types/schemas/affiliate.schema.d.ts.map +1 -1
- package/dist/types/schemas/affiliateInvitation.schema.d.ts +49 -48
- package/dist/types/schemas/affiliateInvitation.schema.d.ts.map +1 -1
- package/dist/types/schemas/audit.schema.d.ts.map +1 -1
- package/dist/types/schemas/auditJob.schema.d.ts +6 -6
- package/dist/types/schemas/auditPage.schema.d.ts +6 -6
- package/dist/types/schemas/cliSessionToken.schema.d.ts +14 -13
- package/dist/types/schemas/cliSessionToken.schema.d.ts.map +1 -1
- package/dist/types/schemas/dictionary.schema.d.ts +60 -59
- package/dist/types/schemas/dictionary.schema.d.ts.map +1 -1
- package/dist/types/schemas/discussion.schema.d.ts +51 -50
- package/dist/types/schemas/discussion.schema.d.ts.map +1 -1
- package/dist/types/schemas/oAuth2.schema.d.ts +18 -17
- package/dist/types/schemas/oAuth2.schema.d.ts.map +1 -1
- package/dist/types/schemas/organization.schema.d.ts +45 -44
- package/dist/types/schemas/organization.schema.d.ts.map +1 -1
- package/dist/types/schemas/plans.schema.d.ts +45 -44
- package/dist/types/schemas/plans.schema.d.ts.map +1 -1
- package/dist/types/schemas/project.schema.d.ts +73 -72
- package/dist/types/schemas/project.schema.d.ts.map +1 -1
- package/dist/types/schemas/promoCode.schema.d.ts +61 -60
- package/dist/types/schemas/promoCode.schema.d.ts.map +1 -1
- package/dist/types/schemas/reviewer.schema.d.ts +221 -220
- package/dist/types/schemas/reviewer.schema.d.ts.map +1 -1
- package/dist/types/schemas/session.schema.d.ts +54 -52
- package/dist/types/schemas/session.schema.d.ts.map +1 -1
- package/dist/types/schemas/showcaseProject.schema.d.ts +61 -60
- package/dist/types/schemas/showcaseProject.schema.d.ts.map +1 -1
- package/dist/types/schemas/tag.schema.d.ts +45 -44
- package/dist/types/schemas/tag.schema.d.ts.map +1 -1
- package/dist/types/schemas/user.schema.d.ts +71 -70
- package/dist/types/schemas/user.schema.d.ts.map +1 -1
- package/dist/types/services/affiliate.service.d.ts.map +1 -1
- package/dist/types/services/audit/analysis/analyzeBundleContent.d.ts.map +1 -1
- package/dist/types/services/audit/analysis/analyzeLinguisticStructure.d.ts.map +1 -1
- package/dist/types/services/audit/analysis/analyzeRobots.d.ts.map +1 -1
- package/dist/types/services/audit/analysis/analyzeSitemap.d.ts.map +1 -1
- package/dist/types/services/audit/analysis/calculateScore.d.ts.map +1 -1
- package/dist/types/services/audit/checkers/bundleChecker.d.ts.map +1 -1
- package/dist/types/services/audit/recursiveAudit.service.d.ts +5 -4
- package/dist/types/services/audit/recursiveAudit.service.d.ts.map +1 -1
- package/dist/types/services/audit/types.d.ts.map +1 -1
- package/dist/types/services/bitbucket.service.d.ts.map +1 -1
- package/dist/types/services/cliSessionToken.service.d.ts.map +1 -1
- package/dist/types/services/dictionary.service.d.ts.map +1 -1
- package/dist/types/services/email.service.d.ts.map +1 -1
- package/dist/types/services/github.service.d.ts.map +1 -1
- package/dist/types/services/gitlab.service.d.ts.map +1 -1
- package/dist/types/services/oAuth2.service.d.ts.map +1 -1
- package/dist/types/services/organization.service.d.ts.map +1 -1
- package/dist/types/services/project/projectScreenshot.service.d.ts.map +1 -1
- package/dist/types/services/project.service.d.ts.map +1 -1
- package/dist/types/services/promoCode.service.d.ts.map +1 -1
- package/dist/types/services/reviewer/pictureUpload.service.d.ts.map +1 -1
- package/dist/types/services/reviewer.service.d.ts.map +1 -1
- package/dist/types/services/reviewerMessage.service.d.ts.map +1 -1
- package/dist/types/services/reviewerMission.service.d.ts.map +1 -1
- package/dist/types/services/session.service.d.ts.map +1 -1
- package/dist/types/services/showcase/showcaseProject.service.d.ts.map +1 -1
- package/dist/types/services/showcase/showcaseScan.service.d.ts.map +1 -1
- package/dist/types/services/showcase/showcaseUploadScreenshot.service.d.ts.map +1 -1
- package/dist/types/services/showcase/showcaseVerifyBundle.service.d.ts.map +1 -1
- package/dist/types/services/showcase/showcaseVerifyGithub.service.d.ts.map +1 -1
- package/dist/types/services/subscription.service.d.ts.map +1 -1
- package/dist/types/services/tag.service.d.ts.map +1 -1
- package/dist/types/services/translationQueue.service.d.ts +2 -1
- package/dist/types/services/translationQueue.service.d.ts.map +1 -1
- package/dist/types/services/translationWorker.service.d.ts.map +1 -1
- package/dist/types/services/user/avatarUpload.service.d.ts.map +1 -1
- package/dist/types/services/user.service.d.ts.map +1 -1
- package/dist/types/services/webhook.service.d.ts.map +1 -1
- package/dist/types/types/Routes.d.ts.map +1 -1
- package/dist/types/types/account.types.d.ts.map +1 -1
- package/dist/types/types/affiliate.types.d.ts.map +1 -1
- package/dist/types/types/affiliateInvitation.types.d.ts.map +1 -1
- package/dist/types/types/dictionary.types.d.ts.map +1 -1
- package/dist/types/types/discussion.types.d.ts.map +1 -1
- package/dist/types/types/oAuth2.types.d.ts.map +1 -1
- package/dist/types/types/organization.types.d.ts.map +1 -1
- package/dist/types/types/plan.types.d.ts.map +1 -1
- package/dist/types/types/project.types.d.ts.map +1 -1
- package/dist/types/types/promoCode.types.d.ts.map +1 -1
- package/dist/types/types/reviewer.types.d.ts.map +1 -1
- package/dist/types/types/session.types.d.ts.map +1 -1
- package/dist/types/types/showcaseProject.types.d.ts.map +1 -1
- package/dist/types/types/tag.types.d.ts.map +1 -1
- package/dist/types/types/user.types.d.ts.map +1 -1
- package/dist/types/utils/AI/askDocQuestion/askDocQuestion.d.ts.map +1 -1
- package/dist/types/utils/AI/askDocQuestion/indexMarkdownFiles.d.ts.map +1 -1
- package/dist/types/utils/AI/auditDictionary/index.d.ts.map +1 -1
- package/dist/types/utils/AI/auditDictionaryField/index.d.ts.map +1 -1
- package/dist/types/utils/AI/auditDictionaryMetadata/index.d.ts.map +1 -1
- package/dist/types/utils/AI/auditTag/index.d.ts.map +1 -1
- package/dist/types/utils/AI/autocomplete/index.d.ts.map +1 -1
- package/dist/types/utils/AI/chat/index.d.ts.map +1 -1
- package/dist/types/utils/AI/chat/mcpInProcessTools.d.ts.map +1 -1
- package/dist/types/utils/AI/chat/sessionTools.d.ts +25 -24
- package/dist/types/utils/AI/chat/sessionTools.d.ts.map +1 -1
- package/dist/types/utils/AI/customQuery/index.d.ts.map +1 -1
- package/dist/types/utils/AI/translateDictionaryDB.d.ts.map +1 -1
- package/dist/types/utils/AI/translateJSON/index.d.ts.map +1 -1
- package/dist/types/utils/auth/getAuth.d.ts.map +1 -1
- package/dist/types/utils/cors.d.ts +1 -7
- package/dist/types/utils/cors.d.ts.map +1 -1
- package/dist/types/utils/demoDictionaries.d.ts.map +1 -1
- package/dist/types/utils/ensureArrayQueryFilter.d.ts.map +1 -1
- package/dist/types/utils/errors/ErrorHandler.d.ts +8 -6
- package/dist/types/utils/errors/ErrorHandler.d.ts.map +1 -1
- package/dist/types/utils/errors/ErrorsClass.d.ts.map +1 -1
- package/dist/types/utils/errors/errorCodes.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getDictionaryFiltersAndPagination.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getDiscussionFiltersAndPagination.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getFiltersAndPaginationFromBody.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getOrganizationFiltersAndPagination.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getProjectFiltersAndPagination.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getTagFiltersAndPagination.d.ts +7 -6
- package/dist/types/utils/filtersAndPagination/getTagFiltersAndPagination.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getUserFiltersAndPagination.d.ts.map +1 -1
- package/dist/types/utils/getFaviconUrl.d.ts.map +1 -1
- package/dist/types/utils/github/connectGithub.d.ts.map +1 -1
- package/dist/types/utils/httpStatusCodes.d.ts.map +1 -1
- package/dist/types/utils/image/resizeImage.d.ts.map +1 -1
- package/dist/types/utils/mapper/dictionary.d.ts.map +1 -1
- package/dist/types/utils/mapper/project.d.ts.map +1 -1
- package/dist/types/utils/mapper/session.d.ts.map +1 -1
- package/dist/types/utils/mapper/showcaseProject.d.ts.map +1 -1
- package/dist/types/utils/mapper/tag.d.ts.map +1 -1
- package/dist/types/utils/mergeFunctionTypes.d.ts.map +1 -1
- package/dist/types/utils/mongoDB/connectDB.d.ts.map +1 -1
- package/dist/types/utils/mongoDB/types.d.ts.map +1 -1
- package/dist/types/utils/oAuth2.d.ts.map +1 -1
- package/dist/types/utils/permissions.d.ts +2 -1
- package/dist/types/utils/permissions.d.ts.map +1 -1
- package/dist/types/utils/plan.d.ts.map +1 -1
- package/dist/types/utils/rateLimiter.d.ts.map +1 -1
- package/dist/types/utils/redis/connectRedis.d.ts.map +1 -1
- package/dist/types/utils/responseData.d.ts.map +1 -1
- package/dist/types/utils/s3/s3Client.d.ts.map +1 -1
- package/dist/types/utils/validation/validateDictionary.d.ts.map +1 -1
- package/dist/types/utils/validation/validateOrganization.d.ts.map +1 -1
- package/dist/types/utils/validation/validateProject.d.ts.map +1 -1
- package/dist/types/utils/validation/validateTag.d.ts.map +1 -1
- package/dist/types/utils/validation/validateUser.d.ts.map +1 -1
- package/package.json +17 -17
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email.service.mjs","names":["EmailComponent"],"sources":["../../../src/services/email.service.tsx"],"sourcesContent":["import {\n AffiliateActivatedEmailAR,\n AffiliateActivatedEmailDE,\n AffiliateActivatedEmailEN,\n AffiliateActivatedEmailEN_GB,\n AffiliateActivatedEmailES,\n AffiliateActivatedEmailFR,\n AffiliateActivatedEmailHI,\n AffiliateActivatedEmailID,\n AffiliateActivatedEmailIT,\n AffiliateActivatedEmailJA,\n AffiliateActivatedEmailKO,\n AffiliateActivatedEmailPL,\n AffiliateActivatedEmailPT,\n AffiliateActivatedEmailRU,\n AffiliateActivatedEmailTR,\n AffiliateActivatedEmailUK,\n AffiliateActivatedEmailVI,\n AffiliateActivatedEmailZH,\n} from '@emails/AffiliateActivatedEmail';\nimport {\n AffiliateConversionEmailAR,\n AffiliateConversionEmailDE,\n AffiliateConversionEmailEN,\n AffiliateConversionEmailEN_GB,\n AffiliateConversionEmailES,\n AffiliateConversionEmailFR,\n AffiliateConversionEmailHI,\n AffiliateConversionEmailID,\n AffiliateConversionEmailIT,\n AffiliateConversionEmailJA,\n AffiliateConversionEmailKO,\n AffiliateConversionEmailPL,\n AffiliateConversionEmailPT,\n AffiliateConversionEmailRU,\n AffiliateConversionEmailTR,\n AffiliateConversionEmailUK,\n AffiliateConversionEmailVI,\n AffiliateConversionEmailZH,\n} from '@emails/AffiliateConversionEmail';\nimport {\n AffiliateInvitationEmailAR,\n AffiliateInvitationEmailDE,\n AffiliateInvitationEmailEN,\n AffiliateInvitationEmailEN_GB,\n AffiliateInvitationEmailES,\n AffiliateInvitationEmailFR,\n AffiliateInvitationEmailHI,\n AffiliateInvitationEmailID,\n AffiliateInvitationEmailIT,\n AffiliateInvitationEmailJA,\n AffiliateInvitationEmailKO,\n AffiliateInvitationEmailPL,\n AffiliateInvitationEmailPT,\n AffiliateInvitationEmailRU,\n AffiliateInvitationEmailTR,\n AffiliateInvitationEmailUK,\n AffiliateInvitationEmailVI,\n AffiliateInvitationEmailZH,\n} from '@emails/AffiliateInvitationEmail';\nimport {\n AffiliateWelcomeEmailAR,\n AffiliateWelcomeEmailDE,\n AffiliateWelcomeEmailEN,\n AffiliateWelcomeEmailEN_GB,\n AffiliateWelcomeEmailES,\n AffiliateWelcomeEmailFR,\n AffiliateWelcomeEmailHI,\n AffiliateWelcomeEmailID,\n AffiliateWelcomeEmailIT,\n AffiliateWelcomeEmailJA,\n AffiliateWelcomeEmailKO,\n AffiliateWelcomeEmailPL,\n AffiliateWelcomeEmailPT,\n AffiliateWelcomeEmailRU,\n AffiliateWelcomeEmailTR,\n AffiliateWelcomeEmailUK,\n AffiliateWelcomeEmailVI,\n AffiliateWelcomeEmailZH,\n} from '@emails/AffiliateWelcomeEmail';\nimport {\n InviteUserEmailAR,\n InviteUserEmailDE,\n InviteUserEmailEN,\n InviteUserEmailEN_GB,\n InviteUserEmailES,\n InviteUserEmailFR,\n InviteUserEmailHI,\n InviteUserEmailID,\n InviteUserEmailIT,\n InviteUserEmailJA,\n InviteUserEmailKO,\n InviteUserEmailPL,\n InviteUserEmailPT,\n InviteUserEmailRU,\n InviteUserEmailTR,\n InviteUserEmailUK,\n InviteUserEmailVI,\n InviteUserEmailZH,\n} from '@emails/InviteUserEmail';\nimport {\n MagicLinkEmailAR,\n MagicLinkEmailDE,\n MagicLinkEmailEN,\n MagicLinkEmailEN_GB,\n MagicLinkEmailES,\n MagicLinkEmailFR,\n MagicLinkEmailHI,\n MagicLinkEmailID,\n MagicLinkEmailIT,\n MagicLinkEmailJA,\n MagicLinkEmailKO,\n MagicLinkEmailPL,\n MagicLinkEmailPT,\n MagicLinkEmailRU,\n MagicLinkEmailTR,\n MagicLinkEmailUK,\n MagicLinkEmailVI,\n MagicLinkEmailZH,\n} from '@emails/MagicLinkEmail';\nimport {\n MissionRequestedClientEmailAR,\n MissionRequestedClientEmailDE,\n MissionRequestedClientEmailEN,\n MissionRequestedClientEmailEN_GB,\n MissionRequestedClientEmailES,\n MissionRequestedClientEmailFR,\n MissionRequestedClientEmailHI,\n MissionRequestedClientEmailID,\n MissionRequestedClientEmailIT,\n MissionRequestedClientEmailJA,\n MissionRequestedClientEmailKO,\n MissionRequestedClientEmailPL,\n MissionRequestedClientEmailPT,\n MissionRequestedClientEmailRU,\n MissionRequestedClientEmailTR,\n MissionRequestedClientEmailUK,\n MissionRequestedClientEmailVI,\n MissionRequestedClientEmailZH,\n} from '@emails/MissionRequestedClientEmail';\nimport {\n MissionRequestedReviewerEmailAR,\n MissionRequestedReviewerEmailDE,\n MissionRequestedReviewerEmailEN,\n MissionRequestedReviewerEmailEN_GB,\n MissionRequestedReviewerEmailES,\n MissionRequestedReviewerEmailFR,\n MissionRequestedReviewerEmailHI,\n MissionRequestedReviewerEmailID,\n MissionRequestedReviewerEmailIT,\n MissionRequestedReviewerEmailJA,\n MissionRequestedReviewerEmailKO,\n MissionRequestedReviewerEmailPL,\n MissionRequestedReviewerEmailPT,\n MissionRequestedReviewerEmailRU,\n MissionRequestedReviewerEmailTR,\n MissionRequestedReviewerEmailUK,\n MissionRequestedReviewerEmailVI,\n MissionRequestedReviewerEmailZH,\n} from '@emails/MissionRequestedReviewerEmail';\nimport {\n OAuthTokenCreatedEmailAR,\n OAuthTokenCreatedEmailDE,\n OAuthTokenCreatedEmailEN,\n OAuthTokenCreatedEmailEN_GB,\n OAuthTokenCreatedEmailES,\n OAuthTokenCreatedEmailFR,\n OAuthTokenCreatedEmailHI,\n OAuthTokenCreatedEmailID,\n OAuthTokenCreatedEmailIT,\n OAuthTokenCreatedEmailJA,\n OAuthTokenCreatedEmailKO,\n OAuthTokenCreatedEmailPL,\n OAuthTokenCreatedEmailPT,\n OAuthTokenCreatedEmailRU,\n OAuthTokenCreatedEmailTR,\n OAuthTokenCreatedEmailUK,\n OAuthTokenCreatedEmailVI,\n OAuthTokenCreatedEmailZH,\n} from '@emails/OAuthTokenCreatedEmail';\nimport {\n PasswordChangeConfirmationEmailAR,\n PasswordChangeConfirmationEmailDE,\n PasswordChangeConfirmationEmailEN,\n PasswordChangeConfirmationEmailEN_GB,\n PasswordChangeConfirmationEmailES,\n PasswordChangeConfirmationEmailFR,\n PasswordChangeConfirmationEmailHI,\n PasswordChangeConfirmationEmailID,\n PasswordChangeConfirmationEmailIT,\n PasswordChangeConfirmationEmailJA,\n PasswordChangeConfirmationEmailKO,\n PasswordChangeConfirmationEmailPL,\n PasswordChangeConfirmationEmailPT,\n PasswordChangeConfirmationEmailRU,\n PasswordChangeConfirmationEmailTR,\n PasswordChangeConfirmationEmailUK,\n PasswordChangeConfirmationEmailVI,\n PasswordChangeConfirmationEmailZH,\n} from '@emails/PasswordChangeConfirmation';\nimport {\n ResetPasswordEmailAR,\n ResetPasswordEmailDE,\n ResetPasswordEmailEN,\n ResetPasswordEmailEN_GB,\n ResetPasswordEmailES,\n ResetPasswordEmailFR,\n ResetPasswordEmailHI,\n ResetPasswordEmailID,\n ResetPasswordEmailIT,\n ResetPasswordEmailJA,\n ResetPasswordEmailKO,\n ResetPasswordEmailPL,\n ResetPasswordEmailPT,\n ResetPasswordEmailRU,\n ResetPasswordEmailTR,\n ResetPasswordEmailUK,\n ResetPasswordEmailVI,\n ResetPasswordEmailZH,\n} from '@emails/ResetUserPassword';\nimport {\n ReviewerApplicationEmailAR,\n ReviewerApplicationEmailDE,\n ReviewerApplicationEmailEN,\n ReviewerApplicationEmailEN_GB,\n ReviewerApplicationEmailES,\n ReviewerApplicationEmailFR,\n ReviewerApplicationEmailHI,\n ReviewerApplicationEmailID,\n ReviewerApplicationEmailIT,\n ReviewerApplicationEmailJA,\n ReviewerApplicationEmailKO,\n ReviewerApplicationEmailPL,\n ReviewerApplicationEmailPT,\n ReviewerApplicationEmailRU,\n ReviewerApplicationEmailTR,\n ReviewerApplicationEmailUK,\n ReviewerApplicationEmailVI,\n ReviewerApplicationEmailZH,\n} from '@emails/ReviewerApplicationEmail';\nimport {\n ReviewerApprovedEmailAR,\n ReviewerApprovedEmailDE,\n ReviewerApprovedEmailEN,\n ReviewerApprovedEmailEN_GB,\n ReviewerApprovedEmailES,\n ReviewerApprovedEmailFR,\n ReviewerApprovedEmailHI,\n ReviewerApprovedEmailID,\n ReviewerApprovedEmailIT,\n ReviewerApprovedEmailJA,\n ReviewerApprovedEmailKO,\n ReviewerApprovedEmailPL,\n ReviewerApprovedEmailPT,\n ReviewerApprovedEmailRU,\n ReviewerApprovedEmailTR,\n ReviewerApprovedEmailUK,\n ReviewerApprovedEmailVI,\n ReviewerApprovedEmailZH,\n} from '@emails/ReviewerApprovedEmail';\nimport { ReviewerContactEmailEN } from '@emails/ReviewerContactEmail';\nimport {\n SubscriptionPaymentCancellationAR,\n SubscriptionPaymentCancellationDE,\n SubscriptionPaymentCancellationEN,\n SubscriptionPaymentCancellationEN_GB,\n SubscriptionPaymentCancellationES,\n SubscriptionPaymentCancellationFR,\n SubscriptionPaymentCancellationHI,\n SubscriptionPaymentCancellationID,\n SubscriptionPaymentCancellationIT,\n SubscriptionPaymentCancellationJA,\n SubscriptionPaymentCancellationKO,\n SubscriptionPaymentCancellationPL,\n SubscriptionPaymentCancellationPT,\n SubscriptionPaymentCancellationRU,\n SubscriptionPaymentCancellationTR,\n SubscriptionPaymentCancellationUK,\n SubscriptionPaymentCancellationVI,\n SubscriptionPaymentCancellationZH,\n} from '@emails/SubscriptionPaymentCancellation';\nimport {\n SubscriptionPaymentErrorAR,\n SubscriptionPaymentErrorDE,\n SubscriptionPaymentErrorEN,\n SubscriptionPaymentErrorEN_GB,\n SubscriptionPaymentErrorES,\n SubscriptionPaymentErrorFR,\n SubscriptionPaymentErrorHI,\n SubscriptionPaymentErrorID,\n SubscriptionPaymentErrorIT,\n SubscriptionPaymentErrorJA,\n SubscriptionPaymentErrorKO,\n SubscriptionPaymentErrorPL,\n SubscriptionPaymentErrorPT,\n SubscriptionPaymentErrorRU,\n SubscriptionPaymentErrorTR,\n SubscriptionPaymentErrorUK,\n SubscriptionPaymentErrorVI,\n SubscriptionPaymentErrorZH,\n} from '@emails/SubscriptionPaymentError';\nimport {\n SubscriptionPaymentSuccessAR,\n SubscriptionPaymentSuccessDE,\n SubscriptionPaymentSuccessEN,\n SubscriptionPaymentSuccessEN_GB,\n SubscriptionPaymentSuccessES,\n SubscriptionPaymentSuccessFR,\n SubscriptionPaymentSuccessHI,\n SubscriptionPaymentSuccessID,\n SubscriptionPaymentSuccessIT,\n SubscriptionPaymentSuccessJA,\n SubscriptionPaymentSuccessKO,\n SubscriptionPaymentSuccessPL,\n SubscriptionPaymentSuccessPT,\n SubscriptionPaymentSuccessRU,\n SubscriptionPaymentSuccessTR,\n SubscriptionPaymentSuccessUK,\n SubscriptionPaymentSuccessVI,\n SubscriptionPaymentSuccessZH,\n} from '@emails/SubscriptionPaymentSuccess';\nimport {\n ValidateUserEmailAR,\n ValidateUserEmailDE,\n ValidateUserEmailEN,\n ValidateUserEmailEN_GB,\n ValidateUserEmailES,\n ValidateUserEmailFR,\n ValidateUserEmailHI,\n ValidateUserEmailID,\n ValidateUserEmailIT,\n ValidateUserEmailJA,\n ValidateUserEmailKO,\n ValidateUserEmailPL,\n ValidateUserEmailPT,\n ValidateUserEmailRU,\n ValidateUserEmailTR,\n ValidateUserEmailUK,\n ValidateUserEmailVI,\n ValidateUserEmailZH,\n} from '@emails/ValidateUserEmail';\nimport {\n WelcomeEmailAR,\n WelcomeEmailDE,\n WelcomeEmailEN,\n WelcomeEmailEN_GB,\n WelcomeEmailES,\n WelcomeEmailFR,\n WelcomeEmailHI,\n WelcomeEmailID,\n WelcomeEmailIT,\n WelcomeEmailJA,\n WelcomeEmailKO,\n WelcomeEmailPL,\n WelcomeEmailPT,\n WelcomeEmailRU,\n WelcomeEmailTR,\n WelcomeEmailUK,\n WelcomeEmailVI,\n WelcomeEmailZH,\n} from '@emails/Welcome';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport { logger } from '@logger';\nimport { t } from 'fastify-intlayer';\nimport type { ComponentProps, JSX } from 'react';\nimport { Resend } from 'resend';\n\ntype EmailComponentsType = (...props: any) => JSX.Element;\ntype EmailComponents = {\n [key: string]: {\n template: EmailComponentsType;\n subject: string;\n };\n};\n\nconst getEmailComponents = (locale?: Locale) =>\n ({\n affiliateInvitation: {\n template: t(\n {\n en: AffiliateInvitationEmailEN,\n fr: AffiliateInvitationEmailFR,\n es: AffiliateInvitationEmailES,\n ru: AffiliateInvitationEmailRU,\n ja: AffiliateInvitationEmailJA,\n ko: AffiliateInvitationEmailKO,\n zh: AffiliateInvitationEmailZH,\n de: AffiliateInvitationEmailDE,\n ar: AffiliateInvitationEmailAR,\n it: AffiliateInvitationEmailIT,\n 'en-GB': AffiliateInvitationEmailEN_GB,\n pt: AffiliateInvitationEmailPT,\n hi: AffiliateInvitationEmailHI,\n tr: AffiliateInvitationEmailTR,\n pl: AffiliateInvitationEmailPL,\n id: AffiliateInvitationEmailID,\n vi: AffiliateInvitationEmailVI,\n uk: AffiliateInvitationEmailUK,\n },\n locale\n ),\n subject: \"You've been invited to the Intlayer Affiliate Program\",\n },\n affiliateWelcome: {\n template: t(\n {\n en: AffiliateWelcomeEmailEN,\n fr: AffiliateWelcomeEmailFR,\n es: AffiliateWelcomeEmailES,\n ru: AffiliateWelcomeEmailRU,\n ja: AffiliateWelcomeEmailJA,\n ko: AffiliateWelcomeEmailKO,\n zh: AffiliateWelcomeEmailZH,\n de: AffiliateWelcomeEmailDE,\n ar: AffiliateWelcomeEmailAR,\n it: AffiliateWelcomeEmailIT,\n 'en-GB': AffiliateWelcomeEmailEN_GB,\n pt: AffiliateWelcomeEmailPT,\n hi: AffiliateWelcomeEmailHI,\n tr: AffiliateWelcomeEmailTR,\n pl: AffiliateWelcomeEmailPL,\n id: AffiliateWelcomeEmailID,\n vi: AffiliateWelcomeEmailVI,\n uk: AffiliateWelcomeEmailUK,\n },\n locale\n ),\n subject:\n 'Welcome to the Intlayer Affiliate Program — complete your setup',\n },\n affiliateConversion: {\n template: t(\n {\n en: AffiliateConversionEmailEN,\n fr: AffiliateConversionEmailFR,\n es: AffiliateConversionEmailES,\n ru: AffiliateConversionEmailRU,\n ja: AffiliateConversionEmailJA,\n ko: AffiliateConversionEmailKO,\n zh: AffiliateConversionEmailZH,\n de: AffiliateConversionEmailDE,\n ar: AffiliateConversionEmailAR,\n it: AffiliateConversionEmailIT,\n 'en-GB': AffiliateConversionEmailEN_GB,\n pt: AffiliateConversionEmailPT,\n hi: AffiliateConversionEmailHI,\n tr: AffiliateConversionEmailTR,\n pl: AffiliateConversionEmailPL,\n id: AffiliateConversionEmailID,\n vi: AffiliateConversionEmailVI,\n uk: AffiliateConversionEmailUK,\n },\n locale\n ),\n subject: 'New referral conversion — you earned a commission!',\n },\n affiliateActivated: {\n template: t(\n {\n en: AffiliateActivatedEmailEN,\n fr: AffiliateActivatedEmailFR,\n es: AffiliateActivatedEmailES,\n ru: AffiliateActivatedEmailRU,\n ja: AffiliateActivatedEmailJA,\n ko: AffiliateActivatedEmailKO,\n zh: AffiliateActivatedEmailZH,\n de: AffiliateActivatedEmailDE,\n ar: AffiliateActivatedEmailAR,\n it: AffiliateActivatedEmailIT,\n 'en-GB': AffiliateActivatedEmailEN_GB,\n pt: AffiliateActivatedEmailPT,\n hi: AffiliateActivatedEmailHI,\n tr: AffiliateActivatedEmailTR,\n pl: AffiliateActivatedEmailPL,\n id: AffiliateActivatedEmailID,\n vi: AffiliateActivatedEmailVI,\n uk: AffiliateActivatedEmailUK,\n },\n locale\n ),\n subject: 'Your Intlayer affiliate account is now active!',\n },\n invite: {\n template: t(\n {\n en: InviteUserEmailEN,\n fr: InviteUserEmailFR,\n es: InviteUserEmailES,\n ru: InviteUserEmailRU,\n ja: InviteUserEmailJA,\n ko: InviteUserEmailKO,\n zh: InviteUserEmailZH,\n de: InviteUserEmailDE,\n ar: InviteUserEmailAR,\n it: InviteUserEmailIT,\n 'en-GB': InviteUserEmailEN_GB,\n pt: InviteUserEmailPT,\n hi: InviteUserEmailHI,\n tr: InviteUserEmailTR,\n pl: InviteUserEmailPL,\n id: InviteUserEmailID,\n vi: InviteUserEmailVI,\n uk: InviteUserEmailUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'You have been invited to join Intlayer',\n fr: 'Vous êtes invité à rejoindre Intlayer',\n es: 'Has sido invitado a unirte a Intlayer',\n ru: 'Вы были приглашены присоединиться к Intlayer',\n ja: 'Intlayerへの招待が届きました',\n ko: 'Intlayer에 초대되었습니다',\n zh: '您已受邀加入 Intlayer',\n de: 'Sie wurden eingeladen, Intlayer beizutreten',\n ar: 'لقد تم دعوتك للانضمام إلى Intlayer',\n it: 'Sei stato invitato a unirti a Intlayer',\n 'en-GB': 'You have been invited to join Intlayer',\n pt: 'Você foi convidado a se juntar ao Intlayer',\n hi: 'आपको Intlayer में शामिल होने के लिए आमंत्रित किया गया है',\n tr: \"Intlayer'a katılmanız için davet edildiniz\",\n pl: 'Zostałeś zaproszony do dołączenia do Intlayer',\n id: 'Anda telah diundang untuk bergabung dengan Intlayer',\n vi: 'Bạn đã được mời tham gia Intlayer',\n uk: 'Вас запросили приєднатися до Intlayer',\n },\n locale\n ),\n },\n validate: {\n template: t(\n {\n en: ValidateUserEmailEN,\n fr: ValidateUserEmailFR,\n es: ValidateUserEmailES,\n ru: ValidateUserEmailRU,\n ja: ValidateUserEmailJA,\n ko: ValidateUserEmailKO,\n zh: ValidateUserEmailZH,\n de: ValidateUserEmailDE,\n ar: ValidateUserEmailAR,\n it: ValidateUserEmailIT,\n 'en-GB': ValidateUserEmailEN_GB,\n pt: ValidateUserEmailPT,\n hi: ValidateUserEmailHI,\n tr: ValidateUserEmailTR,\n pl: ValidateUserEmailPL,\n id: ValidateUserEmailID,\n vi: ValidateUserEmailVI,\n uk: ValidateUserEmailUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'Validate your email for Intlayer',\n fr: 'Validez votre email pour Intlayer',\n es: 'Valida tu correo electrónico para Intlayer',\n ru: 'Подтвердите свой адрес электронной почты для Intlayer',\n ja: 'Intlayerのメールアドレスを認証してください',\n ko: 'Intlayer 이메일 주소를 인증해 주세요',\n zh: '验证您的 Intlayer 电子邮件',\n de: 'Bestätigen Sie Ihre E-Mail für Intlayer',\n ar: 'تأكيد بريدك الإلكتروني لـ Intlayer',\n it: 'Conferma la tua email per Intlayer',\n 'en-GB': 'Validate your email for Intlayer',\n pt: 'Valide seu e-mail para Intlayer',\n hi: 'Intlayer के लिए अपना ईमेल सत्यापित करें',\n tr: 'Intlayer için e-postanızı doğrulayın',\n pl: 'Zweryfikuj swój e-mail dla Intlayer',\n id: 'Validasi email Anda per Intlayer',\n vi: 'Xác thực email của bạn cho Intlayer',\n uk: 'Підтвердьте свою електронну пошту для Intlayer',\n },\n locale\n ),\n },\n resetPassword: {\n template: t(\n {\n en: ResetPasswordEmailEN,\n fr: ResetPasswordEmailFR,\n es: ResetPasswordEmailES,\n ru: ResetPasswordEmailRU,\n ja: ResetPasswordEmailJA,\n ko: ResetPasswordEmailKO,\n zh: ResetPasswordEmailZH,\n de: ResetPasswordEmailDE,\n ar: ResetPasswordEmailAR,\n it: ResetPasswordEmailIT,\n 'en-GB': ResetPasswordEmailEN_GB,\n pt: ResetPasswordEmailPT,\n hi: ResetPasswordEmailHI,\n tr: ResetPasswordEmailTR,\n pl: ResetPasswordEmailPL,\n id: ResetPasswordEmailID,\n vi: ResetPasswordEmailVI,\n uk: ResetPasswordEmailUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'Reset your password for Intlayer',\n fr: 'Réinitialisez votre mot de passe pour Intlayer',\n es: 'Restablece tu contraseña para Intlayer',\n ru: 'Сброс пароля для Intlayer',\n ja: 'Intlayerのパスワードをリセットしてください',\n ko: 'Intlayer 비밀번호를 재설정해 주세요',\n zh: '重置您的 Intlayer 密码',\n de: 'Setzen Sie Ihr Passwort für Intlayer zurück',\n ar: 'إعادة تعيين كلمة المرور لـ Intlayer',\n it: 'Reimposta la tua password per Intlayer',\n 'en-GB': 'Reset your password for Intlayer',\n pt: 'Redefina sua senha para Intlayer',\n hi: 'Intlayer के लिए अपना पासवर्ड रीसेट करें',\n tr: 'Intlayer için şifrenizi sıfırlayın',\n pl: 'Zresetuj swoje hasło do Intlayer',\n id: 'Reset kata sandi Anda untuk Intlayer',\n vi: 'Đặt lại mật khẩu của bạn cho Intlayer',\n uk: 'Скидання пароля для Intlayer',\n },\n locale\n ),\n },\n welcome: {\n template: t(\n {\n en: WelcomeEmailEN,\n fr: WelcomeEmailFR,\n es: WelcomeEmailES,\n ru: WelcomeEmailRU,\n ja: WelcomeEmailJA,\n ko: WelcomeEmailKO,\n zh: WelcomeEmailZH,\n de: WelcomeEmailDE,\n ar: WelcomeEmailAR,\n it: WelcomeEmailIT,\n 'en-GB': WelcomeEmailEN_GB,\n pt: WelcomeEmailPT,\n hi: WelcomeEmailHI,\n tr: WelcomeEmailTR,\n pl: WelcomeEmailPL,\n id: WelcomeEmailID,\n vi: WelcomeEmailVI,\n uk: WelcomeEmailUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'Welcome to Intlayer!',\n fr: 'Bienvenue chez Intlayer!',\n es: '¡Bienvenido a Intlayer!',\n ru: 'Добро пожаловать в Intlayer!',\n ja: 'Intlayerへようこそ!',\n ko: 'Intlayer에 오신 것을 환영합니다!',\n zh: '欢迎来到 Intlayer!',\n de: 'Willkommen bei Intlayer!',\n ar: 'مرحباً بك في Intlayer!',\n it: 'Benvenuto in Intlayer!',\n 'en-GB': 'Welcome to Intlayer!',\n pt: 'Bem-vindo ao Intlayer!',\n hi: 'Intlayer में आपका स्वागत है!',\n tr: \"Intlayer'a hoş geldiniz!\",\n pl: 'Witaj w Intlayer!',\n id: 'Selamat datang di Intlayer!',\n vi: 'Chào mừng bạn đến với Intlayer!',\n uk: 'Ласкаво просимо до Intlayer!',\n },\n locale\n ),\n },\n magicLink: {\n template: t(\n {\n en: MagicLinkEmailEN,\n fr: MagicLinkEmailFR,\n es: MagicLinkEmailES,\n ru: MagicLinkEmailRU,\n ja: MagicLinkEmailJA,\n ko: MagicLinkEmailKO,\n zh: MagicLinkEmailZH,\n de: MagicLinkEmailDE,\n ar: MagicLinkEmailAR,\n it: MagicLinkEmailIT,\n 'en-GB': MagicLinkEmailEN_GB,\n pt: MagicLinkEmailPT,\n hi: MagicLinkEmailHI,\n tr: MagicLinkEmailTR,\n pl: MagicLinkEmailPL,\n id: MagicLinkEmailID,\n vi: MagicLinkEmailVI,\n uk: MagicLinkEmailUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'Sign in to Intlayer',\n fr: 'Connectez-vous à Intlayer',\n es: 'Inicia sesión en Intlayer',\n ru: 'Войти в Intlayer',\n ja: 'Intlayerにサインイン',\n ko: 'Intlayer에 로그인하기',\n zh: '登录到 Intlayer',\n de: 'Bei Intlayer anmelden',\n ar: 'تسجيل الدخول إلى Intlayer',\n it: 'Accedi a Intlayer',\n 'en-GB': 'Sign in to Intlayer',\n pt: 'Entrar no Intlayer',\n hi: 'Intlayer में साइन इन करें',\n tr: \"Intlayer'a giriş yapın\",\n pl: 'Zaloguj się do Intlayer',\n id: 'Masuk ke Intlayer',\n vi: 'Đăng nhập vào Intlayer',\n uk: 'Увійти в Intlayer',\n },\n locale\n ),\n },\n passwordChangeConfirmation: {\n template: t(\n {\n en: PasswordChangeConfirmationEmailEN,\n fr: PasswordChangeConfirmationEmailFR,\n es: PasswordChangeConfirmationEmailES,\n ru: PasswordChangeConfirmationEmailRU,\n ja: PasswordChangeConfirmationEmailJA,\n ko: PasswordChangeConfirmationEmailKO,\n zh: PasswordChangeConfirmationEmailZH,\n de: PasswordChangeConfirmationEmailDE,\n ar: PasswordChangeConfirmationEmailAR,\n it: PasswordChangeConfirmationEmailIT,\n 'en-GB': PasswordChangeConfirmationEmailEN_GB,\n pt: PasswordChangeConfirmationEmailPT,\n hi: PasswordChangeConfirmationEmailHI,\n tr: PasswordChangeConfirmationEmailTR,\n pl: PasswordChangeConfirmationEmailPL,\n id: PasswordChangeConfirmationEmailID,\n vi: PasswordChangeConfirmationEmailVI,\n uk: PasswordChangeConfirmationEmailUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'Your Intlayer password has been changed',\n fr: 'Votre mot de passe Intlayer a été modifié',\n es: 'Tu contraseña de Intlayer ha sido cambiada',\n ru: 'Ваш пароль Intlayer был изменен',\n ja: 'Intlayerのパスワードが変更されました',\n ko: 'Intlayer 비밀번호가 변경되었습니다',\n zh: '您的 Intlayer 密码已更改',\n de: 'Ihr Intlayer-Passwort wurde geändert',\n ar: 'تم تغيير كلمة مرور Intlayer الخاصة بك',\n it: 'La tua password Intlayer è stata modificata',\n 'en-GB': 'Your Intlayer password has been changed',\n pt: 'Sua senha do Intlayer foi alterada',\n hi: 'आपका Intlayer पासवर्ड बदल दिया गया है',\n tr: 'Intlayer şifreniz değiştirildi',\n pl: 'Twoje hasło do Intlayer zostało zmienione',\n id: 'Kata sandi Intlayer Anda telah diubah',\n vi: 'Mật khẩu Intlayer của bạn đã được thay đổi',\n uk: 'Ваш пароль Intlayer було змінено',\n },\n locale\n ),\n },\n subscriptionPaymentSuccess: {\n template: t(\n {\n en: SubscriptionPaymentSuccessEN,\n fr: SubscriptionPaymentSuccessFR,\n es: SubscriptionPaymentSuccessES,\n ru: SubscriptionPaymentSuccessRU,\n ja: SubscriptionPaymentSuccessJA,\n ko: SubscriptionPaymentSuccessKO,\n zh: SubscriptionPaymentSuccessZH,\n de: SubscriptionPaymentSuccessDE,\n ar: SubscriptionPaymentSuccessAR,\n it: SubscriptionPaymentSuccessIT,\n 'en-GB': SubscriptionPaymentSuccessEN_GB,\n pt: SubscriptionPaymentSuccessPT,\n hi: SubscriptionPaymentSuccessHI,\n tr: SubscriptionPaymentSuccessTR,\n pl: SubscriptionPaymentSuccessPL,\n id: SubscriptionPaymentSuccessID,\n vi: SubscriptionPaymentSuccessVI,\n uk: SubscriptionPaymentSuccessUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'Your payment for Intlayer subscription is confirmed',\n fr: \"Votre paiement pour l'abonnement Intlayer est confirmé\",\n es: 'Tu pago por la suscripción de Intlayer ha sido confirmado',\n ru: 'Ваш платеж за подписку Intlayer подтвержден',\n ja: 'Intlayerサブスクリプションの支払いが確認されました',\n ko: 'Intlayer 구독 결제가 완료되었습니다',\n zh: '您的 Intlayer 订阅付款已确认',\n de: 'Ihre Zahlung für das Intlayer-Abonnement wurde bestätigt',\n ar: 'تم تأكيد دفعك لاشتراك Intlayer',\n it: \"Il tuo pagamento per l'abbonamento Intlayer è confermato\",\n 'en-GB': 'Your payment for Intlayer subscription is confirmed',\n pt: 'Seu pagamento para a assinatura Intlayer está confirmado',\n hi: 'Intlayer सदस्यता के लिए आपका भुगतान पुष्ट हो गया है',\n tr: 'Intlayer aboneliği için ödemeniz onaylandı',\n pl: 'Twoja płatność za subskrypcję Intlayer została potwierdzona',\n id: 'Pembayaran Anda untuk langganan Intlayer telah dikonfirmasi',\n vi: 'Thanh toán cho gói thuê bao Intlayer của bạn đã được xác nhận',\n uk: 'Ваш платіж за підписку Intlayer підтверджено',\n },\n locale\n ),\n },\n subscriptionPaymentCancellation: {\n template: t(\n {\n en: SubscriptionPaymentCancellationEN,\n fr: SubscriptionPaymentCancellationFR,\n es: SubscriptionPaymentCancellationES,\n ru: SubscriptionPaymentCancellationRU,\n ja: SubscriptionPaymentCancellationJA,\n ko: SubscriptionPaymentCancellationKO,\n zh: SubscriptionPaymentCancellationZH,\n de: SubscriptionPaymentCancellationDE,\n ar: SubscriptionPaymentCancellationAR,\n it: SubscriptionPaymentCancellationIT,\n 'en-GB': SubscriptionPaymentCancellationEN_GB,\n pt: SubscriptionPaymentCancellationPT,\n hi: SubscriptionPaymentCancellationHI,\n tr: SubscriptionPaymentCancellationTR,\n pl: SubscriptionPaymentCancellationPL,\n id: SubscriptionPaymentCancellationID,\n vi: SubscriptionPaymentCancellationVI,\n uk: SubscriptionPaymentCancellationUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'Your Intlayer subscription has been canceled',\n fr: 'Votre abonnement Intlayer a été annulé',\n es: 'Tu suscripción de Intlayer ha sido cancelada',\n ru: 'Ваша подписка Intlayer была отменена',\n ja: 'Intlayerサブスクリプションがキャンセルされました',\n ko: 'Intlayer 구독이 취소되었습니다',\n zh: '您的 Intlayer 订阅已取消',\n de: 'Ihr Intlayer-Abonnement wurde gekündigt',\n ar: 'تم إلغاء اشتراك Intlayer الخاص بك',\n it: 'Il tuo abbonamento Intlayer è stato annullato',\n 'en-GB': 'Your Intlayer subscription has been cancelled',\n pt: 'Sua assinatura do Intlayer foi cancelada',\n hi: 'आपकी Intlayer सदस्यता रद्द कर दी गई है',\n tr: 'Intlayer aboneliğiniz iptal edildi',\n pl: 'Twoja subskrypcja Intlayer została anulowana',\n id: 'Langganan Intlayer Anda telah dibatalkan',\n vi: 'Gói thuê bao Intlayer của bạn đã bị hủy',\n uk: 'Вашу підписку Intlayer скасовано',\n },\n locale\n ),\n },\n subscriptionPaymentError: {\n template: t(\n {\n en: SubscriptionPaymentErrorEN,\n fr: SubscriptionPaymentErrorFR,\n es: SubscriptionPaymentErrorES,\n ru: SubscriptionPaymentErrorRU,\n ja: SubscriptionPaymentErrorJA,\n ko: SubscriptionPaymentErrorKO,\n zh: SubscriptionPaymentErrorZH,\n de: SubscriptionPaymentErrorDE,\n ar: SubscriptionPaymentErrorAR,\n it: SubscriptionPaymentErrorIT,\n 'en-GB': SubscriptionPaymentErrorEN_GB,\n pt: SubscriptionPaymentErrorPT,\n hi: SubscriptionPaymentErrorHI,\n tr: SubscriptionPaymentErrorTR,\n pl: SubscriptionPaymentErrorPL,\n id: SubscriptionPaymentErrorID,\n vi: SubscriptionPaymentErrorVI,\n uk: SubscriptionPaymentErrorUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'There was an issue with your Intlayer subscription payment',\n fr: \"Un problème est survenu avec votre paiement pour l'abonnement Intlayer\",\n es: 'Hubo un problema con el pago de tu suscripción de Intlayer',\n ru: 'Возникла проблема с оплатой подписки Intlayer',\n ja: 'Intlayerサブスクリプションの支払いに問題が発生しました',\n ko: 'Intlayer 구독 결제 중 문제가 발생했습니다',\n zh: '您的 Intlayer 订阅付款出现问题',\n de: 'Es gab ein Problem mit Ihrer Intlayer-Abonnementzahlung',\n ar: 'كانت هناك مشكلة في دفع اشتراك Intlayer الخاص بك',\n it: \"C'è stato un problema con il pagamento dell'abbonamento Intlayer\",\n 'en-GB': 'There was an issue with your Intlayer subscription payment',\n pt: 'Houve um problema com o pagamento da sua assinatura Intlayer',\n hi: 'आपकी Intlayer सदस्यता के भुगतान में कोई समस्या थी',\n tr: 'Intlayer abonelik ödemenizde bir sorun oluştu',\n pl: 'Wystąpił problem z płatnością za subskrypcję Intlayer',\n id: 'Terjadi masalah dengan pembayaran langganan Intlayer Anda',\n vi: 'Đã xảy ra vấn đề với việc thanh toán gói thuê bao Intlayer của bạn',\n uk: 'Виникла проблема з оплатою підписки Intlayer',\n },\n locale\n ),\n },\n oAuthTokenCreated: {\n template: t(\n {\n en: OAuthTokenCreatedEmailEN,\n fr: OAuthTokenCreatedEmailFR,\n es: OAuthTokenCreatedEmailES,\n ru: OAuthTokenCreatedEmailRU,\n ja: OAuthTokenCreatedEmailJA,\n ko: OAuthTokenCreatedEmailKO,\n zh: OAuthTokenCreatedEmailZH,\n de: OAuthTokenCreatedEmailDE,\n ar: OAuthTokenCreatedEmailAR,\n it: OAuthTokenCreatedEmailIT,\n 'en-GB': OAuthTokenCreatedEmailEN_GB,\n pt: OAuthTokenCreatedEmailPT,\n hi: OAuthTokenCreatedEmailHI,\n tr: OAuthTokenCreatedEmailTR,\n pl: OAuthTokenCreatedEmailPL,\n id: OAuthTokenCreatedEmailID,\n vi: OAuthTokenCreatedEmailVI,\n uk: OAuthTokenCreatedEmailUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'A third-party OAuth application has been added to your Intlayer account',\n fr: 'Une application OAuth tierce a été ajoutée à votre compte Intlayer',\n es: 'Una aplicación OAuth de terceros ha sido añadida a tu cuenta de Intlayer',\n ru: 'Стороннее приложение OAuth было добавлено в ваш аккаунт Intlayer',\n ja: 'サードパーティのOAuthアプリケーションがIntlayerアカウントに追加されました',\n ko: '제3자 OAuth 애플리케이션이 Intlayer 계정에 추가되었습니다',\n zh: '一个第三方 OAuth 应用程序已添加到您的 Intlayer 帐户',\n de: 'Eine OAuth-Anwendung eines Drittanbieters wurde Ihrem Intlayer-Konto hinzugefügt',\n ar: 'تمت إضافة تطبيق OAuth لجهة خارجية إلى حساب Intlayer الخاص بك',\n it: \"Un'applicazione OAuth di terze parti è stata aggiunta al tuo account Intlayer\",\n 'en-GB':\n 'A third-party OAuth application has been added to your Intlayer account',\n pt: 'Um aplicativo OAuth de terceiros foi adicionado à sua conta do Intlayer',\n hi: 'आपके Intlayer खाते में एक तृतीय-पक्ष OAuth एप्लिकेशन जोड़ा गया है',\n tr: 'Intlayer hesabınıza üçüncü taraf bir OAuth uygulaması eklendi',\n pl: 'Do Twojego konta Intlayer dodano aplikację OAuth innej firmy',\n id: 'Aplikasi OAuth pihak ketiga telah ditambahkan ke akun Intlayer Anda',\n vi: 'Một ứng dụng OAuth của bên thứ ba đã được thêm vào tài khoản Intlayer của bạn',\n uk: 'До вашого облікового запису Intlayer додано сторонній додаток OAuth',\n },\n locale\n ),\n },\n missionRequestedClient: {\n template: t(\n {\n en: MissionRequestedClientEmailEN,\n fr: MissionRequestedClientEmailFR,\n es: MissionRequestedClientEmailES,\n ru: MissionRequestedClientEmailRU,\n ja: MissionRequestedClientEmailJA,\n ko: MissionRequestedClientEmailKO,\n zh: MissionRequestedClientEmailZH,\n de: MissionRequestedClientEmailDE,\n ar: MissionRequestedClientEmailAR,\n it: MissionRequestedClientEmailIT,\n 'en-GB': MissionRequestedClientEmailEN_GB,\n pt: MissionRequestedClientEmailPT,\n hi: MissionRequestedClientEmailHI,\n tr: MissionRequestedClientEmailTR,\n pl: MissionRequestedClientEmailPL,\n id: MissionRequestedClientEmailID,\n vi: MissionRequestedClientEmailVI,\n uk: MissionRequestedClientEmailUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'Your request has been sent — Intlayer',\n fr: 'Votre demande a été envoyée — Intlayer',\n es: 'Tu solicitud ha sido enviada — Intlayer',\n ru: 'Ваш запрос отправлен — Intlayer',\n ja: 'リクエストが送信されました — Intlayer',\n ko: '요청이 전송되었습니다 — Intlayer',\n zh: '您的请求已发送 — Intlayer',\n de: 'Ihre Anfrage wurde gesendet — Intlayer',\n ar: 'تم إرسال طلبك — Intlayer',\n it: 'La tua richiesta è stata inviata — Intlayer',\n 'en-GB': 'Your request has been sent — Intlayer',\n pt: 'Sua solicitação foi enviada — Intlayer',\n hi: 'आपका अनुरोध भेज दिया गया — Intlayer',\n tr: 'İsteğiniz gönderildi — Intlayer',\n pl: 'Twoje zgłoszenie zostało wysłane — Intlayer',\n id: 'Permintaan Anda telah dikirim — Intlayer',\n vi: 'Yêu cầu của bạn đã được gửi — Intlayer',\n uk: 'Ваш запит надіслано — Intlayer',\n },\n locale\n ),\n },\n missionRequestedReviewer: {\n template: t(\n {\n en: MissionRequestedReviewerEmailEN,\n fr: MissionRequestedReviewerEmailFR,\n es: MissionRequestedReviewerEmailES,\n ru: MissionRequestedReviewerEmailRU,\n ja: MissionRequestedReviewerEmailJA,\n ko: MissionRequestedReviewerEmailKO,\n zh: MissionRequestedReviewerEmailZH,\n de: MissionRequestedReviewerEmailDE,\n ar: MissionRequestedReviewerEmailAR,\n it: MissionRequestedReviewerEmailIT,\n 'en-GB': MissionRequestedReviewerEmailEN_GB,\n pt: MissionRequestedReviewerEmailPT,\n hi: MissionRequestedReviewerEmailHI,\n tr: MissionRequestedReviewerEmailTR,\n pl: MissionRequestedReviewerEmailPL,\n id: MissionRequestedReviewerEmailID,\n vi: MissionRequestedReviewerEmailVI,\n uk: MissionRequestedReviewerEmailUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'You have a new translation request — Intlayer',\n fr: 'Vous avez une nouvelle demande de traduction — Intlayer',\n es: 'Tienes una nueva solicitud de traducción — Intlayer',\n ru: 'У вас новый запрос на перевод — Intlayer',\n ja: '新しい翻訳リクエストがあります — Intlayer',\n ko: '새 번역 요청이 있습니다 — Intlayer',\n zh: '您有一个新的翻译请求 — Intlayer',\n de: 'Sie haben eine neue Übersetzungsanfrage — Intlayer',\n ar: 'لديك طلب ترجمة جديد — Intlayer',\n it: 'Hai una nuova richiesta di traduzione — Intlayer',\n 'en-GB': 'You have a new translation request — Intlayer',\n pt: 'Você tem uma nova solicitação de tradução — Intlayer',\n hi: 'आपके पास एक नया अनुवाद अनुरोध है — Intlayer',\n tr: 'Yeni bir çeviri talebiniz var — Intlayer',\n pl: 'Masz nowe zlecenie tłumaczenia — Intlayer',\n id: 'Anda memiliki permintaan terjemahan baru — Intlayer',\n vi: 'Bạn có một yêu cầu dịch thuật mới — Intlayer',\n uk: 'У вас новий запит на переклад — Intlayer',\n },\n locale\n ),\n },\n reviewerApplication: {\n template: t(\n {\n en: ReviewerApplicationEmailEN,\n fr: ReviewerApplicationEmailFR,\n es: ReviewerApplicationEmailES,\n ru: ReviewerApplicationEmailRU,\n ja: ReviewerApplicationEmailJA,\n ko: ReviewerApplicationEmailKO,\n zh: ReviewerApplicationEmailZH,\n de: ReviewerApplicationEmailDE,\n ar: ReviewerApplicationEmailAR,\n it: ReviewerApplicationEmailIT,\n 'en-GB': ReviewerApplicationEmailEN_GB,\n pt: ReviewerApplicationEmailPT,\n hi: ReviewerApplicationEmailHI,\n tr: ReviewerApplicationEmailTR,\n pl: ReviewerApplicationEmailPL,\n id: ReviewerApplicationEmailID,\n vi: ReviewerApplicationEmailVI,\n uk: ReviewerApplicationEmailUK,\n },\n locale\n ),\n subject: 'New reviewer application on Intlayer',\n },\n reviewerApproved: {\n template: t(\n {\n en: ReviewerApprovedEmailEN,\n fr: ReviewerApprovedEmailFR,\n es: ReviewerApprovedEmailES,\n ru: ReviewerApprovedEmailRU,\n ja: ReviewerApprovedEmailJA,\n ko: ReviewerApprovedEmailKO,\n zh: ReviewerApprovedEmailZH,\n de: ReviewerApprovedEmailDE,\n ar: ReviewerApprovedEmailAR,\n it: ReviewerApprovedEmailIT,\n 'en-GB': ReviewerApprovedEmailEN_GB,\n pt: ReviewerApprovedEmailPT,\n hi: ReviewerApprovedEmailHI,\n tr: ReviewerApprovedEmailTR,\n pl: ReviewerApprovedEmailPL,\n id: ReviewerApprovedEmailID,\n vi: ReviewerApprovedEmailVI,\n uk: ReviewerApprovedEmailUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'Your Intlayer reviewer profile has been approved!',\n fr: 'Votre profil traducteur Intlayer a été approuvé !',\n es: '¡Tu perfil de traductor en Intlayer ha sido aprobado!',\n ru: 'Ваш профиль переводчика на Intlayer одобрен!',\n ja: 'Intlayerの翻訳者プロフィールが承認されました!',\n ko: 'Intlayer 번역가 프로필이 승인되었습니다!',\n zh: '您的 Intlayer 译者档案已获批准!',\n de: 'Ihr Intlayer Übersetzer-Profil wurde genehmigt!',\n ar: 'تمت الموافقة على ملف المترجم الخاص بك في Intlayer!',\n it: 'Il tuo profilo traduttore su Intlayer è stato approvato!',\n 'en-GB': 'Your Intlayer reviewer profile has been approved!',\n pt: 'Seu perfil de tradutor no Intlayer foi aprovado!',\n hi: 'Intlayer पर आपकी अनुवादक प्रोफ़ाइल को अनुमोदित कर दिया गया है!',\n tr: 'Intlayer çevirmen profiliniz onaylandı!',\n pl: 'Twój profil tłumacza w Intlayer został zatwierdzony!',\n id: 'Profil penerjemah Intlayer Anda telah disetujui!',\n vi: 'Hồ sơ dịch giả Intlayer của bạn đã được phê duyệt!',\n uk: 'Ваш профіль перекладача на Intlayer схвалено!',\n },\n locale\n ),\n },\n reviewerContactInquiry: {\n template: ReviewerContactEmailEN,\n subject: 'New message from a client — Intlayer',\n },\n }) satisfies EmailComponents;\n\ntype EmailType = keyof ReturnType<typeof getEmailComponents>;\n\nexport type SendEmailProps<T extends EmailType> = {\n type: T;\n to: string;\n subject?: string;\n locale?: Locale;\n} & ComponentProps<ReturnType<typeof getEmailComponents>[T]['template']>;\n\nexport const sendEmail = async <T extends EmailType>({\n type,\n to,\n subject,\n locale,\n ...props\n}: SendEmailProps<T>) => {\n const resend = new Resend(process.env.RESEND_API_KEY);\n\n const emailComponents = getEmailComponents(locale);\n\n const { template, subject: baseSubject } = emailComponents[type];\n\n type EmailComponentType = (typeof emailComponents)[T]['template'];\n\n const EmailComponent: EmailComponentType = template;\n\n const react = <EmailComponent {...(props as any)} />;\n\n await resend.emails\n .send({\n from: 'Intlayer <no-reply@intlayer.org>',\n to,\n subject: subject ?? baseSubject,\n react,\n })\n .catch((err) => logger.error(err));\n\n logger.info(`Email sent ${type} to ${to}`);\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"email.service.mjs","names":["EmailComponent"],"sources":["../../../src/services/email.service.tsx"],"sourcesContent":["import {\n AffiliateActivatedEmailAR,\n AffiliateActivatedEmailDE,\n AffiliateActivatedEmailEN,\n AffiliateActivatedEmailEN_GB,\n AffiliateActivatedEmailES,\n AffiliateActivatedEmailFR,\n AffiliateActivatedEmailHI,\n AffiliateActivatedEmailID,\n AffiliateActivatedEmailIT,\n AffiliateActivatedEmailJA,\n AffiliateActivatedEmailKO,\n AffiliateActivatedEmailPL,\n AffiliateActivatedEmailPT,\n AffiliateActivatedEmailRU,\n AffiliateActivatedEmailTR,\n AffiliateActivatedEmailUK,\n AffiliateActivatedEmailVI,\n AffiliateActivatedEmailZH,\n} from '@emails/AffiliateActivatedEmail';\nimport {\n AffiliateConversionEmailAR,\n AffiliateConversionEmailDE,\n AffiliateConversionEmailEN,\n AffiliateConversionEmailEN_GB,\n AffiliateConversionEmailES,\n AffiliateConversionEmailFR,\n AffiliateConversionEmailHI,\n AffiliateConversionEmailID,\n AffiliateConversionEmailIT,\n AffiliateConversionEmailJA,\n AffiliateConversionEmailKO,\n AffiliateConversionEmailPL,\n AffiliateConversionEmailPT,\n AffiliateConversionEmailRU,\n AffiliateConversionEmailTR,\n AffiliateConversionEmailUK,\n AffiliateConversionEmailVI,\n AffiliateConversionEmailZH,\n} from '@emails/AffiliateConversionEmail';\nimport {\n AffiliateInvitationEmailAR,\n AffiliateInvitationEmailDE,\n AffiliateInvitationEmailEN,\n AffiliateInvitationEmailEN_GB,\n AffiliateInvitationEmailES,\n AffiliateInvitationEmailFR,\n AffiliateInvitationEmailHI,\n AffiliateInvitationEmailID,\n AffiliateInvitationEmailIT,\n AffiliateInvitationEmailJA,\n AffiliateInvitationEmailKO,\n AffiliateInvitationEmailPL,\n AffiliateInvitationEmailPT,\n AffiliateInvitationEmailRU,\n AffiliateInvitationEmailTR,\n AffiliateInvitationEmailUK,\n AffiliateInvitationEmailVI,\n AffiliateInvitationEmailZH,\n} from '@emails/AffiliateInvitationEmail';\nimport {\n AffiliateWelcomeEmailAR,\n AffiliateWelcomeEmailDE,\n AffiliateWelcomeEmailEN,\n AffiliateWelcomeEmailEN_GB,\n AffiliateWelcomeEmailES,\n AffiliateWelcomeEmailFR,\n AffiliateWelcomeEmailHI,\n AffiliateWelcomeEmailID,\n AffiliateWelcomeEmailIT,\n AffiliateWelcomeEmailJA,\n AffiliateWelcomeEmailKO,\n AffiliateWelcomeEmailPL,\n AffiliateWelcomeEmailPT,\n AffiliateWelcomeEmailRU,\n AffiliateWelcomeEmailTR,\n AffiliateWelcomeEmailUK,\n AffiliateWelcomeEmailVI,\n AffiliateWelcomeEmailZH,\n} from '@emails/AffiliateWelcomeEmail';\nimport {\n InviteUserEmailAR,\n InviteUserEmailDE,\n InviteUserEmailEN,\n InviteUserEmailEN_GB,\n InviteUserEmailES,\n InviteUserEmailFR,\n InviteUserEmailHI,\n InviteUserEmailID,\n InviteUserEmailIT,\n InviteUserEmailJA,\n InviteUserEmailKO,\n InviteUserEmailPL,\n InviteUserEmailPT,\n InviteUserEmailRU,\n InviteUserEmailTR,\n InviteUserEmailUK,\n InviteUserEmailVI,\n InviteUserEmailZH,\n} from '@emails/InviteUserEmail';\nimport {\n MagicLinkEmailAR,\n MagicLinkEmailDE,\n MagicLinkEmailEN,\n MagicLinkEmailEN_GB,\n MagicLinkEmailES,\n MagicLinkEmailFR,\n MagicLinkEmailHI,\n MagicLinkEmailID,\n MagicLinkEmailIT,\n MagicLinkEmailJA,\n MagicLinkEmailKO,\n MagicLinkEmailPL,\n MagicLinkEmailPT,\n MagicLinkEmailRU,\n MagicLinkEmailTR,\n MagicLinkEmailUK,\n MagicLinkEmailVI,\n MagicLinkEmailZH,\n} from '@emails/MagicLinkEmail';\nimport {\n MissionRequestedClientEmailAR,\n MissionRequestedClientEmailDE,\n MissionRequestedClientEmailEN,\n MissionRequestedClientEmailEN_GB,\n MissionRequestedClientEmailES,\n MissionRequestedClientEmailFR,\n MissionRequestedClientEmailHI,\n MissionRequestedClientEmailID,\n MissionRequestedClientEmailIT,\n MissionRequestedClientEmailJA,\n MissionRequestedClientEmailKO,\n MissionRequestedClientEmailPL,\n MissionRequestedClientEmailPT,\n MissionRequestedClientEmailRU,\n MissionRequestedClientEmailTR,\n MissionRequestedClientEmailUK,\n MissionRequestedClientEmailVI,\n MissionRequestedClientEmailZH,\n} from '@emails/MissionRequestedClientEmail';\nimport {\n MissionRequestedReviewerEmailAR,\n MissionRequestedReviewerEmailDE,\n MissionRequestedReviewerEmailEN,\n MissionRequestedReviewerEmailEN_GB,\n MissionRequestedReviewerEmailES,\n MissionRequestedReviewerEmailFR,\n MissionRequestedReviewerEmailHI,\n MissionRequestedReviewerEmailID,\n MissionRequestedReviewerEmailIT,\n MissionRequestedReviewerEmailJA,\n MissionRequestedReviewerEmailKO,\n MissionRequestedReviewerEmailPL,\n MissionRequestedReviewerEmailPT,\n MissionRequestedReviewerEmailRU,\n MissionRequestedReviewerEmailTR,\n MissionRequestedReviewerEmailUK,\n MissionRequestedReviewerEmailVI,\n MissionRequestedReviewerEmailZH,\n} from '@emails/MissionRequestedReviewerEmail';\nimport {\n OAuthTokenCreatedEmailAR,\n OAuthTokenCreatedEmailDE,\n OAuthTokenCreatedEmailEN,\n OAuthTokenCreatedEmailEN_GB,\n OAuthTokenCreatedEmailES,\n OAuthTokenCreatedEmailFR,\n OAuthTokenCreatedEmailHI,\n OAuthTokenCreatedEmailID,\n OAuthTokenCreatedEmailIT,\n OAuthTokenCreatedEmailJA,\n OAuthTokenCreatedEmailKO,\n OAuthTokenCreatedEmailPL,\n OAuthTokenCreatedEmailPT,\n OAuthTokenCreatedEmailRU,\n OAuthTokenCreatedEmailTR,\n OAuthTokenCreatedEmailUK,\n OAuthTokenCreatedEmailVI,\n OAuthTokenCreatedEmailZH,\n} from '@emails/OAuthTokenCreatedEmail';\nimport {\n PasswordChangeConfirmationEmailAR,\n PasswordChangeConfirmationEmailDE,\n PasswordChangeConfirmationEmailEN,\n PasswordChangeConfirmationEmailEN_GB,\n PasswordChangeConfirmationEmailES,\n PasswordChangeConfirmationEmailFR,\n PasswordChangeConfirmationEmailHI,\n PasswordChangeConfirmationEmailID,\n PasswordChangeConfirmationEmailIT,\n PasswordChangeConfirmationEmailJA,\n PasswordChangeConfirmationEmailKO,\n PasswordChangeConfirmationEmailPL,\n PasswordChangeConfirmationEmailPT,\n PasswordChangeConfirmationEmailRU,\n PasswordChangeConfirmationEmailTR,\n PasswordChangeConfirmationEmailUK,\n PasswordChangeConfirmationEmailVI,\n PasswordChangeConfirmationEmailZH,\n} from '@emails/PasswordChangeConfirmation';\nimport {\n ResetPasswordEmailAR,\n ResetPasswordEmailDE,\n ResetPasswordEmailEN,\n ResetPasswordEmailEN_GB,\n ResetPasswordEmailES,\n ResetPasswordEmailFR,\n ResetPasswordEmailHI,\n ResetPasswordEmailID,\n ResetPasswordEmailIT,\n ResetPasswordEmailJA,\n ResetPasswordEmailKO,\n ResetPasswordEmailPL,\n ResetPasswordEmailPT,\n ResetPasswordEmailRU,\n ResetPasswordEmailTR,\n ResetPasswordEmailUK,\n ResetPasswordEmailVI,\n ResetPasswordEmailZH,\n} from '@emails/ResetUserPassword';\nimport {\n ReviewerApplicationEmailAR,\n ReviewerApplicationEmailDE,\n ReviewerApplicationEmailEN,\n ReviewerApplicationEmailEN_GB,\n ReviewerApplicationEmailES,\n ReviewerApplicationEmailFR,\n ReviewerApplicationEmailHI,\n ReviewerApplicationEmailID,\n ReviewerApplicationEmailIT,\n ReviewerApplicationEmailJA,\n ReviewerApplicationEmailKO,\n ReviewerApplicationEmailPL,\n ReviewerApplicationEmailPT,\n ReviewerApplicationEmailRU,\n ReviewerApplicationEmailTR,\n ReviewerApplicationEmailUK,\n ReviewerApplicationEmailVI,\n ReviewerApplicationEmailZH,\n} from '@emails/ReviewerApplicationEmail';\nimport {\n ReviewerApprovedEmailAR,\n ReviewerApprovedEmailDE,\n ReviewerApprovedEmailEN,\n ReviewerApprovedEmailEN_GB,\n ReviewerApprovedEmailES,\n ReviewerApprovedEmailFR,\n ReviewerApprovedEmailHI,\n ReviewerApprovedEmailID,\n ReviewerApprovedEmailIT,\n ReviewerApprovedEmailJA,\n ReviewerApprovedEmailKO,\n ReviewerApprovedEmailPL,\n ReviewerApprovedEmailPT,\n ReviewerApprovedEmailRU,\n ReviewerApprovedEmailTR,\n ReviewerApprovedEmailUK,\n ReviewerApprovedEmailVI,\n ReviewerApprovedEmailZH,\n} from '@emails/ReviewerApprovedEmail';\nimport { ReviewerContactEmailEN } from '@emails/ReviewerContactEmail';\nimport {\n SubscriptionPaymentCancellationAR,\n SubscriptionPaymentCancellationDE,\n SubscriptionPaymentCancellationEN,\n SubscriptionPaymentCancellationEN_GB,\n SubscriptionPaymentCancellationES,\n SubscriptionPaymentCancellationFR,\n SubscriptionPaymentCancellationHI,\n SubscriptionPaymentCancellationID,\n SubscriptionPaymentCancellationIT,\n SubscriptionPaymentCancellationJA,\n SubscriptionPaymentCancellationKO,\n SubscriptionPaymentCancellationPL,\n SubscriptionPaymentCancellationPT,\n SubscriptionPaymentCancellationRU,\n SubscriptionPaymentCancellationTR,\n SubscriptionPaymentCancellationUK,\n SubscriptionPaymentCancellationVI,\n SubscriptionPaymentCancellationZH,\n} from '@emails/SubscriptionPaymentCancellation';\nimport {\n SubscriptionPaymentErrorAR,\n SubscriptionPaymentErrorDE,\n SubscriptionPaymentErrorEN,\n SubscriptionPaymentErrorEN_GB,\n SubscriptionPaymentErrorES,\n SubscriptionPaymentErrorFR,\n SubscriptionPaymentErrorHI,\n SubscriptionPaymentErrorID,\n SubscriptionPaymentErrorIT,\n SubscriptionPaymentErrorJA,\n SubscriptionPaymentErrorKO,\n SubscriptionPaymentErrorPL,\n SubscriptionPaymentErrorPT,\n SubscriptionPaymentErrorRU,\n SubscriptionPaymentErrorTR,\n SubscriptionPaymentErrorUK,\n SubscriptionPaymentErrorVI,\n SubscriptionPaymentErrorZH,\n} from '@emails/SubscriptionPaymentError';\nimport {\n SubscriptionPaymentSuccessAR,\n SubscriptionPaymentSuccessDE,\n SubscriptionPaymentSuccessEN,\n SubscriptionPaymentSuccessEN_GB,\n SubscriptionPaymentSuccessES,\n SubscriptionPaymentSuccessFR,\n SubscriptionPaymentSuccessHI,\n SubscriptionPaymentSuccessID,\n SubscriptionPaymentSuccessIT,\n SubscriptionPaymentSuccessJA,\n SubscriptionPaymentSuccessKO,\n SubscriptionPaymentSuccessPL,\n SubscriptionPaymentSuccessPT,\n SubscriptionPaymentSuccessRU,\n SubscriptionPaymentSuccessTR,\n SubscriptionPaymentSuccessUK,\n SubscriptionPaymentSuccessVI,\n SubscriptionPaymentSuccessZH,\n} from '@emails/SubscriptionPaymentSuccess';\nimport {\n ValidateUserEmailAR,\n ValidateUserEmailDE,\n ValidateUserEmailEN,\n ValidateUserEmailEN_GB,\n ValidateUserEmailES,\n ValidateUserEmailFR,\n ValidateUserEmailHI,\n ValidateUserEmailID,\n ValidateUserEmailIT,\n ValidateUserEmailJA,\n ValidateUserEmailKO,\n ValidateUserEmailPL,\n ValidateUserEmailPT,\n ValidateUserEmailRU,\n ValidateUserEmailTR,\n ValidateUserEmailUK,\n ValidateUserEmailVI,\n ValidateUserEmailZH,\n} from '@emails/ValidateUserEmail';\nimport {\n WelcomeEmailAR,\n WelcomeEmailDE,\n WelcomeEmailEN,\n WelcomeEmailEN_GB,\n WelcomeEmailES,\n WelcomeEmailFR,\n WelcomeEmailHI,\n WelcomeEmailID,\n WelcomeEmailIT,\n WelcomeEmailJA,\n WelcomeEmailKO,\n WelcomeEmailPL,\n WelcomeEmailPT,\n WelcomeEmailRU,\n WelcomeEmailTR,\n WelcomeEmailUK,\n WelcomeEmailVI,\n WelcomeEmailZH,\n} from '@emails/Welcome';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport { logger } from '@logger';\nimport { t } from 'fastify-intlayer';\nimport type { ComponentProps, JSX } from 'react';\nimport { Resend } from 'resend';\n\ntype EmailComponentsType = (...props: any) => JSX.Element;\ntype EmailComponents = {\n [key: string]: {\n template: EmailComponentsType;\n subject: string;\n };\n};\n\nconst getEmailComponents = (locale?: Locale) =>\n ({\n affiliateInvitation: {\n template: t(\n {\n en: AffiliateInvitationEmailEN,\n fr: AffiliateInvitationEmailFR,\n es: AffiliateInvitationEmailES,\n ru: AffiliateInvitationEmailRU,\n ja: AffiliateInvitationEmailJA,\n ko: AffiliateInvitationEmailKO,\n zh: AffiliateInvitationEmailZH,\n de: AffiliateInvitationEmailDE,\n ar: AffiliateInvitationEmailAR,\n it: AffiliateInvitationEmailIT,\n 'en-GB': AffiliateInvitationEmailEN_GB,\n pt: AffiliateInvitationEmailPT,\n hi: AffiliateInvitationEmailHI,\n tr: AffiliateInvitationEmailTR,\n pl: AffiliateInvitationEmailPL,\n id: AffiliateInvitationEmailID,\n vi: AffiliateInvitationEmailVI,\n uk: AffiliateInvitationEmailUK,\n },\n locale\n ),\n subject: \"You've been invited to the Intlayer Affiliate Program\",\n },\n affiliateWelcome: {\n template: t(\n {\n en: AffiliateWelcomeEmailEN,\n fr: AffiliateWelcomeEmailFR,\n es: AffiliateWelcomeEmailES,\n ru: AffiliateWelcomeEmailRU,\n ja: AffiliateWelcomeEmailJA,\n ko: AffiliateWelcomeEmailKO,\n zh: AffiliateWelcomeEmailZH,\n de: AffiliateWelcomeEmailDE,\n ar: AffiliateWelcomeEmailAR,\n it: AffiliateWelcomeEmailIT,\n 'en-GB': AffiliateWelcomeEmailEN_GB,\n pt: AffiliateWelcomeEmailPT,\n hi: AffiliateWelcomeEmailHI,\n tr: AffiliateWelcomeEmailTR,\n pl: AffiliateWelcomeEmailPL,\n id: AffiliateWelcomeEmailID,\n vi: AffiliateWelcomeEmailVI,\n uk: AffiliateWelcomeEmailUK,\n },\n locale\n ),\n subject:\n 'Welcome to the Intlayer Affiliate Program — complete your setup',\n },\n affiliateConversion: {\n template: t(\n {\n en: AffiliateConversionEmailEN,\n fr: AffiliateConversionEmailFR,\n es: AffiliateConversionEmailES,\n ru: AffiliateConversionEmailRU,\n ja: AffiliateConversionEmailJA,\n ko: AffiliateConversionEmailKO,\n zh: AffiliateConversionEmailZH,\n de: AffiliateConversionEmailDE,\n ar: AffiliateConversionEmailAR,\n it: AffiliateConversionEmailIT,\n 'en-GB': AffiliateConversionEmailEN_GB,\n pt: AffiliateConversionEmailPT,\n hi: AffiliateConversionEmailHI,\n tr: AffiliateConversionEmailTR,\n pl: AffiliateConversionEmailPL,\n id: AffiliateConversionEmailID,\n vi: AffiliateConversionEmailVI,\n uk: AffiliateConversionEmailUK,\n },\n locale\n ),\n subject: 'New referral conversion — you earned a commission!',\n },\n affiliateActivated: {\n template: t(\n {\n en: AffiliateActivatedEmailEN,\n fr: AffiliateActivatedEmailFR,\n es: AffiliateActivatedEmailES,\n ru: AffiliateActivatedEmailRU,\n ja: AffiliateActivatedEmailJA,\n ko: AffiliateActivatedEmailKO,\n zh: AffiliateActivatedEmailZH,\n de: AffiliateActivatedEmailDE,\n ar: AffiliateActivatedEmailAR,\n it: AffiliateActivatedEmailIT,\n 'en-GB': AffiliateActivatedEmailEN_GB,\n pt: AffiliateActivatedEmailPT,\n hi: AffiliateActivatedEmailHI,\n tr: AffiliateActivatedEmailTR,\n pl: AffiliateActivatedEmailPL,\n id: AffiliateActivatedEmailID,\n vi: AffiliateActivatedEmailVI,\n uk: AffiliateActivatedEmailUK,\n },\n locale\n ),\n subject: 'Your Intlayer affiliate account is now active!',\n },\n invite: {\n template: t(\n {\n en: InviteUserEmailEN,\n fr: InviteUserEmailFR,\n es: InviteUserEmailES,\n ru: InviteUserEmailRU,\n ja: InviteUserEmailJA,\n ko: InviteUserEmailKO,\n zh: InviteUserEmailZH,\n de: InviteUserEmailDE,\n ar: InviteUserEmailAR,\n it: InviteUserEmailIT,\n 'en-GB': InviteUserEmailEN_GB,\n pt: InviteUserEmailPT,\n hi: InviteUserEmailHI,\n tr: InviteUserEmailTR,\n pl: InviteUserEmailPL,\n id: InviteUserEmailID,\n vi: InviteUserEmailVI,\n uk: InviteUserEmailUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'You have been invited to join Intlayer',\n fr: 'Vous êtes invité à rejoindre Intlayer',\n es: 'Has sido invitado a unirte a Intlayer',\n ru: 'Вы были приглашены присоединиться к Intlayer',\n ja: 'Intlayerへの招待が届きました',\n ko: 'Intlayer에 초대되었습니다',\n zh: '您已受邀加入 Intlayer',\n de: 'Sie wurden eingeladen, Intlayer beizutreten',\n ar: 'لقد تم دعوتك للانضمام إلى Intlayer',\n it: 'Sei stato invitato a unirti a Intlayer',\n 'en-GB': 'You have been invited to join Intlayer',\n pt: 'Você foi convidado a se juntar ao Intlayer',\n hi: 'आपको Intlayer में शामिल होने के लिए आमंत्रित किया गया है',\n tr: \"Intlayer'a katılmanız için davet edildiniz\",\n pl: 'Zostałeś zaproszony do dołączenia do Intlayer',\n id: 'Anda telah diundang untuk bergabung dengan Intlayer',\n vi: 'Bạn đã được mời tham gia Intlayer',\n uk: 'Вас запросили приєднатися до Intlayer',\n },\n locale\n ),\n },\n validate: {\n template: t(\n {\n en: ValidateUserEmailEN,\n fr: ValidateUserEmailFR,\n es: ValidateUserEmailES,\n ru: ValidateUserEmailRU,\n ja: ValidateUserEmailJA,\n ko: ValidateUserEmailKO,\n zh: ValidateUserEmailZH,\n de: ValidateUserEmailDE,\n ar: ValidateUserEmailAR,\n it: ValidateUserEmailIT,\n 'en-GB': ValidateUserEmailEN_GB,\n pt: ValidateUserEmailPT,\n hi: ValidateUserEmailHI,\n tr: ValidateUserEmailTR,\n pl: ValidateUserEmailPL,\n id: ValidateUserEmailID,\n vi: ValidateUserEmailVI,\n uk: ValidateUserEmailUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'Validate your email for Intlayer',\n fr: 'Validez votre email pour Intlayer',\n es: 'Valida tu correo electrónico para Intlayer',\n ru: 'Подтвердите свой адрес электронной почты для Intlayer',\n ja: 'Intlayerのメールアドレスを認証してください',\n ko: 'Intlayer 이메일 주소를 인증해 주세요',\n zh: '验证您的 Intlayer 电子邮件',\n de: 'Bestätigen Sie Ihre E-Mail für Intlayer',\n ar: 'تأكيد بريدك الإلكتروني لـ Intlayer',\n it: 'Conferma la tua email per Intlayer',\n 'en-GB': 'Validate your email for Intlayer',\n pt: 'Valide seu e-mail para Intlayer',\n hi: 'Intlayer के लिए अपना ईमेल सत्यापित करें',\n tr: 'Intlayer için e-postanızı doğrulayın',\n pl: 'Zweryfikuj swój e-mail dla Intlayer',\n id: 'Validasi email Anda per Intlayer',\n vi: 'Xác thực email của bạn cho Intlayer',\n uk: 'Підтвердьте свою електронну пошту для Intlayer',\n },\n locale\n ),\n },\n resetPassword: {\n template: t(\n {\n en: ResetPasswordEmailEN,\n fr: ResetPasswordEmailFR,\n es: ResetPasswordEmailES,\n ru: ResetPasswordEmailRU,\n ja: ResetPasswordEmailJA,\n ko: ResetPasswordEmailKO,\n zh: ResetPasswordEmailZH,\n de: ResetPasswordEmailDE,\n ar: ResetPasswordEmailAR,\n it: ResetPasswordEmailIT,\n 'en-GB': ResetPasswordEmailEN_GB,\n pt: ResetPasswordEmailPT,\n hi: ResetPasswordEmailHI,\n tr: ResetPasswordEmailTR,\n pl: ResetPasswordEmailPL,\n id: ResetPasswordEmailID,\n vi: ResetPasswordEmailVI,\n uk: ResetPasswordEmailUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'Reset your password for Intlayer',\n fr: 'Réinitialisez votre mot de passe pour Intlayer',\n es: 'Restablece tu contraseña para Intlayer',\n ru: 'Сброс пароля для Intlayer',\n ja: 'Intlayerのパスワードをリセットしてください',\n ko: 'Intlayer 비밀번호를 재설정해 주세요',\n zh: '重置您的 Intlayer 密码',\n de: 'Setzen Sie Ihr Passwort für Intlayer zurück',\n ar: 'إعادة تعيين كلمة المرور لـ Intlayer',\n it: 'Reimposta la tua password per Intlayer',\n 'en-GB': 'Reset your password for Intlayer',\n pt: 'Redefina sua senha para Intlayer',\n hi: 'Intlayer के लिए अपना पासवर्ड रीसेट करें',\n tr: 'Intlayer için şifrenizi sıfırlayın',\n pl: 'Zresetuj swoje hasło do Intlayer',\n id: 'Reset kata sandi Anda untuk Intlayer',\n vi: 'Đặt lại mật khẩu của bạn cho Intlayer',\n uk: 'Скидання пароля для Intlayer',\n },\n locale\n ),\n },\n welcome: {\n template: t(\n {\n en: WelcomeEmailEN,\n fr: WelcomeEmailFR,\n es: WelcomeEmailES,\n ru: WelcomeEmailRU,\n ja: WelcomeEmailJA,\n ko: WelcomeEmailKO,\n zh: WelcomeEmailZH,\n de: WelcomeEmailDE,\n ar: WelcomeEmailAR,\n it: WelcomeEmailIT,\n 'en-GB': WelcomeEmailEN_GB,\n pt: WelcomeEmailPT,\n hi: WelcomeEmailHI,\n tr: WelcomeEmailTR,\n pl: WelcomeEmailPL,\n id: WelcomeEmailID,\n vi: WelcomeEmailVI,\n uk: WelcomeEmailUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'Welcome to Intlayer!',\n fr: 'Bienvenue chez Intlayer!',\n es: '¡Bienvenido a Intlayer!',\n ru: 'Добро пожаловать в Intlayer!',\n ja: 'Intlayerへようこそ!',\n ko: 'Intlayer에 오신 것을 환영합니다!',\n zh: '欢迎来到 Intlayer!',\n de: 'Willkommen bei Intlayer!',\n ar: 'مرحباً بك في Intlayer!',\n it: 'Benvenuto in Intlayer!',\n 'en-GB': 'Welcome to Intlayer!',\n pt: 'Bem-vindo ao Intlayer!',\n hi: 'Intlayer में आपका स्वागत है!',\n tr: \"Intlayer'a hoş geldiniz!\",\n pl: 'Witaj w Intlayer!',\n id: 'Selamat datang di Intlayer!',\n vi: 'Chào mừng bạn đến với Intlayer!',\n uk: 'Ласкаво просимо до Intlayer!',\n },\n locale\n ),\n },\n magicLink: {\n template: t(\n {\n en: MagicLinkEmailEN,\n fr: MagicLinkEmailFR,\n es: MagicLinkEmailES,\n ru: MagicLinkEmailRU,\n ja: MagicLinkEmailJA,\n ko: MagicLinkEmailKO,\n zh: MagicLinkEmailZH,\n de: MagicLinkEmailDE,\n ar: MagicLinkEmailAR,\n it: MagicLinkEmailIT,\n 'en-GB': MagicLinkEmailEN_GB,\n pt: MagicLinkEmailPT,\n hi: MagicLinkEmailHI,\n tr: MagicLinkEmailTR,\n pl: MagicLinkEmailPL,\n id: MagicLinkEmailID,\n vi: MagicLinkEmailVI,\n uk: MagicLinkEmailUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'Sign in to Intlayer',\n fr: 'Connectez-vous à Intlayer',\n es: 'Inicia sesión en Intlayer',\n ru: 'Войти в Intlayer',\n ja: 'Intlayerにサインイン',\n ko: 'Intlayer에 로그인하기',\n zh: '登录到 Intlayer',\n de: 'Bei Intlayer anmelden',\n ar: 'تسجيل الدخول إلى Intlayer',\n it: 'Accedi a Intlayer',\n 'en-GB': 'Sign in to Intlayer',\n pt: 'Entrar no Intlayer',\n hi: 'Intlayer में साइन इन करें',\n tr: \"Intlayer'a giriş yapın\",\n pl: 'Zaloguj się do Intlayer',\n id: 'Masuk ke Intlayer',\n vi: 'Đăng nhập vào Intlayer',\n uk: 'Увійти в Intlayer',\n },\n locale\n ),\n },\n passwordChangeConfirmation: {\n template: t(\n {\n en: PasswordChangeConfirmationEmailEN,\n fr: PasswordChangeConfirmationEmailFR,\n es: PasswordChangeConfirmationEmailES,\n ru: PasswordChangeConfirmationEmailRU,\n ja: PasswordChangeConfirmationEmailJA,\n ko: PasswordChangeConfirmationEmailKO,\n zh: PasswordChangeConfirmationEmailZH,\n de: PasswordChangeConfirmationEmailDE,\n ar: PasswordChangeConfirmationEmailAR,\n it: PasswordChangeConfirmationEmailIT,\n 'en-GB': PasswordChangeConfirmationEmailEN_GB,\n pt: PasswordChangeConfirmationEmailPT,\n hi: PasswordChangeConfirmationEmailHI,\n tr: PasswordChangeConfirmationEmailTR,\n pl: PasswordChangeConfirmationEmailPL,\n id: PasswordChangeConfirmationEmailID,\n vi: PasswordChangeConfirmationEmailVI,\n uk: PasswordChangeConfirmationEmailUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'Your Intlayer password has been changed',\n fr: 'Votre mot de passe Intlayer a été modifié',\n es: 'Tu contraseña de Intlayer ha sido cambiada',\n ru: 'Ваш пароль Intlayer был изменен',\n ja: 'Intlayerのパスワードが変更されました',\n ko: 'Intlayer 비밀번호가 변경되었습니다',\n zh: '您的 Intlayer 密码已更改',\n de: 'Ihr Intlayer-Passwort wurde geändert',\n ar: 'تم تغيير كلمة مرور Intlayer الخاصة بك',\n it: 'La tua password Intlayer è stata modificata',\n 'en-GB': 'Your Intlayer password has been changed',\n pt: 'Sua senha do Intlayer foi alterada',\n hi: 'आपका Intlayer पासवर्ड बदल दिया गया है',\n tr: 'Intlayer şifreniz değiştirildi',\n pl: 'Twoje hasło do Intlayer zostało zmienione',\n id: 'Kata sandi Intlayer Anda telah diubah',\n vi: 'Mật khẩu Intlayer của bạn đã được thay đổi',\n uk: 'Ваш пароль Intlayer було змінено',\n },\n locale\n ),\n },\n subscriptionPaymentSuccess: {\n template: t(\n {\n en: SubscriptionPaymentSuccessEN,\n fr: SubscriptionPaymentSuccessFR,\n es: SubscriptionPaymentSuccessES,\n ru: SubscriptionPaymentSuccessRU,\n ja: SubscriptionPaymentSuccessJA,\n ko: SubscriptionPaymentSuccessKO,\n zh: SubscriptionPaymentSuccessZH,\n de: SubscriptionPaymentSuccessDE,\n ar: SubscriptionPaymentSuccessAR,\n it: SubscriptionPaymentSuccessIT,\n 'en-GB': SubscriptionPaymentSuccessEN_GB,\n pt: SubscriptionPaymentSuccessPT,\n hi: SubscriptionPaymentSuccessHI,\n tr: SubscriptionPaymentSuccessTR,\n pl: SubscriptionPaymentSuccessPL,\n id: SubscriptionPaymentSuccessID,\n vi: SubscriptionPaymentSuccessVI,\n uk: SubscriptionPaymentSuccessUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'Your payment for Intlayer subscription is confirmed',\n fr: \"Votre paiement pour l'abonnement Intlayer est confirmé\",\n es: 'Tu pago por la suscripción de Intlayer ha sido confirmado',\n ru: 'Ваш платеж за подписку Intlayer подтвержден',\n ja: 'Intlayerサブスクリプションの支払いが確認されました',\n ko: 'Intlayer 구독 결제가 완료되었습니다',\n zh: '您的 Intlayer 订阅付款已确认',\n de: 'Ihre Zahlung für das Intlayer-Abonnement wurde bestätigt',\n ar: 'تم تأكيد دفعك لاشتراك Intlayer',\n it: \"Il tuo pagamento per l'abbonamento Intlayer è confermato\",\n 'en-GB': 'Your payment for Intlayer subscription is confirmed',\n pt: 'Seu pagamento para a assinatura Intlayer está confirmado',\n hi: 'Intlayer सदस्यता के लिए आपका भुगतान पुष्ट हो गया है',\n tr: 'Intlayer aboneliği için ödemeniz onaylandı',\n pl: 'Twoja płatność za subskrypcję Intlayer została potwierdzona',\n id: 'Pembayaran Anda untuk langganan Intlayer telah dikonfirmasi',\n vi: 'Thanh toán cho gói thuê bao Intlayer của bạn đã được xác nhận',\n uk: 'Ваш платіж за підписку Intlayer підтверджено',\n },\n locale\n ),\n },\n subscriptionPaymentCancellation: {\n template: t(\n {\n en: SubscriptionPaymentCancellationEN,\n fr: SubscriptionPaymentCancellationFR,\n es: SubscriptionPaymentCancellationES,\n ru: SubscriptionPaymentCancellationRU,\n ja: SubscriptionPaymentCancellationJA,\n ko: SubscriptionPaymentCancellationKO,\n zh: SubscriptionPaymentCancellationZH,\n de: SubscriptionPaymentCancellationDE,\n ar: SubscriptionPaymentCancellationAR,\n it: SubscriptionPaymentCancellationIT,\n 'en-GB': SubscriptionPaymentCancellationEN_GB,\n pt: SubscriptionPaymentCancellationPT,\n hi: SubscriptionPaymentCancellationHI,\n tr: SubscriptionPaymentCancellationTR,\n pl: SubscriptionPaymentCancellationPL,\n id: SubscriptionPaymentCancellationID,\n vi: SubscriptionPaymentCancellationVI,\n uk: SubscriptionPaymentCancellationUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'Your Intlayer subscription has been canceled',\n fr: 'Votre abonnement Intlayer a été annulé',\n es: 'Tu suscripción de Intlayer ha sido cancelada',\n ru: 'Ваша подписка Intlayer была отменена',\n ja: 'Intlayerサブスクリプションがキャンセルされました',\n ko: 'Intlayer 구독이 취소되었습니다',\n zh: '您的 Intlayer 订阅已取消',\n de: 'Ihr Intlayer-Abonnement wurde gekündigt',\n ar: 'تم إلغاء اشتراك Intlayer الخاص بك',\n it: 'Il tuo abbonamento Intlayer è stato annullato',\n 'en-GB': 'Your Intlayer subscription has been cancelled',\n pt: 'Sua assinatura do Intlayer foi cancelada',\n hi: 'आपकी Intlayer सदस्यता रद्द कर दी गई है',\n tr: 'Intlayer aboneliğiniz iptal edildi',\n pl: 'Twoja subskrypcja Intlayer została anulowana',\n id: 'Langganan Intlayer Anda telah dibatalkan',\n vi: 'Gói thuê bao Intlayer của bạn đã bị hủy',\n uk: 'Вашу підписку Intlayer скасовано',\n },\n locale\n ),\n },\n subscriptionPaymentError: {\n template: t(\n {\n en: SubscriptionPaymentErrorEN,\n fr: SubscriptionPaymentErrorFR,\n es: SubscriptionPaymentErrorES,\n ru: SubscriptionPaymentErrorRU,\n ja: SubscriptionPaymentErrorJA,\n ko: SubscriptionPaymentErrorKO,\n zh: SubscriptionPaymentErrorZH,\n de: SubscriptionPaymentErrorDE,\n ar: SubscriptionPaymentErrorAR,\n it: SubscriptionPaymentErrorIT,\n 'en-GB': SubscriptionPaymentErrorEN_GB,\n pt: SubscriptionPaymentErrorPT,\n hi: SubscriptionPaymentErrorHI,\n tr: SubscriptionPaymentErrorTR,\n pl: SubscriptionPaymentErrorPL,\n id: SubscriptionPaymentErrorID,\n vi: SubscriptionPaymentErrorVI,\n uk: SubscriptionPaymentErrorUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'There was an issue with your Intlayer subscription payment',\n fr: \"Un problème est survenu avec votre paiement pour l'abonnement Intlayer\",\n es: 'Hubo un problema con el pago de tu suscripción de Intlayer',\n ru: 'Возникла проблема с оплатой подписки Intlayer',\n ja: 'Intlayerサブスクリプションの支払いに問題が発生しました',\n ko: 'Intlayer 구독 결제 중 문제가 발생했습니다',\n zh: '您的 Intlayer 订阅付款出现问题',\n de: 'Es gab ein Problem mit Ihrer Intlayer-Abonnementzahlung',\n ar: 'كانت هناك مشكلة في دفع اشتراك Intlayer الخاص بك',\n it: \"C'è stato un problema con il pagamento dell'abbonamento Intlayer\",\n 'en-GB': 'There was an issue with your Intlayer subscription payment',\n pt: 'Houve um problema com o pagamento da sua assinatura Intlayer',\n hi: 'आपकी Intlayer सदस्यता के भुगतान में कोई समस्या थी',\n tr: 'Intlayer abonelik ödemenizde bir sorun oluştu',\n pl: 'Wystąpił problem z płatnością za subskrypcję Intlayer',\n id: 'Terjadi masalah dengan pembayaran langganan Intlayer Anda',\n vi: 'Đã xảy ra vấn đề với việc thanh toán gói thuê bao Intlayer của bạn',\n uk: 'Виникла проблема з оплатою підписки Intlayer',\n },\n locale\n ),\n },\n oAuthTokenCreated: {\n template: t(\n {\n en: OAuthTokenCreatedEmailEN,\n fr: OAuthTokenCreatedEmailFR,\n es: OAuthTokenCreatedEmailES,\n ru: OAuthTokenCreatedEmailRU,\n ja: OAuthTokenCreatedEmailJA,\n ko: OAuthTokenCreatedEmailKO,\n zh: OAuthTokenCreatedEmailZH,\n de: OAuthTokenCreatedEmailDE,\n ar: OAuthTokenCreatedEmailAR,\n it: OAuthTokenCreatedEmailIT,\n 'en-GB': OAuthTokenCreatedEmailEN_GB,\n pt: OAuthTokenCreatedEmailPT,\n hi: OAuthTokenCreatedEmailHI,\n tr: OAuthTokenCreatedEmailTR,\n pl: OAuthTokenCreatedEmailPL,\n id: OAuthTokenCreatedEmailID,\n vi: OAuthTokenCreatedEmailVI,\n uk: OAuthTokenCreatedEmailUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'A third-party OAuth application has been added to your Intlayer account',\n fr: 'Une application OAuth tierce a été ajoutée à votre compte Intlayer',\n es: 'Una aplicación OAuth de terceros ha sido añadida a tu cuenta de Intlayer',\n ru: 'Стороннее приложение OAuth было добавлено в ваш аккаунт Intlayer',\n ja: 'サードパーティのOAuthアプリケーションがIntlayerアカウントに追加されました',\n ko: '제3자 OAuth 애플리케이션이 Intlayer 계정에 추가되었습니다',\n zh: '一个第三方 OAuth 应用程序已添加到您的 Intlayer 帐户',\n de: 'Eine OAuth-Anwendung eines Drittanbieters wurde Ihrem Intlayer-Konto hinzugefügt',\n ar: 'تمت إضافة تطبيق OAuth لجهة خارجية إلى حساب Intlayer الخاص بك',\n it: \"Un'applicazione OAuth di terze parti è stata aggiunta al tuo account Intlayer\",\n 'en-GB':\n 'A third-party OAuth application has been added to your Intlayer account',\n pt: 'Um aplicativo OAuth de terceiros foi adicionado à sua conta do Intlayer',\n hi: 'आपके Intlayer खाते में एक तृतीय-पक्ष OAuth एप्लिकेशन जोड़ा गया है',\n tr: 'Intlayer hesabınıza üçüncü taraf bir OAuth uygulaması eklendi',\n pl: 'Do Twojego konta Intlayer dodano aplikację OAuth innej firmy',\n id: 'Aplikasi OAuth pihak ketiga telah ditambahkan ke akun Intlayer Anda',\n vi: 'Một ứng dụng OAuth của bên thứ ba đã được thêm vào tài khoản Intlayer của bạn',\n uk: 'До вашого облікового запису Intlayer додано сторонній додаток OAuth',\n },\n locale\n ),\n },\n missionRequestedClient: {\n template: t(\n {\n en: MissionRequestedClientEmailEN,\n fr: MissionRequestedClientEmailFR,\n es: MissionRequestedClientEmailES,\n ru: MissionRequestedClientEmailRU,\n ja: MissionRequestedClientEmailJA,\n ko: MissionRequestedClientEmailKO,\n zh: MissionRequestedClientEmailZH,\n de: MissionRequestedClientEmailDE,\n ar: MissionRequestedClientEmailAR,\n it: MissionRequestedClientEmailIT,\n 'en-GB': MissionRequestedClientEmailEN_GB,\n pt: MissionRequestedClientEmailPT,\n hi: MissionRequestedClientEmailHI,\n tr: MissionRequestedClientEmailTR,\n pl: MissionRequestedClientEmailPL,\n id: MissionRequestedClientEmailID,\n vi: MissionRequestedClientEmailVI,\n uk: MissionRequestedClientEmailUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'Your request has been sent — Intlayer',\n fr: 'Votre demande a été envoyée — Intlayer',\n es: 'Tu solicitud ha sido enviada — Intlayer',\n ru: 'Ваш запрос отправлен — Intlayer',\n ja: 'リクエストが送信されました — Intlayer',\n ko: '요청이 전송되었습니다 — Intlayer',\n zh: '您的请求已发送 — Intlayer',\n de: 'Ihre Anfrage wurde gesendet — Intlayer',\n ar: 'تم إرسال طلبك — Intlayer',\n it: 'La tua richiesta è stata inviata — Intlayer',\n 'en-GB': 'Your request has been sent — Intlayer',\n pt: 'Sua solicitação foi enviada — Intlayer',\n hi: 'आपका अनुरोध भेज दिया गया — Intlayer',\n tr: 'İsteğiniz gönderildi — Intlayer',\n pl: 'Twoje zgłoszenie zostało wysłane — Intlayer',\n id: 'Permintaan Anda telah dikirim — Intlayer',\n vi: 'Yêu cầu của bạn đã được gửi — Intlayer',\n uk: 'Ваш запит надіслано — Intlayer',\n },\n locale\n ),\n },\n missionRequestedReviewer: {\n template: t(\n {\n en: MissionRequestedReviewerEmailEN,\n fr: MissionRequestedReviewerEmailFR,\n es: MissionRequestedReviewerEmailES,\n ru: MissionRequestedReviewerEmailRU,\n ja: MissionRequestedReviewerEmailJA,\n ko: MissionRequestedReviewerEmailKO,\n zh: MissionRequestedReviewerEmailZH,\n de: MissionRequestedReviewerEmailDE,\n ar: MissionRequestedReviewerEmailAR,\n it: MissionRequestedReviewerEmailIT,\n 'en-GB': MissionRequestedReviewerEmailEN_GB,\n pt: MissionRequestedReviewerEmailPT,\n hi: MissionRequestedReviewerEmailHI,\n tr: MissionRequestedReviewerEmailTR,\n pl: MissionRequestedReviewerEmailPL,\n id: MissionRequestedReviewerEmailID,\n vi: MissionRequestedReviewerEmailVI,\n uk: MissionRequestedReviewerEmailUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'You have a new translation request — Intlayer',\n fr: 'Vous avez une nouvelle demande de traduction — Intlayer',\n es: 'Tienes una nueva solicitud de traducción — Intlayer',\n ru: 'У вас новый запрос на перевод — Intlayer',\n ja: '新しい翻訳リクエストがあります — Intlayer',\n ko: '새 번역 요청이 있습니다 — Intlayer',\n zh: '您有一个新的翻译请求 — Intlayer',\n de: 'Sie haben eine neue Übersetzungsanfrage — Intlayer',\n ar: 'لديك طلب ترجمة جديد — Intlayer',\n it: 'Hai una nuova richiesta di traduzione — Intlayer',\n 'en-GB': 'You have a new translation request — Intlayer',\n pt: 'Você tem uma nova solicitação de tradução — Intlayer',\n hi: 'आपके पास एक नया अनुवाद अनुरोध है — Intlayer',\n tr: 'Yeni bir çeviri talebiniz var — Intlayer',\n pl: 'Masz nowe zlecenie tłumaczenia — Intlayer',\n id: 'Anda memiliki permintaan terjemahan baru — Intlayer',\n vi: 'Bạn có một yêu cầu dịch thuật mới — Intlayer',\n uk: 'У вас новий запит на переклад — Intlayer',\n },\n locale\n ),\n },\n reviewerApplication: {\n template: t(\n {\n en: ReviewerApplicationEmailEN,\n fr: ReviewerApplicationEmailFR,\n es: ReviewerApplicationEmailES,\n ru: ReviewerApplicationEmailRU,\n ja: ReviewerApplicationEmailJA,\n ko: ReviewerApplicationEmailKO,\n zh: ReviewerApplicationEmailZH,\n de: ReviewerApplicationEmailDE,\n ar: ReviewerApplicationEmailAR,\n it: ReviewerApplicationEmailIT,\n 'en-GB': ReviewerApplicationEmailEN_GB,\n pt: ReviewerApplicationEmailPT,\n hi: ReviewerApplicationEmailHI,\n tr: ReviewerApplicationEmailTR,\n pl: ReviewerApplicationEmailPL,\n id: ReviewerApplicationEmailID,\n vi: ReviewerApplicationEmailVI,\n uk: ReviewerApplicationEmailUK,\n },\n locale\n ),\n subject: 'New reviewer application on Intlayer',\n },\n reviewerApproved: {\n template: t(\n {\n en: ReviewerApprovedEmailEN,\n fr: ReviewerApprovedEmailFR,\n es: ReviewerApprovedEmailES,\n ru: ReviewerApprovedEmailRU,\n ja: ReviewerApprovedEmailJA,\n ko: ReviewerApprovedEmailKO,\n zh: ReviewerApprovedEmailZH,\n de: ReviewerApprovedEmailDE,\n ar: ReviewerApprovedEmailAR,\n it: ReviewerApprovedEmailIT,\n 'en-GB': ReviewerApprovedEmailEN_GB,\n pt: ReviewerApprovedEmailPT,\n hi: ReviewerApprovedEmailHI,\n tr: ReviewerApprovedEmailTR,\n pl: ReviewerApprovedEmailPL,\n id: ReviewerApprovedEmailID,\n vi: ReviewerApprovedEmailVI,\n uk: ReviewerApprovedEmailUK,\n },\n locale\n ),\n subject: t(\n {\n en: 'Your Intlayer reviewer profile has been approved!',\n fr: 'Votre profil traducteur Intlayer a été approuvé !',\n es: '¡Tu perfil de traductor en Intlayer ha sido aprobado!',\n ru: 'Ваш профиль переводчика на Intlayer одобрен!',\n ja: 'Intlayerの翻訳者プロフィールが承認されました!',\n ko: 'Intlayer 번역가 프로필이 승인되었습니다!',\n zh: '您的 Intlayer 译者档案已获批准!',\n de: 'Ihr Intlayer Übersetzer-Profil wurde genehmigt!',\n ar: 'تمت الموافقة على ملف المترجم الخاص بك في Intlayer!',\n it: 'Il tuo profilo traduttore su Intlayer è stato approvato!',\n 'en-GB': 'Your Intlayer reviewer profile has been approved!',\n pt: 'Seu perfil de tradutor no Intlayer foi aprovado!',\n hi: 'Intlayer पर आपकी अनुवादक प्रोफ़ाइल को अनुमोदित कर दिया गया है!',\n tr: 'Intlayer çevirmen profiliniz onaylandı!',\n pl: 'Twój profil tłumacza w Intlayer został zatwierdzony!',\n id: 'Profil penerjemah Intlayer Anda telah disetujui!',\n vi: 'Hồ sơ dịch giả Intlayer của bạn đã được phê duyệt!',\n uk: 'Ваш профіль перекладача на Intlayer схвалено!',\n },\n locale\n ),\n },\n reviewerContactInquiry: {\n template: ReviewerContactEmailEN,\n subject: 'New message from a client — Intlayer',\n },\n }) satisfies EmailComponents;\n\ntype EmailType = keyof ReturnType<typeof getEmailComponents>;\n\nexport type SendEmailProps<T extends EmailType> = {\n type: T;\n to: string;\n subject?: string;\n locale?: Locale;\n} & ComponentProps<ReturnType<typeof getEmailComponents>[T]['template']>;\n\nexport const sendEmail = async <T extends EmailType>({\n type,\n to,\n subject,\n locale,\n ...props\n}: SendEmailProps<T>) => {\n const resend = new Resend(process.env.RESEND_API_KEY);\n\n const emailComponents = getEmailComponents(locale);\n\n const { template, subject: baseSubject } = emailComponents[type];\n\n type EmailComponentType = (typeof emailComponents)[T]['template'];\n\n const EmailComponent: EmailComponentType = template;\n\n const react = <EmailComponent {...(props as any)} />;\n\n await resend.emails\n .send({\n from: 'Intlayer <no-reply@intlayer.org>',\n to,\n subject: subject ?? baseSubject,\n react,\n })\n .catch((err) => logger.error(err));\n\n logger.info(`Email sent ${type} to ${to}`);\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAuXA,MAAM,sBAAsB,YACzB;CACC,qBAAqB;EACnB,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS;EACV;CACD,kBAAkB;EAChB,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SACE;EACH;CACD,qBAAqB;EACnB,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS;EACV;CACD,oBAAoB;EAClB,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS;EACV;CACD,QAAQ;EACN,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,UAAU;EACR,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,eAAe;EACb,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,SAAS;EACP,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,WAAW;EACT,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,4BAA4B;EAC1B,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,4BAA4B;EAC1B,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,iCAAiC;EAC/B,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,0BAA0B;EACxB,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,mBAAmB;EACjB,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SACE;GACF,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,wBAAwB;EACtB,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,0BAA0B;EACxB,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,qBAAqB;EACnB,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS;EACV;CACD,kBAAkB;EAChB,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,wBAAwB;EACtB,UAAU;EACV,SAAS;EACV;CACF;AAWH,MAAa,YAAY,OAA4B,EACnD,MACA,IACA,SACA,QACA,GAAG,YACoB;CACvB,MAAM,SAAS,IAAI,OAAO,QAAQ,IAAI,eAAe;CAIrD,MAAM,EAAE,UAAU,SAAS,gBAFH,mBAAmB,OAEe,CAAC;CAM3D,MAAM,QAAQ,oBAACA,UAAD,EAAgB,GAAK,OAAiB;AAEpD,OAAM,OAAO,OACV,KAAK;EACJ,MAAM;EACN;EACA,SAAS,WAAW;EACpB;EACD,CAAC,CACD,OAAO,QAAQ,OAAO,MAAM,IAAI,CAAC;AAEpC,QAAO,KAAK,cAAc,KAAK,MAAM,KAAK"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"environment.service.mjs","names":[],"sources":["../../../src/services/environment.service.ts"],"sourcesContent":["import { SessionModel } from '@schemas/session.schema';\nimport type { EnvironmentAPI, ProjectDocument } from '@/types/project.types';\n\n/**\n * Resolves the active environment from the project's environments list.\n *\n * Convention: `null` in `session.activeEnvironmentId` means \"production\" (the\n * default environment). A real ObjectId string means a non-default environment.\n *\n * - When `sessionEnvironmentId` is null → resolves to the default environment\n * for display purposes; does NOT write to the session (null is already correct).\n * - When `sessionEnvironmentId` is a non-default env ID → returns that env.\n * - When `sessionEnvironmentId` points to the default env (inconsistency) →\n * normalises the session back to null.\n */\nexport const resolveSessionEnvironment = async ({\n projectData,\n sessionId,\n sessionEnvironmentId,\n}: {\n projectData: ProjectDocument | null;\n sessionId: string;\n sessionEnvironmentId: string | null;\n}): Promise<{\n environmentAPI: EnvironmentAPI | null;\n resolvedEnvironmentId: string | null;\n}> => {\n if (!projectData?.environments?.length) {\n return { environmentAPI: null, resolvedEnvironmentId: null };\n }\n\n const matchedEnvironment = sessionEnvironmentId\n ? (projectData.environments.find(\n (environment) => String(environment.id) === sessionEnvironmentId\n ) ?? null)\n : null;\n\n const resolvedEnvironment =\n matchedEnvironment ??\n projectData.environments.find((environment) => environment.isDefault) ??\n projectData.environments[0];\n\n if (!resolvedEnvironment) {\n return { environmentAPI: null, resolvedEnvironmentId: null };\n }\n\n const isDefaultEnvironment = resolvedEnvironment.isDefault;\n\n // null = production (default) convention\n const resolvedEnvironmentId = isDefaultEnvironment\n ? null\n : String(resolvedEnvironment.id);\n\n const envPlain =\n 'toObject' in resolvedEnvironment &&\n typeof (resolvedEnvironment as any).toObject === 'function'\n ? (resolvedEnvironment as any).toObject()\n : resolvedEnvironment;\n\n const environmentAPI: EnvironmentAPI = {\n ...envPlain,\n id: String(resolvedEnvironment.id),\n } as unknown as EnvironmentAPI;\n\n // Sync session when stored value diverges from the resolved convention:\n // - sessionEnvironmentId is set but resolved env is the default → clear to null\n // - session has a stale / missing value for a non-default env → write the id\n const sessionNeedsUpdate = sessionEnvironmentId !== resolvedEnvironmentId;\n\n if (sessionNeedsUpdate) {\n await SessionModel.updateOne(\n { id: sessionId },\n { $set: { activeEnvironmentId: resolvedEnvironmentId } }\n );\n }\n\n return { environmentAPI, resolvedEnvironmentId };\n};\n"],"mappings":";;;;;;;;;;;;;;;AAeA,MAAa,4BAA4B,OAAO,EAC9C,aACA,WACA,2BAQI;
|
|
1
|
+
{"version":3,"file":"environment.service.mjs","names":[],"sources":["../../../src/services/environment.service.ts"],"sourcesContent":["import { SessionModel } from '@schemas/session.schema';\nimport type { EnvironmentAPI, ProjectDocument } from '@/types/project.types';\n\n/**\n * Resolves the active environment from the project's environments list.\n *\n * Convention: `null` in `session.activeEnvironmentId` means \"production\" (the\n * default environment). A real ObjectId string means a non-default environment.\n *\n * - When `sessionEnvironmentId` is null → resolves to the default environment\n * for display purposes; does NOT write to the session (null is already correct).\n * - When `sessionEnvironmentId` is a non-default env ID → returns that env.\n * - When `sessionEnvironmentId` points to the default env (inconsistency) →\n * normalises the session back to null.\n */\nexport const resolveSessionEnvironment = async ({\n projectData,\n sessionId,\n sessionEnvironmentId,\n}: {\n projectData: ProjectDocument | null;\n sessionId: string;\n sessionEnvironmentId: string | null;\n}): Promise<{\n environmentAPI: EnvironmentAPI | null;\n resolvedEnvironmentId: string | null;\n}> => {\n if (!projectData?.environments?.length) {\n return { environmentAPI: null, resolvedEnvironmentId: null };\n }\n\n const matchedEnvironment = sessionEnvironmentId\n ? (projectData.environments.find(\n (environment) => String(environment.id) === sessionEnvironmentId\n ) ?? null)\n : null;\n\n const resolvedEnvironment =\n matchedEnvironment ??\n projectData.environments.find((environment) => environment.isDefault) ??\n projectData.environments[0];\n\n if (!resolvedEnvironment) {\n return { environmentAPI: null, resolvedEnvironmentId: null };\n }\n\n const isDefaultEnvironment = resolvedEnvironment.isDefault;\n\n // null = production (default) convention\n const resolvedEnvironmentId = isDefaultEnvironment\n ? null\n : String(resolvedEnvironment.id);\n\n const envPlain =\n 'toObject' in resolvedEnvironment &&\n typeof (resolvedEnvironment as any).toObject === 'function'\n ? (resolvedEnvironment as any).toObject()\n : resolvedEnvironment;\n\n const environmentAPI: EnvironmentAPI = {\n ...envPlain,\n id: String(resolvedEnvironment.id),\n } as unknown as EnvironmentAPI;\n\n // Sync session when stored value diverges from the resolved convention:\n // - sessionEnvironmentId is set but resolved env is the default → clear to null\n // - session has a stale / missing value for a non-default env → write the id\n const sessionNeedsUpdate = sessionEnvironmentId !== resolvedEnvironmentId;\n\n if (sessionNeedsUpdate) {\n await SessionModel.updateOne(\n { id: sessionId },\n { $set: { activeEnvironmentId: resolvedEnvironmentId } }\n );\n }\n\n return { environmentAPI, resolvedEnvironmentId };\n};\n"],"mappings":";;;;;;;;;;;;;;;AAeA,MAAa,4BAA4B,OAAO,EAC9C,aACA,WACA,2BAQI;AACJ,KAAI,CAAC,aAAa,cAAc,OAC9B,QAAO;EAAE,gBAAgB;EAAM,uBAAuB;EAAM;CAS9D,MAAM,uBANqB,uBACtB,YAAY,aAAa,MACvB,gBAAgB,OAAO,YAAY,GAAG,KAAK,qBAC7C,IAAI,OACL,SAIF,YAAY,aAAa,MAAM,gBAAgB,YAAY,UAAU,IACrE,YAAY,aAAa;AAE3B,KAAI,CAAC,oBACH,QAAO;EAAE,gBAAgB;EAAM,uBAAuB;EAAM;CAM9D,MAAM,wBAHuB,oBAAoB,YAI7C,OACA,OAAO,oBAAoB,GAAG;CAQlC,MAAM,iBAAiC;EACrC,GANA,cAAc,uBACd,OAAQ,oBAA4B,aAAa,aAC5C,oBAA4B,UAAU,GACvC;EAIJ,IAAI,OAAO,oBAAoB,GAAG;EACnC;AAOD,KAF2B,yBAAyB,sBAGlD,OAAM,aAAa,UACjB,EAAE,IAAI,WAAW,EACjB,EAAE,MAAM,EAAE,qBAAqB,uBAAuB,EAAE,CACzD;AAGH,QAAO;EAAE;EAAgB;EAAuB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github.service.mjs","names":[],"sources":["../../../src/services/github.service.ts"],"sourcesContent":["import { configurationFilesCandidates } from '@intlayer/config/node';\nimport { logger } from '@logger';\nimport type { RestEndpointMethodTypes } from '@octokit/rest';\nimport { Octokit } from '@octokit/rest';\nimport { AccountModel } from '@schemas/account.schema';\nimport type { Project } from '@/types/project.types';\n\nexport type GitHubRepository =\n RestEndpointMethodTypes['repos']['listForAuthenticatedUser']['response']['data'][0];\nexport type GitHubFileContent =\n RestEndpointMethodTypes['repos']['getContent']['response']['data'];\n\nexport const getAuthorizationUrl = (\n redirectUri: string,\n login?: string\n): string => {\n const clientId = process.env.GITHUB_CLIENT_ID;\n\n if (!clientId) {\n throw new Error('GitHub Client ID is not configured');\n }\n\n const params = new URLSearchParams({\n client_id: clientId,\n scope: 'repo',\n state: 'github_oauth',\n redirect_uri: redirectUri,\n });\n\n if (login) {\n params.append('login', login);\n }\n\n return `https://github.com/login/oauth/authorize?${params.toString()}`;\n};\n\nexport const exchangeCodeForToken = async (code: string): Promise<string> => {\n const clientId = process.env.GITHUB_CLIENT_ID;\n const clientSecret = process.env.GITHUB_CLIENT_SECRET;\n\n if (!clientId || !clientSecret) {\n throw new Error('GitHub OAuth credentials are not configured');\n }\n\n try {\n const response = await fetch(\n 'https://github.com/login/oauth/access_token',\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify({\n client_id: clientId,\n client_secret: clientSecret,\n code,\n }),\n }\n );\n\n if (!response.ok) {\n throw new Error(`GitHub token exchange failed: ${response.statusText}`);\n }\n\n const data = await response.json();\n\n if (data.error) {\n throw new Error(`GitHub token error: ${data.error_description}`);\n }\n\n return data.access_token;\n } catch (error) {\n logger.error('Error exchanging GitHub code for token:', error);\n throw error;\n }\n};\n\nexport const getUserRepos = async (\n accessToken: string\n): Promise<GitHubRepository[]> => {\n try {\n const octokit = new Octokit({\n auth: accessToken,\n headers: {\n 'X-GitHub-Api-Version': '2026-03-10',\n },\n });\n\n const { data } = await octokit.rest.repos.listForAuthenticatedUser({\n sort: 'updated',\n per_page: 100,\n visibility: 'all',\n headers: {\n 'X-GitHub-Api-Version': '2026-03-10',\n },\n });\n\n return data;\n } catch (error) {\n logger.error('Error fetching GitHub repositories:', error);\n throw error;\n }\n};\n\n/**\n * Check if valid intlayer configuration files exist in a repository (Recursively).\n * Returns an array of file paths found (e.g. ['intlayer.config.ts', 'apps/web/intlayer.config.js']).\n */\nexport const checkIntlayerConfig = async (\n accessToken: string,\n owner: string,\n repo: string,\n branch: string = 'main'\n): Promise<string[]> => {\n try {\n const octokit = new Octokit({\n auth: accessToken,\n });\n\n // Use Git Tree API to get all files recursively\n // This allows finding configs in monorepos/subfolders\n const { data } = await octokit.rest.git.getTree({\n owner,\n repo,\n tree_sha: branch,\n recursive: 'true',\n headers: {\n 'X-GitHub-Api-Version': '2026-03-10',\n },\n });\n\n if (!data.tree || !Array.isArray(data.tree)) {\n return [];\n }\n\n // Filter files that match the configuration candidates\n // We check if the path ends with one of the candidate filenames\n const foundFiles = data.tree\n .filter((item) => {\n if (item.type !== 'blob' || !item.path) return false;\n return (configurationFilesCandidates as readonly string[]).some(\n (candidate) => item.path?.endsWith(candidate)\n );\n })\n .map((item) => item.path as string); // Return the full path (e.g., 'packages/app/intlayer.config.ts')\n\n return foundFiles;\n } catch (error: any) {\n // If branch doesn't exist or repo is empty\n if (error.status === 404 || error.status === 409) return [];\n\n logger.error('Error checking intlayer configuration:', error);\n return [];\n }\n};\n\n/**\n * Get repository file contents and decode it\n */\nexport const getRepositoryFileContents = async (\n accessToken: string,\n owner: string,\n repo: string,\n path: string,\n branch: string = 'main'\n): Promise<string | null> => {\n try {\n const octokit = new Octokit({\n auth: accessToken,\n headers: {\n 'X-GitHub-Api-Version': '2026-03-10',\n },\n });\n\n const { data } = await octokit.rest.repos.getContent({\n owner,\n repo,\n path,\n ref: branch,\n headers: {\n 'X-GitHub-Api-Version': '2026-03-10',\n },\n });\n\n // Octokit types are union types (file | dir | submodule), we need to check if it's a file\n if (Array.isArray(data) || !('content' in data)) {\n throw new Error('Path points to a directory, not a file');\n }\n\n // GitHub returns content in base64, we must decode it to read the actual code\n const decodedContent = Buffer.from(data.content, 'base64').toString(\n 'utf-8'\n );\n\n return decodedContent;\n } catch (error: any) {\n if (error.status === 404) return null;\n\n logger.error('Error fetching repository file contents:', error);\n throw error;\n }\n};\n\nexport const getGitHubTokenFromUser = async (\n userId: string\n): Promise<string | null> => {\n try {\n const account = await AccountModel.findOne({\n userId,\n providerId: 'github',\n });\n\n if (!account) {\n return null;\n }\n\n const accessToken = account.accessToken || account.access_token;\n\n return accessToken || null;\n } catch (error) {\n logger.error('Error retrieving GitHub token from DB:', error);\n return null;\n }\n};\n\ntype DispatchEventOptions = {\n project: Project;\n eventType?: string;\n payload?: Record<string, any>;\n};\n\nexport const triggerGithubDispatch = async ({\n project,\n eventType = 'intlayer_cms_update',\n payload = {},\n}: DispatchEventOptions) => {\n const { repository, oAuth2Access } = project;\n\n if (!repository || repository.provider !== 'github') {\n throw new Error('Project is not connected to a GitHub repository.');\n }\n\n // Get the valid Access Token\n // Assuming the first token is the active one, or implement logic to find the specific user's token\n const tokenData = oAuth2Access?.[0];\n const accessToken = tokenData?.accessToken?.[0]; // Assuming array of tokens\n\n if (!accessToken) {\n throw new Error('No valid OAuth2 access token found for GitHub.');\n }\n\n const { owner, repository: repoName } = repository;\n const url = `https://api.github.com/repos/${owner}/${repoName}/dispatches`;\n\n try {\n // 2. Send the Dispatch Event\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/vnd.github+json',\n 'X-GitHub-Api-Version': '2026-03-10',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n event_type: eventType,\n client_payload: {\n ...payload,\n projectId: project.id,\n timestamp: Date.now(),\n },\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`GitHub API Error: ${response.status} - ${errorText}`);\n }\n\n logger.info(\n `Successfully triggered GitHub Action '${eventType}' for ${owner}/${repoName}`\n );\n return true;\n } catch (error) {\n logger.error(error);\n throw error;\n }\n};\n\n/**\n * Check if a GitHub workflow file exists\n */\nexport const checkWorkflowFileExists = async (\n accessToken: string,\n owner: string,\n repo: string,\n filename: string,\n branch: string = 'main'\n): Promise<boolean> => {\n try {\n const octokit = new Octokit({\n auth: accessToken,\n headers: {\n 'X-GitHub-Api-Version': '2026-03-10',\n },\n });\n await octokit.rest.repos.getContent({\n owner,\n repo,\n path: filename,\n ref: branch,\n headers: {\n 'X-GitHub-Api-Version': '2026-03-10',\n },\n });\n return true;\n } catch (error: any) {\n if (error.status === 404) return false;\n logger.error('Error checking workflow file existence:', error);\n throw error;\n }\n};\n\n/**\n * Create or update a GitHub workflow file\n */\nexport const createWorkflowFile = async (\n accessToken: string,\n owner: string,\n repo: string,\n filename: string,\n content: string,\n branch: string = 'main',\n message: string = 'Add Intlayer CI workflow'\n): Promise<void> => {\n try {\n const octokit = new Octokit({\n auth: accessToken,\n headers: {\n 'X-GitHub-Api-Version': '2026-03-10',\n },\n });\n\n // Check if file exists to get SHA for update\n let sha: string | undefined;\n try {\n const { data } = await octokit.rest.repos.getContent({\n owner,\n repo,\n path: filename,\n ref: branch,\n headers: {\n 'X-GitHub-Api-Version': '2026-03-10',\n },\n });\n\n if (Array.isArray(data) || !('sha' in data)) {\n throw new Error('Path points to a directory, not a file');\n }\n\n sha = data.sha;\n } catch (error: any) {\n if (error.status !== 404) {\n throw error;\n }\n // File doesn't exist, will create new one\n }\n\n // Encode content to base64\n const encodedContent = Buffer.from(content, 'utf-8').toString('base64');\n\n await octokit.rest.repos.createOrUpdateFileContents({\n owner,\n repo,\n path: filename,\n message,\n content: encodedContent,\n branch,\n headers: {\n 'X-GitHub-Api-Version': '2026-03-10',\n },\n ...(sha && { sha }), // Include SHA if updating existing file\n });\n\n logger.info(\n `Successfully ${sha ? 'updated' : 'created'} workflow file ${filename} for ${owner}/${repo}`\n );\n } catch (error) {\n logger.error('Error creating/updating workflow file:', error);\n throw error;\n }\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"github.service.mjs","names":[],"sources":["../../../src/services/github.service.ts"],"sourcesContent":["import { configurationFilesCandidates } from '@intlayer/config/node';\nimport { logger } from '@logger';\nimport type { RestEndpointMethodTypes } from '@octokit/rest';\nimport { Octokit } from '@octokit/rest';\nimport { AccountModel } from '@schemas/account.schema';\nimport type { Project } from '@/types/project.types';\n\nexport type GitHubRepository =\n RestEndpointMethodTypes['repos']['listForAuthenticatedUser']['response']['data'][0];\nexport type GitHubFileContent =\n RestEndpointMethodTypes['repos']['getContent']['response']['data'];\n\nexport const getAuthorizationUrl = (\n redirectUri: string,\n login?: string\n): string => {\n const clientId = process.env.GITHUB_CLIENT_ID;\n\n if (!clientId) {\n throw new Error('GitHub Client ID is not configured');\n }\n\n const params = new URLSearchParams({\n client_id: clientId,\n scope: 'repo',\n state: 'github_oauth',\n redirect_uri: redirectUri,\n });\n\n if (login) {\n params.append('login', login);\n }\n\n return `https://github.com/login/oauth/authorize?${params.toString()}`;\n};\n\nexport const exchangeCodeForToken = async (code: string): Promise<string> => {\n const clientId = process.env.GITHUB_CLIENT_ID;\n const clientSecret = process.env.GITHUB_CLIENT_SECRET;\n\n if (!clientId || !clientSecret) {\n throw new Error('GitHub OAuth credentials are not configured');\n }\n\n try {\n const response = await fetch(\n 'https://github.com/login/oauth/access_token',\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify({\n client_id: clientId,\n client_secret: clientSecret,\n code,\n }),\n }\n );\n\n if (!response.ok) {\n throw new Error(`GitHub token exchange failed: ${response.statusText}`);\n }\n\n const data = await response.json();\n\n if (data.error) {\n throw new Error(`GitHub token error: ${data.error_description}`);\n }\n\n return data.access_token;\n } catch (error) {\n logger.error('Error exchanging GitHub code for token:', error);\n throw error;\n }\n};\n\nexport const getUserRepos = async (\n accessToken: string\n): Promise<GitHubRepository[]> => {\n try {\n const octokit = new Octokit({\n auth: accessToken,\n headers: {\n 'X-GitHub-Api-Version': '2026-03-10',\n },\n });\n\n const { data } = await octokit.rest.repos.listForAuthenticatedUser({\n sort: 'updated',\n per_page: 100,\n visibility: 'all',\n headers: {\n 'X-GitHub-Api-Version': '2026-03-10',\n },\n });\n\n return data;\n } catch (error) {\n logger.error('Error fetching GitHub repositories:', error);\n throw error;\n }\n};\n\n/**\n * Check if valid intlayer configuration files exist in a repository (Recursively).\n * Returns an array of file paths found (e.g. ['intlayer.config.ts', 'apps/web/intlayer.config.js']).\n */\nexport const checkIntlayerConfig = async (\n accessToken: string,\n owner: string,\n repo: string,\n branch: string = 'main'\n): Promise<string[]> => {\n try {\n const octokit = new Octokit({\n auth: accessToken,\n });\n\n // Use Git Tree API to get all files recursively\n // This allows finding configs in monorepos/subfolders\n const { data } = await octokit.rest.git.getTree({\n owner,\n repo,\n tree_sha: branch,\n recursive: 'true',\n headers: {\n 'X-GitHub-Api-Version': '2026-03-10',\n },\n });\n\n if (!data.tree || !Array.isArray(data.tree)) {\n return [];\n }\n\n // Filter files that match the configuration candidates\n // We check if the path ends with one of the candidate filenames\n const foundFiles = data.tree\n .filter((item) => {\n if (item.type !== 'blob' || !item.path) return false;\n return (configurationFilesCandidates as readonly string[]).some(\n (candidate) => item.path?.endsWith(candidate)\n );\n })\n .map((item) => item.path as string); // Return the full path (e.g., 'packages/app/intlayer.config.ts')\n\n return foundFiles;\n } catch (error: any) {\n // If branch doesn't exist or repo is empty\n if (error.status === 404 || error.status === 409) return [];\n\n logger.error('Error checking intlayer configuration:', error);\n return [];\n }\n};\n\n/**\n * Get repository file contents and decode it\n */\nexport const getRepositoryFileContents = async (\n accessToken: string,\n owner: string,\n repo: string,\n path: string,\n branch: string = 'main'\n): Promise<string | null> => {\n try {\n const octokit = new Octokit({\n auth: accessToken,\n headers: {\n 'X-GitHub-Api-Version': '2026-03-10',\n },\n });\n\n const { data } = await octokit.rest.repos.getContent({\n owner,\n repo,\n path,\n ref: branch,\n headers: {\n 'X-GitHub-Api-Version': '2026-03-10',\n },\n });\n\n // Octokit types are union types (file | dir | submodule), we need to check if it's a file\n if (Array.isArray(data) || !('content' in data)) {\n throw new Error('Path points to a directory, not a file');\n }\n\n // GitHub returns content in base64, we must decode it to read the actual code\n const decodedContent = Buffer.from(data.content, 'base64').toString(\n 'utf-8'\n );\n\n return decodedContent;\n } catch (error: any) {\n if (error.status === 404) return null;\n\n logger.error('Error fetching repository file contents:', error);\n throw error;\n }\n};\n\nexport const getGitHubTokenFromUser = async (\n userId: string\n): Promise<string | null> => {\n try {\n const account = await AccountModel.findOne({\n userId,\n providerId: 'github',\n });\n\n if (!account) {\n return null;\n }\n\n const accessToken = account.accessToken || account.access_token;\n\n return accessToken || null;\n } catch (error) {\n logger.error('Error retrieving GitHub token from DB:', error);\n return null;\n }\n};\n\ntype DispatchEventOptions = {\n project: Project;\n eventType?: string;\n payload?: Record<string, any>;\n};\n\nexport const triggerGithubDispatch = async ({\n project,\n eventType = 'intlayer_cms_update',\n payload = {},\n}: DispatchEventOptions) => {\n const { repository, oAuth2Access } = project;\n\n if (!repository || repository.provider !== 'github') {\n throw new Error('Project is not connected to a GitHub repository.');\n }\n\n // Get the valid Access Token\n // Assuming the first token is the active one, or implement logic to find the specific user's token\n const tokenData = oAuth2Access?.[0];\n const accessToken = tokenData?.accessToken?.[0]; // Assuming array of tokens\n\n if (!accessToken) {\n throw new Error('No valid OAuth2 access token found for GitHub.');\n }\n\n const { owner, repository: repoName } = repository;\n const url = `https://api.github.com/repos/${owner}/${repoName}/dispatches`;\n\n try {\n // 2. Send the Dispatch Event\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/vnd.github+json',\n 'X-GitHub-Api-Version': '2026-03-10',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n event_type: eventType,\n client_payload: {\n ...payload,\n projectId: project.id,\n timestamp: Date.now(),\n },\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`GitHub API Error: ${response.status} - ${errorText}`);\n }\n\n logger.info(\n `Successfully triggered GitHub Action '${eventType}' for ${owner}/${repoName}`\n );\n return true;\n } catch (error) {\n logger.error(error);\n throw error;\n }\n};\n\n/**\n * Check if a GitHub workflow file exists\n */\nexport const checkWorkflowFileExists = async (\n accessToken: string,\n owner: string,\n repo: string,\n filename: string,\n branch: string = 'main'\n): Promise<boolean> => {\n try {\n const octokit = new Octokit({\n auth: accessToken,\n headers: {\n 'X-GitHub-Api-Version': '2026-03-10',\n },\n });\n await octokit.rest.repos.getContent({\n owner,\n repo,\n path: filename,\n ref: branch,\n headers: {\n 'X-GitHub-Api-Version': '2026-03-10',\n },\n });\n return true;\n } catch (error: any) {\n if (error.status === 404) return false;\n logger.error('Error checking workflow file existence:', error);\n throw error;\n }\n};\n\n/**\n * Create or update a GitHub workflow file\n */\nexport const createWorkflowFile = async (\n accessToken: string,\n owner: string,\n repo: string,\n filename: string,\n content: string,\n branch: string = 'main',\n message: string = 'Add Intlayer CI workflow'\n): Promise<void> => {\n try {\n const octokit = new Octokit({\n auth: accessToken,\n headers: {\n 'X-GitHub-Api-Version': '2026-03-10',\n },\n });\n\n // Check if file exists to get SHA for update\n let sha: string | undefined;\n try {\n const { data } = await octokit.rest.repos.getContent({\n owner,\n repo,\n path: filename,\n ref: branch,\n headers: {\n 'X-GitHub-Api-Version': '2026-03-10',\n },\n });\n\n if (Array.isArray(data) || !('sha' in data)) {\n throw new Error('Path points to a directory, not a file');\n }\n\n sha = data.sha;\n } catch (error: any) {\n if (error.status !== 404) {\n throw error;\n }\n // File doesn't exist, will create new one\n }\n\n // Encode content to base64\n const encodedContent = Buffer.from(content, 'utf-8').toString('base64');\n\n await octokit.rest.repos.createOrUpdateFileContents({\n owner,\n repo,\n path: filename,\n message,\n content: encodedContent,\n branch,\n headers: {\n 'X-GitHub-Api-Version': '2026-03-10',\n },\n ...(sha && { sha }), // Include SHA if updating existing file\n });\n\n logger.info(\n `Successfully ${sha ? 'updated' : 'created'} workflow file ${filename} for ${owner}/${repo}`\n );\n } catch (error) {\n logger.error('Error creating/updating workflow file:', error);\n throw error;\n }\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gitlab.service.mjs","names":[],"sources":["../../../src/services/gitlab.service.ts"],"sourcesContent":["import { configurationFilesCandidates } from '@intlayer/config/node';\nimport { logger } from '@logger';\nimport { AccountModel } from '@schemas/account.schema';\n\nconst GITLAB_DEFAULT_URL = 'https://gitlab.com';\n\nexport type GitLabProject = {\n id: number;\n name: string;\n path_with_namespace: string;\n web_url: string;\n default_branch: string;\n visibility: string;\n last_activity_at: string;\n namespace: {\n id: number;\n name: string;\n path: string;\n };\n};\n\nexport type GitLabTreeItem = {\n id: string;\n name: string;\n type: 'tree' | 'blob';\n path: string;\n mode: string;\n};\n\n/**\n * Get GitLab authorization URL for OAuth flow\n */\nexport const getAuthorizationUrl = (\n redirectUri: string,\n instanceUrl?: string,\n login?: string\n): string => {\n const clientId = process.env.GITLAB_CLIENT_ID;\n const baseUrl = instanceUrl || GITLAB_DEFAULT_URL;\n\n if (!clientId) {\n throw new Error('GitLab Client ID is not configured');\n }\n\n const params = new URLSearchParams({\n client_id: clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n scope: 'api read_repository',\n state: 'gitlab_oauth',\n });\n\n if (login) {\n params.append('login_hint', login);\n }\n\n return `${baseUrl}/oauth/authorize?${params.toString()}`;\n};\n\n/**\n * Exchange GitLab authorization code for access token\n */\nexport const exchangeCodeForToken = async (\n code: string,\n redirectUri: string,\n instanceUrl?: string\n): Promise<string> => {\n const clientId = process.env.GITLAB_CLIENT_ID;\n const clientSecret = process.env.GITLAB_CLIENT_SECRET;\n const baseUrl = instanceUrl || GITLAB_DEFAULT_URL;\n\n if (!clientId || !clientSecret) {\n throw new Error('GitLab OAuth credentials are not configured');\n }\n\n try {\n const response = await fetch(`${baseUrl}/oauth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify({\n client_id: clientId,\n client_secret: clientSecret,\n code,\n grant_type: 'authorization_code',\n redirect_uri: redirectUri,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`GitLab token exchange failed: ${response.statusText}`);\n }\n\n const data = await response.json();\n\n if (data.error) {\n throw new Error(`GitLab token error: ${data.error_description}`);\n }\n\n return data.access_token;\n } catch (error) {\n logger.error('Error exchanging GitLab code for token:', error);\n throw error;\n }\n};\n\n/**\n * Get user's GitLab projects/repositories\n */\nexport const getUserProjects = async (\n accessToken: string,\n instanceUrl?: string\n): Promise<GitLabProject[]> => {\n const baseUrl = instanceUrl || GITLAB_DEFAULT_URL;\n\n try {\n const response = await fetch(\n `${baseUrl}/api/v4/projects?membership=true&order_by=last_activity_at&per_page=100`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/json',\n },\n }\n );\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch GitLab projects: ${response.statusText}`\n );\n }\n\n const projects: GitLabProject[] = await response.json();\n return projects;\n } catch (error) {\n logger.error('Error fetching GitLab projects:', error);\n throw error;\n }\n};\n\n/**\n * Check if valid intlayer configuration files exist in a GitLab repository (Recursively).\n * Returns an array of file paths found.\n */\nexport const checkIntlayerConfig = async (\n accessToken: string,\n projectId: number,\n branch: string = 'main',\n instanceUrl?: string\n): Promise<string[]> => {\n const baseUrl = instanceUrl || GITLAB_DEFAULT_URL;\n\n try {\n // Use GitLab's repository tree API with recursive option\n const response = await fetch(\n `${baseUrl}/api/v4/projects/${projectId}/repository/tree?ref=${encodeURIComponent(branch)}&recursive=true&per_page=10000`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/json',\n },\n }\n );\n\n if (!response.ok) {\n if (response.status === 404) return [];\n throw new Error(\n `Failed to fetch repository tree: ${response.statusText}`\n );\n }\n\n const tree: GitLabTreeItem[] = await response.json();\n\n // Filter files that match the configuration candidates\n const foundFiles = tree\n .filter((item) => {\n if (item.type !== 'blob') return false;\n return (configurationFilesCandidates as readonly string[]).some(\n (candidate) => item.path.endsWith(candidate)\n );\n })\n .map((item) => item.path);\n\n return foundFiles;\n } catch (error: any) {\n if (error.status === 404) return [];\n logger.error('Error checking intlayer configuration on GitLab:', error);\n return [];\n }\n};\n\n/**\n * Get repository file contents from GitLab and decode it\n */\nexport const getRepositoryFileContents = async (\n accessToken: string,\n projectId: number,\n path: string,\n branch: string = 'main',\n instanceUrl?: string\n): Promise<string | null> => {\n const baseUrl = instanceUrl || GITLAB_DEFAULT_URL;\n\n try {\n const encodedPath = encodeURIComponent(path);\n const response = await fetch(\n `${baseUrl}/api/v4/projects/${projectId}/repository/files/${encodedPath}/raw?ref=${encodeURIComponent(branch)}`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/json',\n },\n }\n );\n\n if (!response.ok) {\n if (response.status === 404) return null;\n throw new Error(`Failed to fetch file contents: ${response.statusText}`);\n }\n\n const content = await response.text();\n return content;\n } catch (error: any) {\n if (error.status === 404) return null;\n logger.error('Error fetching GitLab file contents:', error);\n throw error;\n }\n};\n\n/**\n * Get GitLab access token from user's linked account\n */\nexport const getGitLabTokenFromUser = async (\n userId: string\n): Promise<string | null> => {\n try {\n const account = await AccountModel.findOne({\n userId,\n providerId: 'gitlab',\n });\n\n if (!account) {\n return null;\n }\n\n const accessToken = account.accessToken || account.access_token;\n\n return accessToken || null;\n } catch (error) {\n logger.error('Error retrieving GitLab token from DB:', error);\n return null;\n }\n};\n\n/**\n * Check if a GitLab CI pipeline file exists\n */\nexport const checkPipelineFileExists = async (\n accessToken: string,\n projectId: number,\n filename: string = '.gitlab-ci.yml',\n branch: string = 'main',\n instanceUrl?: string\n): Promise<boolean> => {\n const baseUrl = instanceUrl || GITLAB_DEFAULT_URL;\n\n try {\n const encodedPath = encodeURIComponent(filename);\n const response = await fetch(\n `${baseUrl}/api/v4/projects/${projectId}/repository/files/${encodedPath}?ref=${encodeURIComponent(branch)}`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/json',\n },\n }\n );\n\n if (response.status === 404) return false;\n if (!response.ok) {\n throw new Error(`Failed to check file existence: ${response.statusText}`);\n }\n\n return true;\n } catch (error: any) {\n if (error.status === 404) return false;\n logger.error('Error checking pipeline file existence:', error);\n throw error;\n }\n};\n\n/**\n * Create or update a GitLab CI pipeline file\n */\nexport const createPipelineFile = async (\n accessToken: string,\n projectId: number,\n filename: string = '.gitlab-ci.yml',\n content: string,\n branch: string = 'main',\n instanceUrl?: string,\n message: string = 'Add Intlayer CI pipeline'\n): Promise<void> => {\n const baseUrl = instanceUrl || GITLAB_DEFAULT_URL;\n\n try {\n const encodedPath = encodeURIComponent(filename);\n const encodedContent = Buffer.from(content, 'utf-8').toString('base64');\n\n // Check if file exists to get content_sha256 for update\n let existingContentSha: string | undefined;\n try {\n const checkResponse = await fetch(\n `${baseUrl}/api/v4/projects/${projectId}/repository/files/${encodedPath}?ref=${encodeURIComponent(branch)}`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/json',\n },\n }\n );\n\n if (checkResponse.ok) {\n const fileData = await checkResponse.json();\n existingContentSha = fileData.content_sha256;\n }\n } catch (error: any) {\n if (error.status !== 404) {\n throw error;\n }\n // File doesn't exist, will create new one\n }\n\n const body: any = {\n branch,\n content: encodedContent,\n commit_message: message,\n encoding: 'base64',\n };\n\n if (existingContentSha) {\n body.last_commit_id = existingContentSha;\n }\n\n const response = await fetch(\n `${baseUrl}/api/v4/projects/${projectId}/repository/files/${encodedPath}`,\n {\n method: 'PUT',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify(body),\n }\n );\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to create/update pipeline file: ${errorText}`);\n }\n\n logger.info(\n `Successfully ${existingContentSha ? 'updated' : 'created'} pipeline file ${filename} for project ${projectId}`\n );\n } catch (error) {\n logger.error('Error creating/updating pipeline file:', error);\n throw error;\n }\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"gitlab.service.mjs","names":[],"sources":["../../../src/services/gitlab.service.ts"],"sourcesContent":["import { configurationFilesCandidates } from '@intlayer/config/node';\nimport { logger } from '@logger';\nimport { AccountModel } from '@schemas/account.schema';\n\nconst GITLAB_DEFAULT_URL = 'https://gitlab.com';\n\nexport type GitLabProject = {\n id: number;\n name: string;\n path_with_namespace: string;\n web_url: string;\n default_branch: string;\n visibility: string;\n last_activity_at: string;\n namespace: {\n id: number;\n name: string;\n path: string;\n };\n};\n\nexport type GitLabTreeItem = {\n id: string;\n name: string;\n type: 'tree' | 'blob';\n path: string;\n mode: string;\n};\n\n/**\n * Get GitLab authorization URL for OAuth flow\n */\nexport const getAuthorizationUrl = (\n redirectUri: string,\n instanceUrl?: string,\n login?: string\n): string => {\n const clientId = process.env.GITLAB_CLIENT_ID;\n const baseUrl = instanceUrl || GITLAB_DEFAULT_URL;\n\n if (!clientId) {\n throw new Error('GitLab Client ID is not configured');\n }\n\n const params = new URLSearchParams({\n client_id: clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n scope: 'api read_repository',\n state: 'gitlab_oauth',\n });\n\n if (login) {\n params.append('login_hint', login);\n }\n\n return `${baseUrl}/oauth/authorize?${params.toString()}`;\n};\n\n/**\n * Exchange GitLab authorization code for access token\n */\nexport const exchangeCodeForToken = async (\n code: string,\n redirectUri: string,\n instanceUrl?: string\n): Promise<string> => {\n const clientId = process.env.GITLAB_CLIENT_ID;\n const clientSecret = process.env.GITLAB_CLIENT_SECRET;\n const baseUrl = instanceUrl || GITLAB_DEFAULT_URL;\n\n if (!clientId || !clientSecret) {\n throw new Error('GitLab OAuth credentials are not configured');\n }\n\n try {\n const response = await fetch(`${baseUrl}/oauth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify({\n client_id: clientId,\n client_secret: clientSecret,\n code,\n grant_type: 'authorization_code',\n redirect_uri: redirectUri,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`GitLab token exchange failed: ${response.statusText}`);\n }\n\n const data = await response.json();\n\n if (data.error) {\n throw new Error(`GitLab token error: ${data.error_description}`);\n }\n\n return data.access_token;\n } catch (error) {\n logger.error('Error exchanging GitLab code for token:', error);\n throw error;\n }\n};\n\n/**\n * Get user's GitLab projects/repositories\n */\nexport const getUserProjects = async (\n accessToken: string,\n instanceUrl?: string\n): Promise<GitLabProject[]> => {\n const baseUrl = instanceUrl || GITLAB_DEFAULT_URL;\n\n try {\n const response = await fetch(\n `${baseUrl}/api/v4/projects?membership=true&order_by=last_activity_at&per_page=100`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/json',\n },\n }\n );\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch GitLab projects: ${response.statusText}`\n );\n }\n\n const projects: GitLabProject[] = await response.json();\n return projects;\n } catch (error) {\n logger.error('Error fetching GitLab projects:', error);\n throw error;\n }\n};\n\n/**\n * Check if valid intlayer configuration files exist in a GitLab repository (Recursively).\n * Returns an array of file paths found.\n */\nexport const checkIntlayerConfig = async (\n accessToken: string,\n projectId: number,\n branch: string = 'main',\n instanceUrl?: string\n): Promise<string[]> => {\n const baseUrl = instanceUrl || GITLAB_DEFAULT_URL;\n\n try {\n // Use GitLab's repository tree API with recursive option\n const response = await fetch(\n `${baseUrl}/api/v4/projects/${projectId}/repository/tree?ref=${encodeURIComponent(branch)}&recursive=true&per_page=10000`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/json',\n },\n }\n );\n\n if (!response.ok) {\n if (response.status === 404) return [];\n throw new Error(\n `Failed to fetch repository tree: ${response.statusText}`\n );\n }\n\n const tree: GitLabTreeItem[] = await response.json();\n\n // Filter files that match the configuration candidates\n const foundFiles = tree\n .filter((item) => {\n if (item.type !== 'blob') return false;\n return (configurationFilesCandidates as readonly string[]).some(\n (candidate) => item.path.endsWith(candidate)\n );\n })\n .map((item) => item.path);\n\n return foundFiles;\n } catch (error: any) {\n if (error.status === 404) return [];\n logger.error('Error checking intlayer configuration on GitLab:', error);\n return [];\n }\n};\n\n/**\n * Get repository file contents from GitLab and decode it\n */\nexport const getRepositoryFileContents = async (\n accessToken: string,\n projectId: number,\n path: string,\n branch: string = 'main',\n instanceUrl?: string\n): Promise<string | null> => {\n const baseUrl = instanceUrl || GITLAB_DEFAULT_URL;\n\n try {\n const encodedPath = encodeURIComponent(path);\n const response = await fetch(\n `${baseUrl}/api/v4/projects/${projectId}/repository/files/${encodedPath}/raw?ref=${encodeURIComponent(branch)}`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/json',\n },\n }\n );\n\n if (!response.ok) {\n if (response.status === 404) return null;\n throw new Error(`Failed to fetch file contents: ${response.statusText}`);\n }\n\n const content = await response.text();\n return content;\n } catch (error: any) {\n if (error.status === 404) return null;\n logger.error('Error fetching GitLab file contents:', error);\n throw error;\n }\n};\n\n/**\n * Get GitLab access token from user's linked account\n */\nexport const getGitLabTokenFromUser = async (\n userId: string\n): Promise<string | null> => {\n try {\n const account = await AccountModel.findOne({\n userId,\n providerId: 'gitlab',\n });\n\n if (!account) {\n return null;\n }\n\n const accessToken = account.accessToken || account.access_token;\n\n return accessToken || null;\n } catch (error) {\n logger.error('Error retrieving GitLab token from DB:', error);\n return null;\n }\n};\n\n/**\n * Check if a GitLab CI pipeline file exists\n */\nexport const checkPipelineFileExists = async (\n accessToken: string,\n projectId: number,\n filename: string = '.gitlab-ci.yml',\n branch: string = 'main',\n instanceUrl?: string\n): Promise<boolean> => {\n const baseUrl = instanceUrl || GITLAB_DEFAULT_URL;\n\n try {\n const encodedPath = encodeURIComponent(filename);\n const response = await fetch(\n `${baseUrl}/api/v4/projects/${projectId}/repository/files/${encodedPath}?ref=${encodeURIComponent(branch)}`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/json',\n },\n }\n );\n\n if (response.status === 404) return false;\n if (!response.ok) {\n throw new Error(`Failed to check file existence: ${response.statusText}`);\n }\n\n return true;\n } catch (error: any) {\n if (error.status === 404) return false;\n logger.error('Error checking pipeline file existence:', error);\n throw error;\n }\n};\n\n/**\n * Create or update a GitLab CI pipeline file\n */\nexport const createPipelineFile = async (\n accessToken: string,\n projectId: number,\n filename: string = '.gitlab-ci.yml',\n content: string,\n branch: string = 'main',\n instanceUrl?: string,\n message: string = 'Add Intlayer CI pipeline'\n): Promise<void> => {\n const baseUrl = instanceUrl || GITLAB_DEFAULT_URL;\n\n try {\n const encodedPath = encodeURIComponent(filename);\n const encodedContent = Buffer.from(content, 'utf-8').toString('base64');\n\n // Check if file exists to get content_sha256 for update\n let existingContentSha: string | undefined;\n try {\n const checkResponse = await fetch(\n `${baseUrl}/api/v4/projects/${projectId}/repository/files/${encodedPath}?ref=${encodeURIComponent(branch)}`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/json',\n },\n }\n );\n\n if (checkResponse.ok) {\n const fileData = await checkResponse.json();\n existingContentSha = fileData.content_sha256;\n }\n } catch (error: any) {\n if (error.status !== 404) {\n throw error;\n }\n // File doesn't exist, will create new one\n }\n\n const body: any = {\n branch,\n content: encodedContent,\n commit_message: message,\n encoding: 'base64',\n };\n\n if (existingContentSha) {\n body.last_commit_id = existingContentSha;\n }\n\n const response = await fetch(\n `${baseUrl}/api/v4/projects/${projectId}/repository/files/${encodedPath}`,\n {\n method: 'PUT',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify(body),\n }\n );\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to create/update pipeline file: ${errorText}`);\n }\n\n logger.info(\n `Successfully ${existingContentSha ? 'updated' : 'created'} pipeline file ${filename} for project ${projectId}`\n );\n } catch (error) {\n logger.error('Error creating/updating pipeline file:', error);\n throw error;\n }\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oAuth2.service.mjs","names":[],"sources":["../../../src/services/oAuth2.service.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto';\nimport type { Client } from '@node-oauth/oauth2-server';\nimport { OAuth2AccessTokenModel } from '@schemas/oAuth2.schema';\nimport { ProjectModel } from '@schemas/project.schema';\nimport { ensureMongoDocumentToObject } from '@utils/ensureMongoDocumentToObject';\nimport { GenericError } from '@utils/errors';\nimport { mapOrganizationToAPI } from '@utils/mapper/organization';\nimport { mapProjectToAPI } from '@utils/mapper/project';\nimport { mapUserToAPI } from '@utils/mapper/user';\nimport { getTokenExpireAt, shouldExtendOAuth2Token } from '@utils/oAuth2';\nimport type { Types } from 'mongoose';\nimport type { OAuth2Token } from '@/types/oAuth2.types';\nimport type { Organization } from '@/types/organization.types';\nimport type {\n OAuth2Access,\n OAuth2AccessContext,\n Project,\n ProjectDocument,\n} from '@/types/project.types';\nimport type { User, UserAPI, UserDocument } from '@/types/user.types';\nimport type { Token } from '../schemas/oAuth2.schema';\nimport { getOrganizationById } from './organization.service';\nimport { getUserById } from './user.service';\n\n/**\n * Function to generate client credentials\n *\n * @returns The client id and client secret\n */\nexport const generateClientCredentials = (): {\n clientId: string;\n clientSecret: string;\n} => {\n const clientId = randomBytes(16).toString('hex'); // Generate a 16 character hexadecimal string\n const clientSecret = randomBytes(32).toString('hex'); // Generate a 32 character hexadecimal string\n\n return { clientId, clientSecret };\n};\n\n/**\n * Method to get the client and the project\n *\n * @param clientId - The client id\n * @param clientSecret - The client secret\n * @returns The an object containing the client, the rights and the project or false if not found\n */\nexport const getClientAndProjectByClientId = async (\n clientId: string\n): Promise<\n | {\n client: Client;\n oAuth2Access: OAuth2Access;\n project: ProjectDocument;\n grants: Token['grants'];\n }\n | false\n> => {\n const project = await ProjectModel.findOne({\n 'oAuth2Access.clientId': String(clientId),\n });\n\n if (!project) {\n return false;\n }\n\n const oAuth2Access = project.oAuth2Access.find(\n (access) => access.clientId === clientId\n );\n\n if (!oAuth2Access) {\n return false;\n }\n\n const formattedClient: Client = {\n id: oAuth2Access.clientId,\n clientId,\n clientSecret: oAuth2Access.clientSecret,\n grants: ['client_credentials'],\n };\n\n return {\n client: formattedClient,\n oAuth2Access,\n grants: oAuth2Access.grants,\n project,\n };\n};\n\n/**\n * Get the client and verify that the client secret is correct\n *\n * @param clientId - The client id\n * @param clientSecret - The client secret\n * @returns The client or false if not found\n */\nexport const getClient = async (\n clientId: string,\n clientSecret: string\n): Promise<Client | false> => {\n const result = await getClientAndProjectByClientId(clientId);\n\n if (!result) {\n return false;\n }\n\n const { client } = result;\n\n if (!client || client.clientSecret !== clientSecret) {\n return false;\n }\n\n return client;\n};\n\n/**\n * Format an OAuth2Token\n *\n * @param token - The token to format\n * @param client - The client\n * @param user - The user\n * @param project - The project\n * @param organization - The organization\n * @param grants - The grants\n * @returns The formatted token\n */\nexport const formatOAuth2Token = (\n token: Token,\n client: Client,\n user: UserAPI,\n project: Project,\n organization: Organization,\n grants: Token['grants']\n): OAuth2Token => {\n const { clientId, userId, ...restToken } = token;\n\n if (String(userId) !== String(user.id)) {\n throw new GenericError('USER_ID_MISMATCH');\n }\n\n const formattedToken: OAuth2Token = {\n ...restToken,\n client,\n user: mapUserToAPI(user),\n organization: mapOrganizationToAPI(organization),\n project: mapProjectToAPI(project),\n accessToken: token.accessToken,\n accessTokenExpiresAt: token.accessTokenExpiresAt ?? new Date('999-99-99'),\n grants,\n };\n\n return formattedToken;\n};\n\n/**\n * Format a auth token for the database\n *\n * @param token - The oAuth2 token to format\n * @param clientId - The client ID\n * @param userId - The user ID\n * @returns\n */\nexport const formatDBToken = (\n token: OAuth2Token,\n clientId: Client['id'],\n userId: User['id'] | string\n): Token => {\n const formattedToken: Token = {\n id: token.id,\n clientId: clientId,\n userId: userId as Types.ObjectId,\n accessToken: token.accessToken,\n expiresIn: token.accessTokenExpiresAt ?? getTokenExpireAt(),\n };\n\n return formattedToken;\n};\n\n/**\n * Method to save the token\n *\n * @param token - The token\n * @param client - The client\n * @param user - The user\n * @returns The saved token or false if not saved\n */\nexport const saveToken = async (\n token: OAuth2Token,\n client: Client,\n user: UserAPI\n): Promise<OAuth2Token | false> => {\n const formattedAccessToken: Token = formatDBToken(token, client.id, user.id);\n\n const result = await OAuth2AccessTokenModel.create(formattedAccessToken);\n\n if (!result) {\n return false;\n }\n\n const result2 = await getClientAndProjectByClientId(result.clientId);\n\n if (!result2) {\n return false;\n }\n\n const { project } = result2;\n\n const organization = await getOrganizationById(project.organizationId);\n\n if (!organization) {\n return false;\n }\n\n const formattedResult = formatOAuth2Token(\n formattedAccessToken,\n client,\n user,\n project,\n organization,\n token.rights\n );\n return formattedResult;\n};\n\n/**\n * Sliding-refresh: push the token's expiry forward when it has been used\n * within the refresh threshold. Idempotent and cheap when no extension is due.\n */\nexport const extendOAuth2AccessToken = async (\n accessToken: string\n): Promise<Date> => {\n const nextExpiresAt = getTokenExpireAt();\n await OAuth2AccessTokenModel.updateOne(\n { accessToken: String(accessToken) },\n { $set: { accessTokenExpiresAt: nextExpiresAt, expiresIn: nextExpiresAt } }\n );\n return nextExpiresAt;\n};\n\n/**\n * Method to get the access token\n *\n * @param accessToken - The access token\n * @returns The access token or false if not found\n */\nexport const getAccessToken = async (\n accessToken: string\n): Promise<OAuth2Token | false> => {\n const token = await OAuth2AccessTokenModel.findOne({\n accessToken: String(accessToken),\n });\n\n if (!token) {\n return false;\n }\n\n // Slide the expiry forward when this active token is approaching its\n // deadline so a long-lived integration doesn't have to re-authenticate.\n const currentExpiresAt = token.accessTokenExpiresAt ?? token.expiresIn;\n if (currentExpiresAt && shouldExtendOAuth2Token(currentExpiresAt)) {\n const nextExpiresAt = await extendOAuth2AccessToken(accessToken);\n token.accessTokenExpiresAt = nextExpiresAt;\n token.expiresIn = nextExpiresAt;\n }\n\n const { userId, clientId } = token;\n\n const user = await getUserById(userId);\n\n if (!user) {\n return false;\n }\n\n const result = await getClientAndProjectByClientId(clientId);\n\n if (!result) {\n return false;\n }\n\n const { client, project, grants } = result;\n\n const organization = await getOrganizationById(project.organizationId);\n\n if (!organization) {\n return false;\n }\n\n const formattedAccessToken = formatOAuth2Token(\n token,\n client,\n mapUserToAPI(user),\n project,\n organization,\n grants\n );\n\n return formattedAccessToken;\n};\n\n/**\n * Method to get the user from the client\n *\n * @param client - The client\n * @returns The user or false if not found\n */\nexport const getUserFromClient = async (\n client: Client\n): Promise<UserDocument | false> => {\n const response = await getClientAndProjectByClientId(client.id);\n\n if (!response) {\n return false;\n }\n\n const { userId } = response.oAuth2Access;\n\n if (!userId) {\n return false;\n }\n\n const user = await getUserById(userId);\n\n return user ?? false;\n};\n\n/**\n * Method to verify the permissions (grants)\n *\n * @param token - The token\n * @param scope - The scope\n * @returns True if the token has the required scope, false otherwise\n */\nexport const verifyScope = async (\n _token: OAuth2Token,\n _scope: string[]\n): Promise<boolean> => {\n // Implement the verification of scopes if necessary\n return true;\n};\n\n/**\n * Validate OAuth2 access token and return user context\n */\nexport const validateOAuth2AccessToken = async (\n accessToken: string\n): Promise<Token> => {\n try {\n const token = await OAuth2AccessTokenModel.findOne({\n accessToken,\n });\n\n if (!token) {\n throw new GenericError('INVALID_ACCESS_TOKEN');\n }\n\n // Check if token is expired\n if (new Date() > new Date(token.expiresIn)) {\n throw new GenericError('EXPIRED_ACCESS_TOKEN');\n }\n\n return ensureMongoDocumentToObject(token);\n } catch (_error) {\n throw new GenericError('INVALID_ACCESS_TOKEN');\n }\n};\n\n/**\n * Validate OAuth2 access token and return user context\n */\nexport const getOAuth2AccessTokenContext = async (\n token: Token\n): Promise<OAuth2AccessContext> => {\n const { userId, clientId } = token;\n\n const user = await getUserById(String(userId));\n\n const result = await getClientAndProjectByClientId(clientId);\n\n if (!result) {\n throw new GenericError('INVALID_ACCESS_TOKEN');\n }\n\n const { project, grants, oAuth2Access } = result;\n\n const organization = await getOrganizationById(project.organizationId);\n\n return {\n accessToken: token.accessToken,\n user: user ? mapUserToAPI(user) : undefined,\n project: project ? mapProjectToAPI(project) : undefined,\n organization: organization ? mapOrganizationToAPI(organization) : undefined,\n grants,\n allowedEnvironmentIds:\n (oAuth2Access as OAuth2Access).allowedEnvironmentIds ?? null,\n allowedLocales: (oAuth2Access as OAuth2Access).allowedLocales ?? null,\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"oAuth2.service.mjs","names":[],"sources":["../../../src/services/oAuth2.service.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto';\nimport type { Client } from '@node-oauth/oauth2-server';\nimport { OAuth2AccessTokenModel } from '@schemas/oAuth2.schema';\nimport { ProjectModel } from '@schemas/project.schema';\nimport { ensureMongoDocumentToObject } from '@utils/ensureMongoDocumentToObject';\nimport { GenericError } from '@utils/errors';\nimport { mapOrganizationToAPI } from '@utils/mapper/organization';\nimport { mapProjectToAPI } from '@utils/mapper/project';\nimport { mapUserToAPI } from '@utils/mapper/user';\nimport { getTokenExpireAt, shouldExtendOAuth2Token } from '@utils/oAuth2';\nimport type { Types } from 'mongoose';\nimport type { OAuth2Token } from '@/types/oAuth2.types';\nimport type { Organization } from '@/types/organization.types';\nimport type {\n OAuth2Access,\n OAuth2AccessContext,\n Project,\n ProjectDocument,\n} from '@/types/project.types';\nimport type { User, UserAPI, UserDocument } from '@/types/user.types';\nimport type { Token } from '../schemas/oAuth2.schema';\nimport { getOrganizationById } from './organization.service';\nimport { getUserById } from './user.service';\n\n/**\n * Function to generate client credentials\n *\n * @returns The client id and client secret\n */\nexport const generateClientCredentials = (): {\n clientId: string;\n clientSecret: string;\n} => {\n const clientId = randomBytes(16).toString('hex'); // Generate a 16 character hexadecimal string\n const clientSecret = randomBytes(32).toString('hex'); // Generate a 32 character hexadecimal string\n\n return { clientId, clientSecret };\n};\n\n/**\n * Method to get the client and the project\n *\n * @param clientId - The client id\n * @param clientSecret - The client secret\n * @returns The an object containing the client, the rights and the project or false if not found\n */\nexport const getClientAndProjectByClientId = async (\n clientId: string\n): Promise<\n | {\n client: Client;\n oAuth2Access: OAuth2Access;\n project: ProjectDocument;\n grants: Token['grants'];\n }\n | false\n> => {\n const project = await ProjectModel.findOne({\n 'oAuth2Access.clientId': String(clientId),\n });\n\n if (!project) {\n return false;\n }\n\n const oAuth2Access = project.oAuth2Access.find(\n (access) => access.clientId === clientId\n );\n\n if (!oAuth2Access) {\n return false;\n }\n\n const formattedClient: Client = {\n id: oAuth2Access.clientId,\n clientId,\n clientSecret: oAuth2Access.clientSecret,\n grants: ['client_credentials'],\n };\n\n return {\n client: formattedClient,\n oAuth2Access,\n grants: oAuth2Access.grants,\n project,\n };\n};\n\n/**\n * Get the client and verify that the client secret is correct\n *\n * @param clientId - The client id\n * @param clientSecret - The client secret\n * @returns The client or false if not found\n */\nexport const getClient = async (\n clientId: string,\n clientSecret: string\n): Promise<Client | false> => {\n const result = await getClientAndProjectByClientId(clientId);\n\n if (!result) {\n return false;\n }\n\n const { client } = result;\n\n if (!client || client.clientSecret !== clientSecret) {\n return false;\n }\n\n return client;\n};\n\n/**\n * Format an OAuth2Token\n *\n * @param token - The token to format\n * @param client - The client\n * @param user - The user\n * @param project - The project\n * @param organization - The organization\n * @param grants - The grants\n * @returns The formatted token\n */\nexport const formatOAuth2Token = (\n token: Token,\n client: Client,\n user: UserAPI,\n project: Project,\n organization: Organization,\n grants: Token['grants']\n): OAuth2Token => {\n const { clientId, userId, ...restToken } = token;\n\n if (String(userId) !== String(user.id)) {\n throw new GenericError('USER_ID_MISMATCH');\n }\n\n const formattedToken: OAuth2Token = {\n ...restToken,\n client,\n user: mapUserToAPI(user),\n organization: mapOrganizationToAPI(organization),\n project: mapProjectToAPI(project),\n accessToken: token.accessToken,\n accessTokenExpiresAt: token.accessTokenExpiresAt ?? new Date('999-99-99'),\n grants,\n };\n\n return formattedToken;\n};\n\n/**\n * Format a auth token for the database\n *\n * @param token - The oAuth2 token to format\n * @param clientId - The client ID\n * @param userId - The user ID\n * @returns\n */\nexport const formatDBToken = (\n token: OAuth2Token,\n clientId: Client['id'],\n userId: User['id'] | string\n): Token => {\n const formattedToken: Token = {\n id: token.id,\n clientId: clientId,\n userId: userId as Types.ObjectId,\n accessToken: token.accessToken,\n expiresIn: token.accessTokenExpiresAt ?? getTokenExpireAt(),\n };\n\n return formattedToken;\n};\n\n/**\n * Method to save the token\n *\n * @param token - The token\n * @param client - The client\n * @param user - The user\n * @returns The saved token or false if not saved\n */\nexport const saveToken = async (\n token: OAuth2Token,\n client: Client,\n user: UserAPI\n): Promise<OAuth2Token | false> => {\n const formattedAccessToken: Token = formatDBToken(token, client.id, user.id);\n\n const result = await OAuth2AccessTokenModel.create(formattedAccessToken);\n\n if (!result) {\n return false;\n }\n\n const result2 = await getClientAndProjectByClientId(result.clientId);\n\n if (!result2) {\n return false;\n }\n\n const { project } = result2;\n\n const organization = await getOrganizationById(project.organizationId);\n\n if (!organization) {\n return false;\n }\n\n const formattedResult = formatOAuth2Token(\n formattedAccessToken,\n client,\n user,\n project,\n organization,\n token.rights\n );\n return formattedResult;\n};\n\n/**\n * Sliding-refresh: push the token's expiry forward when it has been used\n * within the refresh threshold. Idempotent and cheap when no extension is due.\n */\nexport const extendOAuth2AccessToken = async (\n accessToken: string\n): Promise<Date> => {\n const nextExpiresAt = getTokenExpireAt();\n await OAuth2AccessTokenModel.updateOne(\n { accessToken: String(accessToken) },\n { $set: { accessTokenExpiresAt: nextExpiresAt, expiresIn: nextExpiresAt } }\n );\n return nextExpiresAt;\n};\n\n/**\n * Method to get the access token\n *\n * @param accessToken - The access token\n * @returns The access token or false if not found\n */\nexport const getAccessToken = async (\n accessToken: string\n): Promise<OAuth2Token | false> => {\n const token = await OAuth2AccessTokenModel.findOne({\n accessToken: String(accessToken),\n });\n\n if (!token) {\n return false;\n }\n\n // Slide the expiry forward when this active token is approaching its\n // deadline so a long-lived integration doesn't have to re-authenticate.\n const currentExpiresAt = token.accessTokenExpiresAt ?? token.expiresIn;\n if (currentExpiresAt && shouldExtendOAuth2Token(currentExpiresAt)) {\n const nextExpiresAt = await extendOAuth2AccessToken(accessToken);\n token.accessTokenExpiresAt = nextExpiresAt;\n token.expiresIn = nextExpiresAt;\n }\n\n const { userId, clientId } = token;\n\n const user = await getUserById(userId);\n\n if (!user) {\n return false;\n }\n\n const result = await getClientAndProjectByClientId(clientId);\n\n if (!result) {\n return false;\n }\n\n const { client, project, grants } = result;\n\n const organization = await getOrganizationById(project.organizationId);\n\n if (!organization) {\n return false;\n }\n\n const formattedAccessToken = formatOAuth2Token(\n token,\n client,\n mapUserToAPI(user),\n project,\n organization,\n grants\n );\n\n return formattedAccessToken;\n};\n\n/**\n * Method to get the user from the client\n *\n * @param client - The client\n * @returns The user or false if not found\n */\nexport const getUserFromClient = async (\n client: Client\n): Promise<UserDocument | false> => {\n const response = await getClientAndProjectByClientId(client.id);\n\n if (!response) {\n return false;\n }\n\n const { userId } = response.oAuth2Access;\n\n if (!userId) {\n return false;\n }\n\n const user = await getUserById(userId);\n\n return user ?? false;\n};\n\n/**\n * Method to verify the permissions (grants)\n *\n * @param token - The token\n * @param scope - The scope\n * @returns True if the token has the required scope, false otherwise\n */\nexport const verifyScope = async (\n _token: OAuth2Token,\n _scope: string[]\n): Promise<boolean> => {\n // Implement the verification of scopes if necessary\n return true;\n};\n\n/**\n * Validate OAuth2 access token and return user context\n */\nexport const validateOAuth2AccessToken = async (\n accessToken: string\n): Promise<Token> => {\n try {\n const token = await OAuth2AccessTokenModel.findOne({\n accessToken,\n });\n\n if (!token) {\n throw new GenericError('INVALID_ACCESS_TOKEN');\n }\n\n // Check if token is expired\n if (new Date() > new Date(token.expiresIn)) {\n throw new GenericError('EXPIRED_ACCESS_TOKEN');\n }\n\n return ensureMongoDocumentToObject(token);\n } catch (_error) {\n throw new GenericError('INVALID_ACCESS_TOKEN');\n }\n};\n\n/**\n * Validate OAuth2 access token and return user context\n */\nexport const getOAuth2AccessTokenContext = async (\n token: Token\n): Promise<OAuth2AccessContext> => {\n const { userId, clientId } = token;\n\n const user = await getUserById(String(userId));\n\n const result = await getClientAndProjectByClientId(clientId);\n\n if (!result) {\n throw new GenericError('INVALID_ACCESS_TOKEN');\n }\n\n const { project, grants, oAuth2Access } = result;\n\n const organization = await getOrganizationById(project.organizationId);\n\n return {\n accessToken: token.accessToken,\n user: user ? mapUserToAPI(user) : undefined,\n project: project ? mapProjectToAPI(project) : undefined,\n organization: organization ? mapOrganizationToAPI(organization) : undefined,\n grants,\n allowedEnvironmentIds:\n (oAuth2Access as OAuth2Access).allowedEnvironmentIds ?? null,\n allowedLocales: (oAuth2Access as OAuth2Access).allowedLocales ?? null,\n };\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"organization.service.mjs","names":[],"sources":["../../../src/services/organization.service.ts"],"sourcesContent":["import { OrganizationModel } from '@schemas/organization.schema';\nimport { GenericError } from '@utils/errors';\nimport type { OrganizationFilters } from '@utils/filtersAndPagination/getOrganizationFiltersAndPagination';\nimport {\n type OrganizationFields,\n validateOrganization,\n} from '@utils/validation/validateOrganization';\nimport type { Types } from 'mongoose';\nimport type {\n Organization,\n OrganizationAPI,\n OrganizationCreationData,\n OrganizationDocument,\n} from '@/types/organization.types';\nimport type { Plan, PlanDocument } from '@/types/plan.types';\n\n/**\n * Finds organizations based on filters and pagination options.\n * @param filters - MongoDB filter query.\n * @param skip - Number of documents to skip.\n * @param limit - Number of documents to limit.\n * @returns List of organizations matching the filters.\n */\nexport const findOrganizations = async (\n filters: OrganizationFilters,\n skip: number,\n limit: number,\n sortOptions?: Record<string, 1 | -1>\n): Promise<OrganizationDocument[]> => {\n let query = OrganizationModel.find(filters).skip(skip).limit(limit);\n\n if (sortOptions && Object.keys(sortOptions).length > 0) {\n query = query.sort(sortOptions);\n }\n\n return await query;\n};\n\n/**\n * Finds an organization by its ID.\n * @param organizationId - The ID of the organization to find.\n * @returns The organization matching the ID.\n */\nexport const getOrganizationById = async (\n organizationId: string | Types.ObjectId\n): Promise<OrganizationDocument> => {\n const organization = await OrganizationModel.findById(organizationId);\n\n if (!organization) {\n throw new GenericError('ORGANIZATION_NOT_FOUND', { organizationId });\n }\n\n return organization;\n};\n\n/**\n * Counts the total number of organizations that match the filters.\n * @param filters - MongoDB filter query.\n * @returns Total number of organizations.\n */\nexport const countOrganizations = async (\n filters: OrganizationFilters\n): Promise<number> => {\n const result = await OrganizationModel.countDocuments(filters);\n\n if (typeof result === 'undefined') {\n throw new GenericError('ORGANIZATION_COUNT_FAILED', { filters });\n }\n\n return result;\n};\n\n/**\n * Creates a new organization in the database.\n * @param organization - The organization data to create.\n * @returns The created organization.\n */\nexport const createOrganization = async (\n organization: OrganizationCreationData,\n userId: string | Types.ObjectId\n): Promise<OrganizationDocument> => {\n const errors = validateOrganization(organization, ['name']);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('ORGANIZATION_INVALID_FIELDS', { errors });\n }\n\n try {\n const result = await OrganizationModel.create({\n creatorId: userId,\n membersIds: [userId],\n adminsIds: [userId],\n ...organization,\n });\n\n return result;\n } catch (error) {\n throw new GenericError('ORGANIZATION_CREATION_FAILED', {\n error: (error as Error).message,\n });\n }\n};\n\n/**\n * Updates an existing organization in the database by its ID.\n * @param organizationId - The ID of the organization to update.\n * @param organization - The updated organization data.\n * @returns The updated organization.\n */\nexport const updateOrganizationById = async (\n organizationId: string | Types.ObjectId,\n organization: Partial<Organization | OrganizationAPI>\n): Promise<OrganizationDocument> => {\n const updatedKeys = Object.keys(organization) as OrganizationFields;\n const errors = validateOrganization(organization, updatedKeys);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('ORGANIZATION_INVALID_FIELDS', {\n organizationId,\n errors,\n });\n }\n\n const result = await OrganizationModel.updateOne(\n { _id: organizationId },\n organization\n );\n\n if (result.matchedCount === 0) {\n throw new GenericError('ORGANIZATION_UPDATE_FAILED', { organizationId });\n }\n\n return await getOrganizationById(organizationId);\n};\n\n/**\n * Deletes an organization from the database by its ID.\n * @param organizationId - The ID of the organization to delete.\n * @returns The result of the deletion operation.\n */\nexport const deleteOrganizationById = async (\n organizationId: string | Types.ObjectId\n): Promise<OrganizationDocument> => {\n const organization =\n await OrganizationModel.findByIdAndDelete(organizationId);\n\n if (!organization) {\n throw new GenericError('ORGANIZATION_NOT_FOUND', { organizationId });\n }\n\n return organization;\n};\n\n/**\n * Updates an existing plan in the database by its ID.\n * @param planId - The ID of the plan to update.\n * @param plan - The updated plan data.\n * @returns The updated plan.\n */\nexport const updatePlan = async (\n organization: Organization | OrganizationDocument,\n plan: Partial<Plan>\n): Promise<OrganizationDocument | null> => {\n let prevPlan = organization.plan ?? {};\n\n if (typeof (prevPlan as PlanDocument)?.toObject === 'function') {\n prevPlan = (prevPlan as PlanDocument).toObject();\n }\n\n const updateOrganizationResult = await OrganizationModel.updateOne(\n { _id: organization.id },\n { $set: { plan: { ...prevPlan, ...plan } } }\n );\n\n if (updateOrganizationResult.matchedCount === 0) {\n throw new GenericError('ORGANIZATION_UPDATE_FAILED', {\n organizationId: organization.id,\n });\n }\n\n const updatedOrganization = await getOrganizationById(organization.id);\n\n return updatedOrganization;\n};\n"],"mappings":";;;;;;;;;;;;AAuBA,MAAa,oBAAoB,OAC/B,SACA,MACA,OACA,gBACoC;CACpC,IAAI,QAAQ,kBAAkB,KAAK,
|
|
1
|
+
{"version":3,"file":"organization.service.mjs","names":[],"sources":["../../../src/services/organization.service.ts"],"sourcesContent":["import { OrganizationModel } from '@schemas/organization.schema';\nimport { GenericError } from '@utils/errors';\nimport type { OrganizationFilters } from '@utils/filtersAndPagination/getOrganizationFiltersAndPagination';\nimport {\n type OrganizationFields,\n validateOrganization,\n} from '@utils/validation/validateOrganization';\nimport type { Types } from 'mongoose';\nimport type {\n Organization,\n OrganizationAPI,\n OrganizationCreationData,\n OrganizationDocument,\n} from '@/types/organization.types';\nimport type { Plan, PlanDocument } from '@/types/plan.types';\n\n/**\n * Finds organizations based on filters and pagination options.\n * @param filters - MongoDB filter query.\n * @param skip - Number of documents to skip.\n * @param limit - Number of documents to limit.\n * @returns List of organizations matching the filters.\n */\nexport const findOrganizations = async (\n filters: OrganizationFilters,\n skip: number,\n limit: number,\n sortOptions?: Record<string, 1 | -1>\n): Promise<OrganizationDocument[]> => {\n let query = OrganizationModel.find(filters).skip(skip).limit(limit);\n\n if (sortOptions && Object.keys(sortOptions).length > 0) {\n query = query.sort(sortOptions);\n }\n\n return await query;\n};\n\n/**\n * Finds an organization by its ID.\n * @param organizationId - The ID of the organization to find.\n * @returns The organization matching the ID.\n */\nexport const getOrganizationById = async (\n organizationId: string | Types.ObjectId\n): Promise<OrganizationDocument> => {\n const organization = await OrganizationModel.findById(organizationId);\n\n if (!organization) {\n throw new GenericError('ORGANIZATION_NOT_FOUND', { organizationId });\n }\n\n return organization;\n};\n\n/**\n * Counts the total number of organizations that match the filters.\n * @param filters - MongoDB filter query.\n * @returns Total number of organizations.\n */\nexport const countOrganizations = async (\n filters: OrganizationFilters\n): Promise<number> => {\n const result = await OrganizationModel.countDocuments(filters);\n\n if (typeof result === 'undefined') {\n throw new GenericError('ORGANIZATION_COUNT_FAILED', { filters });\n }\n\n return result;\n};\n\n/**\n * Creates a new organization in the database.\n * @param organization - The organization data to create.\n * @returns The created organization.\n */\nexport const createOrganization = async (\n organization: OrganizationCreationData,\n userId: string | Types.ObjectId\n): Promise<OrganizationDocument> => {\n const errors = validateOrganization(organization, ['name']);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('ORGANIZATION_INVALID_FIELDS', { errors });\n }\n\n try {\n const result = await OrganizationModel.create({\n creatorId: userId,\n membersIds: [userId],\n adminsIds: [userId],\n ...organization,\n });\n\n return result;\n } catch (error) {\n throw new GenericError('ORGANIZATION_CREATION_FAILED', {\n error: (error as Error).message,\n });\n }\n};\n\n/**\n * Updates an existing organization in the database by its ID.\n * @param organizationId - The ID of the organization to update.\n * @param organization - The updated organization data.\n * @returns The updated organization.\n */\nexport const updateOrganizationById = async (\n organizationId: string | Types.ObjectId,\n organization: Partial<Organization | OrganizationAPI>\n): Promise<OrganizationDocument> => {\n const updatedKeys = Object.keys(organization) as OrganizationFields;\n const errors = validateOrganization(organization, updatedKeys);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('ORGANIZATION_INVALID_FIELDS', {\n organizationId,\n errors,\n });\n }\n\n const result = await OrganizationModel.updateOne(\n { _id: organizationId },\n organization\n );\n\n if (result.matchedCount === 0) {\n throw new GenericError('ORGANIZATION_UPDATE_FAILED', { organizationId });\n }\n\n return await getOrganizationById(organizationId);\n};\n\n/**\n * Deletes an organization from the database by its ID.\n * @param organizationId - The ID of the organization to delete.\n * @returns The result of the deletion operation.\n */\nexport const deleteOrganizationById = async (\n organizationId: string | Types.ObjectId\n): Promise<OrganizationDocument> => {\n const organization =\n await OrganizationModel.findByIdAndDelete(organizationId);\n\n if (!organization) {\n throw new GenericError('ORGANIZATION_NOT_FOUND', { organizationId });\n }\n\n return organization;\n};\n\n/**\n * Updates an existing plan in the database by its ID.\n * @param planId - The ID of the plan to update.\n * @param plan - The updated plan data.\n * @returns The updated plan.\n */\nexport const updatePlan = async (\n organization: Organization | OrganizationDocument,\n plan: Partial<Plan>\n): Promise<OrganizationDocument | null> => {\n let prevPlan = organization.plan ?? {};\n\n if (typeof (prevPlan as PlanDocument)?.toObject === 'function') {\n prevPlan = (prevPlan as PlanDocument).toObject();\n }\n\n const updateOrganizationResult = await OrganizationModel.updateOne(\n { _id: organization.id },\n { $set: { plan: { ...prevPlan, ...plan } } }\n );\n\n if (updateOrganizationResult.matchedCount === 0) {\n throw new GenericError('ORGANIZATION_UPDATE_FAILED', {\n organizationId: organization.id,\n });\n }\n\n const updatedOrganization = await getOrganizationById(organization.id);\n\n return updatedOrganization;\n};\n"],"mappings":";;;;;;;;;;;;AAuBA,MAAa,oBAAoB,OAC/B,SACA,MACA,OACA,gBACoC;CACpC,IAAI,QAAQ,kBAAkB,KAAK,QAAQ,CAAC,KAAK,KAAK,CAAC,MAAM,MAAM;AAEnE,KAAI,eAAe,OAAO,KAAK,YAAY,CAAC,SAAS,EACnD,SAAQ,MAAM,KAAK,YAAY;AAGjC,QAAO,MAAM;;;;;;;AAQf,MAAa,sBAAsB,OACjC,mBACkC;CAClC,MAAM,eAAe,MAAM,kBAAkB,SAAS,eAAe;AAErE,KAAI,CAAC,aACH,OAAM,IAAI,aAAa,0BAA0B,EAAE,gBAAgB,CAAC;AAGtE,QAAO;;;;;;;AAQT,MAAa,qBAAqB,OAChC,YACoB;CACpB,MAAM,SAAS,MAAM,kBAAkB,eAAe,QAAQ;AAE9D,KAAI,OAAO,WAAW,YACpB,OAAM,IAAI,aAAa,6BAA6B,EAAE,SAAS,CAAC;AAGlE,QAAO;;;;;;;AAQT,MAAa,qBAAqB,OAChC,cACA,WACkC;CAClC,MAAM,SAAS,qBAAqB,cAAc,CAAC,OAAO,CAAC;AAE3D,KAAI,OAAO,KAAK,OAAO,CAAC,SAAS,EAC/B,OAAM,IAAI,aAAa,+BAA+B,EAAE,QAAQ,CAAC;AAGnE,KAAI;AAQF,SAAO,MAPc,kBAAkB,OAAO;GAC5C,WAAW;GACX,YAAY,CAAC,OAAO;GACpB,WAAW,CAAC,OAAO;GACnB,GAAG;GACJ,CAAC;UAGK,OAAO;AACd,QAAM,IAAI,aAAa,gCAAgC,EACrD,OAAQ,MAAgB,SACzB,CAAC;;;;;;;;;AAUN,MAAa,yBAAyB,OACpC,gBACA,iBACkC;CAElC,MAAM,SAAS,qBAAqB,cADhB,OAAO,KAAK,aAC6B,CAAC;AAE9D,KAAI,OAAO,KAAK,OAAO,CAAC,SAAS,EAC/B,OAAM,IAAI,aAAa,+BAA+B;EACpD;EACA;EACD,CAAC;AAQJ,MAAI,MALiB,kBAAkB,UACrC,EAAE,KAAK,gBAAgB,EACvB,aACD,EAEU,iBAAiB,EAC1B,OAAM,IAAI,aAAa,8BAA8B,EAAE,gBAAgB,CAAC;AAG1E,QAAO,MAAM,oBAAoB,eAAe;;;;;;;AAQlD,MAAa,yBAAyB,OACpC,mBACkC;CAClC,MAAM,eACJ,MAAM,kBAAkB,kBAAkB,eAAe;AAE3D,KAAI,CAAC,aACH,OAAM,IAAI,aAAa,0BAA0B,EAAE,gBAAgB,CAAC;AAGtE,QAAO;;;;;;;;AAST,MAAa,aAAa,OACxB,cACA,SACyC;CACzC,IAAI,WAAW,aAAa,QAAQ,EAAE;AAEtC,KAAI,OAAQ,UAA2B,aAAa,WAClD,YAAY,SAA0B,UAAU;AAQlD,MAAI,MALmC,kBAAkB,UACvD,EAAE,KAAK,aAAa,IAAI,EACxB,EAAE,MAAM,EAAE,MAAM;EAAE,GAAG;EAAU,GAAG;EAAM,EAAE,EAAE,CAC7C,EAE4B,iBAAiB,EAC5C,OAAM,IAAI,aAAa,8BAA8B,EACnD,gBAAgB,aAAa,IAC9B,CAAC;AAKJ,QAAO,MAF2B,oBAAoB,aAAa,GAAG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projectScreenshot.service.mjs","names":[],"sources":["../../../../src/services/project/projectScreenshot.service.ts"],"sourcesContent":["import { logger } from '@logger';\nimport {\n deleteShowcaseScreenshot,\n uploadShowcaseScreenshot,\n} from '@services/showcase/showcaseUploadScreenshot.service';\nimport { launchBrowser } from '@utils/puppeteer/launchBrowser';\n\nconst PRIVATE_HOSTNAMES =\n /^(localhost|127\\.\\d+\\.\\d+\\.\\d+|10\\.\\d+\\.\\d+\\.\\d+|192\\.168\\.\\d+\\.\\d+|172\\.(1[6-9]|2\\d|3[01])\\.\\d+\\.\\d+|::1)$/i;\n\n/**\n * Returns true if the URL is a publicly reachable domain suitable for screenshotting.\n */\nconst isPublicUrl = (url: string): boolean => {\n try {\n const { hostname, protocol } = new URL(url);\n if (protocol !== 'http:' && protocol !== 'https:') return false;\n if (PRIVATE_HOSTNAMES.test(hostname)) return false;\n // Must contain at least one dot (e.g. \"example.com\") and no bare hostnames\n if (!hostname.includes('.')) return false;\n return true;\n } catch {\n return false;\n }\n};\n\n/**\n * Takes a screenshot of the given URL and uploads it to R2.\n * Returns the public image URL, or null on failure.\n */\nexport const takeAndUploadProjectScreenshot = async (\n applicationUrl: string,\n projectId: string\n): Promise<string | null> => {\n const browser = await launchBrowser({ pipe: false, dumpio: false });\n try {\n const page = await browser.newPage();\n await page.setViewport({ width: 1280, height: 720 });\n await page.goto(applicationUrl, {\n waitUntil: 'domcontentloaded',\n timeout: 45000,\n });\n await page.waitForSelector('body', { timeout: 10000 });\n await page\n .waitForNetworkIdle({ idleTime: 1000, timeout: 10000 })\n .catch(() => {});\n\n const screenshotBuffer = (await page.screenshot({\n type: 'jpeg',\n quality: 30,\n })) as Buffer;\n\n await page.close();\n\n return await uploadShowcaseScreenshot(\n screenshotBuffer,\n applicationUrl,\n projectId\n );\n } catch (err) {\n logger.error(\n `[projectScreenshot] Failed to screenshot ${applicationUrl}: ${err}`\n );\n return null;\n } finally {\n await browser.close();\n }\n};\n\n/**\n * Refreshes the screenshot for a project's applicationURL if it changed.\n * Returns the new imageUrl, or the existing one if unchanged / no URL.\n */\nexport const refreshProjectScreenshotIfChanged = async ({\n newApplicationUrl,\n previousApplicationUrl,\n existingImageUrl,\n projectId,\n}: {\n newApplicationUrl?: string;\n previousApplicationUrl?: string;\n existingImageUrl?: string;\n projectId: string;\n}): Promise<string | undefined> => {\n if (!newApplicationUrl) return existingImageUrl;\n if (newApplicationUrl === previousApplicationUrl) return existingImageUrl;\n if (!isPublicUrl(newApplicationUrl)) return existingImageUrl;\n\n const imageUrl = await takeAndUploadProjectScreenshot(\n newApplicationUrl,\n projectId\n );\n\n // If the URL changed and there was an old screenshot for a different URL, clean it up\n if (\n existingImageUrl &&\n previousApplicationUrl &&\n newApplicationUrl !== previousApplicationUrl\n ) {\n deleteShowcaseScreenshot(existingImageUrl).catch(() => {});\n }\n\n return imageUrl ?? existingImageUrl;\n};\n"],"mappings":";;;;;AAOA,MAAM,oBACJ;;;;AAKF,MAAM,eAAe,QAAyB;
|
|
1
|
+
{"version":3,"file":"projectScreenshot.service.mjs","names":[],"sources":["../../../../src/services/project/projectScreenshot.service.ts"],"sourcesContent":["import { logger } from '@logger';\nimport {\n deleteShowcaseScreenshot,\n uploadShowcaseScreenshot,\n} from '@services/showcase/showcaseUploadScreenshot.service';\nimport { launchBrowser } from '@utils/puppeteer/launchBrowser';\n\nconst PRIVATE_HOSTNAMES =\n /^(localhost|127\\.\\d+\\.\\d+\\.\\d+|10\\.\\d+\\.\\d+\\.\\d+|192\\.168\\.\\d+\\.\\d+|172\\.(1[6-9]|2\\d|3[01])\\.\\d+\\.\\d+|::1)$/i;\n\n/**\n * Returns true if the URL is a publicly reachable domain suitable for screenshotting.\n */\nconst isPublicUrl = (url: string): boolean => {\n try {\n const { hostname, protocol } = new URL(url);\n if (protocol !== 'http:' && protocol !== 'https:') return false;\n if (PRIVATE_HOSTNAMES.test(hostname)) return false;\n // Must contain at least one dot (e.g. \"example.com\") and no bare hostnames\n if (!hostname.includes('.')) return false;\n return true;\n } catch {\n return false;\n }\n};\n\n/**\n * Takes a screenshot of the given URL and uploads it to R2.\n * Returns the public image URL, or null on failure.\n */\nexport const takeAndUploadProjectScreenshot = async (\n applicationUrl: string,\n projectId: string\n): Promise<string | null> => {\n const browser = await launchBrowser({ pipe: false, dumpio: false });\n try {\n const page = await browser.newPage();\n await page.setViewport({ width: 1280, height: 720 });\n await page.goto(applicationUrl, {\n waitUntil: 'domcontentloaded',\n timeout: 45000,\n });\n await page.waitForSelector('body', { timeout: 10000 });\n await page\n .waitForNetworkIdle({ idleTime: 1000, timeout: 10000 })\n .catch(() => {});\n\n const screenshotBuffer = (await page.screenshot({\n type: 'jpeg',\n quality: 30,\n })) as Buffer;\n\n await page.close();\n\n return await uploadShowcaseScreenshot(\n screenshotBuffer,\n applicationUrl,\n projectId\n );\n } catch (err) {\n logger.error(\n `[projectScreenshot] Failed to screenshot ${applicationUrl}: ${err}`\n );\n return null;\n } finally {\n await browser.close();\n }\n};\n\n/**\n * Refreshes the screenshot for a project's applicationURL if it changed.\n * Returns the new imageUrl, or the existing one if unchanged / no URL.\n */\nexport const refreshProjectScreenshotIfChanged = async ({\n newApplicationUrl,\n previousApplicationUrl,\n existingImageUrl,\n projectId,\n}: {\n newApplicationUrl?: string;\n previousApplicationUrl?: string;\n existingImageUrl?: string;\n projectId: string;\n}): Promise<string | undefined> => {\n if (!newApplicationUrl) return existingImageUrl;\n if (newApplicationUrl === previousApplicationUrl) return existingImageUrl;\n if (!isPublicUrl(newApplicationUrl)) return existingImageUrl;\n\n const imageUrl = await takeAndUploadProjectScreenshot(\n newApplicationUrl,\n projectId\n );\n\n // If the URL changed and there was an old screenshot for a different URL, clean it up\n if (\n existingImageUrl &&\n previousApplicationUrl &&\n newApplicationUrl !== previousApplicationUrl\n ) {\n deleteShowcaseScreenshot(existingImageUrl).catch(() => {});\n }\n\n return imageUrl ?? existingImageUrl;\n};\n"],"mappings":";;;;;AAOA,MAAM,oBACJ;;;;AAKF,MAAM,eAAe,QAAyB;AAC5C,KAAI;EACF,MAAM,EAAE,UAAU,aAAa,IAAI,IAAI,IAAI;AAC3C,MAAI,aAAa,WAAW,aAAa,SAAU,QAAO;AAC1D,MAAI,kBAAkB,KAAK,SAAS,CAAE,QAAO;AAE7C,MAAI,CAAC,SAAS,SAAS,IAAI,CAAE,QAAO;AACpC,SAAO;SACD;AACN,SAAO;;;;;;;AAQX,MAAa,iCAAiC,OAC5C,gBACA,cAC2B;CAC3B,MAAM,UAAU,MAAM,cAAc;EAAE,MAAM;EAAO,QAAQ;EAAO,CAAC;AACnE,KAAI;EACF,MAAM,OAAO,MAAM,QAAQ,SAAS;AACpC,QAAM,KAAK,YAAY;GAAE,OAAO;GAAM,QAAQ;GAAK,CAAC;AACpD,QAAM,KAAK,KAAK,gBAAgB;GAC9B,WAAW;GACX,SAAS;GACV,CAAC;AACF,QAAM,KAAK,gBAAgB,QAAQ,EAAE,SAAS,KAAO,CAAC;AACtD,QAAM,KACH,mBAAmB;GAAE,UAAU;GAAM,SAAS;GAAO,CAAC,CACtD,YAAY,GAAG;EAElB,MAAM,mBAAoB,MAAM,KAAK,WAAW;GAC9C,MAAM;GACN,SAAS;GACV,CAAC;AAEF,QAAM,KAAK,OAAO;AAElB,SAAO,MAAM,yBACX,kBACA,gBACA,UACD;UACM,KAAK;AACZ,SAAO,MACL,4CAA4C,eAAe,IAAI,MAChE;AACD,SAAO;WACC;AACR,QAAM,QAAQ,OAAO;;;;;;;AAQzB,MAAa,oCAAoC,OAAO,EACtD,mBACA,wBACA,kBACA,gBAMiC;AACjC,KAAI,CAAC,kBAAmB,QAAO;AAC/B,KAAI,sBAAsB,uBAAwB,QAAO;AACzD,KAAI,CAAC,YAAY,kBAAkB,CAAE,QAAO;CAE5C,MAAM,WAAW,MAAM,+BACrB,mBACA,UACD;AAGD,KACE,oBACA,0BACA,sBAAsB,uBAEtB,0BAAyB,iBAAiB,CAAC,YAAY,GAAG;AAG5D,QAAO,YAAY"}
|