@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":"project.controller.mjs","names":["projectService.findProjects","projectService.countProjects","projectService.createProject","projectService.getProjectById","projectService.updateProjectById","userService.updateUserById","userService.getUsersByIds","webhooksService.triggerAll","webhooksService.triggerSingleWebhook","projectService.deleteProjectById","ciService.getCIStatus","ciService.installCI"],"sources":["../../../src/controllers/project.controller.ts"],"sourcesContent":["import { logger } from '@logger';\nimport { SessionModel } from '@schemas/session.schema';\nimport * as ciService from '@services/ci.service';\nimport { createDemoDictionaries } from '@services/dictionary.service';\nimport { refreshProjectScreenshotIfChanged } from '@services/project/projectScreenshot.service';\nimport * as projectService from '@services/project.service';\nimport * as userService from '@services/user.service';\nimport * as webhooksService from '@services/webhook.service';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport type { FiltersAndPagination } from '@utils/filtersAndPagination/getFiltersAndPaginationFromBody';\nimport {\n getProjectFiltersAndPagination,\n type ProjectFiltersParams,\n} from '@utils/filtersAndPagination/getProjectFiltersAndPagination';\nimport { mapProjectsToAPI, mapProjectToAPI } from '@utils/mapper/project';\nimport { hasPermission } from '@utils/permissions';\nimport { getPlanDetails } from '@utils/plan';\nimport {\n formatPaginatedResponse,\n formatResponse,\n type PaginatedResponse,\n type ResponseData,\n} from '@utils/responseData';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport { t } from 'fastify-intlayer';\nimport type { Types } from 'mongoose';\nimport type {\n ProjectAPI,\n ProjectConfiguration,\n ProjectCreationData,\n ProjectData,\n} from '@/types/project.types';\nimport type { User } from '@/types/user.types';\n\nexport type GetProjectsParams = FiltersAndPagination<ProjectFiltersParams>;\nexport type GetProjectsResult = PaginatedResponse<ProjectAPI>;\n\n/**\n * Retrieves a list of projects based on filters and pagination.\n */\nexport const getProjects = async (\n request: FastifyRequest<{ Querystring: GetProjectsParams }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, roles } = request.session || {};\n const { filters, sortOptions, pageSize, skip, page, getNumberOfPages } =\n getProjectFiltersAndPagination(request);\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n // When no organization is selected in the session,\n // the filter already scopes organizationId to undefined → DB returns []\n // so we can proceed safely without an early 403.\n\n try {\n const projects = await projectService.findProjects(\n filters,\n skip,\n pageSize,\n sortOptions\n );\n\n // Skip permission check when there are no projects to protect.\n // An empty result is safe to return without checking roles.\n if (\n projects.length > 0 &&\n !hasPermission(\n roles || [],\n 'project:read'\n )({\n ...request.session,\n targetProjects: projects,\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PERMISSION_DENIED'\n );\n }\n\n const totalItems = await projectService.countProjects(filters);\n\n const formattedProjects = mapProjectsToAPI(projects);\n\n const responseData = formatPaginatedResponse<ProjectAPI>({\n data: formattedProjects,\n page,\n pageSize,\n totalPages: getNumberOfPages(totalItems),\n totalItems,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type AddProjectBody = ProjectCreationData;\nexport type AddProjectResult = ResponseData<ProjectAPI>;\n\n/**\n * Adds a new project to the database.\n */\nexport const addProject = async (\n request: FastifyRequest<{ Body: AddProjectBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { organization, user, roles } = request.session || {};\n const projectData = request.body;\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (!organization) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'ORGANIZATION_NOT_DEFINED'\n );\n }\n\n if (!projectData) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_DATA_NOT_FOUND'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'organization:admin'\n )({\n ...request.session,\n targetOrganizations: [organization],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n const { plan } = organization;\n\n const planType = getPlanDetails(plan);\n\n if (planType.numberOfProjects) {\n const projectCount = await projectService.countProjects({\n organizationId: organization.id,\n });\n\n if (projectCount >= planType.numberOfProjects) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PLAN_PROJECT_LIMIT_REACHED',\n {\n organizationId: organization.id,\n }\n );\n }\n }\n\n const project: ProjectData = {\n membersIds: [user.id],\n adminsIds: [user.id],\n creatorId: user.id,\n organizationId: organization.id,\n ...projectData,\n };\n\n try {\n const newProject = await projectService.createProject(project);\n\n const formattedProject = mapProjectToAPI(newProject);\n\n const responseData = formatResponse<ProjectAPI>({\n message: t({\n en: 'Project created successfully',\n 'en-GB': 'Project created successfully',\n fr: 'Projet créé avec succès',\n es: 'Proyecto creado con éxito',\n ru: 'Проект успешно создан',\n ja: 'プロジェクトが正常に作成されました',\n ko: '프로젝트가 성공적으로 생성되었습니다',\n zh: '项目已成功创建',\n de: 'Projekt erfolgreich erstellt',\n ar: 'تم إنشاء المشروع بنجاح',\n it: 'Progetto creato con successo',\n pt: 'Projeto criado com sucesso',\n hi: 'प्रोजेक्ट सफलतापूर्वक बनाया गया',\n tr: 'Proje başarıyla oluşturuldu',\n pl: 'Projekt został pomyślnie utworzony',\n id: 'Proyek berhasil dibuat',\n vi: 'Dự án đã được tạo thành công',\n uk: 'Проєкт успішно створено',\n }),\n description: t({\n en: 'Your project has been created successfully',\n 'en-GB': 'Your project has been created successfully',\n fr: 'Votre projet a été créé avec succès',\n es: 'Su proyecto ha sido creado con éxito',\n ru: 'Ваш проект был успешно создан',\n ja: 'プロジェクトは正常に作成されました',\n ko: '프로젝트가 성공적으로 생성되었습니다',\n zh: '您的项目已成功创建',\n de: 'Ihr Projekt wurde erfolgreich erstellt',\n ar: 'لقد تم إنشاء مشروعك بنجاح',\n it: 'Il tuo progetto è stato creato con successo',\n pt: 'Seu projeto foi criado com sucesso',\n hi: 'आपका प्रोजेक्ट सफलतापूर्वक बना लिया गया है',\n tr: 'Projeniz başarıyla oluşturuldu',\n pl: 'Twój projekt został pomyślnie utworzony',\n id: 'Proyek Anda telah berhasil dibuat',\n vi: 'Dự án của bạn đã được tạo thành công',\n uk: 'Ваш проєкт успішно створено',\n }),\n data: formattedProject,\n });\n\n reply.send(responseData);\n\n // Create mock data once it's done\n try {\n await createDemoDictionaries([formattedProject.id], user.id);\n } catch {\n // Skip if fail\n }\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type UpdateProjectBody = Partial<ProjectAPI>;\nexport type UpdateProjectResult = ResponseData<ProjectAPI>;\n\n/**\n * Updates an existing project in the database.\n */\nexport const updateProject = async (\n request: FastifyRequest<{ Body: UpdateProjectBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { organization, project, user, session, roles } = request.session || {};\n const projectData = request.body;\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_DATA_NOT_FOUND'\n );\n }\n\n if (!organization) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'ORGANIZATION_NOT_DEFINED'\n );\n }\n\n if (String(project.organizationId) !== String(organization.id)) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_IN_ORGANIZATION'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:write'\n )({\n ...request.session,\n targetProjectIds: [String(project.id)],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n if (typeof session === 'undefined') {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'SESSION_NOT_DEFINED'\n );\n }\n\n try {\n const existingProject = await projectService.getProjectById(project.id);\n\n const previousApplicationUrl =\n existingProject.configuration?.editor?.applicationURL;\n const newApplicationUrl = projectData.configuration?.editor?.applicationURL;\n\n const updatedProject = await projectService.updateProjectById(\n project.id,\n projectData\n );\n\n // Update session to set activeOrganizationId\n await SessionModel.updateOne(\n { _id: session.id },\n {\n $set: {\n activeOrganizationId: String(organization.id),\n activeProjectId: String(project.id),\n },\n }\n );\n\n if (user) {\n await userService.updateUserById(user.id, {\n lastActiveOrganizationId: String(organization.id),\n lastActiveProjectId: String(project.id),\n });\n }\n\n // Fire-and-forget screenshot generation\n refreshProjectScreenshotIfChanged({\n newApplicationUrl,\n previousApplicationUrl,\n existingImageUrl: existingProject.imageUrl,\n projectId: project.id.toString(),\n })\n .then((imageUrl) => {\n if (imageUrl !== undefined) {\n projectService\n .updateProjectById(project.id, { imageUrl })\n .catch(() => {});\n }\n })\n .catch(() => {});\n\n const formattedProject = mapProjectToAPI(updatedProject);\n\n const responseData = formatResponse<ProjectAPI>({\n message: t({\n en: 'Project updated successfully',\n 'en-GB': 'Project updated successfully',\n fr: 'Projet mis à jour avec succès',\n es: 'Proyecto actualizado con éxito',\n ru: 'Проект успешно обновлен',\n ja: 'プロジェクトが正常に更新されました',\n ko: '프로젝트가 성공적으로 업데이트되었습니다',\n zh: '项目已成功更新',\n de: 'Projekt erfolgreich aktualisiert',\n ar: 'تم تحديث المشروع بنجاح',\n it: 'Progetto aggiornato con successo',\n pt: 'Projeto atualizado com sucesso',\n hi: 'प्रोजेक्ट सफलतापूर्वक अपडेट किया गया',\n tr: 'Proje başarıyla güncellendi',\n pl: 'Projekt został pomyślnie zaktualizowany',\n id: 'Proyek berhasil diperbarui',\n vi: 'Dự án đã được cập nhật thành công',\n uk: 'Проєкт успішно оновлено',\n }),\n description: t({\n en: 'Your project has been updated successfully',\n 'en-GB': 'Your project has been updated successfully',\n fr: 'Votre projet a été mis à jour avec succès',\n es: 'Su proyecto ha sido actualizado con éxito',\n ru: 'Ваш проект был успешно обновлен',\n ja: 'プロジェクトは正常に更新されました',\n ko: '프로젝트가 성공적으로 업데이트되었습니다',\n zh: '您的项目已成功更新',\n de: 'Ihr Projekt wurde erfolgreich aktualisiert',\n ar: 'لقد تم تحديث مشروعك بنجاح',\n it: 'Il tuo progetto è stato aggiornato con successo',\n pt: 'Seu projeto foi atualizado com sucesso',\n hi: 'आपका प्रोजेक्ट सफलतापूर्वक अपडेट कर दिया गया है',\n tr: 'Projeniz başarıyla güncellendi',\n pl: 'Twój projekt został pomyślnie zaktualizowany',\n id: 'Proyek Anda telah berhasil diperbarui',\n vi: 'Dự án của bạn đã được cập nhật thành công',\n uk: 'Ваш проєкт успішно оновлено',\n }),\n data: formattedProject,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\ntype UserAndAdmin = { user: User; isAdmin: boolean };\nexport type ProjectMemberByIdOption = {\n userId: string | Types.ObjectId;\n isAdmin?: boolean;\n};\n\nexport type UpdateProjectMembersBody = Partial<{\n membersIds: ProjectMemberByIdOption[];\n}>;\nexport type UpdateProjectMembersResult = ResponseData<ProjectAPI>;\n\n/**\n * Update members to the dictionary in the database.\n */\nexport const updateProjectMembers = async (\n request: FastifyRequest<{ Body: UpdateProjectMembersBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, project, organization, roles } = request.session || {};\n const { membersIds } = request.body;\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (!organization) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'ORGANIZATION_NOT_DEFINED'\n );\n }\n\n if (membersIds?.length === 0) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_MUST_HAVE_MEMBER'\n );\n }\n\n if (membersIds?.map((el) => el.isAdmin)?.length === 0) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_MUST_HAVE_ADMIN'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:write'\n )({\n ...request.session,\n targetProjectIds: [String(project.id)],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n try {\n const existingUsers: UserAndAdmin[] = [];\n\n if (membersIds) {\n const userIdList = membersIds\n ?.filter(\n (member) =>\n // Remove members that are not in the organization\n !organization?.membersIds.includes(member.userId as Types.ObjectId)\n )\n .map((member) => member.userId);\n\n const users = await userService.getUsersByIds(userIdList);\n\n if (users) {\n const userMap: UserAndAdmin[] = users.map((user) => ({\n user,\n isAdmin:\n membersIds.find(\n (member) => String(member.userId) === String(user.id)\n )?.isAdmin ?? false,\n }));\n\n existingUsers.push(...userMap);\n }\n }\n\n const formattedMembers: Types.ObjectId[] = existingUsers.map(\n (user) => user.user.id\n );\n const formattedAdmin: Types.ObjectId[] = existingUsers\n .filter((el) => el.isAdmin)\n .map((user) => user.user.id);\n\n const updatedOrganization = await projectService.updateProjectById(\n project.id,\n {\n ...project,\n membersIds: formattedMembers,\n adminsIds: formattedAdmin,\n }\n );\n\n const formattedProject = mapProjectToAPI(updatedOrganization);\n\n const responseData = formatResponse<ProjectAPI>({\n message: t({\n en: 'Project members updated successfully',\n 'en-GB': 'Project members updated successfully',\n fr: 'Membres du projet mis à jour avec succès',\n es: 'Miembros del proyecto actualizados con éxito',\n ru: 'Члены проекта успешно обновлены',\n ja: 'プロジェクトメンバーが正常に更新されました',\n ko: '프로젝트 구성원이 성공적으로 업데이트되었습니다',\n zh: '项目成员已成功更新',\n de: 'Projektmitglieder erfolgreich aktualisiert',\n ar: 'تم تحديث أعضاء المشروع بنجاح',\n it: 'Membri del progetto aggiornati con successo',\n pt: 'Membros do projeto atualizados com sucesso',\n hi: 'प्रोजेक्ट सदस्य सफलतापूर्वक अपडेट किए गए',\n tr: 'Proje üyeleri başarıyla güncellendi',\n pl: 'Członkowie projektu zostali pomyślnie zaktualizowani',\n id: 'Anggota proyek berhasil diperbarui',\n vi: 'Thành viên dự án đã được cập nhật thành công',\n uk: 'Члени проєкту успішно оновлені',\n }),\n description: t({\n en: 'Your project members have been updated successfully',\n 'en-GB': 'Your project members have been updated successfully',\n fr: 'Les membres de votre projet ont été mis à jour avec succès',\n es: 'Los miembros de su proyecto han sido actualizados con éxito',\n ru: 'Члены вашего проекта были успешно обновлены',\n ja: 'プロジェクトメンバーは正常に更新されました',\n ko: '프로젝트 구성원이 성공적으로 업데이트되었습니다',\n zh: '您的项目成员已成功更新',\n de: 'Ihre Projektmitglieder wurden erfolgreich aktualisiert',\n ar: 'لقد تم تحديث أعضاء مشروعك بنجاح',\n it: 'I membri del tuo progetto sono stati aggiornati con successo',\n pt: 'Os membros do seu projeto foram atualizados com sucesso',\n hi: 'आपके प्रोजेक्ट के सदस्यों को सफलतापूर्वक अपडेट किया गया है',\n tr: 'Proje üyeleriniz başarıyla güncellendi',\n pl: 'Członkowie Twojego projektu zostali pomyślnie zaktualizowani',\n id: 'Anggota proyek Anda telah berhasil diperbarui',\n vi: 'Thành viên dự án của bạn đã được cập nhật thành công',\n uk: 'Члени вашого проєкту успішно оновлені',\n }),\n data: formattedProject,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type PushProjectConfigurationBody = ProjectConfiguration;\nexport type PushProjectConfigurationResult = ResponseData<ProjectConfiguration>;\n\n/**\n * Pushes a project configuration to the database.\n */\nexport const pushProjectConfiguration = async (\n request: FastifyRequest<{ Body: PushProjectConfigurationBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, project, roles } = request.session || {};\n const projectConfiguration = request.body;\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:write'\n )({\n ...request.session,\n targetProjectIds: [String(project.id)],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n try {\n const projectObject = await projectService.getProjectById(project.id);\n\n // Preserve existing API key if not provided in the update\n if (\n projectConfiguration.ai &&\n !projectConfiguration.ai.apiKey &&\n projectObject.configuration?.ai?.apiKey\n ) {\n projectConfiguration.ai.apiKey = projectObject.configuration.ai.apiKey;\n }\n\n const previousApplicationUrl =\n projectObject.configuration?.editor?.applicationURL;\n const newApplicationUrl = projectConfiguration.editor?.applicationURL;\n\n projectObject.configuration = projectConfiguration;\n\n await projectObject.save();\n\n // Fire-and-forget screenshot generation\n refreshProjectScreenshotIfChanged({\n newApplicationUrl,\n previousApplicationUrl,\n existingImageUrl: projectObject.imageUrl,\n projectId: project.id.toString(),\n })\n .then((imageUrl) => {\n if (imageUrl !== undefined) {\n projectService\n .updateProjectById(project.id, { imageUrl })\n .catch(() => {});\n }\n })\n .catch(() => {});\n\n if (!projectObject.configuration) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_UPDATE_FAILED',\n {\n projectId: project.id,\n }\n );\n }\n\n const responseData = formatResponse<ProjectConfiguration>({\n message: t({\n en: 'Project configuration updated successfully',\n 'en-GB': 'Project configuration updated successfully',\n fr: 'Configuration du projet mise à jour avec succès',\n es: 'Configuración del proyecto actualizada con éxito',\n ru: 'Конфигурация проекта успешно обновлена',\n ja: 'プロジェクト構成が正常に更新されました',\n ko: '프로젝트 설정이 성공적으로 업데이트되었습니다',\n zh: '项目配置已成功更新',\n de: 'Projektkonfiguration erfolgreich aktualisiert',\n ar: 'تم تحديث تكوين المشروع بنجاح',\n it: 'Configurazione del progetto aggiornata con successo',\n pt: 'Configuração do projeto atualizada com sucesso',\n hi: 'प्रोजेक्ट कॉन्फ़िगरेशन सफलतापूर्वक अपडेट किया गया',\n tr: 'Proje yapılandırması başarıyla güncellendi',\n pl: 'Konfiguracja projektu została pomyślnie zaktualizowana',\n id: 'Konfigurasi proyek berhasil diperbarui',\n vi: 'Cấu hình dự án đã được cập nhật thành công',\n uk: 'Конфігурацію проєкту успішно оновлено',\n }),\n description: t({\n en: 'Your project configuration has been updated successfully',\n 'en-GB': 'Your project configuration has been updated successfully',\n fr: 'La configuration du projet a été mise à jour avec succès',\n es: 'Su configuración del proyecto ha sido actualizada con éxito',\n ru: 'Конфигурация вашего проекта была успешно обновлена',\n ja: 'プロジェクト構成は正常に更新されました',\n ko: '프로젝트 설정이 성공적으로 업데이트되었습니다',\n zh: '您的项目配置已成功更新',\n de: 'Ihre Projektkonfiguration wurde erfolgreich aktualisiert',\n ar: 'لقد تم تحديث تكوين مشروعك بنجاح',\n it: 'La configurazione del tuo progetto è stata aggiornata con successo',\n pt: 'A configuração do seu projeto foi atualizada com sucesso',\n hi: 'आपका प्रोजेक्ट कॉन्फ़िगरेशन सफलतापूर्वक अपडेट कर दिया गया है',\n tr: 'Proje yapılandırmanız başarıyla güncellendi',\n pl: 'Konfiguracja Twojego projektu została pomyślnie zaktualizowana',\n id: 'Konfigurasi proyek Anda telah berhasil diperbarui',\n vi: 'Cấu hình dự án của bạn đã được cập nhật thành công',\n uk: 'Конфігурацію вашого проєкту успішно оновлено',\n }),\n data: mapProjectToAPI(projectObject) as ProjectConfiguration,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type TriggerBuildResult = ResponseData<{\n results: Array<{\n target: string;\n success: boolean;\n message?: string;\n }>;\n}>;\n\nexport type TriggerWebhookBody = {\n webhookIndex: number;\n};\n\nexport type TriggerWebhookResult = ResponseData<{\n target: string;\n success: boolean;\n message?: string;\n}>;\n\n/**\n * Triggers CI builds for a project (Git provider pipelines and webhooks)\n */\nexport const triggerBuild = async (\n request: FastifyRequest,\n reply: FastifyReply\n): Promise<void> => {\n const { project, roles } = request.session || {};\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:write'\n )({\n ...request.session,\n targetProjectIds: [String(project.id)],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n try {\n // Get full project with all relations\n const fullProject = await projectService.getProjectById(project.id);\n const results = await webhooksService.triggerAll(fullProject);\n\n const responseData = formatResponse<{\n results: Array<{\n target: string;\n success: boolean;\n message?: string;\n }>;\n }>({\n message: t({\n en: 'Build triggers initiated',\n 'en-GB': 'Build triggers initiated',\n fr: 'Déclenchement des builds initié',\n es: 'Inicio de los triggers de build',\n ru: 'Запуск триггеров сборки инициирован',\n ja: 'ビルドトリガーが開始されました',\n ko: '빌드 트리거가 시작되었습니다',\n zh: '构建触发器已启动',\n de: 'Build-Trigger initiiert',\n ar: 'بدء مشغلات البناء',\n it: 'Trigger di build avviati',\n pt: 'Gatilhos de build iniciados',\n hi: 'बिल्ड ट्रिगर शुरू किए गए',\n tr: 'Build tetikleyicileri başlatıldı',\n pl: 'Zainicjowano wyzwalacze budowania',\n id: 'Pemicu build dimulai',\n vi: 'Đã bắt đầu trình kích hoạt bản dựng',\n uk: 'Запуск тригерів збірки ініційовано',\n }),\n description: t({\n en: 'CI pipelines and webhooks have been triggered',\n 'en-GB': 'CI pipelines and webhooks have been triggered',\n fr: 'Les pipelines CI et webhooks ont été déclenchés',\n es: 'Los pipelines CI y webhooks han sido activados',\n ru: 'CI-конвейеры и вебхуки были запущены',\n ja: 'CIパイプラインとウェブフックがトリガーされました',\n ko: 'CI 파이프라인과 웹후크가 트리거되었습니다',\n zh: 'CI 流水线和 Webhook 已触发',\n de: 'CI-Pipelines und Webhooks wurden ausgelöst',\n ar: 'تم تفعيل أنابيب CI والويب هوك',\n it: 'Le pipeline CI e i webhook sono stati attivati',\n pt: 'Pipelines CI e webhooks foram acionados',\n hi: 'CI पाइपलाइन और वेबहुक ट्रिगर किए गए हैं',\n tr: 'CI hatları ve webhooklar tetiklendi',\n pl: 'Potoki CI i webhooki zostały wyzwolone',\n id: 'Pipa CI dan webhook telah dipicu',\n vi: 'Đường ống CI và webhook đã được kích hoạt',\n uk: 'Конвеєри CI та вебхуки були запущені',\n }),\n data: { results },\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\n/**\n * Triggers a single webhook by index\n */\nexport const triggerWebhook = async (\n request: FastifyRequest<{ Body: TriggerWebhookBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { project, roles } = request.session || {};\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:write'\n )({\n ...request.session,\n targetProjectIds: [String(project.id)],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n try {\n const { webhookIndex } = request.body;\n\n if (typeof webhookIndex !== 'number' || webhookIndex < 0) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'INVALID_REQUEST_BODY'\n );\n }\n\n // Get full project with all relations\n const fullProject = await projectService.getProjectById(project.id);\n const result = await webhooksService.triggerSingleWebhook(\n fullProject,\n webhookIndex\n );\n\n const responseData = formatResponse<{\n target: string;\n success: boolean;\n message?: string;\n }>({\n message: t({\n en: 'Webhook triggered',\n 'en-GB': 'Webhook triggered',\n fr: 'Webhook déclenché',\n es: 'Webhook activado',\n ru: 'Вебхук запущен',\n ja: 'ウェブフックがトリガーされました',\n ko: '웹후크가 트리거되었습니다',\n zh: 'Webhook 已触发',\n de: 'Webhook ausgelöst',\n ar: 'تم تفعيل الويب هوك',\n it: 'Webhook attivato',\n pt: 'Webhook acionado',\n hi: 'वेबहुक ट्रिगर किया गया',\n tr: 'Webhook tetiklendi',\n pl: 'Webhook został wyzwolony',\n id: 'Webhook dipicu',\n vi: 'Webhook đã được kích hoạt',\n uk: 'Вебхук запущено',\n }),\n description: t({\n en: `Webhook \"${result.target}\" has been triggered`,\n 'en-GB': `Webhook \"${result.target}\" has been triggered`,\n fr: `Le webhook \"${result.target}\" a été déclenché`,\n es: `El webhook \"${result.target}\" ha sido activado`,\n ru: `Вебхук \"${result.target}\" был запущен`,\n ja: `ウェブフック \"${result.target}\" がトリガーされました`,\n ko: `웹후크 \"${result.target}\"가 트리거되었습니다`,\n zh: `Webhook \"${result.target}\" 已触发`,\n de: `Webhook \"${result.target}\" wurde ausgelöst`,\n ar: `تم تفعيل الويب هوك \"${result.target}\"`,\n it: `Il webhook \"${result.target}\" è stato attivato`,\n pt: `O webhook \"${result.target}\" foi acionado`,\n hi: `वेबहुक \"${result.target}\" ट्रिगर किया गया है`,\n tr: `\"${result.target}\" webhooku tetiklendi`,\n pl: `Webhook \"${result.target}\" został wyzwolony`,\n id: `Webhook \"${result.target}\" telah dipicu`,\n vi: `Webhook \"${result.target}\" đã được kích hoạt`,\n uk: `Вебхук \"${result.target}\" запущено`,\n }),\n data: result,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type DeleteProjectResult = ResponseData<ProjectAPI>;\n\n/**\n * Deletes a project from the database by its ID.\n */\nexport const deleteProject = async (\n _request: FastifyRequest,\n reply: FastifyReply\n): Promise<void> => {\n const { user, organization, project, session, roles } =\n _request.session || {};\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (!organization) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'ORGANIZATION_NOT_DEFINED'\n );\n }\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (typeof session === 'undefined') {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'SESSION_NOT_DEFINED'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:admin'\n )({\n ..._request.session,\n targetProjectIds: [String(project.id)],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n try {\n const projectToDelete = await projectService.getProjectById(project.id);\n\n if (String(projectToDelete.organizationId) !== String(organization.id)) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_IN_ORGANIZATION'\n );\n }\n\n const deletedProject = await projectService.deleteProjectById(project.id);\n\n if (!deletedProject) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED',\n {\n projectId: project.id,\n }\n );\n }\n\n logger.info(`Project deleted: ${String(deletedProject.id)}`);\n\n const responseData = formatResponse<ProjectAPI>({\n message: t({\n en: 'Project deleted successfully',\n 'en-GB': 'Project deleted successfully',\n fr: 'Projet supprimé avec succès',\n es: 'Proyecto eliminado con éxito',\n ru: 'Проект успешно удален',\n ja: 'プロジェクトが正常に削除されました',\n ko: '프로젝트가 성공적으로 삭제되었습니다',\n zh: '项目已成功删除',\n de: 'Projekt erfolgreich gelöscht',\n ar: 'تم حذف المشروع بنجاح',\n it: 'Progetto eliminato con successo',\n pt: 'Projeto excluído com sucesso',\n hi: 'प्रोजेक्ट सफलतापूर्वक हटा दिया गया',\n tr: 'Proje başarıyla silindi',\n pl: 'Projekt został pomyślnie usunięty',\n id: 'Proyek berhasil dihapus',\n vi: 'Dự án đã được xóa thành công',\n uk: 'Проєкт успішно видалено',\n }),\n description: t({\n en: 'Your project has been deleted successfully',\n 'en-GB': 'Your project has been deleted successfully',\n fr: 'Votre projet a été supprimé avec succès',\n es: 'Su proyecto ha sido eliminado con éxito',\n ru: 'Ваш проект был успешно удален',\n ja: 'プロジェクトは正常に削除されました',\n ko: '프로젝트가 성공적으로 삭제되었습니다',\n zh: '您的项目已成功删除',\n de: 'Ihr Projekt wurde erfolgreich gelöscht',\n ar: 'لقد تم حذف مشروعك بنجاح',\n it: 'Il tuo progetto è stato eliminato con successo',\n pt: 'Seu projeto foi excluído com sucesso',\n hi: 'आपका प्रोजेक्ट सफलतापूर्वक हटा दिया गया है',\n tr: 'Projeniz başarıyla silindi',\n pl: 'Twój projekt został pomyślnie usunięty',\n id: 'Proyek Anda telah berhasil dihapus',\n vi: 'Dự án của bạn đã được xóa thành công',\n uk: 'Ваш проєкт успішно видалено',\n }),\n data: mapProjectToAPI(deletedProject),\n });\n\n await SessionModel.updateOne(\n { _id: session.id },\n { $set: { activeProjectId: null } }\n );\n\n if (user) {\n await userService.updateUserById(user.id, {\n lastActiveProjectId: null,\n });\n }\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type DeleteProjectByIdAdminParams = { projectId: string };\nexport type DeleteProjectByIdAdminResult = ResponseData<ProjectAPI>;\n\n/**\n * Admin-only: Deletes any project from the database by its ID.\n */\nexport const deleteProjectByIdAdmin = async (\n request: FastifyRequest<{ Params: DeleteProjectByIdAdminParams }>,\n reply: FastifyReply\n): Promise<void> => {\n const { projectId } = request.params;\n const { roles } = request.session || {};\n\n try {\n const project = await projectService.getProjectById(projectId);\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:admin'\n )({\n ...request.session,\n targetProjectIds: [String(project.id)],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PERMISSION_DENIED'\n );\n }\n\n const deletedProject = await projectService.deleteProjectById(project.id);\n\n if (!deletedProject) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n const formattedProject = mapProjectToAPI(deletedProject);\n const responseData = formatResponse<ProjectAPI>({\n message: t({\n en: 'Project deleted',\n fr: 'Projet supprimé',\n es: 'Proyecto eliminado',\n 'en-GB': 'Project deleted',\n de: 'Projekt gelöscht',\n ja: 'プロジェクトが削除されました',\n ko: '프로젝트가 삭제되었습니다',\n zh: '项目已删除',\n it: 'Progetto eliminato',\n pt: 'Projeto excluído',\n hi: 'परियोजना हटा दी गई',\n ar: 'تم حذف المشروع',\n ru: 'Проект удален',\n tr: 'Proje silindi',\n pl: 'Projekt usunięty',\n id: 'Proyek dihapus',\n vi: 'Dự án đã bị xóa',\n uk: 'Проєкт видалено',\n }),\n data: formattedProject,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type SelectProjectParam = { projectId: string | Types.ObjectId };\nexport type SelectProjectResult = ResponseData<ProjectAPI>;\n\n/**\n * Select a project.\n */\nexport const selectProject = async (\n request: FastifyRequest<{ Params: SelectProjectParam }>,\n reply: FastifyReply\n) => {\n const { projectId } = request.params;\n const { session, roles, user } = request.session || {};\n\n if (!projectId) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_ID_NOT_FOUND'\n );\n }\n\n if (typeof session === 'undefined') {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'SESSION_NOT_DEFINED'\n );\n }\n\n try {\n const project = await projectService.getProjectById(projectId);\n\n if (\n !hasPermission(\n roles || [],\n 'project:read'\n )({\n ...request.session,\n targetProjects: [project],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PERMISSION_DENIED'\n );\n }\n\n // When selecting a project, reset to its default environment\n const defaultEnvId =\n project.environments?.find((e: any) => e.isDefault)?.id ??\n project.environments?.[0]?.id;\n\n await SessionModel.updateOne(\n { _id: session.id },\n {\n $set: {\n activeProjectId: String(projectId),\n activeEnvironmentId: defaultEnvId ? String(defaultEnvId) : null,\n },\n }\n );\n\n if (user) {\n await userService.updateUserById(user.id, {\n lastActiveProjectId: String(projectId),\n });\n }\n\n const responseData = formatResponse<ProjectAPI>({\n message: t({\n en: 'Project selected successfully',\n 'en-GB': 'Project selected successfully',\n fr: 'Projet sélectionné avec succès',\n es: 'Proyecto seleccionado con éxito',\n ru: 'Проект успешно выбран',\n ja: 'プロジェクトが正常に選択されました',\n ko: '프로젝트가 성공적으로 선택되었습니다',\n zh: '项目已成功选择',\n de: 'Projekt erfolgreich ausgewählt',\n ar: 'تم اختيار المشروع بنجاح',\n it: 'Progetto selezionato con successo',\n pt: 'Projeto selecionado com sucesso',\n hi: 'प्रोजेक्ट सफलतापूर्वक चुना गया',\n tr: 'Proje başarıyla seçildi',\n pl: 'Projekt został pomyślnie wybrany',\n id: 'Proyek berhasil dipilih',\n vi: 'Dự án đã được chọn thành công',\n uk: 'Проєкт успішно вибрано',\n }),\n description: t({\n en: 'Your project has been selected successfully',\n 'en-GB': 'Your project has been selected successfully',\n fr: 'Votre projet a été sélectionné avec succès',\n es: 'Su proyecto ha sido seleccionado con éxito',\n ru: 'Ваш проект был успешно выбран',\n ja: 'プロジェクトは正常に選択されました',\n ko: '프로젝트가 성공적으로 선택되었습니다',\n zh: '您的项目已成功选择',\n de: 'Ihr Projekt wurde erfolgreich ausgewählt',\n ar: 'لقد تم اختيار مشروعك بنجاح',\n it: 'Il tuo progetto è stato selezionato con successo',\n pt: 'Seu projeto foi selecionado com sucesso',\n hi: 'आपका प्रोजेक्ट सफलतापूर्वक चुन लिया गया है',\n tr: 'Projeniz başarıyla seçildi',\n pl: 'Twój projekt został pomyślnie wybrany',\n id: 'Proyek Anda telah berhasil dipilih',\n vi: 'Dự án của bạn đã được chọn thành công',\n uk: 'Ваш проєкт успішно вибрано',\n }),\n data: mapProjectToAPI(project),\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type UnselectProjectResult = ResponseData<null>;\n\n/**\n * Unselect a project.\n */\nexport const unselectProject = async (\n _request: FastifyRequest,\n reply: FastifyReply\n) => {\n const { session, user } = _request.session || {};\n\n if (typeof session === 'undefined') {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'SESSION_NOT_DEFINED'\n );\n }\n\n try {\n await SessionModel.updateOne(\n { _id: session.id },\n { $set: { activeProjectId: null, activeEnvironmentId: null } }\n );\n\n if (user) {\n await userService.updateUserById(user.id, {\n lastActiveProjectId: null,\n });\n }\n\n const responseData = formatResponse<null>({\n message: t({\n en: 'Project unselected successfully',\n 'en-GB': 'Project unselected successfully',\n fr: 'Projet désélectionné avec succès',\n es: 'Proyecto deseleccionado con éxito',\n ru: 'Проект успешно снят с выбора',\n ja: 'プロジェクトの選択が正常に解除されました',\n ko: '프로젝트 선택이 성공적으로 해제되었습니다',\n zh: '项目已成功取消选择',\n de: 'Projekt erfolgreich abgewählt',\n ar: 'تم إلغاء تحديد المشروع بنجاح',\n it: 'Progetto deselezionato con successo',\n pt: 'Projeto desmarcado com sucesso',\n hi: 'प्रोजेक्ट सफलतापूर्वक अनसेलेक्ट किया गया',\n tr: 'Proje seçimi başarıyla kaldırıldı',\n pl: 'Wybór projektu został pomyślnie cofnięty',\n id: 'Proyek berhasil batal dipilih',\n vi: 'Dự án đã được bỏ chọn thành công',\n uk: 'Вибір проєкту успішно скасовано',\n }),\n description: t({\n en: 'Your project has been unselected successfully',\n 'en-GB': 'Your project has been unselected successfully',\n fr: 'Votre projet a été désélectionné avec succès',\n es: 'Su proyecto ha sido deseleccionado con éxito',\n ru: 'Выбор вашего проекта был успешно снят',\n ja: 'プロジェクトの選択は正常に解除されました',\n ko: '프로젝트 선택이 성공적으로 해제되었습니다',\n zh: '您的项目已成功取消选择',\n de: 'Ihr Projekt wurde erfolgreich abgewählt',\n ar: 'لقد تم إلغاء تحديد مشروعك بنجاح',\n it: 'Il tuo progetto è stato deselezionato con successo',\n pt: 'Seu projeto foi desmarcado com sucesso',\n hi: 'आपका प्रोजेक्ट सफलतापूर्वक अनसेलेक्ट कर दिया गया है',\n tr: 'Projenizin seçimi başarıyla kaldırıldı',\n pl: 'Wybór Twojego projektu został pomyślnie cofnięty',\n id: 'Proyek Anda telah berhasil batal dipilih',\n vi: 'Dự án của bạn đã được bỏ chọn thành công',\n uk: 'Вибір вашого проєкту успішно скасовано',\n }),\n data: null,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type GetCIConfigurationResult = ResponseData<ciService.CIStatus>;\n\n/**\n * Get CI configuration status for the current project\n */\nexport const getCIConfiguration = async (\n request: FastifyRequest,\n reply: FastifyReply\n): Promise<void> => {\n const { project, user } = request.session || {};\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n try {\n const ciStatus = await ciService.getCIStatus(project, user.id);\n\n const responseData = formatResponse<ciService.CIStatus>({\n data: ciStatus,\n });\n\n return reply.send(responseData);\n } catch (error) {\n if ((error as any)?.isAppError) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n return ErrorHandler.handleCustomErrorResponse(\n reply,\n 'CI_CONFIG_ERROR',\n 'CI Configuration Error',\n (error as Error)?.message ?? 'Failed to get CI configuration',\n undefined,\n 500\n );\n }\n};\n\nexport type PushCIConfigurationResult = ResponseData<{ success: boolean }>;\n\n/**\n * Push CI configuration file to the repository\n */\nexport const pushCIConfiguration = async (\n request: FastifyRequest,\n reply: FastifyReply\n): Promise<void> => {\n const { project, user } = request.session || {};\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n try {\n await ciService.installCI(project, user.id);\n\n const responseData = formatResponse<{ success: boolean }>({\n message: t({\n en: 'CI configuration installed successfully',\n 'en-GB': 'CI configuration installed successfully',\n fr: 'Configuration CI installée avec succès',\n es: 'Configuración CI instalada con éxito',\n ru: 'CI-конфигурация успешно установлена',\n ja: 'CI構成が正常にインストールされました',\n ko: 'CI 설정이 성공적으로 설치되었습니다',\n zh: 'CI 配置已成功安装',\n de: 'CI-Konfiguration erfolgreich installiert',\n ar: 'تم تثبيت تكوين CI بنجاح',\n it: 'Configurazione CI installata con successo',\n pt: 'Configuração CI installada com successo',\n hi: 'CI कॉन्फ़िगरेशन सफलतापूर्वक स्थापित किया गया',\n tr: 'CI yapılandırması başarıyla yüklendi',\n pl: 'Konfiguracja CI została pomyślnie zainstalowana',\n id: 'Konfigurasi CI berhasil diinstal',\n vi: 'Cài đặt cấu hình CI thành công',\n uk: 'Конфігурацію CI успішно встановлено',\n }),\n description: t({\n en: 'The CI workflow file has been added to your repository',\n 'en-GB': 'The CI workflow file has been added to your repository',\n fr: 'Le fichier de workflow CI a été ajouté à votre dépôt',\n es: 'El archivo de flujo de trabajo CI se ha agregado a su repositorio',\n ru: 'Файл рабочего процесса CI был добавлен в ваш репозиторий',\n ja: 'CIワークフローファイルがリポジトリに追加されました',\n ko: 'CI 워크플로 파일이 저장소에 추가되었습니다',\n zh: 'CI 工作流文件已添加到您的存储库中',\n de: 'Die CI-Workflow-Datei wurde Ihrem Repository hinzugefügt',\n ar: 'تمت إضافة ملف سير عمل CI إلى مستودعك',\n it: 'Il file del workflow CI è stato aggiunto al tuo repository',\n pt: 'O arquivo de fluxo de trabalho CI foi adicionado ao seu repositório',\n hi: 'CI वर्कफ़्लो फ़ाइल आपके रिपॉजिटरी में जोड़ दी गई है',\n tr: 'CI iş akışı dosyası deponuza eklendi',\n pl: 'Plik przepływu pracy CI został dodany do Twojego repozytorium',\n id: 'File alur kerja CI telah ditambahkan ke repositori Anda',\n vi: 'Tệp quy trình làm việc CI đã được thêm vào kho lưu trữ của bạn',\n uk: 'Файл робочого процесу CI додано до вашого репозиторію',\n }),\n data: { success: true },\n });\n\n return reply.send(responseData);\n } catch (error) {\n if ((error as any)?.isAppError) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n return ErrorHandler.handleCustomErrorResponse(\n reply,\n 'CI_INSTALL_ERROR',\n 'CI Installation Error',\n (error as Error)?.message ?? 'Failed to install CI configuration',\n undefined,\n 500\n );\n }\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAwCA,MAAa,cAAc,OACzB,SACA,UACkB;CAClB,MAAM,EAAE,MAAM,UAAU,QAAQ,WAAW,CAAC;CAC5C,MAAM,EAAE,SAAS,aAAa,UAAU,MAAM,MAAM,qBAClD,+BAA+B,OAAO;CAExC,IAAI,CAAC,MACH,OAAO,aAAa,2BAA2B,OAAO,kBAAkB;CAO1E,IAAI;EACF,MAAM,WAAW,MAAMA,aACrB,SACA,MACA,UACA,WACF;EAIA,IACE,SAAS,SAAS,KAClB,CAAC,cACC,SAAS,CAAC,GACV,cACF,CAAC,CAAC;GACA,GAAG,QAAQ;GACX,gBAAgB;EAClB,CAAC,GAED,OAAO,aAAa,2BAClB,OACA,mBACF;EAGF,MAAM,aAAa,MAAMC,cAA6B,OAAO;EAI7D,MAAM,eAAe,wBAAoC;GACvD,MAHwB,iBAAiB,QAGnB;GACtB;GACA;GACA,YAAY,iBAAiB,UAAU;GACvC;EACF,CAAC;EAED,OAAO,MAAM,KAAK,YAAY;CAChC,SAAS,OAAO;EACd,OAAO,aAAa,uBAAuB,OAAO,KAAiB;CACrE;AACF;;;;AAQA,MAAa,aAAa,OACxB,SACA,UACkB;CAClB,MAAM,EAAE,cAAc,MAAM,UAAU,QAAQ,WAAW,CAAC;CAC1D,MAAM,cAAc,QAAQ;CAE5B,IAAI,CAAC,MACH,OAAO,aAAa,2BAA2B,OAAO,kBAAkB;CAG1E,IAAI,CAAC,cACH,OAAO,aAAa,2BAClB,OACA,0BACF;CAGF,IAAI,CAAC,aACH,OAAO,aAAa,2BAClB,OACA,wBACF;CAGF,IACE,CAAC,cACC,SAAS,CAAC,GACV,oBACF,CAAC,CAAC;EACA,GAAG,QAAQ;EACX,qBAAqB,CAAC,YAAY;CACpC,CAAC,GAED,OAAO,aAAa,2BAA2B,OAAO,mBAAmB;CAG3E,MAAM,EAAE,SAAS;CAEjB,MAAM,WAAW,eAAe,IAAI;CAEpC,IAAI,SAAS,kBAKX;MAAI,MAJuBA,cAA6B,EACtD,gBAAgB,aAAa,GAC/B,CAAC,KAEmB,SAAS,kBAC3B,OAAO,aAAa,2BAClB,OACA,8BACA,EACE,gBAAgB,aAAa,GAC/B,CACF;CACF;CAGF,MAAM,UAAuB;EAC3B,YAAY,CAAC,KAAK,EAAE;EACpB,WAAW,CAAC,KAAK,EAAE;EACnB,WAAW,KAAK;EAChB,gBAAgB,aAAa;EAC7B,GAAG;CACL;CAEA,IAAI;EAGF,MAAM,mBAAmB,gBAAgB,MAFhBC,cAA6B,OAAO,CAEV;EAEnD,MAAM,eAAe,eAA2B;GAC9C,SAAS,EAAE;IACT,IAAI;IACJ,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;GACN,CAAC;GACD,aAAa,EAAE;IACb,IAAI;IACJ,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;GACN,CAAC;GACD,MAAM;EACR,CAAC;EAED,MAAM,KAAK,YAAY;EAGvB,IAAI;GACF,MAAM,uBAAuB,CAAC,iBAAiB,EAAE,GAAG,KAAK,EAAE;EAC7D,QAAQ,CAER;CACF,SAAS,OAAO;EACd,OAAO,aAAa,uBAAuB,OAAO,KAAiB;CACrE;AACF;;;;AAQA,MAAa,gBAAgB,OAC3B,SACA,UACkB;CAClB,MAAM,EAAE,cAAc,SAAS,MAAM,SAAS,UAAU,QAAQ,WAAW,CAAC;CAC5E,MAAM,cAAc,QAAQ;CAE5B,IAAI,CAAC,MACH,OAAO,aAAa,2BAA2B,OAAO,kBAAkB;CAG1E,IAAI,CAAC,SACH,OAAO,aAAa,2BAClB,OACA,wBACF;CAGF,IAAI,CAAC,cACH,OAAO,aAAa,2BAClB,OACA,0BACF;CAGF,IAAI,OAAO,QAAQ,cAAc,MAAM,OAAO,aAAa,EAAE,GAC3D,OAAO,aAAa,2BAClB,OACA,6BACF;CAGF,IACE,CAAC,cACC,SAAS,CAAC,GACV,eACF,CAAC,CAAC;EACA,GAAG,QAAQ;EACX,kBAAkB,CAAC,OAAO,QAAQ,EAAE,CAAC;CACvC,CAAC,GAED,OAAO,aAAa,2BAA2B,OAAO,mBAAmB;CAG3E,IAAI,OAAO,YAAY,aACrB,OAAO,aAAa,2BAClB,OACA,qBACF;CAGF,IAAI;EACF,MAAM,kBAAkB,MAAMC,eAA8B,QAAQ,EAAE;EAEtE,MAAM,yBACJ,gBAAgB,eAAe,QAAQ;EACzC,MAAM,oBAAoB,YAAY,eAAe,QAAQ;EAE7D,MAAM,iBAAiB,MAAMC,kBAC3B,QAAQ,IACR,WACF;EAGA,MAAM,aAAa,UACjB,EAAE,KAAK,QAAQ,GAAG,GAClB,EACE,MAAM;GACJ,sBAAsB,OAAO,aAAa,EAAE;GAC5C,iBAAiB,OAAO,QAAQ,EAAE;EACpC,EACF,CACF;EAEA,IAAI,MACF,MAAMC,eAA2B,KAAK,IAAI;GACxC,0BAA0B,OAAO,aAAa,EAAE;GAChD,qBAAqB,OAAO,QAAQ,EAAE;EACxC,CAAC;EAIH,kCAAkC;GAChC;GACA;GACA,kBAAkB,gBAAgB;GAClC,WAAW,QAAQ,GAAG,SAAS;EACjC,CAAC,CAAC,CACC,MAAM,aAAa;GAClB,IAAI,aAAa,QACf,kBACqB,QAAQ,IAAI,EAAE,SAAS,CAAC,CAAC,CAC3C,YAAY,CAAC,CAAC;EAErB,CAAC,CAAC,CACD,YAAY,CAAC,CAAC;EAEjB,MAAM,mBAAmB,gBAAgB,cAAc;EAEvD,MAAM,eAAe,eAA2B;GAC9C,SAAS,EAAE;IACT,IAAI;IACJ,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;GACN,CAAC;GACD,aAAa,EAAE;IACb,IAAI;IACJ,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;GACN,CAAC;GACD,MAAM;EACR,CAAC;EAED,OAAO,MAAM,KAAK,YAAY;CAChC,SAAS,OAAO;EACd,OAAO,aAAa,uBAAuB,OAAO,KAAiB;CACrE;AACF;;;;AAgBA,MAAa,uBAAuB,OAClC,SACA,UACkB;CAClB,MAAM,EAAE,MAAM,SAAS,cAAc,UAAU,QAAQ,WAAW,CAAC;CACnE,MAAM,EAAE,eAAe,QAAQ;CAE/B,IAAI,CAAC,MACH,OAAO,aAAa,2BAA2B,OAAO,kBAAkB;CAG1E,IAAI,CAAC,SACH,OAAO,aAAa,2BAClB,OACA,qBACF;CAGF,IAAI,CAAC,cACH,OAAO,aAAa,2BAClB,OACA,0BACF;CAGF,IAAI,YAAY,WAAW,GACzB,OAAO,aAAa,2BAClB,OACA,0BACF;CAGF,IAAI,YAAY,KAAK,OAAO,GAAG,OAAO,CAAC,EAAE,WAAW,GAClD,OAAO,aAAa,2BAClB,OACA,yBACF;CAGF,IACE,CAAC,cACC,SAAS,CAAC,GACV,eACF,CAAC,CAAC;EACA,GAAG,QAAQ;EACX,kBAAkB,CAAC,OAAO,QAAQ,EAAE,CAAC;CACvC,CAAC,GAED,OAAO,aAAa,2BAA2B,OAAO,mBAAmB;CAG3E,IAAI;EACF,MAAM,gBAAgC,CAAC;EAEvC,IAAI,YAAY;GACd,MAAM,aAAa,YACf,QACC,WAEC,CAAC,cAAc,WAAW,SAAS,OAAO,MAAwB,CACtE,CAAC,CACA,KAAK,WAAW,OAAO,MAAM;GAEhC,MAAM,QAAQ,MAAMC,cAA0B,UAAU;GAExD,IAAI,OAAO;IACT,MAAM,UAA0B,MAAM,KAAK,UAAU;KACnD;KACA,SACE,WAAW,MACR,WAAW,OAAO,OAAO,MAAM,MAAM,OAAO,KAAK,EAAE,CACtD,CAAC,EAAE,WAAW;IAClB,EAAE;IAEF,cAAc,KAAK,GAAG,OAAO;GAC/B;EACF;EAEA,MAAM,mBAAqC,cAAc,KACtD,SAAS,KAAK,KAAK,EACtB;EACA,MAAM,iBAAmC,cACtC,QAAQ,OAAO,GAAG,OAAO,CAAC,CAC1B,KAAK,SAAS,KAAK,KAAK,EAAE;EAW7B,MAAM,mBAAmB,gBAAgB,MATPF,kBAChC,QAAQ,IACR;GACE,GAAG;GACH,YAAY;GACZ,WAAW;EACb,CACF,CAE4D;EAE5D,MAAM,eAAe,eAA2B;GAC9C,SAAS,EAAE;IACT,IAAI;IACJ,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;GACN,CAAC;GACD,aAAa,EAAE;IACb,IAAI;IACJ,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;GACN,CAAC;GACD,MAAM;EACR,CAAC;EAED,OAAO,MAAM,KAAK,YAAY;CAChC,SAAS,OAAO;EACd,OAAO,aAAa,uBAAuB,OAAO,KAAiB;CACrE;AACF;;;;AAQA,MAAa,2BAA2B,OACtC,SACA,UACkB;CAClB,MAAM,EAAE,MAAM,SAAS,UAAU,QAAQ,WAAW,CAAC;CACrD,MAAM,uBAAuB,QAAQ;CAErC,IAAI,CAAC,MACH,OAAO,aAAa,2BAA2B,OAAO,kBAAkB;CAG1E,IAAI,CAAC,SACH,OAAO,aAAa,2BAClB,OACA,qBACF;CAGF,IACE,CAAC,cACC,SAAS,CAAC,GACV,eACF,CAAC,CAAC;EACA,GAAG,QAAQ;EACX,kBAAkB,CAAC,OAAO,QAAQ,EAAE,CAAC;CACvC,CAAC,GAED,OAAO,aAAa,2BAA2B,OAAO,mBAAmB;CAG3E,IAAI;EACF,MAAM,gBAAgB,MAAMD,eAA8B,QAAQ,EAAE;EAGpE,IACE,qBAAqB,MACrB,CAAC,qBAAqB,GAAG,UACzB,cAAc,eAAe,IAAI,QAEjC,qBAAqB,GAAG,SAAS,cAAc,cAAc,GAAG;EAGlE,MAAM,yBACJ,cAAc,eAAe,QAAQ;EACvC,MAAM,oBAAoB,qBAAqB,QAAQ;EAEvD,cAAc,gBAAgB;EAE9B,MAAM,cAAc,KAAK;EAGzB,kCAAkC;GAChC;GACA;GACA,kBAAkB,cAAc;GAChC,WAAW,QAAQ,GAAG,SAAS;EACjC,CAAC,CAAC,CACC,MAAM,aAAa;GAClB,IAAI,aAAa,QACf,kBACqB,QAAQ,IAAI,EAAE,SAAS,CAAC,CAAC,CAC3C,YAAY,CAAC,CAAC;EAErB,CAAC,CAAC,CACD,YAAY,CAAC,CAAC;EAEjB,IAAI,CAAC,cAAc,eACjB,OAAO,aAAa,2BAClB,OACA,yBACA,EACE,WAAW,QAAQ,GACrB,CACF;EAGF,MAAM,eAAe,eAAqC;GACxD,SAAS,EAAE;IACT,IAAI;IACJ,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;GACN,CAAC;GACD,aAAa,EAAE;IACb,IAAI;IACJ,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;GACN,CAAC;GACD,MAAM,gBAAgB,aAAa;EACrC,CAAC;EAED,OAAO,MAAM,KAAK,YAAY;CAChC,SAAS,OAAO;EACd,OAAO,aAAa,uBAAuB,OAAO,KAAiB;CACrE;AACF;;;;AAuBA,MAAa,eAAe,OAC1B,SACA,UACkB;CAClB,MAAM,EAAE,SAAS,UAAU,QAAQ,WAAW,CAAC;CAE/C,IAAI,CAAC,SACH,OAAO,aAAa,2BAClB,OACA,qBACF;CAGF,IACE,CAAC,cACC,SAAS,CAAC,GACV,eACF,CAAC,CAAC;EACA,GAAG,QAAQ;EACX,kBAAkB,CAAC,OAAO,QAAQ,EAAE,CAAC;CACvC,CAAC,GAED,OAAO,aAAa,2BAA2B,OAAO,mBAAmB;CAG3E,IAAI;EAEF,MAAM,cAAc,MAAMA,eAA8B,QAAQ,EAAE;EAClE,MAAM,UAAU,MAAMI,WAA2B,WAAW;EAE5D,MAAM,eAAe,eAMlB;GACD,SAAS,EAAE;IACT,IAAI;IACJ,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;GACN,CAAC;GACD,aAAa,EAAE;IACb,IAAI;IACJ,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;GACN,CAAC;GACD,MAAM,EAAE,QAAQ;EAClB,CAAC;EAED,OAAO,MAAM,KAAK,YAAY;CAChC,SAAS,OAAO;EACd,OAAO,aAAa,uBAAuB,OAAO,KAAiB;CACrE;AACF;;;;AAKA,MAAa,iBAAiB,OAC5B,SACA,UACkB;CAClB,MAAM,EAAE,SAAS,UAAU,QAAQ,WAAW,CAAC;CAE/C,IAAI,CAAC,SACH,OAAO,aAAa,2BAClB,OACA,qBACF;CAGF,IACE,CAAC,cACC,SAAS,CAAC,GACV,eACF,CAAC,CAAC;EACA,GAAG,QAAQ;EACX,kBAAkB,CAAC,OAAO,QAAQ,EAAE,CAAC;CACvC,CAAC,GAED,OAAO,aAAa,2BAA2B,OAAO,mBAAmB;CAG3E,IAAI;EACF,MAAM,EAAE,iBAAiB,QAAQ;EAEjC,IAAI,OAAO,iBAAiB,YAAY,eAAe,GACrD,OAAO,aAAa,2BAClB,OACA,sBACF;EAIF,MAAM,cAAc,MAAMJ,eAA8B,QAAQ,EAAE;EAClE,MAAM,SAAS,MAAMK,qBACnB,aACA,YACF;EAEA,MAAM,eAAe,eAIlB;GACD,SAAS,EAAE;IACT,IAAI;IACJ,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;GACN,CAAC;GACD,aAAa,EAAE;IACb,IAAI,YAAY,OAAO,OAAO;IAC9B,SAAS,YAAY,OAAO,OAAO;IACnC,IAAI,eAAe,OAAO,OAAO;IACjC,IAAI,eAAe,OAAO,OAAO;IACjC,IAAI,WAAW,OAAO,OAAO;IAC7B,IAAI,WAAW,OAAO,OAAO;IAC7B,IAAI,QAAQ,OAAO,OAAO;IAC1B,IAAI,YAAY,OAAO,OAAO;IAC9B,IAAI,YAAY,OAAO,OAAO;IAC9B,IAAI,uBAAuB,OAAO,OAAO;IACzC,IAAI,eAAe,OAAO,OAAO;IACjC,IAAI,cAAc,OAAO,OAAO;IAChC,IAAI,WAAW,OAAO,OAAO;IAC7B,IAAI,IAAI,OAAO,OAAO;IACtB,IAAI,YAAY,OAAO,OAAO;IAC9B,IAAI,YAAY,OAAO,OAAO;IAC9B,IAAI,YAAY,OAAO,OAAO;IAC9B,IAAI,WAAW,OAAO,OAAO;GAC/B,CAAC;GACD,MAAM;EACR,CAAC;EAED,OAAO,MAAM,KAAK,YAAY;CAChC,SAAS,OAAO;EACd,OAAO,aAAa,uBAAuB,OAAO,KAAiB;CACrE;AACF;;;;AAOA,MAAa,gBAAgB,OAC3B,UACA,UACkB;CAClB,MAAM,EAAE,MAAM,cAAc,SAAS,SAAS,UAC5C,SAAS,WAAW,CAAC;CAEvB,IAAI,CAAC,MACH,OAAO,aAAa,2BAA2B,OAAO,kBAAkB;CAG1E,IAAI,CAAC,cACH,OAAO,aAAa,2BAClB,OACA,0BACF;CAGF,IAAI,CAAC,SACH,OAAO,aAAa,2BAClB,OACA,qBACF;CAGF,IAAI,OAAO,YAAY,aACrB,OAAO,aAAa,2BAClB,OACA,qBACF;CAGF,IACE,CAAC,cACC,SAAS,CAAC,GACV,eACF,CAAC,CAAC;EACA,GAAG,SAAS;EACZ,kBAAkB,CAAC,OAAO,QAAQ,EAAE,CAAC;CACvC,CAAC,GAED,OAAO,aAAa,2BAA2B,OAAO,mBAAmB;CAG3E,IAAI;EACF,MAAM,kBAAkB,MAAML,eAA8B,QAAQ,EAAE;EAEtE,IAAI,OAAO,gBAAgB,cAAc,MAAM,OAAO,aAAa,EAAE,GACnE,OAAO,aAAa,2BAClB,OACA,6BACF;EAGF,MAAM,iBAAiB,MAAMM,kBAAiC,QAAQ,EAAE;EAExE,IAAI,CAAC,gBACH,OAAO,aAAa,2BAClB,OACA,uBACA,EACE,WAAW,QAAQ,GACrB,CACF;EAGF,OAAO,KAAK,oBAAoB,OAAO,eAAe,EAAE,GAAG;EAE3D,MAAM,eAAe,eAA2B;GAC9C,SAAS,EAAE;IACT,IAAI;IACJ,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;GACN,CAAC;GACD,aAAa,EAAE;IACb,IAAI;IACJ,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;GACN,CAAC;GACD,MAAM,gBAAgB,cAAc;EACtC,CAAC;EAED,MAAM,aAAa,UACjB,EAAE,KAAK,QAAQ,GAAG,GAClB,EAAE,MAAM,EAAE,iBAAiB,KAAK,EAAE,CACpC;EAEA,IAAI,MACF,MAAMJ,eAA2B,KAAK,IAAI,EACxC,qBAAqB,KACvB,CAAC;EAGH,OAAO,MAAM,KAAK,YAAY;CAChC,SAAS,OAAO;EACd,OAAO,aAAa,uBAAuB,OAAO,KAAiB;CACrE;AACF;;;;AAQA,MAAa,yBAAyB,OACpC,SACA,UACkB;CAClB,MAAM,EAAE,cAAc,QAAQ;CAC9B,MAAM,EAAE,UAAU,QAAQ,WAAW,CAAC;CAEtC,IAAI;EACF,MAAM,UAAU,MAAMF,eAA8B,SAAS;EAE7D,IAAI,CAAC,SACH,OAAO,aAAa,2BAClB,OACA,qBACF;EAGF,IACE,CAAC,cACC,SAAS,CAAC,GACV,eACF,CAAC,CAAC;GACA,GAAG,QAAQ;GACX,kBAAkB,CAAC,OAAO,QAAQ,EAAE,CAAC;EACvC,CAAC,GAED,OAAO,aAAa,2BAClB,OACA,mBACF;EAGF,MAAM,iBAAiB,MAAMM,kBAAiC,QAAQ,EAAE;EAExE,IAAI,CAAC,gBACH,OAAO,aAAa,2BAClB,OACA,qBACF;EAGF,MAAM,mBAAmB,gBAAgB,cAAc;EACvD,MAAM,eAAe,eAA2B;GAC9C,SAAS,EAAE;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;GACN,CAAC;GACD,MAAM;EACR,CAAC;EAED,OAAO,MAAM,KAAK,YAAY;CAChC,SAAS,OAAO;EACd,OAAO,aAAa,uBAAuB,OAAO,KAAiB;CACrE;AACF;;;;AAQA,MAAa,gBAAgB,OAC3B,SACA,UACG;CACH,MAAM,EAAE,cAAc,QAAQ;CAC9B,MAAM,EAAE,SAAS,OAAO,SAAS,QAAQ,WAAW,CAAC;CAErD,IAAI,CAAC,WACH,OAAO,aAAa,2BAClB,OACA,sBACF;CAGF,IAAI,OAAO,YAAY,aACrB,OAAO,aAAa,2BAClB,OACA,qBACF;CAGF,IAAI;EACF,MAAM,UAAU,MAAMN,eAA8B,SAAS;EAE7D,IACE,CAAC,cACC,SAAS,CAAC,GACV,cACF,CAAC,CAAC;GACA,GAAG,QAAQ;GACX,gBAAgB,CAAC,OAAO;EAC1B,CAAC,GAED,OAAO,aAAa,2BAClB,OACA,mBACF;EAIF,MAAM,eACJ,QAAQ,cAAc,MAAM,MAAW,EAAE,SAAS,CAAC,EAAE,MACrD,QAAQ,eAAe,EAAE,EAAE;EAE7B,MAAM,aAAa,UACjB,EAAE,KAAK,QAAQ,GAAG,GAClB,EACE,MAAM;GACJ,iBAAiB,OAAO,SAAS;GACjC,qBAAqB,eAAe,OAAO,YAAY,IAAI;EAC7D,EACF,CACF;EAEA,IAAI,MACF,MAAME,eAA2B,KAAK,IAAI,EACxC,qBAAqB,OAAO,SAAS,EACvC,CAAC;EAGH,MAAM,eAAe,eAA2B;GAC9C,SAAS,EAAE;IACT,IAAI;IACJ,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;GACN,CAAC;GACD,aAAa,EAAE;IACb,IAAI;IACJ,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;GACN,CAAC;GACD,MAAM,gBAAgB,OAAO;EAC/B,CAAC;EAED,OAAO,MAAM,KAAK,YAAY;CAChC,SAAS,OAAO;EACd,OAAO,aAAa,uBAAuB,OAAO,KAAiB;CACrE;AACF;;;;AAOA,MAAa,kBAAkB,OAC7B,UACA,UACG;CACH,MAAM,EAAE,SAAS,SAAS,SAAS,WAAW,CAAC;CAE/C,IAAI,OAAO,YAAY,aACrB,OAAO,aAAa,2BAClB,OACA,qBACF;CAGF,IAAI;EACF,MAAM,aAAa,UACjB,EAAE,KAAK,QAAQ,GAAG,GAClB,EAAE,MAAM;GAAE,iBAAiB;GAAM,qBAAqB;EAAK,EAAE,CAC/D;EAEA,IAAI,MACF,MAAMA,eAA2B,KAAK,IAAI,EACxC,qBAAqB,KACvB,CAAC;EAGH,MAAM,eAAe,eAAqB;GACxC,SAAS,EAAE;IACT,IAAI;IACJ,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;GACN,CAAC;GACD,aAAa,EAAE;IACb,IAAI;IACJ,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;GACN,CAAC;GACD,MAAM;EACR,CAAC;EAED,OAAO,MAAM,KAAK,YAAY;CAChC,SAAS,OAAO;EACd,OAAO,aAAa,uBAAuB,OAAO,KAAiB;CACrE;AACF;;;;AAOA,MAAa,qBAAqB,OAChC,SACA,UACkB;CAClB,MAAM,EAAE,SAAS,SAAS,QAAQ,WAAW,CAAC;CAE9C,IAAI,CAAC,SACH,OAAO,aAAa,2BAClB,OACA,qBACF;CAGF,IAAI,CAAC,MACH,OAAO,aAAa,2BAA2B,OAAO,kBAAkB;CAG1E,IAAI;EAGF,MAAM,eAAe,eAAmC,EACtD,MAAM,MAHeK,YAAsB,SAAS,KAAK,EAAE,EAI7D,CAAC;EAED,OAAO,MAAM,KAAK,YAAY;CAChC,SAAS,OAAO;EACd,IAAK,OAAe,YAClB,OAAO,aAAa,uBAAuB,OAAO,KAAiB;EAErE,OAAO,aAAa,0BAClB,OACA,mBACA,0BACC,OAAiB,WAAW,kCAC7B,QACA,GACF;CACF;AACF;;;;AAOA,MAAa,sBAAsB,OACjC,SACA,UACkB;CAClB,MAAM,EAAE,SAAS,SAAS,QAAQ,WAAW,CAAC;CAE9C,IAAI,CAAC,SACH,OAAO,aAAa,2BAClB,OACA,qBACF;CAGF,IAAI,CAAC,MACH,OAAO,aAAa,2BAA2B,OAAO,kBAAkB;CAG1E,IAAI;EACF,MAAMC,UAAoB,SAAS,KAAK,EAAE;EAE1C,MAAM,eAAe,eAAqC;GACxD,SAAS,EAAE;IACT,IAAI;IACJ,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;GACN,CAAC;GACD,aAAa,EAAE;IACb,IAAI;IACJ,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;GACN,CAAC;GACD,MAAM,EAAE,SAAS,KAAK;EACxB,CAAC;EAED,OAAO,MAAM,KAAK,YAAY;CAChC,SAAS,OAAO;EACd,IAAK,OAAe,YAClB,OAAO,aAAa,uBAAuB,OAAO,KAAiB;EAErE,OAAO,aAAa,0BAClB,OACA,oBACA,yBACC,OAAiB,WAAW,sCAC7B,QACA,GACF;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"project.controller.mjs","names":["projectService.findProjects","projectService.countProjects","projectService.createProject","projectService.getProjectById","projectService.updateProjectById","userService.updateUserById","userService.getUsersByIds","webhooksService.triggerAll","webhooksService.triggerSingleWebhook","projectService.deleteProjectById","ciService.getCIStatus","ciService.installCI"],"sources":["../../../src/controllers/project.controller.ts"],"sourcesContent":["import { logger } from '@logger';\nimport { SessionModel } from '@schemas/session.schema';\nimport * as ciService from '@services/ci.service';\nimport { createDemoDictionaries } from '@services/dictionary.service';\nimport { refreshProjectScreenshotIfChanged } from '@services/project/projectScreenshot.service';\nimport * as projectService from '@services/project.service';\nimport * as userService from '@services/user.service';\nimport * as webhooksService from '@services/webhook.service';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport type { FiltersAndPagination } from '@utils/filtersAndPagination/getFiltersAndPaginationFromBody';\nimport {\n getProjectFiltersAndPagination,\n type ProjectFiltersParams,\n} from '@utils/filtersAndPagination/getProjectFiltersAndPagination';\nimport { mapProjectsToAPI, mapProjectToAPI } from '@utils/mapper/project';\nimport { hasPermission } from '@utils/permissions';\nimport { getPlanDetails } from '@utils/plan';\nimport {\n formatPaginatedResponse,\n formatResponse,\n type PaginatedResponse,\n type ResponseData,\n} from '@utils/responseData';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport { t } from 'fastify-intlayer';\nimport type { Types } from 'mongoose';\nimport type {\n ProjectAPI,\n ProjectConfiguration,\n ProjectCreationData,\n ProjectData,\n} from '@/types/project.types';\nimport type { User } from '@/types/user.types';\n\nexport type GetProjectsParams = FiltersAndPagination<ProjectFiltersParams>;\nexport type GetProjectsResult = PaginatedResponse<ProjectAPI>;\n\n/**\n * Retrieves a list of projects based on filters and pagination.\n */\nexport const getProjects = async (\n request: FastifyRequest<{ Querystring: GetProjectsParams }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, roles } = request.session || {};\n const { filters, sortOptions, pageSize, skip, page, getNumberOfPages } =\n getProjectFiltersAndPagination(request);\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n // When no organization is selected in the session,\n // the filter already scopes organizationId to undefined → DB returns []\n // so we can proceed safely without an early 403.\n\n try {\n const projects = await projectService.findProjects(\n filters,\n skip,\n pageSize,\n sortOptions\n );\n\n // Skip permission check when there are no projects to protect.\n // An empty result is safe to return without checking roles.\n if (\n projects.length > 0 &&\n !hasPermission(\n roles || [],\n 'project:read'\n )({\n ...request.session,\n targetProjects: projects,\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PERMISSION_DENIED'\n );\n }\n\n const totalItems = await projectService.countProjects(filters);\n\n const formattedProjects = mapProjectsToAPI(projects);\n\n const responseData = formatPaginatedResponse<ProjectAPI>({\n data: formattedProjects,\n page,\n pageSize,\n totalPages: getNumberOfPages(totalItems),\n totalItems,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type AddProjectBody = ProjectCreationData;\nexport type AddProjectResult = ResponseData<ProjectAPI>;\n\n/**\n * Adds a new project to the database.\n */\nexport const addProject = async (\n request: FastifyRequest<{ Body: AddProjectBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { organization, user, roles } = request.session || {};\n const projectData = request.body;\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (!organization) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'ORGANIZATION_NOT_DEFINED'\n );\n }\n\n if (!projectData) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_DATA_NOT_FOUND'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'organization:admin'\n )({\n ...request.session,\n targetOrganizations: [organization],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n const { plan } = organization;\n\n const planType = getPlanDetails(plan);\n\n if (planType.numberOfProjects) {\n const projectCount = await projectService.countProjects({\n organizationId: organization.id,\n });\n\n if (projectCount >= planType.numberOfProjects) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PLAN_PROJECT_LIMIT_REACHED',\n {\n organizationId: organization.id,\n }\n );\n }\n }\n\n const project: ProjectData = {\n membersIds: [user.id],\n adminsIds: [user.id],\n creatorId: user.id,\n organizationId: organization.id,\n ...projectData,\n };\n\n try {\n const newProject = await projectService.createProject(project);\n\n const formattedProject = mapProjectToAPI(newProject);\n\n const responseData = formatResponse<ProjectAPI>({\n message: t({\n en: 'Project created successfully',\n 'en-GB': 'Project created successfully',\n fr: 'Projet créé avec succès',\n es: 'Proyecto creado con éxito',\n ru: 'Проект успешно создан',\n ja: 'プロジェクトが正常に作成されました',\n ko: '프로젝트가 성공적으로 생성되었습니다',\n zh: '项目已成功创建',\n de: 'Projekt erfolgreich erstellt',\n ar: 'تم إنشاء المشروع بنجاح',\n it: 'Progetto creato con successo',\n pt: 'Projeto criado com sucesso',\n hi: 'प्रोजेक्ट सफलतापूर्वक बनाया गया',\n tr: 'Proje başarıyla oluşturuldu',\n pl: 'Projekt został pomyślnie utworzony',\n id: 'Proyek berhasil dibuat',\n vi: 'Dự án đã được tạo thành công',\n uk: 'Проєкт успішно створено',\n }),\n description: t({\n en: 'Your project has been created successfully',\n 'en-GB': 'Your project has been created successfully',\n fr: 'Votre projet a été créé avec succès',\n es: 'Su proyecto ha sido creado con éxito',\n ru: 'Ваш проект был успешно создан',\n ja: 'プロジェクトは正常に作成されました',\n ko: '프로젝트가 성공적으로 생성되었습니다',\n zh: '您的项目已成功创建',\n de: 'Ihr Projekt wurde erfolgreich erstellt',\n ar: 'لقد تم إنشاء مشروعك بنجاح',\n it: 'Il tuo progetto è stato creato con successo',\n pt: 'Seu projeto foi criado com sucesso',\n hi: 'आपका प्रोजेक्ट सफलतापूर्वक बना लिया गया है',\n tr: 'Projeniz başarıyla oluşturuldu',\n pl: 'Twój projekt został pomyślnie utworzony',\n id: 'Proyek Anda telah berhasil dibuat',\n vi: 'Dự án của bạn đã được tạo thành công',\n uk: 'Ваш проєкт успішно створено',\n }),\n data: formattedProject,\n });\n\n reply.send(responseData);\n\n // Create mock data once it's done\n try {\n await createDemoDictionaries([formattedProject.id], user.id);\n } catch {\n // Skip if fail\n }\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type UpdateProjectBody = Partial<ProjectAPI>;\nexport type UpdateProjectResult = ResponseData<ProjectAPI>;\n\n/**\n * Updates an existing project in the database.\n */\nexport const updateProject = async (\n request: FastifyRequest<{ Body: UpdateProjectBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { organization, project, user, session, roles } = request.session || {};\n const projectData = request.body;\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_DATA_NOT_FOUND'\n );\n }\n\n if (!organization) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'ORGANIZATION_NOT_DEFINED'\n );\n }\n\n if (String(project.organizationId) !== String(organization.id)) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_IN_ORGANIZATION'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:write'\n )({\n ...request.session,\n targetProjectIds: [String(project.id)],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n if (typeof session === 'undefined') {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'SESSION_NOT_DEFINED'\n );\n }\n\n try {\n const existingProject = await projectService.getProjectById(project.id);\n\n const previousApplicationUrl =\n existingProject.configuration?.editor?.applicationURL;\n const newApplicationUrl = projectData.configuration?.editor?.applicationURL;\n\n const updatedProject = await projectService.updateProjectById(\n project.id,\n projectData\n );\n\n // Update session to set activeOrganizationId\n await SessionModel.updateOne(\n { _id: session.id },\n {\n $set: {\n activeOrganizationId: String(organization.id),\n activeProjectId: String(project.id),\n },\n }\n );\n\n if (user) {\n await userService.updateUserById(user.id, {\n lastActiveOrganizationId: String(organization.id),\n lastActiveProjectId: String(project.id),\n });\n }\n\n // Fire-and-forget screenshot generation\n refreshProjectScreenshotIfChanged({\n newApplicationUrl,\n previousApplicationUrl,\n existingImageUrl: existingProject.imageUrl,\n projectId: project.id.toString(),\n })\n .then((imageUrl) => {\n if (imageUrl !== undefined) {\n projectService\n .updateProjectById(project.id, { imageUrl })\n .catch(() => {});\n }\n })\n .catch(() => {});\n\n const formattedProject = mapProjectToAPI(updatedProject);\n\n const responseData = formatResponse<ProjectAPI>({\n message: t({\n en: 'Project updated successfully',\n 'en-GB': 'Project updated successfully',\n fr: 'Projet mis à jour avec succès',\n es: 'Proyecto actualizado con éxito',\n ru: 'Проект успешно обновлен',\n ja: 'プロジェクトが正常に更新されました',\n ko: '프로젝트가 성공적으로 업데이트되었습니다',\n zh: '项目已成功更新',\n de: 'Projekt erfolgreich aktualisiert',\n ar: 'تم تحديث المشروع بنجاح',\n it: 'Progetto aggiornato con successo',\n pt: 'Projeto atualizado com sucesso',\n hi: 'प्रोजेक्ट सफलतापूर्वक अपडेट किया गया',\n tr: 'Proje başarıyla güncellendi',\n pl: 'Projekt został pomyślnie zaktualizowany',\n id: 'Proyek berhasil diperbarui',\n vi: 'Dự án đã được cập nhật thành công',\n uk: 'Проєкт успішно оновлено',\n }),\n description: t({\n en: 'Your project has been updated successfully',\n 'en-GB': 'Your project has been updated successfully',\n fr: 'Votre projet a été mis à jour avec succès',\n es: 'Su proyecto ha sido actualizado con éxito',\n ru: 'Ваш проект был успешно обновлен',\n ja: 'プロジェクトは正常に更新されました',\n ko: '프로젝트가 성공적으로 업데이트되었습니다',\n zh: '您的项目已成功更新',\n de: 'Ihr Projekt wurde erfolgreich aktualisiert',\n ar: 'لقد تم تحديث مشروعك بنجاح',\n it: 'Il tuo progetto è stato aggiornato con successo',\n pt: 'Seu projeto foi atualizado com sucesso',\n hi: 'आपका प्रोजेक्ट सफलतापूर्वक अपडेट कर दिया गया है',\n tr: 'Projeniz başarıyla güncellendi',\n pl: 'Twój projekt został pomyślnie zaktualizowany',\n id: 'Proyek Anda telah berhasil diperbarui',\n vi: 'Dự án của bạn đã được cập nhật thành công',\n uk: 'Ваш проєкт успішно оновлено',\n }),\n data: formattedProject,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\ntype UserAndAdmin = { user: User; isAdmin: boolean };\nexport type ProjectMemberByIdOption = {\n userId: string | Types.ObjectId;\n isAdmin?: boolean;\n};\n\nexport type UpdateProjectMembersBody = Partial<{\n membersIds: ProjectMemberByIdOption[];\n}>;\nexport type UpdateProjectMembersResult = ResponseData<ProjectAPI>;\n\n/**\n * Update members to the dictionary in the database.\n */\nexport const updateProjectMembers = async (\n request: FastifyRequest<{ Body: UpdateProjectMembersBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, project, organization, roles } = request.session || {};\n const { membersIds } = request.body;\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (!organization) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'ORGANIZATION_NOT_DEFINED'\n );\n }\n\n if (membersIds?.length === 0) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_MUST_HAVE_MEMBER'\n );\n }\n\n if (membersIds?.map((el) => el.isAdmin)?.length === 0) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_MUST_HAVE_ADMIN'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:write'\n )({\n ...request.session,\n targetProjectIds: [String(project.id)],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n try {\n const existingUsers: UserAndAdmin[] = [];\n\n if (membersIds) {\n const userIdList = membersIds\n ?.filter(\n (member) =>\n // Remove members that are not in the organization\n !organization?.membersIds.includes(member.userId as Types.ObjectId)\n )\n .map((member) => member.userId);\n\n const users = await userService.getUsersByIds(userIdList);\n\n if (users) {\n const userMap: UserAndAdmin[] = users.map((user) => ({\n user,\n isAdmin:\n membersIds.find(\n (member) => String(member.userId) === String(user.id)\n )?.isAdmin ?? false,\n }));\n\n existingUsers.push(...userMap);\n }\n }\n\n const formattedMembers: Types.ObjectId[] = existingUsers.map(\n (user) => user.user.id\n );\n const formattedAdmin: Types.ObjectId[] = existingUsers\n .filter((el) => el.isAdmin)\n .map((user) => user.user.id);\n\n const updatedOrganization = await projectService.updateProjectById(\n project.id,\n {\n ...project,\n membersIds: formattedMembers,\n adminsIds: formattedAdmin,\n }\n );\n\n const formattedProject = mapProjectToAPI(updatedOrganization);\n\n const responseData = formatResponse<ProjectAPI>({\n message: t({\n en: 'Project members updated successfully',\n 'en-GB': 'Project members updated successfully',\n fr: 'Membres du projet mis à jour avec succès',\n es: 'Miembros del proyecto actualizados con éxito',\n ru: 'Члены проекта успешно обновлены',\n ja: 'プロジェクトメンバーが正常に更新されました',\n ko: '프로젝트 구성원이 성공적으로 업데이트되었습니다',\n zh: '项目成员已成功更新',\n de: 'Projektmitglieder erfolgreich aktualisiert',\n ar: 'تم تحديث أعضاء المشروع بنجاح',\n it: 'Membri del progetto aggiornati con successo',\n pt: 'Membros do projeto atualizados com sucesso',\n hi: 'प्रोजेक्ट सदस्य सफलतापूर्वक अपडेट किए गए',\n tr: 'Proje üyeleri başarıyla güncellendi',\n pl: 'Członkowie projektu zostali pomyślnie zaktualizowani',\n id: 'Anggota proyek berhasil diperbarui',\n vi: 'Thành viên dự án đã được cập nhật thành công',\n uk: 'Члени проєкту успішно оновлені',\n }),\n description: t({\n en: 'Your project members have been updated successfully',\n 'en-GB': 'Your project members have been updated successfully',\n fr: 'Les membres de votre projet ont été mis à jour avec succès',\n es: 'Los miembros de su proyecto han sido actualizados con éxito',\n ru: 'Члены вашего проекта были успешно обновлены',\n ja: 'プロジェクトメンバーは正常に更新されました',\n ko: '프로젝트 구성원이 성공적으로 업데이트되었습니다',\n zh: '您的项目成员已成功更新',\n de: 'Ihre Projektmitglieder wurden erfolgreich aktualisiert',\n ar: 'لقد تم تحديث أعضاء مشروعك بنجاح',\n it: 'I membri del tuo progetto sono stati aggiornati con successo',\n pt: 'Os membros do seu projeto foram atualizados com sucesso',\n hi: 'आपके प्रोजेक्ट के सदस्यों को सफलतापूर्वक अपडेट किया गया है',\n tr: 'Proje üyeleriniz başarıyla güncellendi',\n pl: 'Członkowie Twojego projektu zostali pomyślnie zaktualizowani',\n id: 'Anggota proyek Anda telah berhasil diperbarui',\n vi: 'Thành viên dự án của bạn đã được cập nhật thành công',\n uk: 'Члени вашого проєкту успішно оновлені',\n }),\n data: formattedProject,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type PushProjectConfigurationBody = ProjectConfiguration;\nexport type PushProjectConfigurationResult = ResponseData<ProjectConfiguration>;\n\n/**\n * Pushes a project configuration to the database.\n */\nexport const pushProjectConfiguration = async (\n request: FastifyRequest<{ Body: PushProjectConfigurationBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, project, roles } = request.session || {};\n const projectConfiguration = request.body;\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:write'\n )({\n ...request.session,\n targetProjectIds: [String(project.id)],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n try {\n const projectObject = await projectService.getProjectById(project.id);\n\n // Preserve existing API key if not provided in the update\n if (\n projectConfiguration.ai &&\n !projectConfiguration.ai.apiKey &&\n projectObject.configuration?.ai?.apiKey\n ) {\n projectConfiguration.ai.apiKey = projectObject.configuration.ai.apiKey;\n }\n\n const previousApplicationUrl =\n projectObject.configuration?.editor?.applicationURL;\n const newApplicationUrl = projectConfiguration.editor?.applicationURL;\n\n projectObject.configuration = projectConfiguration;\n\n await projectObject.save();\n\n // Fire-and-forget screenshot generation\n refreshProjectScreenshotIfChanged({\n newApplicationUrl,\n previousApplicationUrl,\n existingImageUrl: projectObject.imageUrl,\n projectId: project.id.toString(),\n })\n .then((imageUrl) => {\n if (imageUrl !== undefined) {\n projectService\n .updateProjectById(project.id, { imageUrl })\n .catch(() => {});\n }\n })\n .catch(() => {});\n\n if (!projectObject.configuration) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_UPDATE_FAILED',\n {\n projectId: project.id,\n }\n );\n }\n\n const responseData = formatResponse<ProjectConfiguration>({\n message: t({\n en: 'Project configuration updated successfully',\n 'en-GB': 'Project configuration updated successfully',\n fr: 'Configuration du projet mise à jour avec succès',\n es: 'Configuración del proyecto actualizada con éxito',\n ru: 'Конфигурация проекта успешно обновлена',\n ja: 'プロジェクト構成が正常に更新されました',\n ko: '프로젝트 설정이 성공적으로 업데이트되었습니다',\n zh: '项目配置已成功更新',\n de: 'Projektkonfiguration erfolgreich aktualisiert',\n ar: 'تم تحديث تكوين المشروع بنجاح',\n it: 'Configurazione del progetto aggiornata con successo',\n pt: 'Configuração do projeto atualizada com sucesso',\n hi: 'प्रोजेक्ट कॉन्फ़िगरेशन सफलतापूर्वक अपडेट किया गया',\n tr: 'Proje yapılandırması başarıyla güncellendi',\n pl: 'Konfiguracja projektu została pomyślnie zaktualizowana',\n id: 'Konfigurasi proyek berhasil diperbarui',\n vi: 'Cấu hình dự án đã được cập nhật thành công',\n uk: 'Конфігурацію проєкту успішно оновлено',\n }),\n description: t({\n en: 'Your project configuration has been updated successfully',\n 'en-GB': 'Your project configuration has been updated successfully',\n fr: 'La configuration du projet a été mise à jour avec succès',\n es: 'Su configuración del proyecto ha sido actualizada con éxito',\n ru: 'Конфигурация вашего проекта была успешно обновлена',\n ja: 'プロジェクト構成は正常に更新されました',\n ko: '프로젝트 설정이 성공적으로 업데이트되었습니다',\n zh: '您的项目配置已成功更新',\n de: 'Ihre Projektkonfiguration wurde erfolgreich aktualisiert',\n ar: 'لقد تم تحديث تكوين مشروعك بنجاح',\n it: 'La configurazione del tuo progetto è stata aggiornata con successo',\n pt: 'A configuração do seu projeto foi atualizada com sucesso',\n hi: 'आपका प्रोजेक्ट कॉन्फ़िगरेशन सफलतापूर्वक अपडेट कर दिया गया है',\n tr: 'Proje yapılandırmanız başarıyla güncellendi',\n pl: 'Konfiguracja Twojego projektu została pomyślnie zaktualizowana',\n id: 'Konfigurasi proyek Anda telah berhasil diperbarui',\n vi: 'Cấu hình dự án của bạn đã được cập nhật thành công',\n uk: 'Конфігурацію вашого проєкту успішно оновлено',\n }),\n data: mapProjectToAPI(projectObject) as ProjectConfiguration,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type TriggerBuildResult = ResponseData<{\n results: Array<{\n target: string;\n success: boolean;\n message?: string;\n }>;\n}>;\n\nexport type TriggerWebhookBody = {\n webhookIndex: number;\n};\n\nexport type TriggerWebhookResult = ResponseData<{\n target: string;\n success: boolean;\n message?: string;\n}>;\n\n/**\n * Triggers CI builds for a project (Git provider pipelines and webhooks)\n */\nexport const triggerBuild = async (\n request: FastifyRequest,\n reply: FastifyReply\n): Promise<void> => {\n const { project, roles } = request.session || {};\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:write'\n )({\n ...request.session,\n targetProjectIds: [String(project.id)],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n try {\n // Get full project with all relations\n const fullProject = await projectService.getProjectById(project.id);\n const results = await webhooksService.triggerAll(fullProject);\n\n const responseData = formatResponse<{\n results: Array<{\n target: string;\n success: boolean;\n message?: string;\n }>;\n }>({\n message: t({\n en: 'Build triggers initiated',\n 'en-GB': 'Build triggers initiated',\n fr: 'Déclenchement des builds initié',\n es: 'Inicio de los triggers de build',\n ru: 'Запуск триггеров сборки инициирован',\n ja: 'ビルドトリガーが開始されました',\n ko: '빌드 트리거가 시작되었습니다',\n zh: '构建触发器已启动',\n de: 'Build-Trigger initiiert',\n ar: 'بدء مشغلات البناء',\n it: 'Trigger di build avviati',\n pt: 'Gatilhos de build iniciados',\n hi: 'बिल्ड ट्रिगर शुरू किए गए',\n tr: 'Build tetikleyicileri başlatıldı',\n pl: 'Zainicjowano wyzwalacze budowania',\n id: 'Pemicu build dimulai',\n vi: 'Đã bắt đầu trình kích hoạt bản dựng',\n uk: 'Запуск тригерів збірки ініційовано',\n }),\n description: t({\n en: 'CI pipelines and webhooks have been triggered',\n 'en-GB': 'CI pipelines and webhooks have been triggered',\n fr: 'Les pipelines CI et webhooks ont été déclenchés',\n es: 'Los pipelines CI y webhooks han sido activados',\n ru: 'CI-конвейеры и вебхуки были запущены',\n ja: 'CIパイプラインとウェブフックがトリガーされました',\n ko: 'CI 파이프라인과 웹후크가 트리거되었습니다',\n zh: 'CI 流水线和 Webhook 已触发',\n de: 'CI-Pipelines und Webhooks wurden ausgelöst',\n ar: 'تم تفعيل أنابيب CI والويب هوك',\n it: 'Le pipeline CI e i webhook sono stati attivati',\n pt: 'Pipelines CI e webhooks foram acionados',\n hi: 'CI पाइपलाइन और वेबहुक ट्रिगर किए गए हैं',\n tr: 'CI hatları ve webhooklar tetiklendi',\n pl: 'Potoki CI i webhooki zostały wyzwolone',\n id: 'Pipa CI dan webhook telah dipicu',\n vi: 'Đường ống CI và webhook đã được kích hoạt',\n uk: 'Конвеєри CI та вебхуки були запущені',\n }),\n data: { results },\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\n/**\n * Triggers a single webhook by index\n */\nexport const triggerWebhook = async (\n request: FastifyRequest<{ Body: TriggerWebhookBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { project, roles } = request.session || {};\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:write'\n )({\n ...request.session,\n targetProjectIds: [String(project.id)],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n try {\n const { webhookIndex } = request.body;\n\n if (typeof webhookIndex !== 'number' || webhookIndex < 0) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'INVALID_REQUEST_BODY'\n );\n }\n\n // Get full project with all relations\n const fullProject = await projectService.getProjectById(project.id);\n const result = await webhooksService.triggerSingleWebhook(\n fullProject,\n webhookIndex\n );\n\n const responseData = formatResponse<{\n target: string;\n success: boolean;\n message?: string;\n }>({\n message: t({\n en: 'Webhook triggered',\n 'en-GB': 'Webhook triggered',\n fr: 'Webhook déclenché',\n es: 'Webhook activado',\n ru: 'Вебхук запущен',\n ja: 'ウェブフックがトリガーされました',\n ko: '웹후크가 트리거되었습니다',\n zh: 'Webhook 已触发',\n de: 'Webhook ausgelöst',\n ar: 'تم تفعيل الويب هوك',\n it: 'Webhook attivato',\n pt: 'Webhook acionado',\n hi: 'वेबहुक ट्रिगर किया गया',\n tr: 'Webhook tetiklendi',\n pl: 'Webhook został wyzwolony',\n id: 'Webhook dipicu',\n vi: 'Webhook đã được kích hoạt',\n uk: 'Вебхук запущено',\n }),\n description: t({\n en: `Webhook \"${result.target}\" has been triggered`,\n 'en-GB': `Webhook \"${result.target}\" has been triggered`,\n fr: `Le webhook \"${result.target}\" a été déclenché`,\n es: `El webhook \"${result.target}\" ha sido activado`,\n ru: `Вебхук \"${result.target}\" был запущен`,\n ja: `ウェブフック \"${result.target}\" がトリガーされました`,\n ko: `웹후크 \"${result.target}\"가 트리거되었습니다`,\n zh: `Webhook \"${result.target}\" 已触发`,\n de: `Webhook \"${result.target}\" wurde ausgelöst`,\n ar: `تم تفعيل الويب هوك \"${result.target}\"`,\n it: `Il webhook \"${result.target}\" è stato attivato`,\n pt: `O webhook \"${result.target}\" foi acionado`,\n hi: `वेबहुक \"${result.target}\" ट्रिगर किया गया है`,\n tr: `\"${result.target}\" webhooku tetiklendi`,\n pl: `Webhook \"${result.target}\" został wyzwolony`,\n id: `Webhook \"${result.target}\" telah dipicu`,\n vi: `Webhook \"${result.target}\" đã được kích hoạt`,\n uk: `Вебхук \"${result.target}\" запущено`,\n }),\n data: result,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type DeleteProjectResult = ResponseData<ProjectAPI>;\n\n/**\n * Deletes a project from the database by its ID.\n */\nexport const deleteProject = async (\n _request: FastifyRequest,\n reply: FastifyReply\n): Promise<void> => {\n const { user, organization, project, session, roles } =\n _request.session || {};\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (!organization) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'ORGANIZATION_NOT_DEFINED'\n );\n }\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (typeof session === 'undefined') {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'SESSION_NOT_DEFINED'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:admin'\n )({\n ..._request.session,\n targetProjectIds: [String(project.id)],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n try {\n const projectToDelete = await projectService.getProjectById(project.id);\n\n if (String(projectToDelete.organizationId) !== String(organization.id)) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_IN_ORGANIZATION'\n );\n }\n\n const deletedProject = await projectService.deleteProjectById(project.id);\n\n if (!deletedProject) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED',\n {\n projectId: project.id,\n }\n );\n }\n\n logger.info(`Project deleted: ${String(deletedProject.id)}`);\n\n const responseData = formatResponse<ProjectAPI>({\n message: t({\n en: 'Project deleted successfully',\n 'en-GB': 'Project deleted successfully',\n fr: 'Projet supprimé avec succès',\n es: 'Proyecto eliminado con éxito',\n ru: 'Проект успешно удален',\n ja: 'プロジェクトが正常に削除されました',\n ko: '프로젝트가 성공적으로 삭제되었습니다',\n zh: '项目已成功删除',\n de: 'Projekt erfolgreich gelöscht',\n ar: 'تم حذف المشروع بنجاح',\n it: 'Progetto eliminato con successo',\n pt: 'Projeto excluído com sucesso',\n hi: 'प्रोजेक्ट सफलतापूर्वक हटा दिया गया',\n tr: 'Proje başarıyla silindi',\n pl: 'Projekt został pomyślnie usunięty',\n id: 'Proyek berhasil dihapus',\n vi: 'Dự án đã được xóa thành công',\n uk: 'Проєкт успішно видалено',\n }),\n description: t({\n en: 'Your project has been deleted successfully',\n 'en-GB': 'Your project has been deleted successfully',\n fr: 'Votre projet a été supprimé avec succès',\n es: 'Su proyecto ha sido eliminado con éxito',\n ru: 'Ваш проект был успешно удален',\n ja: 'プロジェクトは正常に削除されました',\n ko: '프로젝트가 성공적으로 삭제되었습니다',\n zh: '您的项目已成功删除',\n de: 'Ihr Projekt wurde erfolgreich gelöscht',\n ar: 'لقد تم حذف مشروعك بنجاح',\n it: 'Il tuo progetto è stato eliminato con successo',\n pt: 'Seu projeto foi excluído com sucesso',\n hi: 'आपका प्रोजेक्ट सफलतापूर्वक हटा दिया गया है',\n tr: 'Projeniz başarıyla silindi',\n pl: 'Twój projekt został pomyślnie usunięty',\n id: 'Proyek Anda telah berhasil dihapus',\n vi: 'Dự án của bạn đã được xóa thành công',\n uk: 'Ваш проєкт успішно видалено',\n }),\n data: mapProjectToAPI(deletedProject),\n });\n\n await SessionModel.updateOne(\n { _id: session.id },\n { $set: { activeProjectId: null } }\n );\n\n if (user) {\n await userService.updateUserById(user.id, {\n lastActiveProjectId: null,\n });\n }\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type DeleteProjectByIdAdminParams = { projectId: string };\nexport type DeleteProjectByIdAdminResult = ResponseData<ProjectAPI>;\n\n/**\n * Admin-only: Deletes any project from the database by its ID.\n */\nexport const deleteProjectByIdAdmin = async (\n request: FastifyRequest<{ Params: DeleteProjectByIdAdminParams }>,\n reply: FastifyReply\n): Promise<void> => {\n const { projectId } = request.params;\n const { roles } = request.session || {};\n\n try {\n const project = await projectService.getProjectById(projectId);\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:admin'\n )({\n ...request.session,\n targetProjectIds: [String(project.id)],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PERMISSION_DENIED'\n );\n }\n\n const deletedProject = await projectService.deleteProjectById(project.id);\n\n if (!deletedProject) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n const formattedProject = mapProjectToAPI(deletedProject);\n const responseData = formatResponse<ProjectAPI>({\n message: t({\n en: 'Project deleted',\n fr: 'Projet supprimé',\n es: 'Proyecto eliminado',\n 'en-GB': 'Project deleted',\n de: 'Projekt gelöscht',\n ja: 'プロジェクトが削除されました',\n ko: '프로젝트가 삭제되었습니다',\n zh: '项目已删除',\n it: 'Progetto eliminato',\n pt: 'Projeto excluído',\n hi: 'परियोजना हटा दी गई',\n ar: 'تم حذف المشروع',\n ru: 'Проект удален',\n tr: 'Proje silindi',\n pl: 'Projekt usunięty',\n id: 'Proyek dihapus',\n vi: 'Dự án đã bị xóa',\n uk: 'Проєкт видалено',\n }),\n data: formattedProject,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type SelectProjectParam = { projectId: string | Types.ObjectId };\nexport type SelectProjectResult = ResponseData<ProjectAPI>;\n\n/**\n * Select a project.\n */\nexport const selectProject = async (\n request: FastifyRequest<{ Params: SelectProjectParam }>,\n reply: FastifyReply\n) => {\n const { projectId } = request.params;\n const { session, roles, user } = request.session || {};\n\n if (!projectId) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_ID_NOT_FOUND'\n );\n }\n\n if (typeof session === 'undefined') {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'SESSION_NOT_DEFINED'\n );\n }\n\n try {\n const project = await projectService.getProjectById(projectId);\n\n if (\n !hasPermission(\n roles || [],\n 'project:read'\n )({\n ...request.session,\n targetProjects: [project],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PERMISSION_DENIED'\n );\n }\n\n // When selecting a project, reset to its default environment\n const defaultEnvId =\n project.environments?.find((e: any) => e.isDefault)?.id ??\n project.environments?.[0]?.id;\n\n await SessionModel.updateOne(\n { _id: session.id },\n {\n $set: {\n activeProjectId: String(projectId),\n activeEnvironmentId: defaultEnvId ? String(defaultEnvId) : null,\n },\n }\n );\n\n if (user) {\n await userService.updateUserById(user.id, {\n lastActiveProjectId: String(projectId),\n });\n }\n\n const responseData = formatResponse<ProjectAPI>({\n message: t({\n en: 'Project selected successfully',\n 'en-GB': 'Project selected successfully',\n fr: 'Projet sélectionné avec succès',\n es: 'Proyecto seleccionado con éxito',\n ru: 'Проект успешно выбран',\n ja: 'プロジェクトが正常に選択されました',\n ko: '프로젝트가 성공적으로 선택되었습니다',\n zh: '项目已成功选择',\n de: 'Projekt erfolgreich ausgewählt',\n ar: 'تم اختيار المشروع بنجاح',\n it: 'Progetto selezionato con successo',\n pt: 'Projeto selecionado com sucesso',\n hi: 'प्रोजेक्ट सफलतापूर्वक चुना गया',\n tr: 'Proje başarıyla seçildi',\n pl: 'Projekt został pomyślnie wybrany',\n id: 'Proyek berhasil dipilih',\n vi: 'Dự án đã được chọn thành công',\n uk: 'Проєкт успішно вибрано',\n }),\n description: t({\n en: 'Your project has been selected successfully',\n 'en-GB': 'Your project has been selected successfully',\n fr: 'Votre projet a été sélectionné avec succès',\n es: 'Su proyecto ha sido seleccionado con éxito',\n ru: 'Ваш проект был успешно выбран',\n ja: 'プロジェクトは正常に選択されました',\n ko: '프로젝트가 성공적으로 선택되었습니다',\n zh: '您的项目已成功选择',\n de: 'Ihr Projekt wurde erfolgreich ausgewählt',\n ar: 'لقد تم اختيار مشروعك بنجاح',\n it: 'Il tuo progetto è stato selezionato con successo',\n pt: 'Seu projeto foi selecionado com sucesso',\n hi: 'आपका प्रोजेक्ट सफलतापूर्वक चुन लिया गया है',\n tr: 'Projeniz başarıyla seçildi',\n pl: 'Twój projekt został pomyślnie wybrany',\n id: 'Proyek Anda telah berhasil dipilih',\n vi: 'Dự án của bạn đã được chọn thành công',\n uk: 'Ваш проєкт успішно вибрано',\n }),\n data: mapProjectToAPI(project),\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type UnselectProjectResult = ResponseData<null>;\n\n/**\n * Unselect a project.\n */\nexport const unselectProject = async (\n _request: FastifyRequest,\n reply: FastifyReply\n) => {\n const { session, user } = _request.session || {};\n\n if (typeof session === 'undefined') {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'SESSION_NOT_DEFINED'\n );\n }\n\n try {\n await SessionModel.updateOne(\n { _id: session.id },\n { $set: { activeProjectId: null, activeEnvironmentId: null } }\n );\n\n if (user) {\n await userService.updateUserById(user.id, {\n lastActiveProjectId: null,\n });\n }\n\n const responseData = formatResponse<null>({\n message: t({\n en: 'Project unselected successfully',\n 'en-GB': 'Project unselected successfully',\n fr: 'Projet désélectionné avec succès',\n es: 'Proyecto deseleccionado con éxito',\n ru: 'Проект успешно снят с выбора',\n ja: 'プロジェクトの選択が正常に解除されました',\n ko: '프로젝트 선택이 성공적으로 해제되었습니다',\n zh: '项目已成功取消选择',\n de: 'Projekt erfolgreich abgewählt',\n ar: 'تم إلغاء تحديد المشروع بنجاح',\n it: 'Progetto deselezionato con successo',\n pt: 'Projeto desmarcado com sucesso',\n hi: 'प्रोजेक्ट सफलतापूर्वक अनसेलेक्ट किया गया',\n tr: 'Proje seçimi başarıyla kaldırıldı',\n pl: 'Wybór projektu został pomyślnie cofnięty',\n id: 'Proyek berhasil batal dipilih',\n vi: 'Dự án đã được bỏ chọn thành công',\n uk: 'Вибір проєкту успішно скасовано',\n }),\n description: t({\n en: 'Your project has been unselected successfully',\n 'en-GB': 'Your project has been unselected successfully',\n fr: 'Votre projet a été désélectionné avec succès',\n es: 'Su proyecto ha sido deseleccionado con éxito',\n ru: 'Выбор вашего проекта был успешно снят',\n ja: 'プロジェクトの選択は正常に解除されました',\n ko: '프로젝트 선택이 성공적으로 해제되었습니다',\n zh: '您的项目已成功取消选择',\n de: 'Ihr Projekt wurde erfolgreich abgewählt',\n ar: 'لقد تم إلغاء تحديد مشروعك بنجاح',\n it: 'Il tuo progetto è stato deselezionato con successo',\n pt: 'Seu projeto foi desmarcado com sucesso',\n hi: 'आपका प्रोजेक्ट सफलतापूर्वक अनसेलेक्ट कर दिया गया है',\n tr: 'Projenizin seçimi başarıyla kaldırıldı',\n pl: 'Wybór Twojego projektu został pomyślnie cofnięty',\n id: 'Proyek Anda telah berhasil batal dipilih',\n vi: 'Dự án của bạn đã được bỏ chọn thành công',\n uk: 'Вибір вашого проєкту успішно скасовано',\n }),\n data: null,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type GetCIConfigurationResult = ResponseData<ciService.CIStatus>;\n\n/**\n * Get CI configuration status for the current project\n */\nexport const getCIConfiguration = async (\n request: FastifyRequest,\n reply: FastifyReply\n): Promise<void> => {\n const { project, user } = request.session || {};\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n try {\n const ciStatus = await ciService.getCIStatus(project, user.id);\n\n const responseData = formatResponse<ciService.CIStatus>({\n data: ciStatus,\n });\n\n return reply.send(responseData);\n } catch (error) {\n if ((error as any)?.isAppError) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n return ErrorHandler.handleCustomErrorResponse(\n reply,\n 'CI_CONFIG_ERROR',\n 'CI Configuration Error',\n (error as Error)?.message ?? 'Failed to get CI configuration',\n undefined,\n 500\n );\n }\n};\n\nexport type PushCIConfigurationResult = ResponseData<{ success: boolean }>;\n\n/**\n * Push CI configuration file to the repository\n */\nexport const pushCIConfiguration = async (\n request: FastifyRequest,\n reply: FastifyReply\n): Promise<void> => {\n const { project, user } = request.session || {};\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n try {\n await ciService.installCI(project, user.id);\n\n const responseData = formatResponse<{ success: boolean }>({\n message: t({\n en: 'CI configuration installed successfully',\n 'en-GB': 'CI configuration installed successfully',\n fr: 'Configuration CI installée avec succès',\n es: 'Configuración CI instalada con éxito',\n ru: 'CI-конфигурация успешно установлена',\n ja: 'CI構成が正常にインストールされました',\n ko: 'CI 설정이 성공적으로 설치되었습니다',\n zh: 'CI 配置已成功安装',\n de: 'CI-Konfiguration erfolgreich installiert',\n ar: 'تم تثبيت تكوين CI بنجاح',\n it: 'Configurazione CI installata con successo',\n pt: 'Configuração CI installada com successo',\n hi: 'CI कॉन्फ़िगरेशन सफलतापूर्वक स्थापित किया गया',\n tr: 'CI yapılandırması başarıyla yüklendi',\n pl: 'Konfiguracja CI została pomyślnie zainstalowana',\n id: 'Konfigurasi CI berhasil diinstal',\n vi: 'Cài đặt cấu hình CI thành công',\n uk: 'Конфігурацію CI успішно встановлено',\n }),\n description: t({\n en: 'The CI workflow file has been added to your repository',\n 'en-GB': 'The CI workflow file has been added to your repository',\n fr: 'Le fichier de workflow CI a été ajouté à votre dépôt',\n es: 'El archivo de flujo de trabajo CI se ha agregado a su repositorio',\n ru: 'Файл рабочего процесса CI был добавлен в ваш репозиторий',\n ja: 'CIワークフローファイルがリポジトリに追加されました',\n ko: 'CI 워크플로 파일이 저장소에 추가되었습니다',\n zh: 'CI 工作流文件已添加到您的存储库中',\n de: 'Die CI-Workflow-Datei wurde Ihrem Repository hinzugefügt',\n ar: 'تمت إضافة ملف سير عمل CI إلى مستودعك',\n it: 'Il file del workflow CI è stato aggiunto al tuo repository',\n pt: 'O arquivo de fluxo de trabalho CI foi adicionado ao seu repositório',\n hi: 'CI वर्कफ़्लो फ़ाइल आपके रिपॉजिटरी में जोड़ दी गई है',\n tr: 'CI iş akışı dosyası deponuza eklendi',\n pl: 'Plik przepływu pracy CI został dodany do Twojego repozytorium',\n id: 'File alur kerja CI telah ditambahkan ke repositori Anda',\n vi: 'Tệp quy trình làm việc CI đã được thêm vào kho lưu trữ của bạn',\n uk: 'Файл робочого процесу CI додано до вашого репозиторію',\n }),\n data: { success: true },\n });\n\n return reply.send(responseData);\n } catch (error) {\n if ((error as any)?.isAppError) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n return ErrorHandler.handleCustomErrorResponse(\n reply,\n 'CI_INSTALL_ERROR',\n 'CI Installation Error',\n (error as Error)?.message ?? 'Failed to install CI configuration',\n undefined,\n 500\n );\n }\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projectAccessKey.controller.mjs","names":["projectAccessKeyService.addNewAccessKey","projectAccessKeyService.deleteAccessKey","projectAccessKeyService.refreshAccessKey"],"sources":["../../../src/controllers/projectAccessKey.controller.ts"],"sourcesContent":["import { sendEmail } from '@services/email.service';\nimport * as projectAccessKeyService from '@services/projectAccessKey.service';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport {\n hasPermission,\n intersectPermissions,\n type Permission,\n} from '@utils/permissions';\nimport { formatResponse, type ResponseData } from '@utils/responseData';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport { t } from 'fastify-intlayer';\nimport type { AccessKeyData, OAuth2Access } from '@/types/project.types';\n\nexport type AddNewAccessKeyBody = AccessKeyData;\nexport type AddNewAccessKeyResponse = ResponseData<OAuth2Access>;\n\n/**\n * Adds a new access key to a project.\n */\nexport const addNewAccessKey = async (\n request: FastifyRequest<{ Body: AddNewAccessKeyBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, project, roles, permissions } = request.session || {};\n const { grants, name, expiresAt, allowedEnvironmentIds, allowedLocales } =\n request.body;\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:write'\n )({\n ...request.session,\n targetProjectIds: [project.id],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n const filteredPermisions = intersectPermissions(\n permissions || [],\n (grants as Permission[]) || []\n );\n\n try {\n const newAccessKey = await projectAccessKeyService.addNewAccessKey(\n {\n name,\n expiresAt,\n grants: filteredPermisions,\n allowedEnvironmentIds: allowedEnvironmentIds ?? null,\n allowedLocales: allowedLocales ?? null,\n },\n project.id,\n user\n );\n\n const responseData = formatResponse<OAuth2Access>({\n message: t({\n en: 'Access key created successfully',\n es: 'Clave de acceso creada con éxito',\n fr: \"Clé d'accès créée avec succès\",\n ru: 'Ключ доступа успешно создан',\n ja: 'アクセスキーが正常に作成されました',\n ko: '액세스 키가 성공적으로 생성되었습니다',\n zh: '访问密钥创建成功',\n de: 'Zugriffsschlüssel erfolgreich erstellt',\n ar: 'تم إنشاء مفتاح الوصول بنجاح',\n it: 'Chiave di accesso creata con successo',\n 'en-GB': 'Access key created successfully',\n pt: 'Chave de acesso criada com sucesso',\n hi: 'एक्सेस कुंजी सफलतापूर्वक बनाई गई',\n tr: 'Erişim anahtarı başarıyla oluşturuldu',\n pl: 'Klucz dostępu został pomyślnie utworzony',\n id: 'Kunci akses berhasil dibuat',\n vi: 'Khóa truy cập đã được tạo thành công',\n uk: 'Ключ доступу успішно створено',\n }),\n description: t({\n en: 'The access key has been created successfully',\n es: 'La clave de acceso ha sido creada con éxito',\n fr: \"La clé d'accès a été créée avec succès\",\n ru: 'Ключ доступа был успешно создан',\n ja: 'アクセスキーが正常に作成されました',\n ko: '액세스 키가 성공적으로 생성되었습니다',\n zh: '访问密钥已成功创建',\n de: 'Der Zugriffsschlüssel wurde erfolgreich erstellt',\n ar: 'تم إنشاء مفتاح الوصول بنجاح',\n it: 'La chiave di accesso è stata creata con successo',\n 'en-GB': 'The access key has been created successfully',\n pt: 'A chave de acesso foi criada com sucesso',\n hi: 'एक्सेस कुंजी सफलतापूर्वक बना दी गई है',\n tr: 'Erişim anahtarı başarıyla oluşturuldu',\n pl: 'Klucz dostępu został pomyślnie utworzony',\n id: 'Kunci akses telah berhasil dibuat',\n vi: 'Khóa truy cập đã được tạo thành công',\n uk: 'Ключ доступу було успішно створено',\n }),\n data: newAccessKey,\n });\n\n sendEmail({\n type: 'oAuthTokenCreated',\n to: user.email,\n username: user.name,\n applicationName: newAccessKey.name ?? newAccessKey.clientId,\n scopes: newAccessKey.grants,\n tokenDetailsUrl: `${process.env.APP_URL}/oauth2/token`,\n securityLogUrl: `${process.env.APP_URL}/security-log`,\n supportUrl: `${process.env.APP_URL}/support`,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type DeleteAccessKeyBody = { clientId: string };\nexport type DeleteAccessKeyResponse = ResponseData<null>;\n\n/**\n * Deletes an access key from a project.\n */\nexport const deleteAccessKey = async (\n request: FastifyRequest<{ Body: DeleteAccessKeyBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, project, roles } = request.session || {};\n const { clientId } = request.body;\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (!clientId) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'CLIENT_ID_NOT_FOUND'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:write'\n )({\n ...request.session,\n targetProjectIds: [project.id],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n try {\n const deletedAccessKey = await projectAccessKeyService.deleteAccessKey(\n clientId,\n project,\n user.id\n );\n\n if (!deletedAccessKey) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'ACCESS_KEY_NOT_FOUND',\n {\n clientId,\n }\n );\n }\n\n const responseData = formatResponse<null>({\n message: t({\n en: 'Access key deleted successfully',\n es: 'Clave de acceso eliminada con éxito',\n fr: \"Clé d'accès supprimée avec succès\",\n ru: 'Ключ доступа успешно удален',\n ja: 'アクセスキーが正常に削除されました',\n ko: '액세스 키가 성공적으로 삭제되었습니다',\n zh: '访问密钥删除成功',\n de: 'Zugriffsschlüssel erfolgreich gelöscht',\n ar: 'تم حذف مفتاح الوصول بنجاح',\n it: 'Chiave di accesso eliminata con successo',\n 'en-GB': 'Access key deleted successfully',\n pt: 'Chave de acesso eliminada com sucesso',\n hi: 'एक्सेस कुंजी सफलतापूर्वक हटा दी गई',\n tr: 'Erişim anahtarı başarıyla silindi',\n pl: 'Klucz dostępu został pomyślnie usunięty',\n id: 'Kunci akses berhasil dihapus',\n vi: 'Khóa truy cập đã được xóa thành công',\n uk: 'Ключ доступу успішно видалено',\n }),\n description: t({\n en: 'The access key has been deleted successfully',\n es: 'La clave de acceso ha sido eliminada con éxito',\n fr: \"La clé d'accès a été supprimée avec succès\",\n ru: 'Ключ доступа был успешно удален',\n ja: 'アクセスキーが正常に削除されました',\n ko: '액세스 키가 성공적으로 삭제되었습니다',\n zh: '访问密钥已成功删除',\n de: 'Der Zugriffsschlüssel wurde erfolgreich gelöscht',\n ar: 'تم حذف مفتاح الوصول بنجاح',\n it: 'La chiave di accesso è stata eliminata con successo',\n 'en-GB': 'The access key has been deleted successfully',\n pt: 'A chave de acesso foi eliminada com sucesso',\n hi: 'एक्सेस कुंजी सफलतापूर्वक हटा दी गई है',\n tr: 'Erişim anahtarı başarıyla silindi',\n pl: 'Klucz dostępu został pomyślnie usunięty',\n id: 'Kunci akses telah berhasil dihapus',\n vi: 'Khóa truy cập đã được xóa thành công',\n uk: 'Ключ доступу було успішно видалено',\n }),\n data: null,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type RefreshAccessKeyBody = { clientId: string };\nexport type RefreshAccessKeyResponse = ResponseData<OAuth2Access>;\n\n/**\n * Refreshes an access key from a project.\n */\nexport const refreshAccessKey = async (\n request: FastifyRequest<{ Body: RefreshAccessKeyBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, project, roles } = request.session || {};\n const { clientId } = request.body;\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (!clientId) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'CLIENT_ID_NOT_FOUND'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:write'\n )({\n ...request.session,\n targetProjectIds: [project?.id],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n try {\n const newAccessKey = await projectAccessKeyService.refreshAccessKey(\n clientId,\n project?.id,\n user?.id\n );\n\n const responseData = formatResponse<OAuth2Access>({\n message: t({\n en: 'Access key refreshed successfully',\n es: 'Clave de acceso actualizada con éxito',\n fr: \"Clé d'accès actualisée avec succès\",\n ru: 'Ключ доступа успешно обновлен',\n ja: 'アクセスキーが正常に更新されました',\n ko: '액세스 키가 성공적으로 새로 고쳐졌습니다',\n zh: '访问密钥刷新成功',\n de: 'Zugriffsschlüssel erfolgreich aktualisiert',\n ar: 'تم تحديث مفتاح الوصول بنجاح',\n it: 'Chiave di accesso aggiornata con successo',\n 'en-GB': 'Access key refreshed successfully',\n pt: 'Chave de acesso atualizada com sucesso',\n hi: 'एक्सेस कुंजी सफलतापूर्वक रिफ्रेश कर दी गई',\n tr: 'Erişim anahtarı başarıyla yenilendi',\n pl: 'Klucz dostępu został pomyślnie odświeżony',\n id: 'Kunci akses berhasil diperbarui',\n vi: 'Khóa truy cập đã được làm mới thành công',\n uk: 'Ключ доступу успішно оновлено',\n }),\n description: t({\n en: 'The access key has been refreshed successfully',\n es: 'La clave de acceso ha sido actualizada con éxito',\n fr: \"La clé d'accès a été actualisée avec succès\",\n ru: 'Ключ доступа был успешно обновлен',\n ja: 'アクセスキーが正常に更新されました',\n ko: '액세스 키가 성공적으로 새로 고쳐졌습니다',\n zh: '访问密钥已成功刷新',\n de: 'Der Zugriffsschlüssel wurde erfolgreich aktualisiert',\n ar: 'تم تحديث مفتاح الوصول بنجاح',\n it: 'La chiave di accesso è stata aggiornata con successo',\n 'en-GB': 'The access key has been refreshed successfully',\n pt: 'A chave de acesso foi atualizada com sucesso',\n hi: 'एक्सेस कुंजी सफलतापूर्वक रिफ्रेश कर दी गई है',\n tr: 'Erişim anahtarı başarıyla yenilendi',\n pl: 'Klucz dostępu został pomyślnie odświeżony',\n id: 'Kunci akses telah berhasil diperbarui',\n vi: 'Khóa truy cập đã được làm mới thành công',\n uk: 'Ключ доступу було успішно оновлено',\n }),\n data: newAccessKey,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"projectAccessKey.controller.mjs","names":["projectAccessKeyService.addNewAccessKey","projectAccessKeyService.deleteAccessKey","projectAccessKeyService.refreshAccessKey"],"sources":["../../../src/controllers/projectAccessKey.controller.ts"],"sourcesContent":["import { sendEmail } from '@services/email.service';\nimport * as projectAccessKeyService from '@services/projectAccessKey.service';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport {\n hasPermission,\n intersectPermissions,\n type Permission,\n} from '@utils/permissions';\nimport { formatResponse, type ResponseData } from '@utils/responseData';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport { t } from 'fastify-intlayer';\nimport type { AccessKeyData, OAuth2Access } from '@/types/project.types';\n\nexport type AddNewAccessKeyBody = AccessKeyData;\nexport type AddNewAccessKeyResponse = ResponseData<OAuth2Access>;\n\n/**\n * Adds a new access key to a project.\n */\nexport const addNewAccessKey = async (\n request: FastifyRequest<{ Body: AddNewAccessKeyBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, project, roles, permissions } = request.session || {};\n const { grants, name, expiresAt, allowedEnvironmentIds, allowedLocales } =\n request.body;\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:write'\n )({\n ...request.session,\n targetProjectIds: [project.id],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n const filteredPermisions = intersectPermissions(\n permissions || [],\n (grants as Permission[]) || []\n );\n\n try {\n const newAccessKey = await projectAccessKeyService.addNewAccessKey(\n {\n name,\n expiresAt,\n grants: filteredPermisions,\n allowedEnvironmentIds: allowedEnvironmentIds ?? null,\n allowedLocales: allowedLocales ?? null,\n },\n project.id,\n user\n );\n\n const responseData = formatResponse<OAuth2Access>({\n message: t({\n en: 'Access key created successfully',\n es: 'Clave de acceso creada con éxito',\n fr: \"Clé d'accès créée avec succès\",\n ru: 'Ключ доступа успешно создан',\n ja: 'アクセスキーが正常に作成されました',\n ko: '액세스 키가 성공적으로 생성되었습니다',\n zh: '访问密钥创建成功',\n de: 'Zugriffsschlüssel erfolgreich erstellt',\n ar: 'تم إنشاء مفتاح الوصول بنجاح',\n it: 'Chiave di accesso creata con successo',\n 'en-GB': 'Access key created successfully',\n pt: 'Chave de acesso criada com sucesso',\n hi: 'एक्सेस कुंजी सफलतापूर्वक बनाई गई',\n tr: 'Erişim anahtarı başarıyla oluşturuldu',\n pl: 'Klucz dostępu został pomyślnie utworzony',\n id: 'Kunci akses berhasil dibuat',\n vi: 'Khóa truy cập đã được tạo thành công',\n uk: 'Ключ доступу успішно створено',\n }),\n description: t({\n en: 'The access key has been created successfully',\n es: 'La clave de acceso ha sido creada con éxito',\n fr: \"La clé d'accès a été créée avec succès\",\n ru: 'Ключ доступа был успешно создан',\n ja: 'アクセスキーが正常に作成されました',\n ko: '액세스 키가 성공적으로 생성되었습니다',\n zh: '访问密钥已成功创建',\n de: 'Der Zugriffsschlüssel wurde erfolgreich erstellt',\n ar: 'تم إنشاء مفتاح الوصول بنجاح',\n it: 'La chiave di accesso è stata creata con successo',\n 'en-GB': 'The access key has been created successfully',\n pt: 'A chave de acesso foi criada com sucesso',\n hi: 'एक्सेस कुंजी सफलतापूर्वक बना दी गई है',\n tr: 'Erişim anahtarı başarıyla oluşturuldu',\n pl: 'Klucz dostępu został pomyślnie utworzony',\n id: 'Kunci akses telah berhasil dibuat',\n vi: 'Khóa truy cập đã được tạo thành công',\n uk: 'Ключ доступу було успішно створено',\n }),\n data: newAccessKey,\n });\n\n sendEmail({\n type: 'oAuthTokenCreated',\n to: user.email,\n username: user.name,\n applicationName: newAccessKey.name ?? newAccessKey.clientId,\n scopes: newAccessKey.grants,\n tokenDetailsUrl: `${process.env.APP_URL}/oauth2/token`,\n securityLogUrl: `${process.env.APP_URL}/security-log`,\n supportUrl: `${process.env.APP_URL}/support`,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type DeleteAccessKeyBody = { clientId: string };\nexport type DeleteAccessKeyResponse = ResponseData<null>;\n\n/**\n * Deletes an access key from a project.\n */\nexport const deleteAccessKey = async (\n request: FastifyRequest<{ Body: DeleteAccessKeyBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, project, roles } = request.session || {};\n const { clientId } = request.body;\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (!clientId) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'CLIENT_ID_NOT_FOUND'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:write'\n )({\n ...request.session,\n targetProjectIds: [project.id],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n try {\n const deletedAccessKey = await projectAccessKeyService.deleteAccessKey(\n clientId,\n project,\n user.id\n );\n\n if (!deletedAccessKey) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'ACCESS_KEY_NOT_FOUND',\n {\n clientId,\n }\n );\n }\n\n const responseData = formatResponse<null>({\n message: t({\n en: 'Access key deleted successfully',\n es: 'Clave de acceso eliminada con éxito',\n fr: \"Clé d'accès supprimée avec succès\",\n ru: 'Ключ доступа успешно удален',\n ja: 'アクセスキーが正常に削除されました',\n ko: '액세스 키가 성공적으로 삭제되었습니다',\n zh: '访问密钥删除成功',\n de: 'Zugriffsschlüssel erfolgreich gelöscht',\n ar: 'تم حذف مفتاح الوصول بنجاح',\n it: 'Chiave di accesso eliminata con successo',\n 'en-GB': 'Access key deleted successfully',\n pt: 'Chave de acesso eliminada com sucesso',\n hi: 'एक्सेस कुंजी सफलतापूर्वक हटा दी गई',\n tr: 'Erişim anahtarı başarıyla silindi',\n pl: 'Klucz dostępu został pomyślnie usunięty',\n id: 'Kunci akses berhasil dihapus',\n vi: 'Khóa truy cập đã được xóa thành công',\n uk: 'Ключ доступу успішно видалено',\n }),\n description: t({\n en: 'The access key has been deleted successfully',\n es: 'La clave de acceso ha sido eliminada con éxito',\n fr: \"La clé d'accès a été supprimée avec succès\",\n ru: 'Ключ доступа был успешно удален',\n ja: 'アクセスキーが正常に削除されました',\n ko: '액세스 키가 성공적으로 삭제되었습니다',\n zh: '访问密钥已成功删除',\n de: 'Der Zugriffsschlüssel wurde erfolgreich gelöscht',\n ar: 'تم حذف مفتاح الوصول بنجاح',\n it: 'La chiave di accesso è stata eliminata con successo',\n 'en-GB': 'The access key has been deleted successfully',\n pt: 'A chave de acesso foi eliminada com sucesso',\n hi: 'एक्सेस कुंजी सफलतापूर्वक हटा दी गई है',\n tr: 'Erişim anahtarı başarıyla silindi',\n pl: 'Klucz dostępu został pomyślnie usunięty',\n id: 'Kunci akses telah berhasil dihapus',\n vi: 'Khóa truy cập đã được xóa thành công',\n uk: 'Ключ доступу було успішно видалено',\n }),\n data: null,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type RefreshAccessKeyBody = { clientId: string };\nexport type RefreshAccessKeyResponse = ResponseData<OAuth2Access>;\n\n/**\n * Refreshes an access key from a project.\n */\nexport const refreshAccessKey = async (\n request: FastifyRequest<{ Body: RefreshAccessKeyBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, project, roles } = request.session || {};\n const { clientId } = request.body;\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (!clientId) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'CLIENT_ID_NOT_FOUND'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:write'\n )({\n ...request.session,\n targetProjectIds: [project?.id],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n try {\n const newAccessKey = await projectAccessKeyService.refreshAccessKey(\n clientId,\n project?.id,\n user?.id\n );\n\n const responseData = formatResponse<OAuth2Access>({\n message: t({\n en: 'Access key refreshed successfully',\n es: 'Clave de acceso actualizada con éxito',\n fr: \"Clé d'accès actualisée avec succès\",\n ru: 'Ключ доступа успешно обновлен',\n ja: 'アクセスキーが正常に更新されました',\n ko: '액세스 키가 성공적으로 새로 고쳐졌습니다',\n zh: '访问密钥刷新成功',\n de: 'Zugriffsschlüssel erfolgreich aktualisiert',\n ar: 'تم تحديث مفتاح الوصول بنجاح',\n it: 'Chiave di accesso aggiornata con successo',\n 'en-GB': 'Access key refreshed successfully',\n pt: 'Chave de acesso atualizada com sucesso',\n hi: 'एक्सेस कुंजी सफलतापूर्वक रिफ्रेश कर दी गई',\n tr: 'Erişim anahtarı başarıyla yenilendi',\n pl: 'Klucz dostępu został pomyślnie odświeżony',\n id: 'Kunci akses berhasil diperbarui',\n vi: 'Khóa truy cập đã được làm mới thành công',\n uk: 'Ключ доступу успішно оновлено',\n }),\n description: t({\n en: 'The access key has been refreshed successfully',\n es: 'La clave de acceso ha sido actualizada con éxito',\n fr: \"La clé d'accès a été actualisée avec succès\",\n ru: 'Ключ доступа был успешно обновлен',\n ja: 'アクセスキーが正常に更新されました',\n ko: '액세스 키가 성공적으로 새로 고쳐졌습니다',\n zh: '访问密钥已成功刷新',\n de: 'Der Zugriffsschlüssel wurde erfolgreich aktualisiert',\n ar: 'تم تحديث مفتاح الوصول بنجاح',\n it: 'La chiave di accesso è stata aggiornata con successo',\n 'en-GB': 'The access key has been refreshed successfully',\n pt: 'A chave de acesso foi atualizada com sucesso',\n hi: 'एक्सेस कुंजी सफलतापूर्वक रिफ्रेश कर दी गई है',\n tr: 'Erişim anahtarı başarıyla yenilendi',\n pl: 'Klucz dostępu został pomyślnie odświeżony',\n id: 'Kunci akses telah berhasil diperbarui',\n vi: 'Khóa truy cập đã được làm mới thành công',\n uk: 'Ключ доступу було успішно оновлено',\n }),\n data: newAccessKey,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projectMemberAccess.controller.mjs","names":[],"sources":["../../../src/controllers/projectMemberAccess.controller.ts"],"sourcesContent":["import { ProjectModel } from '@schemas/project.schema';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport { hasPermission } from '@utils/permissions';\nimport { formatResponse, type ResponseData } from '@utils/responseData';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport { t } from 'fastify-intlayer';\nimport type {\n ProjectMemberGranularAccess,\n ProjectMemberGranularAccessAPI,\n} from '@/types/project.types';\n\nexport type UpdateMemberAccessParams = { userId: string };\n\nexport type UpdateMemberAccessBody = {\n /** null = unrestricted; [] = no env access; array = env IDs (null item = production). */\n allowedEnvironmentIds: (string | null)[] | null;\n /** null = unrestricted; [] = no locale access; array of locale strings. */\n allowedLocales: string[] | null;\n};\n\nexport type UpdateMemberAccessResult =\n ResponseData<ProjectMemberGranularAccessAPI>;\n\n/**\n * Updates granular access constraints for a project member.\n * Requires project:write on the current project.\n */\nexport const updateMemberAccess = async (\n request: FastifyRequest<{\n Params: UpdateMemberAccessParams;\n Body: UpdateMemberAccessBody;\n }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, project, roles } = request.session || {};\n const { userId } = request.params;\n const { allowedEnvironmentIds, allowedLocales } = request.body;\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (!userId) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:write'\n )({\n ...request.session,\n targetProjectIds: [project.id],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n try {\n const projectDoc = await ProjectModel.findById(project.id);\n\n if (!projectDoc) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n const memberAccessArray =\n projectDoc.memberAccess as unknown as ProjectMemberGranularAccess[];\n\n const existingIndex = memberAccessArray.findIndex(\n (entry) => String(entry.userId) === String(userId)\n );\n\n const accessEntry = {\n userId,\n allowedEnvironmentIds: allowedEnvironmentIds ?? null,\n allowedLocales:\n (allowedLocales as ProjectMemberGranularAccess['allowedLocales']) ??\n null,\n };\n\n if (existingIndex >= 0) {\n memberAccessArray[existingIndex] =\n accessEntry as unknown as ProjectMemberGranularAccess;\n } else {\n memberAccessArray.push(\n accessEntry as unknown as ProjectMemberGranularAccess\n );\n }\n\n await projectDoc.save();\n\n const responseData = formatResponse<ProjectMemberGranularAccessAPI>({\n message: t({\n en: 'Member access updated successfully',\n fr: 'Accès du membre mis à jour avec succès',\n es: 'Acceso del miembro actualizado con éxito',\n de: 'Mitgliederzugriff erfolgreich aktualisiert',\n ja: 'メンバーアクセスが正常に更新されました',\n ko: '멤버 액세스가 성공적으로 업데이트되었습니다',\n zh: '成员访问权限已成功更新',\n ar: 'تم تحديث وصول العضو بنجاح',\n it: 'Accesso del membro aggiornato con successo',\n pt: 'Acesso do membro atualizado com sucesso',\n 'en-GB': 'Member access updated successfully',\n hi: 'सदस्य पहुँच सफलतापूर्वक अपडेट की गई',\n tr: 'Üye erişimi başarıyla güncellendi',\n pl: 'Dostęp członka został pomyślnie zaktualizowany',\n id: 'Akses anggota berhasil diperbarui',\n vi: 'Quyền truy cập thành viên đã được cập nhật thành công',\n ru: 'Доступ участника успешно обновлён',\n uk: 'Доступ учасника успішно оновлено',\n }),\n data: accessEntry as unknown as ProjectMemberGranularAccessAPI,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n"],"mappings":";;;;;;;;;;;AA2BA,MAAa,qBAAqB,OAChC,SAIA,UACkB;CAClB,MAAM,EAAE,MAAM,SAAS,UAAU,QAAQ,WAAW,
|
|
1
|
+
{"version":3,"file":"projectMemberAccess.controller.mjs","names":[],"sources":["../../../src/controllers/projectMemberAccess.controller.ts"],"sourcesContent":["import { ProjectModel } from '@schemas/project.schema';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport { hasPermission } from '@utils/permissions';\nimport { formatResponse, type ResponseData } from '@utils/responseData';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport { t } from 'fastify-intlayer';\nimport type {\n ProjectMemberGranularAccess,\n ProjectMemberGranularAccessAPI,\n} from '@/types/project.types';\n\nexport type UpdateMemberAccessParams = { userId: string };\n\nexport type UpdateMemberAccessBody = {\n /** null = unrestricted; [] = no env access; array = env IDs (null item = production). */\n allowedEnvironmentIds: (string | null)[] | null;\n /** null = unrestricted; [] = no locale access; array of locale strings. */\n allowedLocales: string[] | null;\n};\n\nexport type UpdateMemberAccessResult =\n ResponseData<ProjectMemberGranularAccessAPI>;\n\n/**\n * Updates granular access constraints for a project member.\n * Requires project:write on the current project.\n */\nexport const updateMemberAccess = async (\n request: FastifyRequest<{\n Params: UpdateMemberAccessParams;\n Body: UpdateMemberAccessBody;\n }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, project, roles } = request.session || {};\n const { userId } = request.params;\n const { allowedEnvironmentIds, allowedLocales } = request.body;\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (!userId) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:write'\n )({\n ...request.session,\n targetProjectIds: [project.id],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n try {\n const projectDoc = await ProjectModel.findById(project.id);\n\n if (!projectDoc) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n const memberAccessArray =\n projectDoc.memberAccess as unknown as ProjectMemberGranularAccess[];\n\n const existingIndex = memberAccessArray.findIndex(\n (entry) => String(entry.userId) === String(userId)\n );\n\n const accessEntry = {\n userId,\n allowedEnvironmentIds: allowedEnvironmentIds ?? null,\n allowedLocales:\n (allowedLocales as ProjectMemberGranularAccess['allowedLocales']) ??\n null,\n };\n\n if (existingIndex >= 0) {\n memberAccessArray[existingIndex] =\n accessEntry as unknown as ProjectMemberGranularAccess;\n } else {\n memberAccessArray.push(\n accessEntry as unknown as ProjectMemberGranularAccess\n );\n }\n\n await projectDoc.save();\n\n const responseData = formatResponse<ProjectMemberGranularAccessAPI>({\n message: t({\n en: 'Member access updated successfully',\n fr: 'Accès du membre mis à jour avec succès',\n es: 'Acceso del miembro actualizado con éxito',\n de: 'Mitgliederzugriff erfolgreich aktualisiert',\n ja: 'メンバーアクセスが正常に更新されました',\n ko: '멤버 액세스가 성공적으로 업데이트되었습니다',\n zh: '成员访问权限已成功更新',\n ar: 'تم تحديث وصول العضو بنجاح',\n it: 'Accesso del membro aggiornato con successo',\n pt: 'Acesso do membro atualizado com sucesso',\n 'en-GB': 'Member access updated successfully',\n hi: 'सदस्य पहुँच सफलतापूर्वक अपडेट की गई',\n tr: 'Üye erişimi başarıyla güncellendi',\n pl: 'Dostęp członka został pomyślnie zaktualizowany',\n id: 'Akses anggota berhasil diperbarui',\n vi: 'Quyền truy cập thành viên đã được cập nhật thành công',\n ru: 'Доступ участника успешно обновлён',\n uk: 'Доступ учасника успішно оновлено',\n }),\n data: accessEntry as unknown as ProjectMemberGranularAccessAPI,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n"],"mappings":";;;;;;;;;;;AA2BA,MAAa,qBAAqB,OAChC,SAIA,UACkB;CAClB,MAAM,EAAE,MAAM,SAAS,UAAU,QAAQ,WAAW,EAAE;CACtD,MAAM,EAAE,WAAW,QAAQ;CAC3B,MAAM,EAAE,uBAAuB,mBAAmB,QAAQ;AAE1D,KAAI,CAAC,KACH,QAAO,aAAa,2BAA2B,OAAO,mBAAmB;AAG3E,KAAI,CAAC,QACH,QAAO,aAAa,2BAClB,OACA,sBACD;AAGH,KAAI,CAAC,OACH,QAAO,aAAa,2BAA2B,OAAO,mBAAmB;AAG3E,KACE,CAAC,cACC,SAAS,EAAE,EACX,gBACD,CAAC;EACA,GAAG,QAAQ;EACX,kBAAkB,CAAC,QAAQ,GAAG;EAC/B,CAAC,CAEF,QAAO,aAAa,2BAA2B,OAAO,oBAAoB;AAG5E,KAAI;EACF,MAAM,aAAa,MAAM,aAAa,SAAS,QAAQ,GAAG;AAE1D,MAAI,CAAC,WACH,QAAO,aAAa,2BAClB,OACA,sBACD;EAGH,MAAM,oBACJ,WAAW;EAEb,MAAM,gBAAgB,kBAAkB,WACrC,UAAU,OAAO,MAAM,OAAO,KAAK,OAAO,OAAO,CACnD;EAED,MAAM,cAAc;GAClB;GACA,uBAAuB,yBAAyB;GAChD,gBACG,kBACD;GACH;AAED,MAAI,iBAAiB,EACnB,mBAAkB,iBAChB;MAEF,mBAAkB,KAChB,YACD;AAGH,QAAM,WAAW,MAAM;EAEvB,MAAM,eAAe,eAA+C;GAClE,SAAS,EAAE;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACL,CAAC;GACF,MAAM;GACP,CAAC;AAEF,SAAO,MAAM,KAAK,aAAa;UACxB,OAAO;AACd,SAAO,aAAa,uBAAuB,OAAO,MAAkB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recursiveAudit.controller.mjs","names":[],"sources":["../../../src/controllers/recursiveAudit.controller.ts"],"sourcesContent":["import {\n cancelAuditJob,\n discoverUrlsFromSitemap,\n getAuditJobStatus,\n pauseAuditJob,\n resumeAuditJob,\n startRecursiveAuditJob,\n} from '@services/audit/recursiveAudit.service';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\n\nexport type StartRecursiveAuditResult = {\n jobId: string;\n};\n\nexport type DiscoverUrlsResult = {\n urls: string[];\n};\n\nexport type RecursiveAuditJobInfo = {\n _id: string;\n targetUrl: string;\n userId?: string;\n status:\n | 'pending'\n | 'running'\n | 'paused'\n | 'cancelled'\n | 'completed'\n | 'failed';\n progress: number;\n totalPageCount: number;\n completedPageCount: number;\n createdAt: Date;\n updatedAt: Date;\n};\n\nexport type RecursiveAuditPageInfo = {\n _id: string;\n url: string;\n status: 'pending' | 'running' | 'completed' | 'failed';\n score?: number;\n error?: string;\n results?: any[];\n};\n\nexport type GetRecursiveAuditStatusResult = {\n job: RecursiveAuditJobInfo;\n pages: RecursiveAuditPageInfo[];\n};\n\nexport const discoverUrls = async (\n request: FastifyRequest<{ Querystring: { url: string } }>,\n reply: FastifyReply\n) => {\n const { url } = request.query;\n\n if (!url || typeof url !== 'string') {\n return reply.status(400).send({ error: 'URL is required' });\n }\n\n try {\n new URL(url);\n } catch {\n return reply.status(400).send({ error: 'Invalid URL format' });\n }\n\n try {\n const urls = await discoverUrlsFromSitemap(url);\n return reply.send({ urls });\n } catch (err) {\n return reply.status(500).send({ error: String(err) });\n }\n};\n\nexport const startRecursiveAudit = async (\n request: FastifyRequest<{\n Querystring: { url: string };\n Body: { urls?: string[] };\n }>,\n reply: FastifyReply\n) => {\n const { url } = request.query;\n const { urls } = request.body ?? {};\n const userId = request.headers['x-user-id'] as string;\n\n if (!url || typeof url !== 'string') {\n return reply.status(400).send({ error: 'URL is required' });\n }\n\n try {\n const jobId = await startRecursiveAuditJob(url, userId, urls);\n return reply.send({ jobId });\n } catch (err) {\n return reply.status(500).send({ error: String(err) });\n }\n};\n\nexport const getRecursiveAuditStatus = async (\n request: FastifyRequest<{ Params: { jobId: string } }>,\n reply: FastifyReply\n) => {\n const { jobId } = request.params;\n\n if (!jobId) {\n return reply.status(400).send({ error: 'jobId is required' });\n }\n\n try {\n const status = await getAuditJobStatus(jobId);\n if (!status) {\n return reply.status(404).send({ error: 'Job not found' });\n }\n return reply.send(status);\n } catch (err) {\n return reply.status(500).send({ error: String(err) });\n }\n};\n\nexport const cancelRecursiveAudit = async (\n request: FastifyRequest<{ Params: { jobId: string } }>,\n reply: FastifyReply\n) => {\n const { jobId } = request.params;\n\n if (!jobId) {\n return reply.status(400).send({ error: 'jobId is required' });\n }\n\n try {\n const success = await cancelAuditJob(jobId);\n if (!success) {\n return reply.status(404).send({ error: 'Job not found' });\n }\n return reply.send({ success: true });\n } catch (err) {\n return reply.status(500).send({ error: String(err) });\n }\n};\n\nexport const pauseRecursiveAudit = async (\n request: FastifyRequest<{ Params: { jobId: string } }>,\n reply: FastifyReply\n) => {\n const { jobId } = request.params;\n\n if (!jobId) {\n return reply.status(400).send({ error: 'jobId is required' });\n }\n\n try {\n const success = await pauseAuditJob(jobId);\n if (!success) {\n return reply.status(404).send({ error: 'Job not found' });\n }\n return reply.send({ success: true });\n } catch (err) {\n return reply.status(500).send({ error: String(err) });\n }\n};\n\nexport const resumeRecursiveAudit = async (\n request: FastifyRequest<{ Params: { jobId: string } }>,\n reply: FastifyReply\n) => {\n const { jobId } = request.params;\n\n if (!jobId) {\n return reply.status(400).send({ error: 'jobId is required' });\n }\n\n try {\n const success = await resumeAuditJob(jobId);\n if (!success) {\n return reply.status(404).send({ error: 'Job not found' });\n }\n return reply.send({ success: true });\n } catch (err) {\n return reply.status(500).send({ error: String(err) });\n }\n};\n"],"mappings":";;;AAkDA,MAAa,eAAe,OAC1B,SACA,UACG;CACH,MAAM,EAAE,QAAQ,QAAQ;
|
|
1
|
+
{"version":3,"file":"recursiveAudit.controller.mjs","names":[],"sources":["../../../src/controllers/recursiveAudit.controller.ts"],"sourcesContent":["import {\n cancelAuditJob,\n discoverUrlsFromSitemap,\n getAuditJobStatus,\n pauseAuditJob,\n resumeAuditJob,\n startRecursiveAuditJob,\n} from '@services/audit/recursiveAudit.service';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\n\nexport type StartRecursiveAuditResult = {\n jobId: string;\n};\n\nexport type DiscoverUrlsResult = {\n urls: string[];\n};\n\nexport type RecursiveAuditJobInfo = {\n _id: string;\n targetUrl: string;\n userId?: string;\n status:\n | 'pending'\n | 'running'\n | 'paused'\n | 'cancelled'\n | 'completed'\n | 'failed';\n progress: number;\n totalPageCount: number;\n completedPageCount: number;\n createdAt: Date;\n updatedAt: Date;\n};\n\nexport type RecursiveAuditPageInfo = {\n _id: string;\n url: string;\n status: 'pending' | 'running' | 'completed' | 'failed';\n score?: number;\n error?: string;\n results?: any[];\n};\n\nexport type GetRecursiveAuditStatusResult = {\n job: RecursiveAuditJobInfo;\n pages: RecursiveAuditPageInfo[];\n};\n\nexport const discoverUrls = async (\n request: FastifyRequest<{ Querystring: { url: string } }>,\n reply: FastifyReply\n) => {\n const { url } = request.query;\n\n if (!url || typeof url !== 'string') {\n return reply.status(400).send({ error: 'URL is required' });\n }\n\n try {\n new URL(url);\n } catch {\n return reply.status(400).send({ error: 'Invalid URL format' });\n }\n\n try {\n const urls = await discoverUrlsFromSitemap(url);\n return reply.send({ urls });\n } catch (err) {\n return reply.status(500).send({ error: String(err) });\n }\n};\n\nexport const startRecursiveAudit = async (\n request: FastifyRequest<{\n Querystring: { url: string };\n Body: { urls?: string[] };\n }>,\n reply: FastifyReply\n) => {\n const { url } = request.query;\n const { urls } = request.body ?? {};\n const userId = request.headers['x-user-id'] as string;\n\n if (!url || typeof url !== 'string') {\n return reply.status(400).send({ error: 'URL is required' });\n }\n\n try {\n const jobId = await startRecursiveAuditJob(url, userId, urls);\n return reply.send({ jobId });\n } catch (err) {\n return reply.status(500).send({ error: String(err) });\n }\n};\n\nexport const getRecursiveAuditStatus = async (\n request: FastifyRequest<{ Params: { jobId: string } }>,\n reply: FastifyReply\n) => {\n const { jobId } = request.params;\n\n if (!jobId) {\n return reply.status(400).send({ error: 'jobId is required' });\n }\n\n try {\n const status = await getAuditJobStatus(jobId);\n if (!status) {\n return reply.status(404).send({ error: 'Job not found' });\n }\n return reply.send(status);\n } catch (err) {\n return reply.status(500).send({ error: String(err) });\n }\n};\n\nexport const cancelRecursiveAudit = async (\n request: FastifyRequest<{ Params: { jobId: string } }>,\n reply: FastifyReply\n) => {\n const { jobId } = request.params;\n\n if (!jobId) {\n return reply.status(400).send({ error: 'jobId is required' });\n }\n\n try {\n const success = await cancelAuditJob(jobId);\n if (!success) {\n return reply.status(404).send({ error: 'Job not found' });\n }\n return reply.send({ success: true });\n } catch (err) {\n return reply.status(500).send({ error: String(err) });\n }\n};\n\nexport const pauseRecursiveAudit = async (\n request: FastifyRequest<{ Params: { jobId: string } }>,\n reply: FastifyReply\n) => {\n const { jobId } = request.params;\n\n if (!jobId) {\n return reply.status(400).send({ error: 'jobId is required' });\n }\n\n try {\n const success = await pauseAuditJob(jobId);\n if (!success) {\n return reply.status(404).send({ error: 'Job not found' });\n }\n return reply.send({ success: true });\n } catch (err) {\n return reply.status(500).send({ error: String(err) });\n }\n};\n\nexport const resumeRecursiveAudit = async (\n request: FastifyRequest<{ Params: { jobId: string } }>,\n reply: FastifyReply\n) => {\n const { jobId } = request.params;\n\n if (!jobId) {\n return reply.status(400).send({ error: 'jobId is required' });\n }\n\n try {\n const success = await resumeAuditJob(jobId);\n if (!success) {\n return reply.status(404).send({ error: 'Job not found' });\n }\n return reply.send({ success: true });\n } catch (err) {\n return reply.status(500).send({ error: String(err) });\n }\n};\n"],"mappings":"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"}
|