ghost 5.115.0 → 5.116.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/components/{tryghost-api-framework-5.115.0.tgz → tryghost-api-framework-5.116.0.tgz} +0 -0
- package/components/tryghost-constants-5.116.0.tgz +0 -0
- package/components/tryghost-custom-fonts-5.116.0.tgz +0 -0
- package/components/{tryghost-custom-theme-settings-service-5.115.0.tgz → tryghost-custom-theme-settings-service-5.116.0.tgz} +0 -0
- package/components/tryghost-domain-events-5.116.0.tgz +0 -0
- package/components/{tryghost-donations-5.115.0.tgz → tryghost-donations-5.116.0.tgz} +0 -0
- package/components/tryghost-email-addresses-5.116.0.tgz +0 -0
- package/components/tryghost-email-service-5.116.0.tgz +0 -0
- package/components/tryghost-email-suppression-list-5.116.0.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.116.0.tgz +0 -0
- package/components/tryghost-i18n-5.116.0.tgz +0 -0
- package/components/tryghost-job-manager-5.116.0.tgz +0 -0
- package/components/tryghost-link-replacer-5.116.0.tgz +0 -0
- package/components/tryghost-magic-link-5.116.0.tgz +0 -0
- package/components/tryghost-member-attribution-5.116.0.tgz +0 -0
- package/components/tryghost-member-events-5.116.0.tgz +0 -0
- package/components/tryghost-members-api-5.116.0.tgz +0 -0
- package/components/tryghost-members-csv-5.116.0.tgz +0 -0
- package/components/{tryghost-members-offers-5.115.0.tgz → tryghost-members-offers-5.116.0.tgz} +0 -0
- package/components/{tryghost-milestones-5.115.0.tgz → tryghost-milestones-5.116.0.tgz} +0 -0
- package/components/{tryghost-mw-error-handler-5.115.0.tgz → tryghost-mw-error-handler-5.116.0.tgz} +0 -0
- package/components/tryghost-mw-vhost-5.116.0.tgz +0 -0
- package/components/{tryghost-post-events-5.115.0.tgz → tryghost-post-events-5.116.0.tgz} +0 -0
- package/components/{tryghost-post-revisions-5.115.0.tgz → tryghost-post-revisions-5.116.0.tgz} +0 -0
- package/components/tryghost-posts-service-5.116.0.tgz +0 -0
- package/components/{tryghost-prometheus-metrics-5.115.0.tgz → tryghost-prometheus-metrics-5.116.0.tgz} +0 -0
- package/components/tryghost-security-5.116.0.tgz +0 -0
- package/components/{tryghost-tiers-5.115.0.tgz → tryghost-tiers-5.116.0.tgz} +0 -0
- package/components/tryghost-webmentions-5.116.0.tgz +0 -0
- package/content/themes/casper/LICENSE +1 -1
- package/content/themes/casper/README.md +1 -1
- package/content/themes/source/LICENSE +1 -1
- package/content/themes/source/README.md +1 -1
- package/content/themes/source/assets/built/screen.css +1 -1
- package/content/themes/source/assets/built/screen.css.map +1 -1
- package/content/themes/source/assets/css/screen.css +11 -6
- package/content/themes/source/partials/feature-image.hbs +2 -2
- package/core/boot.js +3 -43
- package/core/built/admin/assets/admin-x-activitypub/admin-x-activitypub.js +30494 -29403
- package/core/built/admin/assets/admin-x-demo/admin-x-demo.js +1 -1
- package/core/built/admin/assets/admin-x-demo/{index-0040480a.mjs → index-a9601514.mjs} +5 -4
- package/core/built/admin/assets/admin-x-demo/{modals-fb35c86c.mjs → modals-c1789d04.mjs} +67 -65
- package/core/built/admin/assets/admin-x-settings/{CodeEditorView-806ef39c.mjs → CodeEditorView-e9c9deb8.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-376f847c.mjs → index-84580c3a.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-8fa19303.mjs → index-f744cab7.mjs} +3147 -3123
- package/core/built/admin/assets/admin-x-settings/{modals-36775d71.mjs → modals-d9ca60c5.mjs} +1198 -1192
- package/core/built/admin/assets/chunk.524.8371443ef8f60db429d0.js +35 -0
- package/core/built/admin/assets/chunk.582.f90151775f2e53dd21d9.js +37 -0
- package/core/built/admin/assets/{chunk.874.461cb3cf5b6b36915f8c.js → chunk.713.e9027c0cc3c56110f5da.js} +125 -98
- package/core/built/admin/assets/{ghost-938b3d9c29e3564a53a22f8c8f82d351.js → ghost-03b64c086f3c60cabc85fe7a7e2b640a.js} +272 -251
- package/core/built/admin/assets/ghost-ba58e9822f7384461e926c7e23f04a75.css +1 -0
- package/core/built/admin/assets/ghost-dark-f1f29683b14ffa11615b3bba8b6ab92c.css +1 -0
- package/core/built/admin/assets/koenig-lexical/index.css +1 -1
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +20563 -20891
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +139 -139
- package/core/built/admin/assets/posts/posts.js +5732 -5667
- package/core/built/admin/assets/stats/stats.js +75373 -0
- package/core/built/admin/assets/{vendor-68a4aa424a179a90f5bbc2b750def576.js → vendor-72026232b36d97babc6320917c16c321.js} +36 -34
- package/core/built/admin/index.html +6 -6
- package/core/cli/generate-data.js +1 -1
- package/core/frontend/helpers/ghost_head.js +8 -1
- package/core/frontend/public/ghost-stats.js +55 -2
- package/core/frontend/services/assets-minification/AdminAuthAssets.js +2 -1
- package/core/frontend/services/assets-minification/CardAssets.js +1 -1
- package/core/frontend/services/assets-minification/CommentCountsAssets.js +1 -1
- package/core/frontend/services/assets-minification/MemberAttributionAssets.js +1 -1
- package/core/frontend/services/assets-minification/Minifier.js +191 -0
- package/core/frontend/services/routing/controllers/previews.js +2 -1
- package/core/frontend/src/cards/css/cta.css +1 -1
- package/core/server/adapters/cache/Redis.js +1 -1
- package/core/server/adapters/lib/redis/AdapterCacheRedis.js +287 -0
- package/core/server/adapters/lib/redis/redis-store-factory.js +22 -0
- package/core/server/api/endpoints/posts.js +9 -3
- package/core/server/api/endpoints/previews.js +35 -1
- package/core/server/api/endpoints/slugs.js +6 -2
- package/core/server/api/endpoints/utils/serializers/output/utils/post-gating.js +6 -9
- package/core/server/api/endpoints/utils/validators/input/settings.js +1 -1
- package/core/server/data/db/connection.js +2 -0
- package/core/server/data/db/index.js +1 -0
- package/core/server/data/importer/handlers/ImporterContentFileHandler.js +90 -0
- package/core/server/data/importer/import-manager.js +3 -3
- package/core/server/data/importer/importers/importer-revue.js +128 -0
- package/core/server/data/importer/importers/json-to-html.js +107 -0
- package/core/server/data/migrations/utils/tables.js +2 -4
- package/core/server/data/seeders/DataGenerator.js +288 -0
- package/core/server/data/seeders/importers/BenefitsImporter.js +28 -0
- package/core/server/data/seeders/importers/CommentsImporter.js +73 -0
- package/core/server/data/seeders/importers/EmailBatchesImporter.js +38 -0
- package/core/server/data/seeders/importers/EmailRecipientFailuresImporter.js +67 -0
- package/core/server/data/seeders/importers/EmailRecipientsImporter.js +212 -0
- package/core/server/data/seeders/importers/EmailsImporter.js +99 -0
- package/core/server/data/seeders/importers/LabelsImporter.js +41 -0
- package/core/server/data/seeders/importers/MembersClickEventsImporter.js +69 -0
- package/core/server/data/seeders/importers/MembersCreatedEventsImporter.js +103 -0
- package/core/server/data/seeders/importers/MembersFeedbackImporter.js +45 -0
- package/core/server/data/seeders/importers/MembersImporter.js +111 -0
- package/core/server/data/seeders/importers/MembersLabelsImporter.js +39 -0
- package/core/server/data/seeders/importers/MembersLoginEventsImporter.js +69 -0
- package/core/server/data/seeders/importers/MembersNewslettersImporter.js +38 -0
- package/core/server/data/seeders/importers/MembersPaidSubscriptionEventsImporter.js +99 -0
- package/core/server/data/seeders/importers/MembersProductsImporter.js +42 -0
- package/core/server/data/seeders/importers/MembersStatusEventsImporter.js +58 -0
- package/core/server/data/seeders/importers/MembersStripeCustomersImporter.js +60 -0
- package/core/server/data/seeders/importers/MembersStripeCustomersSubscriptionsImporter.js +259 -0
- package/core/server/data/seeders/importers/MembersSubscribeEventsImporter.js +69 -0
- package/core/server/data/seeders/importers/MembersSubscriptionCreatedEventsImporter.js +95 -0
- package/core/server/data/seeders/importers/NewslettersImporter.js +40 -0
- package/core/server/data/seeders/importers/OffersImporter.js +70 -0
- package/core/server/data/seeders/importers/PostsAuthorsImporter.js +32 -0
- package/core/server/data/seeders/importers/PostsImporter.js +102 -0
- package/core/server/data/seeders/importers/PostsProductsImporter.js +35 -0
- package/core/server/data/seeders/importers/PostsTagsImporter.js +46 -0
- package/core/server/data/seeders/importers/ProductsBenefitsImporter.js +54 -0
- package/core/server/data/seeders/importers/ProductsImporter.js +90 -0
- package/core/server/data/seeders/importers/RecommendationClickEventsImporter.js +32 -0
- package/core/server/data/seeders/importers/RecommendationSubscribeEventsImporter.js +32 -0
- package/core/server/data/seeders/importers/RecommendationsImporter.js +34 -0
- package/core/server/data/seeders/importers/RedirectsImporter.js +49 -0
- package/core/server/data/seeders/importers/RolesUsersImporter.js +42 -0
- package/core/server/data/seeders/importers/StripePricesImporter.js +69 -0
- package/core/server/data/seeders/importers/StripeProductsImporter.js +34 -0
- package/core/server/data/seeders/importers/TableImporter.js +187 -0
- package/core/server/data/seeders/importers/TagsImporter.js +41 -0
- package/core/server/data/seeders/importers/UsersImporter.js +31 -0
- package/core/server/data/seeders/importers/WebMentionsImporter.js +42 -0
- package/core/server/data/seeders/importers/index.js +41 -0
- package/core/server/data/seeders/utils/JsonImporter.js +39 -0
- package/core/server/data/seeders/utils/blog-info.js +3 -0
- package/core/server/data/seeders/utils/database-date.js +7 -0
- package/core/server/data/seeders/utils/event-generator.js +48 -0
- package/core/server/data/seeders/utils/random.js +13 -0
- package/core/server/data/seeders/utils/topological-sort.js +33 -0
- package/core/server/lib/bootstrap-socket.js +87 -0
- package/core/server/lib/package-json/index.js +1 -0
- package/core/server/lib/package-json/package-json.js +160 -0
- package/core/server/lib/package-json/parse.js +57 -0
- package/core/server/models/base/plugins/actions.js +44 -31
- package/core/server/models/base/plugins/generate-slug.js +6 -0
- package/core/server/notify.js +1 -1
- package/core/server/services/activitypub/ActivityPubService.ts +1 -1
- package/core/server/services/adapter-manager/AdapterManager.js +161 -0
- package/core/server/services/adapter-manager/index.js +1 -1
- package/core/server/services/announcement-bar-service/AnnouncementBarSettings.js +54 -0
- package/core/server/services/announcement-bar-service/AnnouncementVisibilityValues.js +11 -0
- package/core/server/services/announcement-bar-service/index.js +1 -1
- package/core/server/services/api-version-compatibility/APIVersionCompatibilityService.js +99 -0
- package/core/server/services/api-version-compatibility/VersionNotificationsDataService.js +80 -0
- package/core/server/services/api-version-compatibility/extract-api-key.js +57 -0
- package/core/server/services/api-version-compatibility/index.js +2 -2
- package/core/server/services/api-version-compatibility/mw-api-version-mismatch.js +31 -0
- package/core/server/services/audience-feedback/AudienceFeedbackController.js +85 -0
- package/core/server/services/audience-feedback/AudienceFeedbackService.js +34 -0
- package/core/server/services/audience-feedback/Feedback.js +35 -0
- package/core/server/services/audience-feedback/index.js +4 -2
- package/core/server/services/auth/session/emails/signin.js +168 -0
- package/core/server/services/auth/session/index.js +2 -2
- package/core/server/services/auth/session/session-from-token.js +69 -0
- package/core/server/services/auth/session/session-service.js +374 -0
- package/core/server/services/custom-redirects/index.js +1 -1
- package/core/server/services/email-analytics/EmailAnalyticsProviderMailgun.js +62 -0
- package/core/server/services/email-analytics/EmailAnalyticsService.js +552 -0
- package/core/server/services/email-analytics/EmailAnalyticsServiceWrapper.js +3 -3
- package/core/server/services/email-analytics/EventProcessingResult.js +66 -0
- package/core/server/services/email-service/EmailServiceWrapper.js +4 -4
- package/core/server/services/email-suppression-list/MailgunEmailSuppressionList.js +1 -1
- package/core/server/services/email-suppression-list/service.js +1 -1
- package/core/server/services/explore-ping/ExplorePingService.js +106 -0
- package/core/server/services/explore-ping/index.js +31 -0
- package/core/server/services/identity-tokens/IdentityTokenService.js +30 -0
- package/core/server/services/identity-tokens/IdentityTokenService.ts +28 -0
- package/core/server/services/identity-tokens/IdentityTokenServiceWrapper.js +1 -1
- package/core/server/services/invitations/accept.js +5 -2
- package/core/server/services/lib/DynamicRedirectManager.js +156 -0
- package/core/server/services/lib/EmailContentGenerator.js +54 -0
- package/core/server/services/lib/InMemoryRepository.js +62 -0
- package/core/server/services/lib/InMemoryRepository.ts +80 -0
- package/core/server/services/lib/MailgunClient.js +364 -0
- package/core/server/services/link-redirection/LinkRedirect.js +26 -0
- package/core/server/services/link-redirection/LinkRedirectRepository.js +7 -7
- package/core/server/services/link-redirection/LinkRedirectsService.js +123 -0
- package/core/server/services/link-redirection/README.md +151 -0
- package/core/server/services/link-redirection/RedirectEvent.js +24 -0
- package/core/server/services/link-redirection/index.js +1 -1
- package/core/server/services/link-tracking/LinkClickTrackingService.js +1 -1
- package/core/server/services/mail/index.js +1 -1
- package/core/server/services/mail-events/BookshelfMailEventRepository.js +2 -2
- package/core/server/services/mail-events/InMemoryMailEventRepository.js +10 -0
- package/core/server/services/mail-events/InMemoryMailEventRepository.ts +8 -0
- package/core/server/services/mail-events/MailEvent.js +20 -0
- package/core/server/services/mail-events/MailEvent.ts +10 -0
- package/core/server/services/mail-events/MailEventRepository.js +2 -0
- package/core/server/services/mail-events/MailEventRepository.ts +5 -0
- package/core/server/services/mail-events/MailEventService.js +124 -0
- package/core/server/services/mail-events/MailEventService.ts +169 -0
- package/core/server/services/mail-events/index.js +1 -1
- package/core/server/services/mail-events/libraries.d.ts +2 -0
- package/core/server/services/members/CaptchaService.js +80 -0
- package/core/server/services/members/api.js +1 -1
- package/core/server/services/members/importer/MembersCSVImporter.js +464 -0
- package/core/server/services/members/importer/MembersCSVImporterStripeUtils.js +194 -0
- package/core/server/services/members/importer/email-template.js +182 -0
- package/core/server/services/members/importer/index.js +30 -0
- package/core/server/services/members/members-ssr.js +333 -0
- package/core/server/services/members/service.js +2 -2
- package/core/server/services/members-events/LastSeenAtUpdater.js +1 -1
- package/core/server/services/offers/service.js +1 -1
- package/core/server/services/posts/stats/PostStats.js +13 -0
- package/core/server/services/recommendations/RecommendationServiceWrapper.js +8 -8
- package/core/server/services/recommendations/service/BookshelfClickEventRepository.js +48 -0
- package/core/server/services/recommendations/service/BookshelfClickEventRepository.ts +49 -0
- package/core/server/services/recommendations/service/BookshelfRecommendationRepository.js +98 -0
- package/core/server/services/recommendations/service/BookshelfRecommendationRepository.ts +117 -0
- package/core/server/services/recommendations/service/BookshelfRepository.js +134 -0
- package/core/server/services/recommendations/service/BookshelfRepository.ts +196 -0
- package/core/server/services/recommendations/service/BookshelfSubscribeEventRepository.js +48 -0
- package/core/server/services/recommendations/service/BookshelfSubscribeEventRepository.ts +49 -0
- package/core/server/services/recommendations/service/ClickEvent.js +33 -0
- package/core/server/services/recommendations/service/ClickEvent.ts +32 -0
- package/core/server/services/recommendations/service/InMemoryRecommendationRepository.js +19 -0
- package/core/server/services/recommendations/service/InMemoryRecommendationRepository.ts +20 -0
- package/core/server/services/recommendations/service/IncomingRecommendationController.js +34 -0
- package/core/server/services/recommendations/service/IncomingRecommendationController.ts +51 -0
- package/core/server/services/recommendations/service/IncomingRecommendationEmailRenderer.js +25 -0
- package/core/server/services/recommendations/service/IncomingRecommendationEmailRenderer.ts +37 -0
- package/core/server/services/recommendations/service/IncomingRecommendationService.js +93 -0
- package/core/server/services/recommendations/service/IncomingRecommendationService.ts +160 -0
- package/core/server/services/recommendations/service/Recommendation.js +140 -0
- package/core/server/services/recommendations/service/Recommendation.ts +201 -0
- package/core/server/services/recommendations/service/RecommendationController.js +208 -0
- package/core/server/services/recommendations/service/RecommendationController.ts +258 -0
- package/core/server/services/recommendations/service/RecommendationMetadataService.js +86 -0
- package/core/server/services/recommendations/service/RecommendationMetadataService.ts +128 -0
- package/core/server/services/recommendations/service/RecommendationRepository.js +2 -0
- package/core/server/services/recommendations/service/RecommendationRepository.ts +13 -0
- package/core/server/services/recommendations/service/RecommendationService.js +228 -0
- package/core/server/services/recommendations/service/RecommendationService.ts +281 -0
- package/core/server/services/recommendations/service/SubscribeEvent.js +33 -0
- package/core/server/services/recommendations/service/SubscribeEvent.ts +32 -0
- package/core/server/services/recommendations/service/UnsafeData.js +183 -0
- package/core/server/services/recommendations/service/UnsafeData.ts +217 -0
- package/core/server/services/recommendations/service/WellknownService.js +36 -0
- package/core/server/services/recommendations/service/WellknownService.ts +47 -0
- package/core/server/services/recommendations/service/index.js +31 -0
- package/core/server/services/recommendations/service/index.ts +15 -0
- package/core/server/services/recommendations/service/libraries.d.ts +5 -0
- package/core/server/services/route-settings/SettingsPathManager.js +47 -0
- package/core/server/services/route-settings/index.js +1 -1
- package/core/server/services/slack-notifications/SlackNotifications.js +211 -0
- package/core/server/services/slack-notifications/SlackNotificationsService.js +90 -0
- package/core/server/services/slack-notifications/service.js +4 -6
- package/core/server/services/stripe/README.md +63 -0
- package/core/server/services/stripe/StripeAPI.js +931 -0
- package/core/server/services/stripe/StripeMigrations.js +613 -0
- package/core/server/services/stripe/StripeService.js +175 -0
- package/core/server/services/stripe/WebhookController.js +100 -0
- package/core/server/services/stripe/WebhookManager.js +175 -0
- package/core/server/services/stripe/events/StripeLiveDisabledEvent.js +23 -0
- package/core/server/services/stripe/events/StripeLiveEnabledEvent.js +23 -0
- package/core/server/services/stripe/events/index.js +4 -0
- package/core/server/services/stripe/service.js +1 -1
- package/core/server/services/stripe/services/webhook/CheckoutSessionEventService.js +255 -0
- package/core/server/services/stripe/services/webhook/InvoiceEventService.js +70 -0
- package/core/server/services/stripe/services/webhook/SubscriptionEventService.js +54 -0
- package/core/server/services/themes/loader.js +1 -1
- package/core/server/services/themes/to-json.js +1 -1
- package/core/server/web/api/endpoints/admin/app.js +1 -21
- package/core/server/web/api/endpoints/admin/routes.js +1 -0
- package/core/server/web/api/middleware/version-match.js +41 -0
- package/core/server/web/shared/middleware/cache-control.js +51 -0
- package/core/server/web/shared/middleware/index.js +1 -1
- package/core/server/web/well-known.js +1 -1
- package/core/shared/labs.js +5 -3
- package/core/shared/settings-cache/CacheManager.js +64 -6
- package/package.json +98 -146
- package/tsconfig.tsbuildinfo +1 -1
- package/yarn.lock +1478 -1634
- package/components/tryghost-adapter-cache-redis-5.115.0.tgz +0 -0
- package/components/tryghost-adapter-manager-5.115.0.tgz +0 -0
- package/components/tryghost-announcement-bar-settings-5.115.0.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.115.0.tgz +0 -0
- package/components/tryghost-audience-feedback-5.115.0.tgz +0 -0
- package/components/tryghost-bookshelf-repository-5.115.0.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.115.0.tgz +0 -0
- package/components/tryghost-captcha-service-5.115.0.tgz +0 -0
- package/components/tryghost-constants-5.115.0.tgz +0 -0
- package/components/tryghost-custom-fonts-5.115.0.tgz +0 -0
- package/components/tryghost-data-generator-5.115.0.tgz +0 -0
- package/components/tryghost-domain-events-5.115.0.tgz +0 -0
- package/components/tryghost-email-addresses-5.115.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.115.0.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.115.0.tgz +0 -0
- package/components/tryghost-email-content-generator-5.115.0.tgz +0 -0
- package/components/tryghost-email-events-5.115.0.tgz +0 -0
- package/components/tryghost-email-service-5.115.0.tgz +0 -0
- package/components/tryghost-email-suppression-list-5.115.0.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.115.0.tgz +0 -0
- package/components/tryghost-extract-api-key-5.115.0.tgz +0 -0
- package/components/tryghost-ghost-5.115.0.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.115.0.tgz +0 -0
- package/components/tryghost-i18n-5.115.0.tgz +0 -0
- package/components/tryghost-identity-token-service-5.115.0.tgz +0 -0
- package/components/tryghost-importer-handler-content-files-5.115.0.tgz +0 -0
- package/components/tryghost-importer-revue-5.115.0.tgz +0 -0
- package/components/tryghost-in-memory-repository-5.115.0.tgz +0 -0
- package/components/tryghost-job-manager-5.115.0.tgz +0 -0
- package/components/tryghost-link-redirects-5.115.0.tgz +0 -0
- package/components/tryghost-link-replacer-5.115.0.tgz +0 -0
- package/components/tryghost-magic-link-5.115.0.tgz +0 -0
- package/components/tryghost-mail-events-5.115.0.tgz +0 -0
- package/components/tryghost-mailgun-client-5.115.0.tgz +0 -0
- package/components/tryghost-member-attribution-5.115.0.tgz +0 -0
- package/components/tryghost-member-events-5.115.0.tgz +0 -0
- package/components/tryghost-members-api-5.115.0.tgz +0 -0
- package/components/tryghost-members-csv-5.115.0.tgz +0 -0
- package/components/tryghost-members-importer-5.115.0.tgz +0 -0
- package/components/tryghost-members-payments-5.115.0.tgz +0 -0
- package/components/tryghost-members-ssr-5.115.0.tgz +0 -0
- package/components/tryghost-members-stripe-service-5.115.0.tgz +0 -0
- package/components/tryghost-minifier-5.115.0.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.115.0.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.115.0.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.115.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.115.0.tgz +0 -0
- package/components/tryghost-mw-version-match-5.115.0.tgz +0 -0
- package/components/tryghost-mw-vhost-5.115.0.tgz +0 -0
- package/components/tryghost-package-json-5.115.0.tgz +0 -0
- package/components/tryghost-posts-service-5.115.0.tgz +0 -0
- package/components/tryghost-recommendations-5.115.0.tgz +0 -0
- package/components/tryghost-referrers-5.115.0.tgz +0 -0
- package/components/tryghost-security-5.115.0.tgz +0 -0
- package/components/tryghost-session-service-5.115.0.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.115.0.tgz +0 -0
- package/components/tryghost-slack-notifications-5.115.0.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.115.0.tgz +0 -0
- package/components/tryghost-webmentions-5.115.0.tgz +0 -0
- package/core/built/admin/assets/chunk.524.31419fdf6fb3859ecc1e.js +0 -35
- package/core/built/admin/assets/chunk.582.08c816d5e4ab766486a7.js +0 -37
- package/core/built/admin/assets/ghost-c2a7c4a1b76550c4219adb2ed4124ce0.css +0 -1
- package/core/built/admin/assets/ghost-dark-f91e4a479c6d38d94d5d1b14727871dc.css +0 -1
- /package/core/built/admin/assets/{chunk.874.461cb3cf5b6b36915f8c.js.LICENSE.txt → chunk.713.e9027c0cc3c56110f5da.js.LICENSE.txt} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const MailgunClient = require('@tryghost/mailgun-client');
|
|
2
1
|
const models = require('../../models');
|
|
3
2
|
const configService = require('../../../shared/config');
|
|
4
3
|
const settingsCache = require('../../../shared/settings-cache');
|
|
4
|
+
const MailgunClient = require('../lib/MailgunClient');
|
|
5
5
|
const MailgunEmailSuppressionList = require('./MailgunEmailSuppressionList');
|
|
6
6
|
|
|
7
7
|
const mailgunClient = new MailgunClient({
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
module.exports = class ExplorePingService {
|
|
2
|
+
/**
|
|
3
|
+
* @param {object} deps
|
|
4
|
+
* @param {{getPublic: () => import('../../../shared/settings-cache/CacheManager').PublicSettingsCache}} deps.settingsCache
|
|
5
|
+
* @param {object} deps.config
|
|
6
|
+
* @param {object} deps.labs
|
|
7
|
+
* @param {object} deps.logging
|
|
8
|
+
* @param {object} deps.ghostVersion
|
|
9
|
+
* @param {object} deps.request
|
|
10
|
+
* @param {{stats: {
|
|
11
|
+
* getMostRecentlyPublishedPostDate: () => Promise<Date>,
|
|
12
|
+
* getFirstPublishedPostDate: () => Promise<Date>,
|
|
13
|
+
* getTotalPostsPublished: () => Promise<number>
|
|
14
|
+
* }}} deps.posts
|
|
15
|
+
* @param {{stats: {
|
|
16
|
+
* getTotalMembers: () => Promise<number>
|
|
17
|
+
* }}} deps.members
|
|
18
|
+
*/
|
|
19
|
+
constructor({settingsCache, config, labs, logging, ghostVersion, request, posts, members}) {
|
|
20
|
+
this.settingsCache = settingsCache;
|
|
21
|
+
this.config = config;
|
|
22
|
+
this.labs = labs;
|
|
23
|
+
this.logging = logging;
|
|
24
|
+
this.ghostVersion = ghostVersion;
|
|
25
|
+
this.request = request;
|
|
26
|
+
this.posts = posts;
|
|
27
|
+
this.members = members;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async constructPayload() {
|
|
31
|
+
/* eslint-disable camelcase */
|
|
32
|
+
const {title, description, icon, locale, accent_color, twitter, facebook} = this.settingsCache.getPublic();
|
|
33
|
+
|
|
34
|
+
// Get post statistics
|
|
35
|
+
const [totalPosts, lastPublishedAt, firstPublishedAt] = await Promise.all([
|
|
36
|
+
this.posts.stats.getTotalPostsPublished(),
|
|
37
|
+
this.posts.stats.getMostRecentlyPublishedPostDate(),
|
|
38
|
+
this.posts.stats.getFirstPublishedPostDate()
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
// Get member statistics with error handling
|
|
42
|
+
let totalMembers = null;
|
|
43
|
+
try {
|
|
44
|
+
totalMembers = await this.members.stats.getTotalMembers();
|
|
45
|
+
} catch (err) {
|
|
46
|
+
this.logging.warn('Failed to fetch member statistics', {
|
|
47
|
+
error: err.message,
|
|
48
|
+
context: 'explore-ping-service'
|
|
49
|
+
});
|
|
50
|
+
// Continue without member statistics
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
ghost: this.ghostVersion.full,
|
|
55
|
+
url: this.config.get('url'),
|
|
56
|
+
title,
|
|
57
|
+
description,
|
|
58
|
+
icon,
|
|
59
|
+
locale,
|
|
60
|
+
accent_color,
|
|
61
|
+
twitter,
|
|
62
|
+
facebook,
|
|
63
|
+
posts_first: firstPublishedAt ? firstPublishedAt.toISOString() : null,
|
|
64
|
+
posts_last: lastPublishedAt ? lastPublishedAt.toISOString() : null,
|
|
65
|
+
posts_total: totalPosts,
|
|
66
|
+
members_total: totalMembers
|
|
67
|
+
};
|
|
68
|
+
/* eslint-enable camelcase */
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async makeRequest(exploreUrl, payload) {
|
|
72
|
+
const json = JSON.stringify(payload);
|
|
73
|
+
this.logging.info('Pinging Explore with Payload', exploreUrl, json);
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
const response = await this.request(exploreUrl, {
|
|
77
|
+
method: 'POST',
|
|
78
|
+
body: json,
|
|
79
|
+
headers: {
|
|
80
|
+
'Content-Type': 'application/json'
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
this.logging.info('Explore Response', response.statusCode, response.statusMessage);
|
|
85
|
+
|
|
86
|
+
return response;
|
|
87
|
+
} catch (err) {
|
|
88
|
+
this.logging.warn('Explore Error', err.message);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async ping() {
|
|
93
|
+
if (!this.labs.isSet('explore')) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const exploreUrl = this.config.get('explore:url');
|
|
98
|
+
if (!exploreUrl) {
|
|
99
|
+
this.logging.warn('Explore URL not set');
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const payload = await this.constructPayload();
|
|
104
|
+
await this.makeRequest(exploreUrl, payload);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const ExplorePingService = require('./ExplorePingService');
|
|
2
|
+
const config = require('../../../shared/config');
|
|
3
|
+
const labs = require('../../../shared/labs');
|
|
4
|
+
const logging = require('@tryghost/logging');
|
|
5
|
+
const ghostVersion = require('@tryghost/version');
|
|
6
|
+
const request = require('@tryghost/request');
|
|
7
|
+
const settingsCache = require('../../../shared/settings-cache');
|
|
8
|
+
const posts = require('../posts/posts-service');
|
|
9
|
+
const members = require('../members');
|
|
10
|
+
|
|
11
|
+
// Export the creation function for testing
|
|
12
|
+
module.exports.createService = function createService() {
|
|
13
|
+
return new ExplorePingService({
|
|
14
|
+
settingsCache,
|
|
15
|
+
config,
|
|
16
|
+
labs,
|
|
17
|
+
logging,
|
|
18
|
+
ghostVersion,
|
|
19
|
+
request,
|
|
20
|
+
posts: posts(),
|
|
21
|
+
members
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
module.exports.init = async function init() {
|
|
26
|
+
const explorePingService = module.exports.createService();
|
|
27
|
+
|
|
28
|
+
// The final intention is to have this run on a schedule
|
|
29
|
+
// For the initial version, we'll just ping when the server starts
|
|
30
|
+
await explorePingService.ping();
|
|
31
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IdentityTokenService = void 0;
|
|
4
|
+
const jsonwebtoken_1 = require("jsonwebtoken");
|
|
5
|
+
class IdentityTokenService {
|
|
6
|
+
privateKey;
|
|
7
|
+
issuer;
|
|
8
|
+
keyId;
|
|
9
|
+
constructor(privateKey, issuer, keyId) {
|
|
10
|
+
this.privateKey = privateKey;
|
|
11
|
+
this.issuer = issuer;
|
|
12
|
+
this.keyId = keyId;
|
|
13
|
+
}
|
|
14
|
+
async getTokenForUser(email, role) {
|
|
15
|
+
const claims = {
|
|
16
|
+
sub: email
|
|
17
|
+
};
|
|
18
|
+
if (typeof role === 'string') {
|
|
19
|
+
claims.role = role;
|
|
20
|
+
}
|
|
21
|
+
const token = (0, jsonwebtoken_1.sign)(claims, this.privateKey, {
|
|
22
|
+
issuer: this.issuer,
|
|
23
|
+
expiresIn: '5m',
|
|
24
|
+
algorithm: 'RS256',
|
|
25
|
+
keyid: this.keyId
|
|
26
|
+
});
|
|
27
|
+
return token;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.IdentityTokenService = IdentityTokenService;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {sign} from 'jsonwebtoken';
|
|
2
|
+
|
|
3
|
+
export class IdentityTokenService {
|
|
4
|
+
constructor(
|
|
5
|
+
private privateKey: string,
|
|
6
|
+
private issuer: string,
|
|
7
|
+
private keyId: string
|
|
8
|
+
) {}
|
|
9
|
+
|
|
10
|
+
async getTokenForUser(email: string, role?: string) {
|
|
11
|
+
const claims: Record<string, string> = {
|
|
12
|
+
sub: email
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
if (typeof role === 'string') {
|
|
16
|
+
claims.role = role;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const token = sign(claims, this.privateKey, {
|
|
20
|
+
issuer: this.issuer,
|
|
21
|
+
expiresIn: '5m',
|
|
22
|
+
algorithm: 'RS256',
|
|
23
|
+
keyid: this.keyId
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return token;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -2,13 +2,16 @@ const errors = require('@tryghost/errors');
|
|
|
2
2
|
const tpl = require('@tryghost/tpl');
|
|
3
3
|
const models = require('../../models');
|
|
4
4
|
const security = require('@tryghost/security');
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
const messages = {
|
|
7
|
+
inviteNotFound: 'Invite not found.',
|
|
6
8
|
inviteExpired: 'Invite is expired.',
|
|
7
9
|
inviteEmailAlreadyExist: {
|
|
8
10
|
message: 'Could not create an account, email is already in use.',
|
|
9
11
|
context: 'Attempting to create an account with existing email address.',
|
|
10
12
|
help: 'Use different email address to register your account.'
|
|
11
|
-
}
|
|
13
|
+
}
|
|
14
|
+
};
|
|
12
15
|
|
|
13
16
|
async function accept(invitation) {
|
|
14
17
|
const data = invitation.invitation[0];
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const {parse: parseURL, format: formatURL} = require('url');
|
|
3
|
+
const {parse: parseQuerystring, stringify: formatQuerystring} = require('querystring');
|
|
4
|
+
|
|
5
|
+
class DynamicRedirectManager {
|
|
6
|
+
/**
|
|
7
|
+
* @param {object} config
|
|
8
|
+
* @param {number} config.permanentMaxAge
|
|
9
|
+
* @param {function} config.getSubdirectoryURL
|
|
10
|
+
*/
|
|
11
|
+
constructor({permanentMaxAge, getSubdirectoryURL}) {
|
|
12
|
+
/** @private */
|
|
13
|
+
this.permanentMaxAge = permanentMaxAge;
|
|
14
|
+
|
|
15
|
+
this.getSubdirectoryURL = getSubdirectoryURL;
|
|
16
|
+
|
|
17
|
+
/** @private */
|
|
18
|
+
this.router = express.Router();
|
|
19
|
+
/** @private @type {Object.<string, {fromRegex: RegExp, to: string, options: {permanent: boolean}}>} */
|
|
20
|
+
this.redirects = {};
|
|
21
|
+
|
|
22
|
+
this.handleRequest = this.handleRequest.bind(this);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @private
|
|
27
|
+
* @param {string} string
|
|
28
|
+
* @returns {RegExp}
|
|
29
|
+
*/
|
|
30
|
+
buildRegex(string) {
|
|
31
|
+
let flags = '';
|
|
32
|
+
|
|
33
|
+
if (string.startsWith('/') && string.endsWith('/i')) {
|
|
34
|
+
string = string.slice(1, -2);
|
|
35
|
+
flags += 'i';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (string.endsWith('/')) {
|
|
39
|
+
string = string.slice(0, -1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!string.endsWith('$')) {
|
|
43
|
+
string += '/?$';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return new RegExp(string, flags);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @private
|
|
51
|
+
* @param {string} redirectId
|
|
52
|
+
* @returns {void}
|
|
53
|
+
*/
|
|
54
|
+
setupRedirect(redirectId) {
|
|
55
|
+
const {fromRegex, to, options: {permanent}} = this.redirects[redirectId];
|
|
56
|
+
|
|
57
|
+
this.router.get(fromRegex, (req, res) => {
|
|
58
|
+
const maxAge = permanent ? this.permanentMaxAge : 0;
|
|
59
|
+
const toURL = parseURL(to);
|
|
60
|
+
const toURLParams = parseQuerystring(toURL.query);
|
|
61
|
+
const currentURL = parseURL(req.url);
|
|
62
|
+
const currentURLParams = parseQuerystring(currentURL.query);
|
|
63
|
+
const params = Object.assign({}, currentURLParams, toURLParams);
|
|
64
|
+
const search = formatQuerystring(params);
|
|
65
|
+
|
|
66
|
+
toURL.pathname = currentURL.pathname.replace(fromRegex, toURL.pathname);
|
|
67
|
+
toURL.search = search !== '' ? `?${search}` : null;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Only if the url is internal should we prepend the Ghost subdirectory
|
|
71
|
+
* @see https://github.com/TryGhost/Ghost/issues/10776
|
|
72
|
+
*/
|
|
73
|
+
if (!toURL.hostname) {
|
|
74
|
+
toURL.pathname = this.getSubdirectoryURL(toURL.pathname);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
res.set({
|
|
78
|
+
'Cache-Control': `public, max-age=${maxAge}`
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
res.redirect(permanent ? 301 : 302, formatURL(toURL));
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {string} from
|
|
87
|
+
* @param {string} to
|
|
88
|
+
* @param {object} [options]
|
|
89
|
+
* @param {boolean} [options.permanent]
|
|
90
|
+
*
|
|
91
|
+
* @returns {string} The redirect ID
|
|
92
|
+
*/
|
|
93
|
+
addRedirect(from, to, options = {}) {
|
|
94
|
+
try {
|
|
95
|
+
// encode "from" only if it's not a regex
|
|
96
|
+
try {
|
|
97
|
+
new RegExp(from);
|
|
98
|
+
} catch (e) {
|
|
99
|
+
from = encodeURI(from);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const fromRegex = this.buildRegex(from);
|
|
103
|
+
const redirectId = from;
|
|
104
|
+
|
|
105
|
+
this.redirects[redirectId] = {
|
|
106
|
+
fromRegex,
|
|
107
|
+
to,
|
|
108
|
+
options
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
this.setupRedirect(redirectId);
|
|
112
|
+
|
|
113
|
+
return redirectId;
|
|
114
|
+
} catch (error) {
|
|
115
|
+
if (error.message.match(/Invalid regular expression/gi)) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* @param {string} redirectId
|
|
125
|
+
* @returns {void}
|
|
126
|
+
*/
|
|
127
|
+
removeRedirect(redirectId) {
|
|
128
|
+
delete this.redirects[redirectId];
|
|
129
|
+
|
|
130
|
+
this.router = express.Router();
|
|
131
|
+
Object.keys(this.redirects).forEach(id => this.setupRedirect(id));
|
|
132
|
+
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @returns {void}
|
|
138
|
+
*/
|
|
139
|
+
removeAllRedirects() {
|
|
140
|
+
this.redirects = {};
|
|
141
|
+
this.router = express.Router();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @param {express.Request} req
|
|
146
|
+
* @param {express.Response} res
|
|
147
|
+
* @param {express.NextFunction} next
|
|
148
|
+
*
|
|
149
|
+
* @returns {void}
|
|
150
|
+
*/
|
|
151
|
+
handleRequest(req, res, next) {
|
|
152
|
+
this.router(req, res, next);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
module.exports = DynamicRedirectManager;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const _ = require('lodash').runInContext();
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
_.templateSettings.interpolate = /{{([\s\S]+?)}}/g;
|
|
6
|
+
|
|
7
|
+
class EmailContentGenerator {
|
|
8
|
+
/**
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} options
|
|
11
|
+
* @param {function} options.getSiteUrl
|
|
12
|
+
* @param {function} options.getSiteTitle
|
|
13
|
+
* @param {string} options.templatesDir - path to the directory containing email templates
|
|
14
|
+
*/
|
|
15
|
+
constructor({getSiteUrl, getSiteTitle, templatesDir}) {
|
|
16
|
+
this.getSiteUrl = getSiteUrl;
|
|
17
|
+
this.getSiteTitle = getSiteTitle;
|
|
18
|
+
this.templatesDir = templatesDir;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
*
|
|
23
|
+
* @param {Object} options
|
|
24
|
+
* @param {string} options.template - HTML template name to use for generation
|
|
25
|
+
* @param {Object} [options.data] - variable data to use during HTML template compilation
|
|
26
|
+
* @returns {Promise<{html: String, text: String}>} resolves with an object containing html and text properties
|
|
27
|
+
*/
|
|
28
|
+
async getContent(options) {
|
|
29
|
+
const defaults = {
|
|
30
|
+
siteUrl: this.getSiteUrl(),
|
|
31
|
+
siteTitle: this.getSiteTitle()
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const data = _.defaults(defaults, options.data);
|
|
35
|
+
|
|
36
|
+
// read the proper email body template
|
|
37
|
+
const content = await fs.readFile(path.join(this.templatesDir, options.template + '.html'), 'utf8');
|
|
38
|
+
|
|
39
|
+
// insert user-specific data into the email
|
|
40
|
+
const compiled = _.template(content);
|
|
41
|
+
const htmlContent = compiled(data);
|
|
42
|
+
|
|
43
|
+
// lazyload the lib, and generate a plain-text version of the same email
|
|
44
|
+
const htmlToText = require('html-to-text');
|
|
45
|
+
const textContent = htmlToText.fromString(htmlContent);
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
html: htmlContent,
|
|
49
|
+
text: textContent
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = EmailContentGenerator;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.InMemoryRepository = void 0;
|
|
7
|
+
const nql_1 = __importDefault(require("@tryghost/nql"));
|
|
8
|
+
class InMemoryRepository {
|
|
9
|
+
store = [];
|
|
10
|
+
ids = new Map();
|
|
11
|
+
async save(entity) {
|
|
12
|
+
if (entity.deleted) {
|
|
13
|
+
this.store = this.store.filter(item => item.id !== entity.id);
|
|
14
|
+
this.ids.delete(entity.id);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (this.ids.has(entity.id)) {
|
|
18
|
+
this.store = this.store.map((item) => {
|
|
19
|
+
if (item.id === entity.id) {
|
|
20
|
+
return entity;
|
|
21
|
+
}
|
|
22
|
+
return item;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
this.store.push(entity);
|
|
27
|
+
this.ids.set(entity.id, true);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async getById(id) {
|
|
31
|
+
return this.store.find(item => item.id === id) || null;
|
|
32
|
+
}
|
|
33
|
+
async getAll(options = {}) {
|
|
34
|
+
const filter = (0, nql_1.default)(options.filter);
|
|
35
|
+
const results = this.store.slice().filter(item => filter.queryJSON(this.toPrimitive(item)));
|
|
36
|
+
if (options.order) {
|
|
37
|
+
for (const order of options.order) {
|
|
38
|
+
results.sort((a, b) => {
|
|
39
|
+
if (order.direction === 'asc') {
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
41
|
+
return a[order.field] > b[order.field] ? 1 : -1;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
return a[order.field] < b[order.field] ? 1 : -1;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return results;
|
|
50
|
+
}
|
|
51
|
+
async getPage(options = { page: 1, limit: 15 }) {
|
|
52
|
+
const results = await this.getAll(options);
|
|
53
|
+
const start = (options.page - 1) * options.limit;
|
|
54
|
+
const end = start + options.limit;
|
|
55
|
+
return results.slice(start, end);
|
|
56
|
+
}
|
|
57
|
+
async getCount(options) {
|
|
58
|
+
const results = await this.getAll(options);
|
|
59
|
+
return results.length;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.InMemoryRepository = InMemoryRepository;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import nql from '@tryghost/nql';
|
|
2
|
+
|
|
3
|
+
type Entity<T> = {
|
|
4
|
+
id: T;
|
|
5
|
+
deleted: boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
type Order<T> = {
|
|
9
|
+
field: keyof T;
|
|
10
|
+
direction: 'asc' | 'desc';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
type OrderOption<T extends Entity<any>> = Order<T>[];
|
|
15
|
+
|
|
16
|
+
export abstract class InMemoryRepository<IDType, T extends Entity<IDType>> {
|
|
17
|
+
protected store: T[] = [];
|
|
18
|
+
private ids: Map<IDType, true> = new Map();
|
|
19
|
+
|
|
20
|
+
protected abstract toPrimitive(entity: T): object;
|
|
21
|
+
|
|
22
|
+
public async save(entity: T): Promise<void> {
|
|
23
|
+
if (entity.deleted) {
|
|
24
|
+
this.store = this.store.filter(item => item.id !== entity.id);
|
|
25
|
+
this.ids.delete(entity.id);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (this.ids.has(entity.id)) {
|
|
30
|
+
this.store = this.store.map((item) => {
|
|
31
|
+
if (item.id === entity.id) {
|
|
32
|
+
return entity;
|
|
33
|
+
}
|
|
34
|
+
return item;
|
|
35
|
+
});
|
|
36
|
+
} else {
|
|
37
|
+
this.store.push(entity);
|
|
38
|
+
this.ids.set(entity.id, true);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public async getById(id: string): Promise<T | null> {
|
|
43
|
+
return this.store.find(item => item.id === id) || null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public async getAll(options: { filter?: string; order?: OrderOption<T> } = {}): Promise<T[]> {
|
|
47
|
+
const filter = nql(options.filter);
|
|
48
|
+
|
|
49
|
+
const results = this.store.slice().filter(item => filter.queryJSON(this.toPrimitive(item)));
|
|
50
|
+
|
|
51
|
+
if (options.order) {
|
|
52
|
+
for (const order of options.order) {
|
|
53
|
+
results.sort((a, b) => {
|
|
54
|
+
if (order.direction === 'asc') {
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
56
|
+
return a[order.field] as any > (b[order.field] as any) ? 1 : -1;
|
|
57
|
+
} else {
|
|
58
|
+
return a[order.field] < b[order.field] ? 1 : -1;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return results;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public async getPage(options: { filter?: string; page: number; limit: number; order?: Order<T>[] } = {page: 1, limit: 15}): Promise<T[]> {
|
|
68
|
+
const results = await this.getAll(options);
|
|
69
|
+
|
|
70
|
+
const start = (options.page - 1) * options.limit;
|
|
71
|
+
const end = start + options.limit;
|
|
72
|
+
|
|
73
|
+
return results.slice(start, end);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public async getCount(options: { filter?: string }): Promise<number> {
|
|
77
|
+
const results = await this.getAll(options);
|
|
78
|
+
return results.length;
|
|
79
|
+
}
|
|
80
|
+
}
|