@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":"ai.controller.mjs","names":["customQueryUtil.aiDefaultOptions","customQueryUtil.customQuery","translateJSONUtil.aiDefaultOptions","translateJSONUtil.translateJSON","auditContentDeclarationUtil.aiDefaultOptions","auditContentDeclarationUtil.auditDictionary","auditContentDeclarationFieldUtil.aiDefaultOptions","auditContentDeclarationFieldUtil.auditDictionaryField","auditContentDeclarationMetadataUtil.aiDefaultOptions","tagService.findTags","auditContentDeclarationMetadataUtil.auditDictionaryMetadata","auditTagUtil.aiDefaultOptions","auditTagUtil.auditTag","askDocQuestionUtil.askDocQuestion","chatUtil.chat","autocompleteUtil.aiDefaultOptions","autocompleteUtil.autocomplete"],"sources":["../../../src/controllers/ai.controller.ts"],"sourcesContent":["import {\n type AIConfig,\n type AIOptions,\n type ChatCompletionRequestMessage,\n getAIConfig,\n} from '@intlayer/ai';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport { logger } from '@logger';\nimport { DiscussionModel } from '@schemas/discussion.schema';\nimport { getDictionariesByTags } from '@services/dictionary.service';\nimport * as tagService from '@services/tag.service';\nimport { getTagsByKeys } from '@services/tag.service';\nimport * as askDocQuestionUtil from '@utils/AI/askDocQuestion/askDocQuestion';\nimport * as auditContentDeclarationUtil from '@utils/AI/auditDictionary';\nimport * as auditContentDeclarationFieldUtil from '@utils/AI/auditDictionaryField';\nimport * as auditContentDeclarationMetadataUtil from '@utils/AI/auditDictionaryMetadata';\nimport * as auditTagUtil from '@utils/AI/auditTag';\nimport * as autocompleteUtil from '@utils/AI/autocomplete';\nimport * as chatUtil from '@utils/AI/chat';\nimport { createSessionTools } from '@utils/AI/chat/sessionTools';\nimport * as customQueryUtil from '@utils/AI/customQuery';\nimport { getProjectAIOptions } from '@utils/AI/getProjectAIOptions';\nimport * as translateJSONUtil from '@utils/AI/translateJSON';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport {\n type DiscussionFiltersParams,\n getDiscussionFiltersAndPagination,\n} from '@utils/filtersAndPagination/getDiscussionFiltersAndPagination';\nimport {\n formatPaginatedResponse,\n formatResponse,\n type PaginatedResponse,\n type ResponseData,\n} from '@utils/responseData';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { Dictionary } from '@/types/dictionary.types';\nimport type { DiscussionAPI } from '@/types/discussion.types';\nimport type { Tag, TagAPI } from '@/types/tag.types';\n\nexport type {\n AIConfig,\n AIOptions,\n AIProvider,\n ChatCompletionRequestMessage,\n} from '@intlayer/ai';\n\ntype ReplaceAIConfigByOptions<T> = Omit<T, 'aiConfig'> & {\n aiOptions?: AIOptions;\n};\n\nexport type CustomQueryBody =\n ReplaceAIConfigByOptions<customQueryUtil.CustomQueryOptions> & {\n tagsKeys?: string[];\n applicationContext?: string;\n };\nexport type CustomQueryResult =\n ResponseData<customQueryUtil.CustomQueryResultData>;\n\nexport const customQuery = async (\n request: FastifyRequest<{ Body: CustomQueryBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { aiOptions, tagsKeys, ...rest } = request.body;\n const { user, project } = request.session || {};\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: customQueryUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n const auditResponse = await customQueryUtil.customQuery({\n ...rest,\n aiConfig,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'QUERY_FAILED');\n }\n\n const responseData = formatResponse<customQueryUtil.CustomQueryResultData>({\n data: auditResponse,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type TranslateJSONBody = Omit<\n ReplaceAIConfigByOptions<translateJSONUtil.TranslateJSONOptions<JSON>>,\n 'tags'\n> & {\n tagsKeys?: string[];\n};\nexport type TranslateJSONResult = ResponseData<\n translateJSONUtil.TranslateJSONResultData<JSON>\n>;\n\nexport const translateJSON = async (\n request: FastifyRequest<{ Body: TranslateJSONBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { project, user } = request.session || {};\n const { aiOptions, tagsKeys, ...rest } = request.body;\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: translateJSONUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n let tags: Tag[] = [];\n\n if (project?.organizationId && tagsKeys) {\n tags = await getTagsByKeys(tagsKeys, project.organizationId);\n }\n\n const auditResponse = await translateJSONUtil.translateJSON<any>({\n ...rest,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n tags,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AUDIT_FAILED');\n }\n\n const responseData = formatResponse<\n translateJSONUtil.TranslateJSONResultData<any>\n >({\n data: auditResponse,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type AuditContentDeclarationBody = {\n aiOptions?: AIOptions;\n locales: Locale[];\n defaultLocale: Locale;\n fileContent: string;\n filePath?: string;\n tagsKeys?: string[];\n};\nexport type AuditContentDeclarationResult =\n ResponseData<auditContentDeclarationUtil.AuditFileResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditContentDeclaration = async (\n request: FastifyRequest<{ Body: AuditContentDeclarationBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { project, user } = request.session || {};\n const { fileContent, filePath, aiOptions, locales, defaultLocale, tagsKeys } =\n request.body;\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: auditContentDeclarationUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n let tags: Tag[] = [];\n\n if (project?.organizationId) {\n tags = await getTagsByKeys(tagsKeys ?? [], project.organizationId);\n }\n\n const auditResponse = await auditContentDeclarationUtil.auditDictionary({\n fileContent,\n filePath,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n locales,\n defaultLocale,\n tags,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AUDIT_FAILED');\n }\n\n const responseData =\n formatResponse<auditContentDeclarationUtil.AuditFileResultData>({\n data: auditResponse,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type AuditContentDeclarationFieldBody = {\n aiOptions?: AIOptions;\n locales: Locale[];\n fileContent: string;\n filePath?: string;\n tagsKeys?: string[];\n keyPath: KeyPath[];\n};\nexport type AuditContentDeclarationFieldResult =\n ResponseData<auditContentDeclarationFieldUtil.AuditDictionaryFieldResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditContentDeclarationField = async (\n request: FastifyRequest<{ Body: AuditContentDeclarationFieldBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { project, user } = request.session || {};\n const { fileContent, aiOptions, locales, tagsKeys, keyPath } = request.body;\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: auditContentDeclarationFieldUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n let tags: Tag[] = [];\n\n if (project?.organizationId) {\n tags = await getTagsByKeys(tagsKeys ?? [], project.organizationId);\n }\n\n const auditResponse =\n await auditContentDeclarationFieldUtil.auditDictionaryField({\n fileContent,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n locales,\n tags,\n keyPath,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AUDIT_FAILED');\n }\n\n const responseData =\n formatResponse<auditContentDeclarationFieldUtil.AuditDictionaryFieldResultData>(\n {\n data: auditResponse,\n }\n );\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type AuditContentDeclarationMetadataBody = {\n aiOptions?: AIOptions;\n fileContent: string;\n};\n\nexport type AuditContentDeclarationMetadataResult =\n ResponseData<auditContentDeclarationMetadataUtil.AuditFileResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditContentDeclarationMetadata = async (\n request: FastifyRequest<{ Body: AuditContentDeclarationMetadataBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { organization, user } = request.session || {};\n const { fileContent, aiOptions } = request.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n defaultOptions: auditContentDeclarationMetadataUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n const tags: Tag[] = await tagService.findTags(\n {\n organizationId: organization?.id,\n },\n 0,\n 1000\n );\n\n const auditResponse =\n await auditContentDeclarationMetadataUtil.auditDictionaryMetadata({\n fileContent,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n tags,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AUDIT_FAILED');\n }\n\n const responseData =\n formatResponse<auditContentDeclarationMetadataUtil.AuditFileResultData>({\n data: auditResponse,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type AuditTagBody = {\n aiOptions?: AIOptions;\n tag: TagAPI;\n};\nexport type AuditTagResult = ResponseData<auditTagUtil.TranslateJSONResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditTag = async (\n request: FastifyRequest<{ Body: AuditTagBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { project, user } = request.session || {};\n const { aiOptions, tag } = request.body;\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: auditTagUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n let dictionaries: Dictionary[] = [];\n if (project?.organizationId) {\n dictionaries = await getDictionariesByTags([tag.key], project.id);\n }\n\n const auditResponse = await auditTagUtil.auditTag({\n aiConfig,\n dictionaries,\n tag,\n applicationContext: aiOptions?.applicationContext,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AUDIT_FAILED');\n }\n\n const responseData = formatResponse<auditTagUtil.TranslateJSONResultData>({\n data: auditResponse,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type AskDocQuestionBody = {\n messages: ChatCompletionRequestMessage[];\n discussionId: string;\n};\nexport type AskDocQuestionResult =\n ResponseData<askDocQuestionUtil.AskDocQuestionResult>;\n\nexport const askDocQuestion = async (\n request: FastifyRequest<{ Body: AskDocQuestionBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { messages = [], discussionId } = request.body;\n const { user, project, organization } = request.session || {};\n\n // Hijack response\n reply.hijack();\n\n // Copy all Fastify-managed headers (including CORS) to the raw response\n // immediately after hijacking, before any early returns.\n const headers = reply.getHeaders();\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) {\n reply.raw.setHeader(key, value);\n }\n }\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n let aiConfig: AIConfig;\n\n // Wrap EVERYTHING in a main try/catch block\n // Auth Check\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: {\n temperature: 0.2,\n },\n projectOptions: projectAIOptions,\n accessType: ['public'],\n },\n !!user\n );\n } catch (error) {\n console.error(error);\n\n // Manually handle this specific error case\n const errorPayload = {\n code: 'AI_ACCESS_DENIED',\n title: 'Access Denied',\n message: 'Unable to configure AI access.',\n };\n reply.raw.write(`event: error\\ndata: ${JSON.stringify(errorPayload)}\\n\\n`);\n\n reply.raw.end();\n return;\n }\n\n console.log({ projectAIOptions, aiConfig });\n\n try {\n // Set Stream Headers & Flush\n reply.raw.setHeader('Content-Type', 'text/event-stream; charset=utf-8');\n reply.raw.setHeader('Cache-Control', 'no-cache, no-transform');\n reply.raw.setHeader('Connection', 'keep-alive');\n reply.raw.setHeader('X-Accel-Buffering', 'no');\n\n if (reply.raw.flushHeaders) {\n reply.raw.flushHeaders();\n }\n\n reply.raw.write(': connected\\n\\n');\n\n // Execute AI Logic (Awaited properly)\n // This is where 'generateEmbedding' or 'streamText' will throw\n const fullResponse = await askDocQuestionUtil.askDocQuestion(\n messages,\n aiConfig,\n {\n onMessage: (chunk) => {\n if (!reply.raw.writableEnded) {\n reply.raw.write(`data: ${JSON.stringify({ chunk })}\\n\\n`);\n }\n },\n }\n );\n\n // Persist Discussion (Only on success)\n const reversedMessages = [...messages].reverse();\n const lastUserMessageContent = reversedMessages.find(\n (message) => message.role === 'user'\n )?.content;\n const lastUserMessageNbWords =\n typeof lastUserMessageContent === 'string'\n ? lastUserMessageContent.split(' ').length\n : 0;\n\n if (lastUserMessageNbWords >= 2 || messages.length >= 2) {\n const updatePayload: any = {\n discussionId,\n type: 'doc',\n messages: [\n ...messages.map((msg) => ({\n role: msg.role,\n content: msg.content,\n timestamp: msg.timestamp ?? new Date(),\n })),\n {\n role: 'assistant',\n content: fullResponse.response,\n relatedFiles: fullResponse.relatedFiles,\n timestamp: new Date(),\n },\n ],\n };\n\n if (user?.id) updatePayload.userId = user.id;\n if (project?.id) updatePayload.projectId = project.id;\n if (organization?.id) updatePayload.organizationId = organization.id;\n\n await DiscussionModel.findOneAndUpdate(\n { discussionId: String(discussionId) },\n { $set: updatePayload },\n { upsert: true, returnDocument: 'after' }\n );\n }\n\n // Send Completion Event\n if (!reply.raw.writableEnded) {\n reply.raw.write(\n `data: ${JSON.stringify({ done: true, response: fullResponse })}\\n\\n`\n );\n reply.raw.end();\n }\n } catch (err) {\n // -------------------------------------------------------------------------\n // CENTRALIZED ERROR CATCHER\n // -------------------------------------------------------------------------\n const errorMessage = err instanceof Error ? err.message : String(err);\n const errorStack = err instanceof Error ? err.stack : undefined;\n\n // Log the full error to your backend console\n logger.error('AI Stream Error Caught:', {\n message: errorMessage,\n stack: errorStack,\n });\n\n // Determine if it's an Auth error (common with OpenAI 401)\n const isAuthError =\n errorMessage.includes('401') ||\n errorMessage.includes('Incorrect API key');\n\n // Format error for Frontend\n const errorPayload = {\n code: isAuthError ? 'AI_AUTH_ERROR' : 'AI_STREAM_ERROR',\n title: isAuthError ? 'AI Configuration Error' : 'Generation Failed',\n message: errorMessage,\n };\n\n // Send error event to client\n if (!reply.raw.writableEnded) {\n reply.raw.write(\n `event: error\\ndata: ${JSON.stringify(errorPayload)}\\n\\n`\n );\n reply.raw.end();\n }\n }\n};\n\nexport type ChatBody = {\n messages: ChatCompletionRequestMessage[];\n discussionId: string;\n};\nexport type ChatResult = ResponseData<chatUtil.ChatResultData>;\n\nexport const chat = async (\n request: FastifyRequest<{ Body: ChatBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { messages = [], discussionId } = request.body;\n const { user, project, organization, roles } = request.session || {};\n\n reply.hijack();\n\n const headers = reply.getHeaders();\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) {\n reply.raw.setHeader(key, value);\n }\n }\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n try {\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: {},\n projectOptions: projectAIOptions,\n accessType: ['registered_user'],\n },\n !!user\n );\n } catch (error) {\n console.error(error);\n\n const errorPayload = {\n code: 'AI_ACCESS_DENIED',\n title: 'Access Denied',\n message: 'Unable to configure AI access.',\n };\n reply.raw.write(\n `event: error\\ndata: ${JSON.stringify(errorPayload)}\\n\\n`\n );\n reply.raw.end();\n return;\n }\n\n reply.raw.setHeader('Content-Type', 'text/event-stream; charset=utf-8');\n reply.raw.setHeader('Cache-Control', 'no-cache, no-transform');\n reply.raw.setHeader('Connection', 'keep-alive');\n reply.raw.setHeader('X-Accel-Buffering', 'no');\n\n if (reply.raw.flushHeaders) {\n reply.raw.flushHeaders();\n }\n\n reply.raw.write(': connected\\n\\n');\n\n const sessionTools = createSessionTools({\n projectId: project?.id ? String(project.id) : undefined,\n organizationId: organization?.id ? String(organization.id) : undefined,\n userId: user?.id ? String(user.id) : undefined,\n roles: roles || [],\n session: request.session,\n onAction: (action) => {\n if (!reply.raw.writableEnded) {\n reply.raw.write(`data: ${JSON.stringify({ action })}\\n\\n`);\n }\n },\n });\n\n const fullResponse = await chatUtil.chat(messages, aiConfig, {\n tools: sessionTools,\n onMessage: (chunk) => {\n if (!reply.raw.writableEnded) {\n reply.raw.write(`data: ${JSON.stringify({ chunk })}\\n\\n`);\n }\n },\n });\n\n const reversedMessages = [...messages].reverse();\n const lastUserMessageContent = reversedMessages.find(\n (message) => message.role === 'user'\n )?.content;\n const lastUserMessageNbWords =\n typeof lastUserMessageContent === 'string'\n ? lastUserMessageContent.split(' ').length\n : 0;\n\n if (lastUserMessageNbWords >= 2 || messages.length >= 2) {\n const updatePayload: any = {\n discussionId,\n type: 'dashboard',\n messages: [\n ...messages.map((msg) => ({\n role: msg.role,\n content: msg.content,\n timestamp: msg.timestamp ?? new Date(),\n })),\n {\n role: 'assistant',\n content: fullResponse.response,\n timestamp: new Date(),\n },\n ],\n };\n\n if (user?.id) updatePayload.userId = user.id;\n if (project?.id) updatePayload.projectId = project.id;\n if (organization?.id) updatePayload.organizationId = organization.id;\n\n await DiscussionModel.findOneAndUpdate(\n { discussionId: String(discussionId) },\n { $set: updatePayload },\n { upsert: true, returnDocument: 'after' }\n );\n }\n\n if (!reply.raw.writableEnded) {\n reply.raw.write(\n `data: ${JSON.stringify({ done: true, response: fullResponse })}\\n\\n`\n );\n reply.raw.end();\n }\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n const errorStack = err instanceof Error ? err.stack : undefined;\n\n logger.error('AI Chat Stream Error:', {\n message: errorMessage,\n stack: errorStack,\n });\n\n const isAuthError =\n errorMessage.includes('401') ||\n errorMessage.includes('Incorrect API key');\n\n const errorPayload = {\n code: isAuthError ? 'AI_AUTH_ERROR' : 'AI_STREAM_ERROR',\n title: isAuthError ? 'AI Configuration Error' : 'Generation Failed',\n message: errorMessage,\n };\n\n if (!reply.raw.writableEnded) {\n reply.raw.write(\n `event: error\\ndata: ${JSON.stringify(errorPayload)}\\n\\n`\n );\n reply.raw.end();\n }\n }\n};\n\nexport type AutocompleteBody = {\n text: string;\n aiOptions?: AIOptions;\n contextBefore?: string;\n currentLine?: string;\n contextAfter?: string;\n};\n\nexport type AutocompleteResponse = ResponseData<{\n autocompletion: string;\n}>;\n\nexport const autocomplete = async (\n request: FastifyRequest<{ Body: AutocompleteBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, project } = request.session || {};\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n try {\n const { text, aiOptions, contextBefore, currentLine, contextAfter } =\n request.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: autocompleteUtil.aiDefaultOptions,\n accessType: ['public'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n const response = (await autocompleteUtil.autocomplete({\n text,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n contextBefore,\n currentLine,\n contextAfter,\n })) ?? {\n autocompletion: '',\n tokenUsed: 0,\n };\n\n const responseData =\n formatResponse<autocompleteUtil.AutocompleteFileResultData>({\n data: response,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type GetDiscussionsParams =\n | ({\n page?: string | number;\n pageSize?: string | number;\n includeMessages?: 'true' | 'false';\n } & DiscussionFiltersParams)\n | undefined;\n\nexport type GetDiscussionsResult = PaginatedResponse<DiscussionAPI>;\n\n/**\n * Retrieves a list of discussions with filters and pagination.\n * Only the owner or admins can access. By default, users only see their own.\n */\nexport const getDiscussions = async (\n request: FastifyRequest<{ Querystring: GetDiscussionsParams }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, roles } = request.session || {};\n const { filters, sortOptions, pageSize, skip, page, getNumberOfPages } =\n getDiscussionFiltersAndPagination(request);\n const includeMessagesParam = (request.query as any)?.includeMessages as\n | 'true'\n | 'false'\n | undefined;\n const includeMessages = includeMessagesParam !== 'false';\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n try {\n const projection = includeMessages ? {} : { messages: 0 };\n const discussions = await DiscussionModel.find(filters, projection)\n .sort(sortOptions)\n .skip(skip)\n .limit(pageSize)\n .lean();\n\n // Compute number of messages for each discussion\n const numberOfMessagesById: Record<string, number> = {};\n if (!includeMessages && discussions.length > 0) {\n const ids = discussions.map((d: any) => d._id);\n const counts = await DiscussionModel.aggregate([\n { $match: { _id: { $in: ids } } },\n {\n $project: {\n numberOfMessages: { $size: { $ifNull: ['$messages', []] } },\n },\n },\n ]);\n for (const c of counts as any[]) {\n numberOfMessagesById[String(c._id)] = c.numberOfMessages ?? 0;\n }\n }\n\n // Permission: allow admin, or the owner for all returned entries\n const allOwnedByUser = discussions.every(\n (d) => String(d.userId) === String(user.id)\n );\n const isAllowed = roles?.includes('admin') || allOwnedByUser;\n\n if (!isAllowed) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PERMISSION_DENIED'\n );\n }\n\n const totalItems = await DiscussionModel.countDocuments(filters);\n\n const responseData = formatPaginatedResponse({\n data: discussions.map((d: any) => ({\n ...d,\n id: String(d._id ?? d.id),\n numberOfMessages: includeMessages\n ? Array.isArray(d.messages)\n ? d.messages.length\n : 0\n : (numberOfMessagesById[String(d._id ?? d.id)] ?? 0),\n })),\n page,\n pageSize,\n totalPages: getNumberOfPages(totalItems),\n totalItems,\n });\n\n return reply.send(responseData as any);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ai.controller.mjs","names":["customQueryUtil.aiDefaultOptions","customQueryUtil.customQuery","translateJSONUtil.aiDefaultOptions","translateJSONUtil.translateJSON","auditContentDeclarationUtil.aiDefaultOptions","auditContentDeclarationUtil.auditDictionary","auditContentDeclarationFieldUtil.aiDefaultOptions","auditContentDeclarationFieldUtil.auditDictionaryField","auditContentDeclarationMetadataUtil.aiDefaultOptions","tagService.findTags","auditContentDeclarationMetadataUtil.auditDictionaryMetadata","auditTagUtil.aiDefaultOptions","auditTagUtil.auditTag","askDocQuestionUtil.askDocQuestion","chatUtil.chat","autocompleteUtil.aiDefaultOptions","autocompleteUtil.autocomplete"],"sources":["../../../src/controllers/ai.controller.ts"],"sourcesContent":["import {\n type AIConfig,\n type AIOptions,\n type ChatCompletionRequestMessage,\n getAIConfig,\n} from '@intlayer/ai';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport { logger } from '@logger';\nimport { DiscussionModel } from '@schemas/discussion.schema';\nimport { getDictionariesByTags } from '@services/dictionary.service';\nimport * as tagService from '@services/tag.service';\nimport { getTagsByKeys } from '@services/tag.service';\nimport * as askDocQuestionUtil from '@utils/AI/askDocQuestion/askDocQuestion';\nimport * as auditContentDeclarationUtil from '@utils/AI/auditDictionary';\nimport * as auditContentDeclarationFieldUtil from '@utils/AI/auditDictionaryField';\nimport * as auditContentDeclarationMetadataUtil from '@utils/AI/auditDictionaryMetadata';\nimport * as auditTagUtil from '@utils/AI/auditTag';\nimport * as autocompleteUtil from '@utils/AI/autocomplete';\nimport * as chatUtil from '@utils/AI/chat';\nimport { createSessionTools } from '@utils/AI/chat/sessionTools';\nimport * as customQueryUtil from '@utils/AI/customQuery';\nimport { getProjectAIOptions } from '@utils/AI/getProjectAIOptions';\nimport * as translateJSONUtil from '@utils/AI/translateJSON';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport {\n type DiscussionFiltersParams,\n getDiscussionFiltersAndPagination,\n} from '@utils/filtersAndPagination/getDiscussionFiltersAndPagination';\nimport {\n formatPaginatedResponse,\n formatResponse,\n type PaginatedResponse,\n type ResponseData,\n} from '@utils/responseData';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { Dictionary } from '@/types/dictionary.types';\nimport type { DiscussionAPI } from '@/types/discussion.types';\nimport type { Tag, TagAPI } from '@/types/tag.types';\n\nexport type {\n AIConfig,\n AIOptions,\n AIProvider,\n ChatCompletionRequestMessage,\n} from '@intlayer/ai';\n\ntype ReplaceAIConfigByOptions<T> = Omit<T, 'aiConfig'> & {\n aiOptions?: AIOptions;\n};\n\nexport type CustomQueryBody =\n ReplaceAIConfigByOptions<customQueryUtil.CustomQueryOptions> & {\n tagsKeys?: string[];\n applicationContext?: string;\n };\nexport type CustomQueryResult =\n ResponseData<customQueryUtil.CustomQueryResultData>;\n\nexport const customQuery = async (\n request: FastifyRequest<{ Body: CustomQueryBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { aiOptions, tagsKeys, ...rest } = request.body;\n const { user, project } = request.session || {};\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: customQueryUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n const auditResponse = await customQueryUtil.customQuery({\n ...rest,\n aiConfig,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'QUERY_FAILED');\n }\n\n const responseData = formatResponse<customQueryUtil.CustomQueryResultData>({\n data: auditResponse,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type TranslateJSONBody = Omit<\n ReplaceAIConfigByOptions<translateJSONUtil.TranslateJSONOptions<JSON>>,\n 'tags'\n> & {\n tagsKeys?: string[];\n};\nexport type TranslateJSONResult = ResponseData<\n translateJSONUtil.TranslateJSONResultData<JSON>\n>;\n\nexport const translateJSON = async (\n request: FastifyRequest<{ Body: TranslateJSONBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { project, user } = request.session || {};\n const { aiOptions, tagsKeys, ...rest } = request.body;\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: translateJSONUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n let tags: Tag[] = [];\n\n if (project?.organizationId && tagsKeys) {\n tags = await getTagsByKeys(tagsKeys, project.organizationId);\n }\n\n const auditResponse = await translateJSONUtil.translateJSON<any>({\n ...rest,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n tags,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AUDIT_FAILED');\n }\n\n const responseData = formatResponse<\n translateJSONUtil.TranslateJSONResultData<any>\n >({\n data: auditResponse,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type AuditContentDeclarationBody = {\n aiOptions?: AIOptions;\n locales: Locale[];\n defaultLocale: Locale;\n fileContent: string;\n filePath?: string;\n tagsKeys?: string[];\n};\nexport type AuditContentDeclarationResult =\n ResponseData<auditContentDeclarationUtil.AuditFileResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditContentDeclaration = async (\n request: FastifyRequest<{ Body: AuditContentDeclarationBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { project, user } = request.session || {};\n const { fileContent, filePath, aiOptions, locales, defaultLocale, tagsKeys } =\n request.body;\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: auditContentDeclarationUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n let tags: Tag[] = [];\n\n if (project?.organizationId) {\n tags = await getTagsByKeys(tagsKeys ?? [], project.organizationId);\n }\n\n const auditResponse = await auditContentDeclarationUtil.auditDictionary({\n fileContent,\n filePath,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n locales,\n defaultLocale,\n tags,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AUDIT_FAILED');\n }\n\n const responseData =\n formatResponse<auditContentDeclarationUtil.AuditFileResultData>({\n data: auditResponse,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type AuditContentDeclarationFieldBody = {\n aiOptions?: AIOptions;\n locales: Locale[];\n fileContent: string;\n filePath?: string;\n tagsKeys?: string[];\n keyPath: KeyPath[];\n};\nexport type AuditContentDeclarationFieldResult =\n ResponseData<auditContentDeclarationFieldUtil.AuditDictionaryFieldResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditContentDeclarationField = async (\n request: FastifyRequest<{ Body: AuditContentDeclarationFieldBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { project, user } = request.session || {};\n const { fileContent, aiOptions, locales, tagsKeys, keyPath } = request.body;\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: auditContentDeclarationFieldUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n let tags: Tag[] = [];\n\n if (project?.organizationId) {\n tags = await getTagsByKeys(tagsKeys ?? [], project.organizationId);\n }\n\n const auditResponse =\n await auditContentDeclarationFieldUtil.auditDictionaryField({\n fileContent,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n locales,\n tags,\n keyPath,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AUDIT_FAILED');\n }\n\n const responseData =\n formatResponse<auditContentDeclarationFieldUtil.AuditDictionaryFieldResultData>(\n {\n data: auditResponse,\n }\n );\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type AuditContentDeclarationMetadataBody = {\n aiOptions?: AIOptions;\n fileContent: string;\n};\n\nexport type AuditContentDeclarationMetadataResult =\n ResponseData<auditContentDeclarationMetadataUtil.AuditFileResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditContentDeclarationMetadata = async (\n request: FastifyRequest<{ Body: AuditContentDeclarationMetadataBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { organization, user } = request.session || {};\n const { fileContent, aiOptions } = request.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n defaultOptions: auditContentDeclarationMetadataUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n const tags: Tag[] = await tagService.findTags(\n {\n organizationId: organization?.id,\n },\n 0,\n 1000\n );\n\n const auditResponse =\n await auditContentDeclarationMetadataUtil.auditDictionaryMetadata({\n fileContent,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n tags,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AUDIT_FAILED');\n }\n\n const responseData =\n formatResponse<auditContentDeclarationMetadataUtil.AuditFileResultData>({\n data: auditResponse,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type AuditTagBody = {\n aiOptions?: AIOptions;\n tag: TagAPI;\n};\nexport type AuditTagResult = ResponseData<auditTagUtil.TranslateJSONResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditTag = async (\n request: FastifyRequest<{ Body: AuditTagBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { project, user } = request.session || {};\n const { aiOptions, tag } = request.body;\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: auditTagUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n let dictionaries: Dictionary[] = [];\n if (project?.organizationId) {\n dictionaries = await getDictionariesByTags([tag.key], project.id);\n }\n\n const auditResponse = await auditTagUtil.auditTag({\n aiConfig,\n dictionaries,\n tag,\n applicationContext: aiOptions?.applicationContext,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AUDIT_FAILED');\n }\n\n const responseData = formatResponse<auditTagUtil.TranslateJSONResultData>({\n data: auditResponse,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type AskDocQuestionBody = {\n messages: ChatCompletionRequestMessage[];\n discussionId: string;\n};\nexport type AskDocQuestionResult =\n ResponseData<askDocQuestionUtil.AskDocQuestionResult>;\n\nexport const askDocQuestion = async (\n request: FastifyRequest<{ Body: AskDocQuestionBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { messages = [], discussionId } = request.body;\n const { user, project, organization } = request.session || {};\n\n // Hijack response\n reply.hijack();\n\n // Copy all Fastify-managed headers (including CORS) to the raw response\n // immediately after hijacking, before any early returns.\n const headers = reply.getHeaders();\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) {\n reply.raw.setHeader(key, value);\n }\n }\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n let aiConfig: AIConfig;\n\n // Wrap EVERYTHING in a main try/catch block\n // Auth Check\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: {\n temperature: 0.2,\n },\n projectOptions: projectAIOptions,\n accessType: ['public'],\n },\n !!user\n );\n } catch (error) {\n console.error(error);\n\n // Manually handle this specific error case\n const errorPayload = {\n code: 'AI_ACCESS_DENIED',\n title: 'Access Denied',\n message: 'Unable to configure AI access.',\n };\n reply.raw.write(`event: error\\ndata: ${JSON.stringify(errorPayload)}\\n\\n`);\n\n reply.raw.end();\n return;\n }\n\n console.log({ projectAIOptions, aiConfig });\n\n try {\n // Set Stream Headers & Flush\n reply.raw.setHeader('Content-Type', 'text/event-stream; charset=utf-8');\n reply.raw.setHeader('Cache-Control', 'no-cache, no-transform');\n reply.raw.setHeader('Connection', 'keep-alive');\n reply.raw.setHeader('X-Accel-Buffering', 'no');\n\n if (reply.raw.flushHeaders) {\n reply.raw.flushHeaders();\n }\n\n reply.raw.write(': connected\\n\\n');\n\n // Execute AI Logic (Awaited properly)\n // This is where 'generateEmbedding' or 'streamText' will throw\n const fullResponse = await askDocQuestionUtil.askDocQuestion(\n messages,\n aiConfig,\n {\n onMessage: (chunk) => {\n if (!reply.raw.writableEnded) {\n reply.raw.write(`data: ${JSON.stringify({ chunk })}\\n\\n`);\n }\n },\n }\n );\n\n // Persist Discussion (Only on success)\n const reversedMessages = [...messages].reverse();\n const lastUserMessageContent = reversedMessages.find(\n (message) => message.role === 'user'\n )?.content;\n const lastUserMessageNbWords =\n typeof lastUserMessageContent === 'string'\n ? lastUserMessageContent.split(' ').length\n : 0;\n\n if (lastUserMessageNbWords >= 2 || messages.length >= 2) {\n const updatePayload: any = {\n discussionId,\n type: 'doc',\n messages: [\n ...messages.map((msg) => ({\n role: msg.role,\n content: msg.content,\n timestamp: msg.timestamp ?? new Date(),\n })),\n {\n role: 'assistant',\n content: fullResponse.response,\n relatedFiles: fullResponse.relatedFiles,\n timestamp: new Date(),\n },\n ],\n };\n\n if (user?.id) updatePayload.userId = user.id;\n if (project?.id) updatePayload.projectId = project.id;\n if (organization?.id) updatePayload.organizationId = organization.id;\n\n await DiscussionModel.findOneAndUpdate(\n { discussionId: String(discussionId) },\n { $set: updatePayload },\n { upsert: true, returnDocument: 'after' }\n );\n }\n\n // Send Completion Event\n if (!reply.raw.writableEnded) {\n reply.raw.write(\n `data: ${JSON.stringify({ done: true, response: fullResponse })}\\n\\n`\n );\n reply.raw.end();\n }\n } catch (err) {\n // -------------------------------------------------------------------------\n // CENTRALIZED ERROR CATCHER\n // -------------------------------------------------------------------------\n const errorMessage = err instanceof Error ? err.message : String(err);\n const errorStack = err instanceof Error ? err.stack : undefined;\n\n // Log the full error to your backend console\n logger.error('AI Stream Error Caught:', {\n message: errorMessage,\n stack: errorStack,\n });\n\n // Determine if it's an Auth error (common with OpenAI 401)\n const isAuthError =\n errorMessage.includes('401') ||\n errorMessage.includes('Incorrect API key');\n\n // Format error for Frontend\n const errorPayload = {\n code: isAuthError ? 'AI_AUTH_ERROR' : 'AI_STREAM_ERROR',\n title: isAuthError ? 'AI Configuration Error' : 'Generation Failed',\n message: errorMessage,\n };\n\n // Send error event to client\n if (!reply.raw.writableEnded) {\n reply.raw.write(\n `event: error\\ndata: ${JSON.stringify(errorPayload)}\\n\\n`\n );\n reply.raw.end();\n }\n }\n};\n\nexport type ChatBody = {\n messages: ChatCompletionRequestMessage[];\n discussionId: string;\n};\nexport type ChatResult = ResponseData<chatUtil.ChatResultData>;\n\nexport const chat = async (\n request: FastifyRequest<{ Body: ChatBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { messages = [], discussionId } = request.body;\n const { user, project, organization, roles } = request.session || {};\n\n reply.hijack();\n\n const headers = reply.getHeaders();\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) {\n reply.raw.setHeader(key, value);\n }\n }\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n try {\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: {},\n projectOptions: projectAIOptions,\n accessType: ['registered_user'],\n },\n !!user\n );\n } catch (error) {\n console.error(error);\n\n const errorPayload = {\n code: 'AI_ACCESS_DENIED',\n title: 'Access Denied',\n message: 'Unable to configure AI access.',\n };\n reply.raw.write(\n `event: error\\ndata: ${JSON.stringify(errorPayload)}\\n\\n`\n );\n reply.raw.end();\n return;\n }\n\n reply.raw.setHeader('Content-Type', 'text/event-stream; charset=utf-8');\n reply.raw.setHeader('Cache-Control', 'no-cache, no-transform');\n reply.raw.setHeader('Connection', 'keep-alive');\n reply.raw.setHeader('X-Accel-Buffering', 'no');\n\n if (reply.raw.flushHeaders) {\n reply.raw.flushHeaders();\n }\n\n reply.raw.write(': connected\\n\\n');\n\n const sessionTools = createSessionTools({\n projectId: project?.id ? String(project.id) : undefined,\n organizationId: organization?.id ? String(organization.id) : undefined,\n userId: user?.id ? String(user.id) : undefined,\n roles: roles || [],\n session: request.session,\n onAction: (action) => {\n if (!reply.raw.writableEnded) {\n reply.raw.write(`data: ${JSON.stringify({ action })}\\n\\n`);\n }\n },\n });\n\n const fullResponse = await chatUtil.chat(messages, aiConfig, {\n tools: sessionTools,\n onMessage: (chunk) => {\n if (!reply.raw.writableEnded) {\n reply.raw.write(`data: ${JSON.stringify({ chunk })}\\n\\n`);\n }\n },\n });\n\n const reversedMessages = [...messages].reverse();\n const lastUserMessageContent = reversedMessages.find(\n (message) => message.role === 'user'\n )?.content;\n const lastUserMessageNbWords =\n typeof lastUserMessageContent === 'string'\n ? lastUserMessageContent.split(' ').length\n : 0;\n\n if (lastUserMessageNbWords >= 2 || messages.length >= 2) {\n const updatePayload: any = {\n discussionId,\n type: 'dashboard',\n messages: [\n ...messages.map((msg) => ({\n role: msg.role,\n content: msg.content,\n timestamp: msg.timestamp ?? new Date(),\n })),\n {\n role: 'assistant',\n content: fullResponse.response,\n timestamp: new Date(),\n },\n ],\n };\n\n if (user?.id) updatePayload.userId = user.id;\n if (project?.id) updatePayload.projectId = project.id;\n if (organization?.id) updatePayload.organizationId = organization.id;\n\n await DiscussionModel.findOneAndUpdate(\n { discussionId: String(discussionId) },\n { $set: updatePayload },\n { upsert: true, returnDocument: 'after' }\n );\n }\n\n if (!reply.raw.writableEnded) {\n reply.raw.write(\n `data: ${JSON.stringify({ done: true, response: fullResponse })}\\n\\n`\n );\n reply.raw.end();\n }\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n const errorStack = err instanceof Error ? err.stack : undefined;\n\n logger.error('AI Chat Stream Error:', {\n message: errorMessage,\n stack: errorStack,\n });\n\n const isAuthError =\n errorMessage.includes('401') ||\n errorMessage.includes('Incorrect API key');\n\n const errorPayload = {\n code: isAuthError ? 'AI_AUTH_ERROR' : 'AI_STREAM_ERROR',\n title: isAuthError ? 'AI Configuration Error' : 'Generation Failed',\n message: errorMessage,\n };\n\n if (!reply.raw.writableEnded) {\n reply.raw.write(\n `event: error\\ndata: ${JSON.stringify(errorPayload)}\\n\\n`\n );\n reply.raw.end();\n }\n }\n};\n\nexport type AutocompleteBody = {\n text: string;\n aiOptions?: AIOptions;\n contextBefore?: string;\n currentLine?: string;\n contextAfter?: string;\n};\n\nexport type AutocompleteResponse = ResponseData<{\n autocompletion: string;\n}>;\n\nexport const autocomplete = async (\n request: FastifyRequest<{ Body: AutocompleteBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, project } = request.session || {};\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n try {\n const { text, aiOptions, contextBefore, currentLine, contextAfter } =\n request.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: autocompleteUtil.aiDefaultOptions,\n accessType: ['public'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n const response = (await autocompleteUtil.autocomplete({\n text,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n contextBefore,\n currentLine,\n contextAfter,\n })) ?? {\n autocompletion: '',\n tokenUsed: 0,\n };\n\n const responseData =\n formatResponse<autocompleteUtil.AutocompleteFileResultData>({\n data: response,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type GetDiscussionsParams =\n | ({\n page?: string | number;\n pageSize?: string | number;\n includeMessages?: 'true' | 'false';\n } & DiscussionFiltersParams)\n | undefined;\n\nexport type GetDiscussionsResult = PaginatedResponse<DiscussionAPI>;\n\n/**\n * Retrieves a list of discussions with filters and pagination.\n * Only the owner or admins can access. By default, users only see their own.\n */\nexport const getDiscussions = async (\n request: FastifyRequest<{ Querystring: GetDiscussionsParams }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, roles } = request.session || {};\n const { filters, sortOptions, pageSize, skip, page, getNumberOfPages } =\n getDiscussionFiltersAndPagination(request);\n const includeMessagesParam = (request.query as any)?.includeMessages as\n | 'true'\n | 'false'\n | undefined;\n const includeMessages = includeMessagesParam !== 'false';\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n try {\n const projection = includeMessages ? {} : { messages: 0 };\n const discussions = await DiscussionModel.find(filters, projection)\n .sort(sortOptions)\n .skip(skip)\n .limit(pageSize)\n .lean();\n\n // Compute number of messages for each discussion\n const numberOfMessagesById: Record<string, number> = {};\n if (!includeMessages && discussions.length > 0) {\n const ids = discussions.map((d: any) => d._id);\n const counts = await DiscussionModel.aggregate([\n { $match: { _id: { $in: ids } } },\n {\n $project: {\n numberOfMessages: { $size: { $ifNull: ['$messages', []] } },\n },\n },\n ]);\n for (const c of counts as any[]) {\n numberOfMessagesById[String(c._id)] = c.numberOfMessages ?? 0;\n }\n }\n\n // Permission: allow admin, or the owner for all returned entries\n const allOwnedByUser = discussions.every(\n (d) => String(d.userId) === String(user.id)\n );\n const isAllowed = roles?.includes('admin') || allOwnedByUser;\n\n if (!isAllowed) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PERMISSION_DENIED'\n );\n }\n\n const totalItems = await DiscussionModel.countDocuments(filters);\n\n const responseData = formatPaginatedResponse({\n data: discussions.map((d: any) => ({\n ...d,\n id: String(d._id ?? d.id),\n numberOfMessages: includeMessages\n ? Array.isArray(d.messages)\n ? d.messages.length\n : 0\n : (numberOfMessagesById[String(d._id ?? d.id)] ?? 0),\n })),\n page,\n pageSize,\n totalPages: getNumberOfPages(totalItems),\n totalItems,\n });\n\n return reply.send(responseData as any);\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":"audit.controller.mjs","names":["dnsLookup"],"sources":["../../../src/controllers/audit.controller.ts"],"sourcesContent":["import { lookup as dnsLookup } from 'node:dns/promises';\nimport net from 'node:net';\nimport { logger } from '@logger';\nimport { AuditModel } from '@schemas/audit.schema';\nimport {\n mutateScore,\n type Score,\n} from '@services/audit/analysis/calculateScore';\nimport { runSingleAudit } from '@services/audit/seoAudit.service';\nimport type { AuditEvent } from '@services/audit/types';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\n\n/**\n * Returns true if the given IP address is private, loopback, or link-local.\n * Covers IPv4 and IPv6.\n */\nconst isPrivateOrReservedIp = (ip: string): boolean => {\n if (net.isIPv4(ip)) {\n const parts = ip.split('.').map(Number);\n const [a, b] = parts;\n // Loopback: 127.0.0.0/8\n if (a === 127) return true;\n // Any address starting with 0\n if (a === 0) return true;\n // Private: 10.0.0.0/8\n if (a === 10) return true;\n // Private: 172.16.0.0/12\n if (a === 172 && b >= 16 && b <= 31) return true;\n // Private: 192.168.0.0/16\n if (a === 192 && b === 168) return true;\n // Link-local: 169.254.0.0/16\n if (a === 169 && b === 254) return true;\n return false;\n }\n\n if (net.isIPv6(ip)) {\n const normalized = ip.toLowerCase();\n // Loopback: ::1\n if (normalized === '::1') return true;\n // Link-local: fe80::/10\n if (\n normalized.startsWith('fe80:') ||\n normalized.startsWith('fe8') ||\n normalized.startsWith('fe9') ||\n normalized.startsWith('fea') ||\n normalized.startsWith('feb')\n )\n return true;\n // IPv4-mapped: ::ffff:x.x.x.x — check the embedded IPv4\n const ipv4MappedMatch = normalized.match(/^::ffff:(\\d+\\.\\d+\\.\\d+\\.\\d+)$/);\n if (ipv4MappedMatch) return isPrivateOrReservedIp(ipv4MappedMatch[1]);\n return false;\n }\n\n // Unknown format — block it\n return true;\n};\n\nconst sendSSE = (res: FastifyReply, data: AuditEvent) => {\n res.raw.write(`data: ${JSON.stringify(data)}\\n\\n`);\n};\n\n/**\n * GET /api/scan?url=<targetUrl>\n * Streams audit results as Server-Sent Events.\n */\nexport const auditGetHandler = async (\n req: FastifyRequest,\n res: FastifyReply\n) => {\n res.hijack();\n\n const headers = res.getHeaders();\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) {\n res.raw.setHeader(key, value as string | number | readonly string[]);\n }\n }\n\n res.raw.setHeader('Content-Type', 'text/event-stream; charset=utf-8');\n res.raw.setHeader('Cache-Control', 'no-cache, no-transform');\n res.raw.setHeader('Connection', 'keep-alive');\n res.raw.setHeader('X-Accel-Buffering', 'no');\n\n if ((res.raw as any).flushHeaders) {\n (res.raw as any).flushHeaders();\n }\n\n res.raw.write(': connected\\n\\n');\n\n const { url: targetUrl } = req.query as { url?: string };\n\n let score: Score = { score: 0, totalScore: 0 };\n let currentProgress = 0;\n\n if (!targetUrl) {\n sendSSE(res, { status: 'error', globalError: 'Missing URL parameter' });\n res.raw.end();\n return;\n }\n\n let parsedUrl: URL;\n try {\n parsedUrl = new URL(targetUrl);\n } catch {\n sendSSE(res, { status: 'error', globalError: 'Invalid URL format' });\n res.raw.end();\n return;\n }\n\n // Only allow http: and https: — block file://, data:, ftp://, etc.\n if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {\n sendSSE(res, {\n status: 'error',\n globalError: 'Only http and https URLs are allowed',\n });\n res.raw.end();\n return;\n }\n\n // Resolve the hostname and block private / loopback / link-local addresses (SSRF prevention)\n try {\n const { address } = await dnsLookup(parsedUrl.hostname);\n if (isPrivateOrReservedIp(address)) {\n sendSSE(res, {\n status: 'error',\n globalError: 'URL resolves to a private or reserved address',\n });\n res.raw.end();\n return;\n }\n } catch {\n sendSSE(res, {\n status: 'error',\n globalError: 'Could not resolve hostname',\n });\n res.raw.end();\n return;\n }\n\n let aborted = false;\n\n req.raw.on('close', () => {\n logger.info('Client connection closed');\n aborted = true;\n });\n\n try {\n await runSingleAudit(targetUrl, (event) => {\n score = mutateScore(score, event);\n\n if (event.progress !== undefined) {\n currentProgress = event.progress;\n }\n\n if (!aborted) {\n sendSSE(res, {\n ...event,\n progress: currentProgress,\n score: Math.round(\n score.totalScore > 0 ? (score.score / score.totalScore) * 100 : 0\n ),\n });\n }\n });\n\n try {\n const url = new URL(targetUrl);\n const domain = url.hostname;\n const finalScore = Math.round(\n score.totalScore > 0 ? (score.score / score.totalScore) * 100 : 0\n );\n\n const audit = new AuditModel({ domain, score: finalScore });\n await audit.save();\n logger.info(\n `Audit saved for domain: ${domain} with score: ${finalScore}`\n );\n } catch (dbError) {\n logger.error('Failed to save audit to database:', dbError);\n }\n\n res.raw.end();\n } catch (error) {\n logger.error('Audit GET error:', error);\n if (!aborted) {\n sendSSE(res, {\n globalError:\n error instanceof Error ? error.message : 'Internal server error',\n });\n res.raw.end();\n }\n }\n};\n"],"mappings":";;;;;;;;;;;;AAgBA,MAAM,yBAAyB,OAAwB;
|
|
1
|
+
{"version":3,"file":"audit.controller.mjs","names":["dnsLookup"],"sources":["../../../src/controllers/audit.controller.ts"],"sourcesContent":["import { lookup as dnsLookup } from 'node:dns/promises';\nimport net from 'node:net';\nimport { logger } from '@logger';\nimport { AuditModel } from '@schemas/audit.schema';\nimport {\n mutateScore,\n type Score,\n} from '@services/audit/analysis/calculateScore';\nimport { runSingleAudit } from '@services/audit/seoAudit.service';\nimport type { AuditEvent } from '@services/audit/types';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\n\n/**\n * Returns true if the given IP address is private, loopback, or link-local.\n * Covers IPv4 and IPv6.\n */\nconst isPrivateOrReservedIp = (ip: string): boolean => {\n if (net.isIPv4(ip)) {\n const parts = ip.split('.').map(Number);\n const [a, b] = parts;\n // Loopback: 127.0.0.0/8\n if (a === 127) return true;\n // Any address starting with 0\n if (a === 0) return true;\n // Private: 10.0.0.0/8\n if (a === 10) return true;\n // Private: 172.16.0.0/12\n if (a === 172 && b >= 16 && b <= 31) return true;\n // Private: 192.168.0.0/16\n if (a === 192 && b === 168) return true;\n // Link-local: 169.254.0.0/16\n if (a === 169 && b === 254) return true;\n return false;\n }\n\n if (net.isIPv6(ip)) {\n const normalized = ip.toLowerCase();\n // Loopback: ::1\n if (normalized === '::1') return true;\n // Link-local: fe80::/10\n if (\n normalized.startsWith('fe80:') ||\n normalized.startsWith('fe8') ||\n normalized.startsWith('fe9') ||\n normalized.startsWith('fea') ||\n normalized.startsWith('feb')\n )\n return true;\n // IPv4-mapped: ::ffff:x.x.x.x — check the embedded IPv4\n const ipv4MappedMatch = normalized.match(/^::ffff:(\\d+\\.\\d+\\.\\d+\\.\\d+)$/);\n if (ipv4MappedMatch) return isPrivateOrReservedIp(ipv4MappedMatch[1]);\n return false;\n }\n\n // Unknown format — block it\n return true;\n};\n\nconst sendSSE = (res: FastifyReply, data: AuditEvent) => {\n res.raw.write(`data: ${JSON.stringify(data)}\\n\\n`);\n};\n\n/**\n * GET /api/scan?url=<targetUrl>\n * Streams audit results as Server-Sent Events.\n */\nexport const auditGetHandler = async (\n req: FastifyRequest,\n res: FastifyReply\n) => {\n res.hijack();\n\n const headers = res.getHeaders();\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) {\n res.raw.setHeader(key, value as string | number | readonly string[]);\n }\n }\n\n res.raw.setHeader('Content-Type', 'text/event-stream; charset=utf-8');\n res.raw.setHeader('Cache-Control', 'no-cache, no-transform');\n res.raw.setHeader('Connection', 'keep-alive');\n res.raw.setHeader('X-Accel-Buffering', 'no');\n\n if ((res.raw as any).flushHeaders) {\n (res.raw as any).flushHeaders();\n }\n\n res.raw.write(': connected\\n\\n');\n\n const { url: targetUrl } = req.query as { url?: string };\n\n let score: Score = { score: 0, totalScore: 0 };\n let currentProgress = 0;\n\n if (!targetUrl) {\n sendSSE(res, { status: 'error', globalError: 'Missing URL parameter' });\n res.raw.end();\n return;\n }\n\n let parsedUrl: URL;\n try {\n parsedUrl = new URL(targetUrl);\n } catch {\n sendSSE(res, { status: 'error', globalError: 'Invalid URL format' });\n res.raw.end();\n return;\n }\n\n // Only allow http: and https: — block file://, data:, ftp://, etc.\n if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {\n sendSSE(res, {\n status: 'error',\n globalError: 'Only http and https URLs are allowed',\n });\n res.raw.end();\n return;\n }\n\n // Resolve the hostname and block private / loopback / link-local addresses (SSRF prevention)\n try {\n const { address } = await dnsLookup(parsedUrl.hostname);\n if (isPrivateOrReservedIp(address)) {\n sendSSE(res, {\n status: 'error',\n globalError: 'URL resolves to a private or reserved address',\n });\n res.raw.end();\n return;\n }\n } catch {\n sendSSE(res, {\n status: 'error',\n globalError: 'Could not resolve hostname',\n });\n res.raw.end();\n return;\n }\n\n let aborted = false;\n\n req.raw.on('close', () => {\n logger.info('Client connection closed');\n aborted = true;\n });\n\n try {\n await runSingleAudit(targetUrl, (event) => {\n score = mutateScore(score, event);\n\n if (event.progress !== undefined) {\n currentProgress = event.progress;\n }\n\n if (!aborted) {\n sendSSE(res, {\n ...event,\n progress: currentProgress,\n score: Math.round(\n score.totalScore > 0 ? (score.score / score.totalScore) * 100 : 0\n ),\n });\n }\n });\n\n try {\n const url = new URL(targetUrl);\n const domain = url.hostname;\n const finalScore = Math.round(\n score.totalScore > 0 ? (score.score / score.totalScore) * 100 : 0\n );\n\n const audit = new AuditModel({ domain, score: finalScore });\n await audit.save();\n logger.info(\n `Audit saved for domain: ${domain} with score: ${finalScore}`\n );\n } catch (dbError) {\n logger.error('Failed to save audit to database:', dbError);\n }\n\n res.raw.end();\n } catch (error) {\n logger.error('Audit GET error:', error);\n if (!aborted) {\n sendSSE(res, {\n globalError:\n error instanceof Error ? error.message : 'Internal server error',\n });\n res.raw.end();\n }\n }\n};\n"],"mappings":";;;;;;;;;;;;AAgBA,MAAM,yBAAyB,OAAwB;AACrD,KAAI,IAAI,OAAO,GAAG,EAAE;EAElB,MAAM,CAAC,GAAG,KADI,GAAG,MAAM,IAAI,CAAC,IAAI,OACZ;AAEpB,MAAI,MAAM,IAAK,QAAO;AAEtB,MAAI,MAAM,EAAG,QAAO;AAEpB,MAAI,MAAM,GAAI,QAAO;AAErB,MAAI,MAAM,OAAO,KAAK,MAAM,KAAK,GAAI,QAAO;AAE5C,MAAI,MAAM,OAAO,MAAM,IAAK,QAAO;AAEnC,MAAI,MAAM,OAAO,MAAM,IAAK,QAAO;AACnC,SAAO;;AAGT,KAAI,IAAI,OAAO,GAAG,EAAE;EAClB,MAAM,aAAa,GAAG,aAAa;AAEnC,MAAI,eAAe,MAAO,QAAO;AAEjC,MACE,WAAW,WAAW,QAAQ,IAC9B,WAAW,WAAW,MAAM,IAC5B,WAAW,WAAW,MAAM,IAC5B,WAAW,WAAW,MAAM,IAC5B,WAAW,WAAW,MAAM,CAE5B,QAAO;EAET,MAAM,kBAAkB,WAAW,MAAM,gCAAgC;AACzE,MAAI,gBAAiB,QAAO,sBAAsB,gBAAgB,GAAG;AACrE,SAAO;;AAIT,QAAO;;AAGT,MAAM,WAAW,KAAmB,SAAqB;AACvD,KAAI,IAAI,MAAM,SAAS,KAAK,UAAU,KAAK,CAAC,MAAM;;;;;;AAOpD,MAAa,kBAAkB,OAC7B,KACA,QACG;AACH,KAAI,QAAQ;CAEZ,MAAM,UAAU,IAAI,YAAY;AAChC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,CAChD,KAAI,UAAU,OACZ,KAAI,IAAI,UAAU,KAAK,MAA6C;AAIxE,KAAI,IAAI,UAAU,gBAAgB,mCAAmC;AACrE,KAAI,IAAI,UAAU,iBAAiB,yBAAyB;AAC5D,KAAI,IAAI,UAAU,cAAc,aAAa;AAC7C,KAAI,IAAI,UAAU,qBAAqB,KAAK;AAE5C,KAAK,IAAI,IAAY,aACnB,CAAC,IAAI,IAAY,cAAc;AAGjC,KAAI,IAAI,MAAM,kBAAkB;CAEhC,MAAM,EAAE,KAAK,cAAc,IAAI;CAE/B,IAAI,QAAe;EAAE,OAAO;EAAG,YAAY;EAAG;CAC9C,IAAI,kBAAkB;AAEtB,KAAI,CAAC,WAAW;AACd,UAAQ,KAAK;GAAE,QAAQ;GAAS,aAAa;GAAyB,CAAC;AACvE,MAAI,IAAI,KAAK;AACb;;CAGF,IAAI;AACJ,KAAI;AACF,cAAY,IAAI,IAAI,UAAU;SACxB;AACN,UAAQ,KAAK;GAAE,QAAQ;GAAS,aAAa;GAAsB,CAAC;AACpE,MAAI,IAAI,KAAK;AACb;;AAIF,KAAI,UAAU,aAAa,WAAW,UAAU,aAAa,UAAU;AACrE,UAAQ,KAAK;GACX,QAAQ;GACR,aAAa;GACd,CAAC;AACF,MAAI,IAAI,KAAK;AACb;;AAIF,KAAI;EACF,MAAM,EAAE,YAAY,MAAMA,OAAU,UAAU,SAAS;AACvD,MAAI,sBAAsB,QAAQ,EAAE;AAClC,WAAQ,KAAK;IACX,QAAQ;IACR,aAAa;IACd,CAAC;AACF,OAAI,IAAI,KAAK;AACb;;SAEI;AACN,UAAQ,KAAK;GACX,QAAQ;GACR,aAAa;GACd,CAAC;AACF,MAAI,IAAI,KAAK;AACb;;CAGF,IAAI,UAAU;AAEd,KAAI,IAAI,GAAG,eAAe;AACxB,SAAO,KAAK,2BAA2B;AACvC,YAAU;GACV;AAEF,KAAI;AACF,QAAM,eAAe,YAAY,UAAU;AACzC,WAAQ,YAAY,OAAO,MAAM;AAEjC,OAAI,MAAM,aAAa,OACrB,mBAAkB,MAAM;AAG1B,OAAI,CAAC,QACH,SAAQ,KAAK;IACX,GAAG;IACH,UAAU;IACV,OAAO,KAAK,MACV,MAAM,aAAa,IAAK,MAAM,QAAQ,MAAM,aAAc,MAAM,EACjE;IACF,CAAC;IAEJ;AAEF,MAAI;GAEF,MAAM,SAAS,IADC,IAAI,UACF,CAAC;GACnB,MAAM,aAAa,KAAK,MACtB,MAAM,aAAa,IAAK,MAAM,QAAQ,MAAM,aAAc,MAAM,EACjE;AAGD,SAAM,IADY,WAAW;IAAE;IAAQ,OAAO;IAAY,CAC/C,CAAC,MAAM;AAClB,UAAO,KACL,2BAA2B,OAAO,eAAe,aAClD;WACM,SAAS;AAChB,UAAO,MAAM,qCAAqC,QAAQ;;AAG5D,MAAI,IAAI,KAAK;UACN,OAAO;AACd,SAAO,MAAM,oBAAoB,MAAM;AACvC,MAAI,CAAC,SAAS;AACZ,WAAQ,KAAK,EACX,aACE,iBAAiB,QAAQ,MAAM,UAAU,yBAC5C,CAAC;AACF,OAAI,IAAI,KAAK"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bitbucket.controller.mjs","names":["bitbucketService.getAuthorizationUrl","bitbucketService.exchangeCodeForToken","bitbucketService.getUserRepositories","bitbucketService.checkIntlayerConfig","bitbucketService.getRepositoryFileContents"],"sources":["../../../src/controllers/bitbucket.controller.ts"],"sourcesContent":["import * as bitbucketService from '@services/bitbucket.service';\nimport { getBitbucketTokenFromUser } from '@services/bitbucket.service';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport { formatResponse, type ResponseData } from '@utils/responseData';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\n\nexport type BitbucketGetAuthUrlQuerystring = {\n redirectUri: string;\n};\n\nexport type BitbucketGetAuthUrlResult = ResponseData<{\n authUrl: string;\n}>;\n\nexport const getAuthUrl = async (\n request: FastifyRequest<{ Querystring: BitbucketGetAuthUrlQuerystring }>,\n reply: FastifyReply\n): Promise<void> => {\n const { redirectUri } = request.query;\n\n if (!redirectUri) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'BITBUCKET_REDIRECT_URI_MISSING'\n );\n }\n\n try {\n const authUrl = bitbucketService.getAuthorizationUrl(redirectUri);\n const responseData = formatResponse<{ authUrl: string }>({\n data: { authUrl },\n });\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type BitbucketAuthCallbackBody = {\n code: string;\n};\n\nexport type BitbucketAuthCallbackResult = ResponseData<{\n token: string;\n}>;\n\nexport const authCallback = async (\n request: FastifyRequest<{ Body: BitbucketAuthCallbackBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { code } = request.body;\n\n if (!code) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'BITBUCKET_CODE_MISSING'\n );\n }\n\n try {\n const token = await bitbucketService.exchangeCodeForToken(code);\n const responseData = formatResponse<{ token: string }>({\n data: { token },\n });\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type BitbucketListReposQuerystring = {\n token?: string;\n};\n\nexport type BitbucketListReposResult = ResponseData<\n bitbucketService.BitbucketRepository[]\n>;\n\nexport const listRepos = async (\n request: FastifyRequest<{ Querystring: BitbucketListReposQuerystring }>,\n reply: FastifyReply\n): Promise<void> => {\n const { token } = request.query;\n const userId = request.session?.user?.id;\n\n try {\n let accessToken: string | undefined = token;\n\n if (!accessToken && userId) {\n accessToken =\n (await getBitbucketTokenFromUser(String(userId))) ?? undefined;\n }\n\n if (!accessToken) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'BITBUCKET_TOKEN_MISSING'\n );\n }\n\n const repos = await bitbucketService.getUserRepositories(accessToken);\n const responseData = formatResponse<bitbucketService.BitbucketRepository[]>(\n {\n data: repos,\n }\n );\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type BitbucketCheckConfigBody = {\n token?: string;\n workspace: string;\n repoSlug: string;\n branch?: string;\n};\n\nexport type BitbucketCheckConfigResult = ResponseData<{\n hasConfig: boolean;\n configPaths: string[];\n}>;\n\n/**\n * Check if intlayer.config.ts (or candidates) exists in a Bitbucket repository\n */\nexport const checkConfig = async (\n request: FastifyRequest<{ Body: BitbucketCheckConfigBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { token, workspace, repoSlug, branch = 'main' } = request.body;\n const userId = request.session?.user?.id;\n\n try {\n let accessToken: string | undefined = token;\n\n if (!accessToken && userId) {\n accessToken =\n (await getBitbucketTokenFromUser(String(userId))) ?? undefined;\n }\n\n if (!accessToken || !workspace || !repoSlug) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'BITBUCKET_CHECK_CONFIG_MISSING_PARAMS'\n );\n }\n\n const configPaths = await bitbucketService.checkIntlayerConfig(\n accessToken,\n workspace,\n repoSlug,\n branch\n );\n\n const responseData = formatResponse<{\n hasConfig: boolean;\n configPaths: string[];\n }>({\n data: {\n hasConfig: configPaths.length > 0,\n configPaths: configPaths,\n },\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type BitbucketGetConfigFileBody = {\n token?: string;\n workspace: string;\n repoSlug: string;\n branch?: string;\n path?: string;\n};\n\nexport type BitbucketGetConfigFileResult = ResponseData<{\n content: string;\n}>;\n\nexport const getConfigFile = async (\n request: FastifyRequest<{ Body: BitbucketGetConfigFileBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const {\n token,\n workspace,\n repoSlug,\n branch = 'main',\n path = 'intlayer.config.ts',\n } = request.body;\n const userId = request.session?.user?.id;\n\n try {\n let accessToken: string | undefined = token;\n\n if (!accessToken && userId) {\n accessToken =\n (await getBitbucketTokenFromUser(String(userId))) ?? undefined;\n }\n\n if (!accessToken || !workspace || !repoSlug) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'BITBUCKET_GET_CONFIG_FILE_MISSING_PARAMS'\n );\n }\n\n const content = await bitbucketService.getRepositoryFileContents(\n accessToken,\n workspace,\n repoSlug,\n path,\n branch\n );\n\n if (!content) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'BITBUCKET_CONFIG_FILE_NOT_FOUND'\n );\n }\n\n const responseData = formatResponse<{ content: string }>({\n data: { content },\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n"],"mappings":";;;;;AAcA,MAAa,aAAa,OACxB,SACA,UACkB;CAClB,MAAM,EAAE,gBAAgB,QAAQ;
|
|
1
|
+
{"version":3,"file":"bitbucket.controller.mjs","names":["bitbucketService.getAuthorizationUrl","bitbucketService.exchangeCodeForToken","bitbucketService.getUserRepositories","bitbucketService.checkIntlayerConfig","bitbucketService.getRepositoryFileContents"],"sources":["../../../src/controllers/bitbucket.controller.ts"],"sourcesContent":["import * as bitbucketService from '@services/bitbucket.service';\nimport { getBitbucketTokenFromUser } from '@services/bitbucket.service';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport { formatResponse, type ResponseData } from '@utils/responseData';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\n\nexport type BitbucketGetAuthUrlQuerystring = {\n redirectUri: string;\n};\n\nexport type BitbucketGetAuthUrlResult = ResponseData<{\n authUrl: string;\n}>;\n\nexport const getAuthUrl = async (\n request: FastifyRequest<{ Querystring: BitbucketGetAuthUrlQuerystring }>,\n reply: FastifyReply\n): Promise<void> => {\n const { redirectUri } = request.query;\n\n if (!redirectUri) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'BITBUCKET_REDIRECT_URI_MISSING'\n );\n }\n\n try {\n const authUrl = bitbucketService.getAuthorizationUrl(redirectUri);\n const responseData = formatResponse<{ authUrl: string }>({\n data: { authUrl },\n });\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type BitbucketAuthCallbackBody = {\n code: string;\n};\n\nexport type BitbucketAuthCallbackResult = ResponseData<{\n token: string;\n}>;\n\nexport const authCallback = async (\n request: FastifyRequest<{ Body: BitbucketAuthCallbackBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { code } = request.body;\n\n if (!code) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'BITBUCKET_CODE_MISSING'\n );\n }\n\n try {\n const token = await bitbucketService.exchangeCodeForToken(code);\n const responseData = formatResponse<{ token: string }>({\n data: { token },\n });\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type BitbucketListReposQuerystring = {\n token?: string;\n};\n\nexport type BitbucketListReposResult = ResponseData<\n bitbucketService.BitbucketRepository[]\n>;\n\nexport const listRepos = async (\n request: FastifyRequest<{ Querystring: BitbucketListReposQuerystring }>,\n reply: FastifyReply\n): Promise<void> => {\n const { token } = request.query;\n const userId = request.session?.user?.id;\n\n try {\n let accessToken: string | undefined = token;\n\n if (!accessToken && userId) {\n accessToken =\n (await getBitbucketTokenFromUser(String(userId))) ?? undefined;\n }\n\n if (!accessToken) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'BITBUCKET_TOKEN_MISSING'\n );\n }\n\n const repos = await bitbucketService.getUserRepositories(accessToken);\n const responseData = formatResponse<bitbucketService.BitbucketRepository[]>(\n {\n data: repos,\n }\n );\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type BitbucketCheckConfigBody = {\n token?: string;\n workspace: string;\n repoSlug: string;\n branch?: string;\n};\n\nexport type BitbucketCheckConfigResult = ResponseData<{\n hasConfig: boolean;\n configPaths: string[];\n}>;\n\n/**\n * Check if intlayer.config.ts (or candidates) exists in a Bitbucket repository\n */\nexport const checkConfig = async (\n request: FastifyRequest<{ Body: BitbucketCheckConfigBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { token, workspace, repoSlug, branch = 'main' } = request.body;\n const userId = request.session?.user?.id;\n\n try {\n let accessToken: string | undefined = token;\n\n if (!accessToken && userId) {\n accessToken =\n (await getBitbucketTokenFromUser(String(userId))) ?? undefined;\n }\n\n if (!accessToken || !workspace || !repoSlug) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'BITBUCKET_CHECK_CONFIG_MISSING_PARAMS'\n );\n }\n\n const configPaths = await bitbucketService.checkIntlayerConfig(\n accessToken,\n workspace,\n repoSlug,\n branch\n );\n\n const responseData = formatResponse<{\n hasConfig: boolean;\n configPaths: string[];\n }>({\n data: {\n hasConfig: configPaths.length > 0,\n configPaths: configPaths,\n },\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type BitbucketGetConfigFileBody = {\n token?: string;\n workspace: string;\n repoSlug: string;\n branch?: string;\n path?: string;\n};\n\nexport type BitbucketGetConfigFileResult = ResponseData<{\n content: string;\n}>;\n\nexport const getConfigFile = async (\n request: FastifyRequest<{ Body: BitbucketGetConfigFileBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const {\n token,\n workspace,\n repoSlug,\n branch = 'main',\n path = 'intlayer.config.ts',\n } = request.body;\n const userId = request.session?.user?.id;\n\n try {\n let accessToken: string | undefined = token;\n\n if (!accessToken && userId) {\n accessToken =\n (await getBitbucketTokenFromUser(String(userId))) ?? undefined;\n }\n\n if (!accessToken || !workspace || !repoSlug) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'BITBUCKET_GET_CONFIG_FILE_MISSING_PARAMS'\n );\n }\n\n const content = await bitbucketService.getRepositoryFileContents(\n accessToken,\n workspace,\n repoSlug,\n path,\n branch\n );\n\n if (!content) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'BITBUCKET_CONFIG_FILE_NOT_FOUND'\n );\n }\n\n const responseData = formatResponse<{ content: string }>({\n data: { content },\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n"],"mappings":";;;;;AAcA,MAAa,aAAa,OACxB,SACA,UACkB;CAClB,MAAM,EAAE,gBAAgB,QAAQ;AAEhC,KAAI,CAAC,YACH,QAAO,aAAa,2BAClB,OACA,iCACD;AAGH,KAAI;EAEF,MAAM,eAAe,eAAoC,EACvD,MAAM,EAAE,SAFMA,oBAAqC,YAEpC,EAAE,EAClB,CAAC;AACF,SAAO,MAAM,KAAK,aAAa;UACxB,OAAO;AACd,SAAO,aAAa,uBAAuB,OAAO,MAAkB;;;AAYxE,MAAa,eAAe,OAC1B,SACA,UACkB;CAClB,MAAM,EAAE,SAAS,QAAQ;AAEzB,KAAI,CAAC,KACH,QAAO,aAAa,2BAClB,OACA,yBACD;AAGH,KAAI;EAEF,MAAM,eAAe,eAAkC,EACrD,MAAM,EAAE,aAFUC,qBAAsC,KAAK,EAE9C,EAChB,CAAC;AACF,SAAO,MAAM,KAAK,aAAa;UACxB,OAAO;AACd,SAAO,aAAa,uBAAuB,OAAO,MAAkB;;;AAYxE,MAAa,YAAY,OACvB,SACA,UACkB;CAClB,MAAM,EAAE,UAAU,QAAQ;CAC1B,MAAM,SAAS,QAAQ,SAAS,MAAM;AAEtC,KAAI;EACF,IAAI,cAAkC;AAEtC,MAAI,CAAC,eAAe,OAClB,eACG,MAAM,0BAA0B,OAAO,OAAO,CAAC,IAAK;AAGzD,MAAI,CAAC,YACH,QAAO,aAAa,2BAClB,OACA,0BACD;EAIH,MAAM,eAAe,eACnB,EACE,MAAM,MAHUC,oBAAqC,YAAY,EAIlE,CACF;AACD,SAAO,MAAM,KAAK,aAAa;UACxB,OAAO;AACd,SAAO,aAAa,uBAAuB,OAAO,MAAkB;;;;;;AAmBxE,MAAa,cAAc,OACzB,SACA,UACkB;CAClB,MAAM,EAAE,OAAO,WAAW,UAAU,SAAS,WAAW,QAAQ;CAChE,MAAM,SAAS,QAAQ,SAAS,MAAM;AAEtC,KAAI;EACF,IAAI,cAAkC;AAEtC,MAAI,CAAC,eAAe,OAClB,eACG,MAAM,0BAA0B,OAAO,OAAO,CAAC,IAAK;AAGzD,MAAI,CAAC,eAAe,CAAC,aAAa,CAAC,SACjC,QAAO,aAAa,2BAClB,OACA,wCACD;EAGH,MAAM,cAAc,MAAMC,oBACxB,aACA,WACA,UACA,OACD;EAED,MAAM,eAAe,eAGlB,EACD,MAAM;GACJ,WAAW,YAAY,SAAS;GACnB;GACd,EACF,CAAC;AAEF,SAAO,MAAM,KAAK,aAAa;UACxB,OAAO;AACd,SAAO,aAAa,uBAAuB,OAAO,MAAkB;;;AAgBxE,MAAa,gBAAgB,OAC3B,SACA,UACkB;CAClB,MAAM,EACJ,OACA,WACA,UACA,SAAS,QACT,OAAO,yBACL,QAAQ;CACZ,MAAM,SAAS,QAAQ,SAAS,MAAM;AAEtC,KAAI;EACF,IAAI,cAAkC;AAEtC,MAAI,CAAC,eAAe,OAClB,eACG,MAAM,0BAA0B,OAAO,OAAO,CAAC,IAAK;AAGzD,MAAI,CAAC,eAAe,CAAC,aAAa,CAAC,SACjC,QAAO,aAAa,2BAClB,OACA,2CACD;EAGH,MAAM,UAAU,MAAMC,0BACpB,aACA,WACA,UACA,MACA,OACD;AAED,MAAI,CAAC,QACH,QAAO,aAAa,2BAClB,OACA,kCACD;EAGH,MAAM,eAAe,eAAoC,EACvD,MAAM,EAAE,SAAS,EAClB,CAAC;AAEF,SAAO,MAAM,KAAK,aAAa;UACxB,OAAO;AACd,SAAO,aAAa,uBAAuB,OAAO,MAAkB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cliSessionToken.controller.mjs","names":[],"sources":["../../../src/controllers/cliSessionToken.controller.ts"],"sourcesContent":["import { createCliSessionToken } from '@services/cliSessionToken.service';\nimport { getProjectById } from '@services/project.service';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport { mapProjectToAPI } from '@utils/mapper/project';\nimport { formatResponse, type ResponseData } from '@utils/responseData';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { ProjectAPI } from '@/types/project.types';\n\nexport type CreateCliSessionTokenResult = ResponseData<{\n token: string;\n expiresAt: Date;\n}>;\n\nexport type GetCliSessionMeResult = ResponseData<{\n project: ProjectAPI;\n}>;\n\n/**\n * Creates a short-lived (2h) CLI session token tied to the authenticated user's\n * current organization and project. Requires a valid browser session.\n */\nexport const createCliSessionTokenHandler = async (\n request: FastifyRequest,\n reply: FastifyReply\n): Promise<void> => {\n const { user, organization, project } = 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 try {\n const result = await createCliSessionToken(\n user.id,\n String(organization.id),\n String(project.id)\n );\n\n return reply.send(\n formatResponse<{ token: string; expiresAt: Date }>({\n data: result,\n })\n );\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\n/**\n * Returns the project context for the currently authenticated CLI session.\n * Used by the CLI to verify the session token and check config consistency.\n */\nexport const getCliSessionMeHandler = async (\n request: FastifyRequest,\n reply: FastifyReply\n): Promise<void> => {\n const { project } = request.session || {};\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n try {\n const projectData = await getProjectById(String(project.id));\n\n if (!projectData) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n return reply.send(\n formatResponse<{ project: ProjectAPI }>({\n data: { project: mapProjectToAPI(projectData) },\n })\n );\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n"],"mappings":";;;;;;;;;;;AAqBA,MAAa,+BAA+B,OAC1C,SACA,UACkB;CAClB,MAAM,EAAE,MAAM,cAAc,YAAY,QAAQ,WAAW,
|
|
1
|
+
{"version":3,"file":"cliSessionToken.controller.mjs","names":[],"sources":["../../../src/controllers/cliSessionToken.controller.ts"],"sourcesContent":["import { createCliSessionToken } from '@services/cliSessionToken.service';\nimport { getProjectById } from '@services/project.service';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport { mapProjectToAPI } from '@utils/mapper/project';\nimport { formatResponse, type ResponseData } from '@utils/responseData';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { ProjectAPI } from '@/types/project.types';\n\nexport type CreateCliSessionTokenResult = ResponseData<{\n token: string;\n expiresAt: Date;\n}>;\n\nexport type GetCliSessionMeResult = ResponseData<{\n project: ProjectAPI;\n}>;\n\n/**\n * Creates a short-lived (2h) CLI session token tied to the authenticated user's\n * current organization and project. Requires a valid browser session.\n */\nexport const createCliSessionTokenHandler = async (\n request: FastifyRequest,\n reply: FastifyReply\n): Promise<void> => {\n const { user, organization, project } = 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 try {\n const result = await createCliSessionToken(\n user.id,\n String(organization.id),\n String(project.id)\n );\n\n return reply.send(\n formatResponse<{ token: string; expiresAt: Date }>({\n data: result,\n })\n );\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\n/**\n * Returns the project context for the currently authenticated CLI session.\n * Used by the CLI to verify the session token and check config consistency.\n */\nexport const getCliSessionMeHandler = async (\n request: FastifyRequest,\n reply: FastifyReply\n): Promise<void> => {\n const { project } = request.session || {};\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n try {\n const projectData = await getProjectById(String(project.id));\n\n if (!projectData) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n return reply.send(\n formatResponse<{ project: ProjectAPI }>({\n data: { project: mapProjectToAPI(projectData) },\n })\n );\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n"],"mappings":";;;;;;;;;;;AAqBA,MAAa,+BAA+B,OAC1C,SACA,UACkB;CAClB,MAAM,EAAE,MAAM,cAAc,YAAY,QAAQ,WAAW,EAAE;AAE7D,KAAI,CAAC,KACH,QAAO,aAAa,2BAA2B,OAAO,mBAAmB;AAG3E,KAAI,CAAC,aACH,QAAO,aAAa,2BAClB,OACA,2BACD;AAGH,KAAI,CAAC,QACH,QAAO,aAAa,2BAClB,OACA,sBACD;AAGH,KAAI;EACF,MAAM,SAAS,MAAM,sBACnB,KAAK,IACL,OAAO,aAAa,GAAG,EACvB,OAAO,QAAQ,GAAG,CACnB;AAED,SAAO,MAAM,KACX,eAAmD,EACjD,MAAM,QACP,CAAC,CACH;UACM,OAAO;AACd,SAAO,aAAa,uBAAuB,OAAO,MAAkB;;;;;;;AAQxE,MAAa,yBAAyB,OACpC,SACA,UACkB;CAClB,MAAM,EAAE,YAAY,QAAQ,WAAW,EAAE;AAEzC,KAAI,CAAC,QACH,QAAO,aAAa,2BAClB,OACA,sBACD;AAGH,KAAI;EACF,MAAM,cAAc,MAAM,eAAe,OAAO,QAAQ,GAAG,CAAC;AAE5D,MAAI,CAAC,YACH,QAAO,aAAa,2BAClB,OACA,sBACD;AAGH,SAAO,MAAM,KACX,eAAwC,EACtC,MAAM,EAAE,SAAS,gBAAgB,YAAY,EAAE,EAChD,CAAC,CACH;UACM,OAAO;AACd,SAAO,aAAa,uBAAuB,OAAO,MAAkB"}
|
|
@@ -10,9 +10,6 @@ import { Types } from "mongoose";
|
|
|
10
10
|
import { hashPassword } from "better-auth/crypto";
|
|
11
11
|
|
|
12
12
|
//#region src/controllers/demo.controller.ts
|
|
13
|
-
const DEMO_ADMIN_EMAIL = process.env.DEMO_ADMIN_EMAIL;
|
|
14
|
-
const DEMO_EMAIL = process.env.DEMO_USER_EMAIL;
|
|
15
|
-
const DEMO_PASSWORD = process.env.DEMO_USER_PASSWORD;
|
|
16
13
|
let _initialized = false;
|
|
17
14
|
let _demoOrgId = null;
|
|
18
15
|
let _demoProjectId = null;
|
|
@@ -21,6 +18,9 @@ const ensureDemoResources = async () => {
|
|
|
21
18
|
demoOrgId: _demoOrgId,
|
|
22
19
|
demoProjectId: _demoProjectId
|
|
23
20
|
};
|
|
21
|
+
const DEMO_ADMIN_EMAIL = process.env.DEMO_ADMIN_EMAIL;
|
|
22
|
+
const DEMO_EMAIL = process.env.DEMO_USER_EMAIL;
|
|
23
|
+
const DEMO_PASSWORD = process.env.DEMO_USER_PASSWORD;
|
|
24
24
|
if (!DEMO_ADMIN_EMAIL || !DEMO_EMAIL || !DEMO_PASSWORD) throw new Error("[demo] missing required env vars: DEMO_ADMIN_EMAIL, DEMO_USER_EMAIL, DEMO_USER_PASSWORD");
|
|
25
25
|
let demoAdmin = await getUserByEmail(DEMO_ADMIN_EMAIL);
|
|
26
26
|
if (!demoAdmin) {
|
|
@@ -39,14 +39,17 @@ const ensureDemoResources = async () => {
|
|
|
39
39
|
emailVerified: true,
|
|
40
40
|
name: "Demo"
|
|
41
41
|
});
|
|
42
|
-
const hashedPassword = await hashPassword(DEMO_PASSWORD);
|
|
43
|
-
await AccountModel.create({
|
|
44
|
-
userId: String(demoUser._id),
|
|
45
|
-
accountId: String(demoUser._id),
|
|
46
|
-
providerId: "credential",
|
|
47
|
-
password: hashedPassword
|
|
48
|
-
});
|
|
49
42
|
}
|
|
43
|
+
const hashedPassword = await hashPassword(DEMO_PASSWORD);
|
|
44
|
+
await AccountModel.findOneAndUpdate({
|
|
45
|
+
userId: String(demoUser._id),
|
|
46
|
+
providerId: "credential"
|
|
47
|
+
}, {
|
|
48
|
+
userId: String(demoUser._id),
|
|
49
|
+
accountId: String(demoUser._id),
|
|
50
|
+
providerId: "credential",
|
|
51
|
+
password: hashedPassword
|
|
52
|
+
}, { upsert: true });
|
|
50
53
|
if (!demoAdmin || !demoUser) throw new Error("[demo] failed to create demo users");
|
|
51
54
|
const demoAdminId = new Types.ObjectId(String(demoAdmin.id));
|
|
52
55
|
const demoUserId = new Types.ObjectId(String(demoUser.id));
|
|
@@ -75,16 +78,19 @@ const ensureDemoResources = async () => {
|
|
|
75
78
|
membersIds: [demoAdminId],
|
|
76
79
|
adminsIds: [demoAdminId],
|
|
77
80
|
viewersIds: [demoUserId],
|
|
78
|
-
configuration: {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
81
|
+
configuration: {
|
|
82
|
+
internationalization: {
|
|
83
|
+
locales: [
|
|
84
|
+
"en",
|
|
85
|
+
"fr",
|
|
86
|
+
"es",
|
|
87
|
+
"de",
|
|
88
|
+
"ja"
|
|
89
|
+
],
|
|
90
|
+
defaultLocale: "en"
|
|
91
|
+
},
|
|
92
|
+
editor: { applicationURL: "https://intlayer.org" }
|
|
93
|
+
}
|
|
88
94
|
});
|
|
89
95
|
await createDemoDictionaries([String(demoProject._id)], String(demoAdminId));
|
|
90
96
|
} else if (!demoProject.viewersIds?.map(String).includes(String(demoUserId))) {
|
|
@@ -106,7 +112,11 @@ const ensureDemoResources = async () => {
|
|
|
106
112
|
const getDemoSessionHandler = async (request, reply) => {
|
|
107
113
|
try {
|
|
108
114
|
await ensureDemoResources();
|
|
109
|
-
const
|
|
115
|
+
const auth = getAuthSingleton();
|
|
116
|
+
const DEMO_EMAIL = process.env.DEMO_USER_EMAIL;
|
|
117
|
+
const DEMO_PASSWORD = process.env.DEMO_USER_PASSWORD;
|
|
118
|
+
if (!DEMO_EMAIL || !DEMO_PASSWORD) throw new Error("[demo] missing required env vars: DEMO_USER_EMAIL, DEMO_USER_PASSWORD");
|
|
119
|
+
const signInResponse = await auth.api.signInEmail({
|
|
110
120
|
body: {
|
|
111
121
|
email: DEMO_EMAIL,
|
|
112
122
|
password: DEMO_PASSWORD
|
|
@@ -119,11 +129,8 @@ const getDemoSessionHandler = async (request, reply) => {
|
|
|
119
129
|
});
|
|
120
130
|
if (typeof signInResponse.headers.getSetCookie === "function") {
|
|
121
131
|
const setCookies = signInResponse.headers.getSetCookie();
|
|
122
|
-
if (setCookies.length > 0) reply.
|
|
132
|
+
if (setCookies.length > 0) reply.raw.setHeader("set-cookie", setCookies);
|
|
123
133
|
}
|
|
124
|
-
signInResponse.headers.forEach((value, key) => {
|
|
125
|
-
if (key.toLowerCase() !== "set-cookie") reply.header(key, value);
|
|
126
|
-
});
|
|
127
134
|
reply.status(200).send({ ok: true });
|
|
128
135
|
} catch (error) {
|
|
129
136
|
logger.error("[demo] getDemoSessionHandler error:", error);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"demo.controller.mjs","names":[],"sources":["../../../src/controllers/demo.controller.ts"],"sourcesContent":["import { logger } from '@logger';\nimport { AccountModel } from '@schemas/account.schema';\nimport { OrganizationModel } from '@schemas/organization.schema';\nimport { ProjectModel } from '@schemas/project.schema';\nimport { UserModel } from '@schemas/user.schema';\nimport { createDemoDictionaries } from '@services/dictionary.service';\nimport { createUser, getUserByEmail } from '@services/user.service';\nimport { getAuthSingleton } from '@utils/auth/getAuth';\nimport { hashPassword } from 'better-auth/crypto';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport { Types } from 'mongoose';\n\
|
|
1
|
+
{"version":3,"file":"demo.controller.mjs","names":[],"sources":["../../../src/controllers/demo.controller.ts"],"sourcesContent":["import { logger } from '@logger';\nimport { AccountModel } from '@schemas/account.schema';\nimport { OrganizationModel } from '@schemas/organization.schema';\nimport { ProjectModel } from '@schemas/project.schema';\nimport { UserModel } from '@schemas/user.schema';\nimport { createDemoDictionaries } from '@services/dictionary.service';\nimport { createUser, getUserByEmail } from '@services/user.service';\nimport { getAuthSingleton } from '@utils/auth/getAuth';\nimport { hashPassword } from 'better-auth/crypto';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport { Types } from 'mongoose';\n\nlet _initialized = false;\nlet _demoOrgId: string | null = null;\nlet _demoProjectId: string | null = null;\n\nconst ensureDemoResources = async (): Promise<{\n demoOrgId: string;\n demoProjectId: string;\n}> => {\n if (_initialized && _demoOrgId && _demoProjectId) {\n return { demoOrgId: _demoOrgId, demoProjectId: _demoProjectId };\n }\n\n const DEMO_ADMIN_EMAIL = process.env.DEMO_ADMIN_EMAIL;\n const DEMO_EMAIL = process.env.DEMO_USER_EMAIL;\n const DEMO_PASSWORD = process.env.DEMO_USER_PASSWORD;\n\n if (!DEMO_ADMIN_EMAIL || !DEMO_EMAIL || !DEMO_PASSWORD) {\n throw new Error(\n '[demo] missing required env vars: DEMO_ADMIN_EMAIL, DEMO_USER_EMAIL, DEMO_USER_PASSWORD'\n );\n }\n\n // Demo admin (system user, no credentials)\n let demoAdmin = await getUserByEmail(DEMO_ADMIN_EMAIL);\n if (!demoAdmin) {\n logger.info('[demo] creating demo-admin user');\n demoAdmin = await createUser({\n email: DEMO_ADMIN_EMAIL,\n emailVerified: true,\n name: 'Demo Admin',\n });\n }\n\n // Demo user (with credentials)\n let demoUser = await getUserByEmail(DEMO_EMAIL);\n if (!demoUser) {\n logger.info('[demo] creating demo user');\n demoUser = await createUser({\n email: DEMO_EMAIL,\n emailVerified: true,\n name: 'Demo',\n });\n }\n\n const hashedPassword = await hashPassword(DEMO_PASSWORD);\n await AccountModel.findOneAndUpdate(\n { userId: String(demoUser._id), providerId: 'credential' },\n {\n userId: String(demoUser._id),\n accountId: String(demoUser._id),\n providerId: 'credential',\n password: hashedPassword,\n },\n { upsert: true }\n );\n\n if (!demoAdmin || !demoUser) {\n throw new Error('[demo] failed to create demo users');\n }\n\n const demoAdminId = new Types.ObjectId(String(demoAdmin.id));\n const demoUserId = new Types.ObjectId(String(demoUser.id));\n\n // Demo organization\n let demoOrg = await OrganizationModel.findOne({ creatorId: demoAdminId });\n if (!demoOrg) {\n logger.info('[demo] creating demo organization');\n demoOrg = await OrganizationModel.create({\n name: 'Intlayer Demo',\n creatorId: demoAdminId,\n membersIds: [demoAdminId, demoUserId],\n adminsIds: [demoAdminId],\n ssoEnabled: false,\n domain: '',\n });\n } else if (!demoOrg.membersIds.map(String).includes(String(demoUserId))) {\n await OrganizationModel.updateOne(\n { _id: demoOrg._id },\n { $addToSet: { membersIds: demoUserId } }\n );\n demoOrg = (await OrganizationModel.findById(demoOrg._id))!;\n }\n\n // Demo project\n let demoProject = await ProjectModel.findOne({ organizationId: demoOrg._id });\n if (!demoProject) {\n logger.info('[demo] creating demo project');\n demoProject = await ProjectModel.create({\n organizationId: demoOrg._id,\n name: 'Demo Project',\n creatorId: demoAdminId,\n membersIds: [demoAdminId],\n adminsIds: [demoAdminId],\n viewersIds: [demoUserId],\n configuration: {\n internationalization: {\n locales: ['en', 'fr', 'es', 'de', 'ja'],\n defaultLocale: 'en',\n },\n editor: {\n applicationURL: 'https://intlayer.org',\n },\n },\n });\n\n await createDemoDictionaries(\n [String(demoProject._id)],\n String(demoAdminId)\n );\n } else if (\n !demoProject.viewersIds?.map(String).includes(String(demoUserId))\n ) {\n await ProjectModel.updateOne(\n { _id: demoProject._id },\n { $addToSet: { viewersIds: demoUserId } }\n );\n demoProject = (await ProjectModel.findById(demoProject._id))!;\n }\n\n // Keep demo user's last-active context up to date ────────────────────\n await UserModel.updateOne(\n { _id: demoUser.id },\n {\n $set: {\n lastActiveOrganizationId: String(demoOrg._id),\n lastActiveProjectId: String(demoProject._id),\n },\n }\n );\n\n _demoOrgId = String(demoOrg._id);\n _demoProjectId = String(demoProject._id);\n _initialized = true;\n\n return { demoOrgId: _demoOrgId, demoProjectId: _demoProjectId };\n};\n\nexport const getDemoSessionHandler = async (\n request: FastifyRequest,\n reply: FastifyReply\n): Promise<void> => {\n try {\n await ensureDemoResources();\n\n const auth = getAuthSingleton();\n\n const DEMO_EMAIL = process.env.DEMO_USER_EMAIL;\n const DEMO_PASSWORD = process.env.DEMO_USER_PASSWORD;\n\n if (!DEMO_EMAIL || !DEMO_PASSWORD) {\n throw new Error(\n '[demo] missing required env vars: DEMO_USER_EMAIL, DEMO_USER_PASSWORD'\n );\n }\n\n const signInResponse = await auth.api.signInEmail({\n body: { email: DEMO_EMAIL, password: DEMO_PASSWORD },\n headers: new Headers({\n 'user-agent':\n (request.headers['user-agent'] as string) ?? 'Demo Browser',\n 'x-forwarded-for': request.ip ?? '127.0.0.1',\n }),\n asResponse: true,\n });\n\n if (typeof signInResponse.headers.getSetCookie === 'function') {\n const setCookies = signInResponse.headers.getSetCookie();\n // reply.raw.setHeader correctly forwards an array as multiple Set-Cookie\n // entries; reply.header() with an array joins with commas which breaks cookies.\n if (setCookies.length > 0) {\n reply.raw.setHeader('set-cookie', setCookies);\n }\n }\n\n reply.status(200).send({ ok: true });\n } catch (error) {\n logger.error('[demo] getDemoSessionHandler error:', error);\n reply.status(500).send({ error: 'Failed to create demo session' });\n }\n};\n"],"mappings":"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"}
|
|
@@ -105,6 +105,7 @@ const getDictionaryByKey = async (request, reply) => {
|
|
|
105
105
|
if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
|
|
106
106
|
try {
|
|
107
107
|
const dictionary = await getDictionaryByKey$1(dictionaryKey, project.id);
|
|
108
|
+
if (!dictionary) return ErrorHandler.handleGenericErrorResponse(reply, "DICTIONARY_NOT_FOUND");
|
|
108
109
|
if (!hasPermission(roles || [], "dictionary:read")({
|
|
109
110
|
...request.session,
|
|
110
111
|
targetDictionaries: [dictionary]
|