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
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ```
|
|
3
|
+
* const Minifier = require('./Minifier');
|
|
4
|
+
* const minifier = new Minifier({
|
|
5
|
+
* src: 'my/src/path',
|
|
6
|
+
* dest: 'my/dest/path'
|
|
7
|
+
* });
|
|
8
|
+
*
|
|
9
|
+
* minifier.minify({
|
|
10
|
+
* 'some.css': '*.css',
|
|
11
|
+
* 'then.js': '!(other).js'
|
|
12
|
+
* });
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* - Minfier constructor requires a src and a dest
|
|
16
|
+
* - minify() function takes an object with destination file as the key and source glob as the value
|
|
17
|
+
* - globs can be anything tiny-glob supports
|
|
18
|
+
* - destination files must end with .css or .js
|
|
19
|
+
* - src files will be minified according to their destination file extension
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const errors = require('@tryghost/errors');
|
|
23
|
+
const debug = require('@tryghost/debug')('minifier');
|
|
24
|
+
const tpl = require('@tryghost/tpl');
|
|
25
|
+
const glob = require('tiny-glob');
|
|
26
|
+
const path = require('path');
|
|
27
|
+
const fs = require('fs').promises;
|
|
28
|
+
const isWin = process.platform === 'win32';
|
|
29
|
+
|
|
30
|
+
const messages = {
|
|
31
|
+
badDestination: {
|
|
32
|
+
message: 'Unexpected destination {dest}',
|
|
33
|
+
context: 'Minifier expected a destination that ended in .css or .js'
|
|
34
|
+
},
|
|
35
|
+
badSource: {
|
|
36
|
+
message: 'Unable to read source files {src}',
|
|
37
|
+
context: 'Minifier was unable to locate or read the source files'
|
|
38
|
+
},
|
|
39
|
+
missingConstructorOption: {
|
|
40
|
+
message: 'Minifier missing {opt} option',
|
|
41
|
+
context: 'new Minifier({}) requires a {opt} option'
|
|
42
|
+
},
|
|
43
|
+
globalHelp: 'Refer to the README for Minifier for how to use this module'
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// public API for minify hooks
|
|
47
|
+
class Minifier {
|
|
48
|
+
constructor({src, dest}) {
|
|
49
|
+
if (!src) {
|
|
50
|
+
throw new errors.IncorrectUsageError({
|
|
51
|
+
message: tpl(messages.missingConstructorOption.message, {opt: 'src'}),
|
|
52
|
+
context: tpl(messages.missingConstructorOption.context, {opt: 'src'}),
|
|
53
|
+
help: tpl(messages.globalHelp)
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
if (!dest) {
|
|
57
|
+
throw new errors.IncorrectUsageError({
|
|
58
|
+
message: tpl(messages.missingConstructorOption.message, {opt: 'dest'}),
|
|
59
|
+
context: tpl(messages.missingConstructorOption.context, {opt: 'dest'}),
|
|
60
|
+
help: tpl(messages.globalHelp)
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
this.srcPath = src;
|
|
64
|
+
this.destPath = dest;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
getFullSrc(src) {
|
|
68
|
+
return path.join(this.srcPath, src);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
getFullDest(dest) {
|
|
72
|
+
return path.join(this.destPath, dest);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async minifyCSS(contents) {
|
|
76
|
+
const csso = require('csso');
|
|
77
|
+
const result = await csso.minify(contents);
|
|
78
|
+
if (result && result.css) {
|
|
79
|
+
return result.css;
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async minifyJS(contents) {
|
|
85
|
+
const terser = require('terser');
|
|
86
|
+
const result = await terser.minify(contents);
|
|
87
|
+
if (result && result.code) {
|
|
88
|
+
return result.code;
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async getMatchingFiles(src) {
|
|
94
|
+
let fullSrc = this.getFullSrc(src);
|
|
95
|
+
if (isWin) {
|
|
96
|
+
fullSrc = fullSrc.replace(/\\/g,'/');
|
|
97
|
+
}
|
|
98
|
+
return await glob(fullSrc);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async readFiles(files) {
|
|
102
|
+
let mergedFiles = '';
|
|
103
|
+
for (const file of files) {
|
|
104
|
+
const contents = await fs.readFile(file, 'utf8');
|
|
105
|
+
mergedFiles += contents;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return mergedFiles;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async getSrcFileContents(src) {
|
|
112
|
+
try {
|
|
113
|
+
const files = await this.getMatchingFiles(src);
|
|
114
|
+
|
|
115
|
+
if (files) {
|
|
116
|
+
return await this.readFiles(files);
|
|
117
|
+
}
|
|
118
|
+
} catch (error) {
|
|
119
|
+
throw new errors.IncorrectUsageError({
|
|
120
|
+
message: tpl(messages.badSource.message, {src}),
|
|
121
|
+
context: tpl(messages.badSource.context),
|
|
122
|
+
help: tpl(messages.globalHelp)
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async writeFile(contents, dest) {
|
|
128
|
+
if (contents) {
|
|
129
|
+
let writePath = this.getFullDest(dest);
|
|
130
|
+
// Ensure the output folder exists
|
|
131
|
+
await fs.mkdir(this.destPath, {recursive: true});
|
|
132
|
+
// Create the file
|
|
133
|
+
await fs.writeFile(writePath, contents);
|
|
134
|
+
return writePath;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Minify files
|
|
140
|
+
*
|
|
141
|
+
* @param {Object} globs An object in the form of
|
|
142
|
+
* ```js
|
|
143
|
+
* {
|
|
144
|
+
* 'destination1.js': 'glob/*.js',
|
|
145
|
+
* 'destination2.js': 'glob2/*.js'
|
|
146
|
+
* }
|
|
147
|
+
* ```
|
|
148
|
+
* @param {Object} [options]
|
|
149
|
+
* @param {Object} [options.replacements] Key value pairs that should get replaced in the content before minifying
|
|
150
|
+
* @returns {Promise<string[]>} List of minified files (keys of globs)
|
|
151
|
+
*/
|
|
152
|
+
async minify(globs, options) {
|
|
153
|
+
debug('Begin', globs);
|
|
154
|
+
const destinations = Object.keys(globs);
|
|
155
|
+
const minifiedFiles = [];
|
|
156
|
+
|
|
157
|
+
for (const dest of destinations) {
|
|
158
|
+
const src = globs[dest];
|
|
159
|
+
let contents = await this.getSrcFileContents(src);
|
|
160
|
+
|
|
161
|
+
if (options?.replacements) {
|
|
162
|
+
for (const key of Object.keys(options.replacements)) {
|
|
163
|
+
contents = contents.replace(key, options.replacements[key]);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
let minifiedContents;
|
|
167
|
+
|
|
168
|
+
if (dest.endsWith('.css')) {
|
|
169
|
+
minifiedContents = await this.minifyCSS(contents);
|
|
170
|
+
} else if (dest.endsWith('.js')) {
|
|
171
|
+
minifiedContents = await this.minifyJS(contents);
|
|
172
|
+
} else {
|
|
173
|
+
throw new errors.IncorrectUsageError({
|
|
174
|
+
message: tpl(messages.badDestination.message, {dest}),
|
|
175
|
+
context: tpl(messages.badDestination.context),
|
|
176
|
+
help: tpl(messages.globalHelp)
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const result = await this.writeFile(minifiedContents, dest);
|
|
181
|
+
if (result) {
|
|
182
|
+
minifiedFiles.push(dest);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
debug('End');
|
|
187
|
+
return minifiedFiles;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
module.exports = Minifier;
|
|
@@ -19,7 +19,8 @@ module.exports = function previewController(req, res, next) {
|
|
|
19
19
|
const params = {
|
|
20
20
|
uuid: req.params.uuid,
|
|
21
21
|
status: 'all',
|
|
22
|
-
include: 'authors,tags,tiers'
|
|
22
|
+
include: 'authors,tags,tiers',
|
|
23
|
+
member_status: req.query?.member_status
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
return api[res.routerOptions.query.controller]
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
const BaseCacheAdapter = require('@tryghost/adapter-base-cache');
|
|
2
|
+
const logging = require('@tryghost/logging');
|
|
3
|
+
const metrics = require('@tryghost/metrics');
|
|
4
|
+
const debug = require('@tryghost/debug')('redis-cache');
|
|
5
|
+
const cacheManager = require('cache-manager');
|
|
6
|
+
const redisStoreFactory = require('./redis-store-factory');
|
|
7
|
+
const calculateSlot = require('cluster-key-slot');
|
|
8
|
+
|
|
9
|
+
class AdapterCacheRedis extends BaseCacheAdapter {
|
|
10
|
+
/**
|
|
11
|
+
*
|
|
12
|
+
* @param {Object} config
|
|
13
|
+
* @param {Object} [config.cache] - caching instance compatible with cache-manager's redis store
|
|
14
|
+
* @param {String} [config.host] - redis host used in case no cache instance provided
|
|
15
|
+
* @param {Number} [config.port] - redis port used in case no cache instance provided
|
|
16
|
+
* @param {String} [config.password] - redis password used in case no cache instance provided
|
|
17
|
+
* @param {Object} [config.clusterConfig] - redis cluster config used in case no cache instance provided
|
|
18
|
+
* @param {Object} [config.storeConfig] - extra redis client config used in case no cache instance provided
|
|
19
|
+
* @param {Number} [config.ttl] - default cached value Time To Live (expiration) in *seconds*
|
|
20
|
+
* @param {Number} [config.getTimeoutMilliseconds] - default timeout for cache get operations in *milliseconds*
|
|
21
|
+
* @param {Number} [config.refreshAheadFactor] - 0-1 number to use to determine how old (as a percentage of ttl) an entry should be before refreshing it
|
|
22
|
+
* @param {String} [config.keyPrefix] - prefix to use when building a unique cache key, e.g.: 'some_id:image-sizes:'
|
|
23
|
+
* @param {Boolean} [config.reuseConnection] - specifies if the redis store/connection should be reused within the process
|
|
24
|
+
*/
|
|
25
|
+
constructor(config) {
|
|
26
|
+
super();
|
|
27
|
+
|
|
28
|
+
this.cache = config.cache;
|
|
29
|
+
|
|
30
|
+
if (!this.cache) {
|
|
31
|
+
// @NOTE: this condition can be avoided if we add merging of nested options
|
|
32
|
+
// to adapter configuration. Than adding adapter-specific {clusterConfig: {options: {ttl: XXX}}}
|
|
33
|
+
// will be enough to set ttl for redis cluster.
|
|
34
|
+
if (config.ttl && config.clusterConfig) {
|
|
35
|
+
if (!config.clusterConfig.options) {
|
|
36
|
+
config.clusterConfig.options = {};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
config.clusterConfig.options.ttl = config.ttl;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const storeOptions = {
|
|
43
|
+
ttl: config.ttl,
|
|
44
|
+
host: config.host,
|
|
45
|
+
port: config.port,
|
|
46
|
+
username: config.username,
|
|
47
|
+
password: config.password,
|
|
48
|
+
retryStrategy: () => {
|
|
49
|
+
return (config.storeConfig.retryConnectSeconds || 10) * 1000;
|
|
50
|
+
},
|
|
51
|
+
...config.storeConfig,
|
|
52
|
+
clusterConfig: config.clusterConfig
|
|
53
|
+
};
|
|
54
|
+
const store = redisStoreFactory.getRedisStore(storeOptions, config.reuseConnection);
|
|
55
|
+
|
|
56
|
+
this.cache = cacheManager.caching({
|
|
57
|
+
store: store,
|
|
58
|
+
...storeOptions
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
this.ttl = config.ttl;
|
|
63
|
+
this.refreshAheadFactor = config.refreshAheadFactor || 0;
|
|
64
|
+
this.getTimeoutMilliseconds = config.getTimeoutMilliseconds || null;
|
|
65
|
+
this.currentlyExecutingBackgroundRefreshes = new Set();
|
|
66
|
+
this.keyPrefix = config.keyPrefix;
|
|
67
|
+
this._keysPattern = config.keyPrefix ? `${config.keyPrefix}*` : '';
|
|
68
|
+
this.redisClient = this.cache.store.getClient();
|
|
69
|
+
this.redisClient.on('error', this.handleRedisError);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
handleRedisError(error) {
|
|
73
|
+
logging.error(error);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
#getPrimaryRedisNode() {
|
|
77
|
+
debug('getPrimaryRedisNode');
|
|
78
|
+
if (this.redisClient.constructor.name !== 'Cluster') {
|
|
79
|
+
return this.redisClient;
|
|
80
|
+
}
|
|
81
|
+
const slot = calculateSlot(this.keyPrefix);
|
|
82
|
+
const [ip, port] = this.redisClient.slots[slot][0].split(':');
|
|
83
|
+
for (const node of this.redisClient.nodes()) {
|
|
84
|
+
if (node.options.host === ip && node.options.port === parseInt(port)) {
|
|
85
|
+
return node;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
#scanNodeForKeys(node) {
|
|
92
|
+
debug(`scanNodeForKeys matching ${this._keysPattern}`);
|
|
93
|
+
return new Promise((resolve, reject) => {
|
|
94
|
+
const stream = node.scanStream({match: this._keysPattern, count: 100});
|
|
95
|
+
let keys = [];
|
|
96
|
+
stream.on('data', (resultKeys) => {
|
|
97
|
+
keys = keys.concat(resultKeys);
|
|
98
|
+
});
|
|
99
|
+
stream.on('error', (e) => {
|
|
100
|
+
reject(e);
|
|
101
|
+
});
|
|
102
|
+
stream.on('end', () => {
|
|
103
|
+
resolve(keys);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async #getKeys() {
|
|
109
|
+
debug('#getKeys');
|
|
110
|
+
const primaryNode = this.#getPrimaryRedisNode();
|
|
111
|
+
if (primaryNode === null) {
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
return await this.#scanNodeForKeys(primaryNode);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* This is a recommended way to build cache key prefixes from
|
|
119
|
+
* the cache-manager package. Might be a good contribution to make
|
|
120
|
+
* in the package itself (https://github.com/node-cache-manager/node-cache-manager/issues/158)
|
|
121
|
+
* @param {string} key
|
|
122
|
+
* @returns {string}
|
|
123
|
+
*/
|
|
124
|
+
_buildKey(key) {
|
|
125
|
+
if (this.keyPrefix) {
|
|
126
|
+
return `${this.keyPrefix}${key}`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return key;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* This is a method to remove the key prefix from any raw key returned from redis.
|
|
134
|
+
* @param {string} key
|
|
135
|
+
* @returns {string}
|
|
136
|
+
*/
|
|
137
|
+
_removeKeyPrefix(key) {
|
|
138
|
+
return key.slice(this.keyPrefix.length);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
*
|
|
143
|
+
* @param {String} internalKey
|
|
144
|
+
*/
|
|
145
|
+
async shouldRefresh(internalKey) {
|
|
146
|
+
if (this.refreshAheadFactor === 0) {
|
|
147
|
+
debug(`shouldRefresh ${internalKey}: false - refreshAheadFactor = 0`);
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
if (this.refreshAheadFactor === 1) {
|
|
151
|
+
debug(`shouldRefresh ${internalKey}: true - refreshAheadFactor = 1`);
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
const ttlRemainingForEntry = await this.cache.ttl(internalKey);
|
|
156
|
+
const shouldRefresh = ttlRemainingForEntry < this.refreshAheadFactor * this.ttl;
|
|
157
|
+
debug(`shouldRefresh ${internalKey}: ${shouldRefresh} - TTL remaining = ${ttlRemainingForEntry}`);
|
|
158
|
+
return shouldRefresh;
|
|
159
|
+
} catch (err) {
|
|
160
|
+
logging.error(err);
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Returns the specified key from the cache if it exists, otherwise returns null
|
|
167
|
+
* - If getTimeoutMilliseconds is set, the method will return a promise that resolves with the value or null if the timeout is exceeded
|
|
168
|
+
*
|
|
169
|
+
* @param {string} key
|
|
170
|
+
*/
|
|
171
|
+
async _get(key) {
|
|
172
|
+
if (typeof this.getTimeoutMilliseconds !== 'number') {
|
|
173
|
+
return this.cache.get(key);
|
|
174
|
+
} else {
|
|
175
|
+
return new Promise((resolve) => {
|
|
176
|
+
const timer = setTimeout(() => {
|
|
177
|
+
debug('get', key, 'timeout');
|
|
178
|
+
resolve(null);
|
|
179
|
+
}, this.getTimeoutMilliseconds);
|
|
180
|
+
|
|
181
|
+
this.cache.get(key).then((result) => {
|
|
182
|
+
clearTimeout(timer);
|
|
183
|
+
resolve(result);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
*
|
|
191
|
+
* @param {string} key
|
|
192
|
+
* @param {() => Promise<any>} [fetchData] An optional function to fetch the data, which will be used in the case of a cache MISS or a background refresh
|
|
193
|
+
*/
|
|
194
|
+
async get(key, fetchData) {
|
|
195
|
+
const internalKey = this._buildKey(key);
|
|
196
|
+
try {
|
|
197
|
+
const result = await this._get(internalKey);
|
|
198
|
+
debug(`get ${internalKey}: Cache ${result ? 'HIT' : 'MISS'}`);
|
|
199
|
+
if (!fetchData) {
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
if (result) {
|
|
203
|
+
const shouldRefresh = await this.shouldRefresh(internalKey);
|
|
204
|
+
const isRefreshing = this.currentlyExecutingBackgroundRefreshes.has(internalKey);
|
|
205
|
+
if (isRefreshing) {
|
|
206
|
+
debug(`Background refresh already happening for ${internalKey}`);
|
|
207
|
+
}
|
|
208
|
+
if (!isRefreshing && shouldRefresh) {
|
|
209
|
+
debug(`Doing background refresh for ${internalKey}`);
|
|
210
|
+
this.currentlyExecutingBackgroundRefreshes.add(internalKey);
|
|
211
|
+
fetchData().then(async (data) => {
|
|
212
|
+
await this.set(key, data); // We don't use `internalKey` here because `set` handles it
|
|
213
|
+
this.currentlyExecutingBackgroundRefreshes.delete(internalKey);
|
|
214
|
+
}).catch((error) => {
|
|
215
|
+
this.currentlyExecutingBackgroundRefreshes.delete(internalKey);
|
|
216
|
+
logging.error({
|
|
217
|
+
message: 'There was an error refreshing cache data in the background',
|
|
218
|
+
error: error
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
return result;
|
|
223
|
+
} else {
|
|
224
|
+
const data = await fetchData();
|
|
225
|
+
await this.set(key, data); // We don't use `internalKey` here because `set` handles it
|
|
226
|
+
return data;
|
|
227
|
+
}
|
|
228
|
+
} catch (err) {
|
|
229
|
+
logging.error(err);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
*
|
|
235
|
+
* @param {String} key
|
|
236
|
+
* @param {*} value
|
|
237
|
+
*/
|
|
238
|
+
async set(key, value) {
|
|
239
|
+
const internalKey = this._buildKey(key);
|
|
240
|
+
debug('set', internalKey);
|
|
241
|
+
try {
|
|
242
|
+
return await this.cache.set(internalKey, value);
|
|
243
|
+
} catch (err) {
|
|
244
|
+
logging.error(err);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Reset the cache by deleting everything from redis
|
|
250
|
+
*/
|
|
251
|
+
async reset() {
|
|
252
|
+
debug('reset');
|
|
253
|
+
try {
|
|
254
|
+
const t0 = performance.now();
|
|
255
|
+
logging.debug(`[RedisAdapter] Clearing cache: scanning for keys matching ${this._keysPattern}`);
|
|
256
|
+
const keys = await this.#getKeys();
|
|
257
|
+
logging.debug(`[RedisAdapter] Clearing cache: found ${keys.length} keys matching ${this._keysPattern} in ${(performance.now() - t0).toFixed(1)}ms`);
|
|
258
|
+
metrics.metric('cache-reset-scan', (performance.now() - t0).toFixed(1));
|
|
259
|
+
const t1 = performance.now();
|
|
260
|
+
for (const key of keys) {
|
|
261
|
+
await this.cache.del(key);
|
|
262
|
+
}
|
|
263
|
+
logging.debug(`[RedisAdapter] Clearing cache: deleted ${keys.length} keys matching ${this._keysPattern} in ${(performance.now() - t1).toFixed(1)}ms`);
|
|
264
|
+
metrics.metric('cache-reset-delete', (performance.now() - t1).toFixed(1));
|
|
265
|
+
metrics.metric('cache-reset', (performance.now() - t0).toFixed(1));
|
|
266
|
+
metrics.metric('cache-reset-key-count', keys.length);
|
|
267
|
+
} catch (err) {
|
|
268
|
+
logging.error(err);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Helper method to assist "getAll" type of operations
|
|
274
|
+
* @returns {Promise<Array<String>>} all keys present in the cache
|
|
275
|
+
*/
|
|
276
|
+
async keys() {
|
|
277
|
+
try {
|
|
278
|
+
return (await this.#getKeys()).map((key) => {
|
|
279
|
+
return this._removeKeyPrefix(key);
|
|
280
|
+
});
|
|
281
|
+
} catch (err) {
|
|
282
|
+
logging.error(err);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
module.exports = AdapterCacheRedis;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const defaultCacheManager = require('cache-manager-ioredis');
|
|
2
|
+
let redisStoreSingletonInstance;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param {object} [storeOptions] options to pass to the Redis store instance
|
|
7
|
+
* @param {boolean} [reuseConnection] specifies if the Redis store/connection should be reused within the process
|
|
8
|
+
* @param {object} [CacheManager] CacheManager constructor to instantiate, defaults to cache-manager-ioredis
|
|
9
|
+
*/
|
|
10
|
+
const getRedisStore = (storeOptions, reuseConnection = true, CacheManager = defaultCacheManager) => {
|
|
11
|
+
if (storeOptions && reuseConnection) {
|
|
12
|
+
if (!redisStoreSingletonInstance) {
|
|
13
|
+
redisStoreSingletonInstance = CacheManager.create(storeOptions);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return redisStoreSingletonInstance;
|
|
17
|
+
} else {
|
|
18
|
+
return CacheManager;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
module.exports.getRedisStore = getRedisStore;
|
|
@@ -30,10 +30,16 @@ function getCacheHeaderFromEventString(event, dto) {
|
|
|
30
30
|
return true;
|
|
31
31
|
}
|
|
32
32
|
if (event === 'scheduled_updated' || event === 'draft_updated') {
|
|
33
|
+
const baseUrl = urlUtils.urlFor({
|
|
34
|
+
relativeUrl: urlUtils.urlJoin('/p', dto.uuid, '/')
|
|
35
|
+
});
|
|
33
36
|
return {
|
|
34
|
-
value:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
value: [
|
|
38
|
+
baseUrl,
|
|
39
|
+
`${baseUrl}?member_status=anonymous`,
|
|
40
|
+
`${baseUrl}?member_status=free`,
|
|
41
|
+
`${baseUrl}?member_status=paid`
|
|
42
|
+
].join(', ')
|
|
37
43
|
};
|
|
38
44
|
}
|
|
39
45
|
}
|
|
@@ -2,11 +2,39 @@ const tpl = require('@tryghost/tpl');
|
|
|
2
2
|
const errors = require('@tryghost/errors');
|
|
3
3
|
const models = require('../../models');
|
|
4
4
|
const ALLOWED_INCLUDES = ['authors', 'tags'];
|
|
5
|
+
const ALLOWED_MEMBER_STATUSES = ['anonymous', 'free', 'paid'];
|
|
5
6
|
|
|
6
7
|
const messages = {
|
|
7
8
|
postNotFound: 'Post not found.'
|
|
8
9
|
};
|
|
9
10
|
|
|
11
|
+
// Simulate serving content as different member states by setting the minimal
|
|
12
|
+
// member context needed for content gating to function
|
|
13
|
+
const _addMemberContextToFrame = (frame) => {
|
|
14
|
+
if (!frame?.options?.member_status) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// only set apiType when given a member_status to preserve backwards compatibility
|
|
19
|
+
// where we used to serve "Admin API" content with no gating for all previews
|
|
20
|
+
frame.apiType = 'content';
|
|
21
|
+
|
|
22
|
+
frame.original ??= {};
|
|
23
|
+
frame.original.context ??= {};
|
|
24
|
+
|
|
25
|
+
if (frame.options?.member_status === 'free') {
|
|
26
|
+
frame.original.context.member = {
|
|
27
|
+
status: 'free'
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (frame.options?.member_status === 'paid') {
|
|
32
|
+
frame.original.context.member = {
|
|
33
|
+
status: 'paid'
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
10
38
|
/** @type {import('@tryghost/api-framework').Controller} */
|
|
11
39
|
const controller = {
|
|
12
40
|
docName: 'previews',
|
|
@@ -17,7 +45,8 @@ const controller = {
|
|
|
17
45
|
},
|
|
18
46
|
permissions: true,
|
|
19
47
|
options: [
|
|
20
|
-
'include'
|
|
48
|
+
'include',
|
|
49
|
+
'member_status'
|
|
21
50
|
],
|
|
22
51
|
data: [
|
|
23
52
|
'uuid'
|
|
@@ -26,6 +55,9 @@ const controller = {
|
|
|
26
55
|
options: {
|
|
27
56
|
include: {
|
|
28
57
|
values: ALLOWED_INCLUDES
|
|
58
|
+
},
|
|
59
|
+
member_status: {
|
|
60
|
+
values: ALLOWED_MEMBER_STATUSES
|
|
29
61
|
}
|
|
30
62
|
},
|
|
31
63
|
data: {
|
|
@@ -35,6 +67,8 @@ const controller = {
|
|
|
35
67
|
}
|
|
36
68
|
},
|
|
37
69
|
query(frame) {
|
|
70
|
+
_addMemberContextToFrame(frame);
|
|
71
|
+
|
|
38
72
|
return models.Post.findOne(Object.assign({status: 'all'}, frame.data), frame.options)
|
|
39
73
|
.then((model) => {
|
|
40
74
|
if (!model) {
|
|
@@ -21,7 +21,8 @@ const controller = {
|
|
|
21
21
|
},
|
|
22
22
|
options: [
|
|
23
23
|
'include',
|
|
24
|
-
'type'
|
|
24
|
+
'type',
|
|
25
|
+
'id'
|
|
25
26
|
],
|
|
26
27
|
data: [
|
|
27
28
|
'name'
|
|
@@ -32,6 +33,9 @@ const controller = {
|
|
|
32
33
|
type: {
|
|
33
34
|
required: true,
|
|
34
35
|
values: Object.keys(allowedTypes)
|
|
36
|
+
},
|
|
37
|
+
id: {
|
|
38
|
+
required: false
|
|
35
39
|
}
|
|
36
40
|
},
|
|
37
41
|
data: {
|
|
@@ -41,7 +45,7 @@ const controller = {
|
|
|
41
45
|
}
|
|
42
46
|
},
|
|
43
47
|
query(frame) {
|
|
44
|
-
return models.Base.Model.generateSlug(allowedTypes[frame.options.type], frame.data.name, {status: 'all'})
|
|
48
|
+
return models.Base.Model.generateSlug(allowedTypes[frame.options.type], frame.data.name, {status: 'all', modelId: frame.options.id})
|
|
45
49
|
.then((slug) => {
|
|
46
50
|
if (!slug) {
|
|
47
51
|
return Promise.reject(new errors.InternalServerError({
|
|
@@ -8,7 +8,7 @@ const {PERMIT_ACCESS} = membersService.contentGating;
|
|
|
8
8
|
const HAS_GATED_BLOCKS_REGEX = /<!--\s*kg-gated-block:begin/;
|
|
9
9
|
// Match gated block comments
|
|
10
10
|
// e.g. <!--kg-gated-block:begin nonMember:true memberSegment:"status:free"-->...gated content<!--kg-gated-block:end-->
|
|
11
|
-
const GATED_BLOCK_REGEX =
|
|
11
|
+
const GATED_BLOCK_REGEX = /<!--kg-gated-block:begin ([^\n]+?)\s*-->([\s\S]*?)<!--kg-gated-block:end-->/g;
|
|
12
12
|
// Match the key-value pairs (with optional quotes around the value) in the gated-block:begin comment
|
|
13
13
|
const GATED_BLOCK_PARAM_REGEX = /\b(?<key>\w+):["']?(?<value>[^"'\s]+)["']?/g;
|
|
14
14
|
|
|
@@ -70,15 +70,14 @@ const stripGatedBlocks = function (html, member) {
|
|
|
70
70
|
});
|
|
71
71
|
};
|
|
72
72
|
|
|
73
|
-
function
|
|
73
|
+
function _updateTextAttrs(attrs) {
|
|
74
74
|
if (attrs.html) {
|
|
75
75
|
attrs.plaintext = htmlToPlaintext.excerpt(attrs.html);
|
|
76
76
|
}
|
|
77
|
-
}
|
|
78
77
|
|
|
79
|
-
function _updateExcerpt(attrs) {
|
|
80
78
|
if (!attrs.custom_excerpt && attrs.excerpt) {
|
|
81
|
-
attrs.
|
|
79
|
+
const plaintext = attrs.plaintext || htmlToPlaintext.excerpt(attrs.html);
|
|
80
|
+
attrs.excerpt = plaintext.substring(0, 500);
|
|
82
81
|
}
|
|
83
82
|
}
|
|
84
83
|
|
|
@@ -96,8 +95,7 @@ const forPost = (attrs, frame) => {
|
|
|
96
95
|
|
|
97
96
|
if (paywallIndex !== -1) {
|
|
98
97
|
attrs.html = attrs.html.slice(0, paywallIndex);
|
|
99
|
-
|
|
100
|
-
_updateExcerpt(attrs);
|
|
98
|
+
_updateTextAttrs(attrs);
|
|
101
99
|
} else {
|
|
102
100
|
['plaintext', 'html', 'excerpt'].forEach((field) => {
|
|
103
101
|
if (attrs[field] !== undefined) {
|
|
@@ -111,8 +109,7 @@ const forPost = (attrs, frame) => {
|
|
|
111
109
|
const hasGatedBlocks = HAS_GATED_BLOCKS_REGEX.test(attrs.html);
|
|
112
110
|
if (hasGatedBlocks) {
|
|
113
111
|
attrs.html = module.exports.stripGatedBlocks(attrs.html, frame.original.context.member);
|
|
114
|
-
|
|
115
|
-
_updateExcerpt(attrs);
|
|
112
|
+
_updateTextAttrs(attrs);
|
|
116
113
|
}
|
|
117
114
|
}
|
|
118
115
|
|