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,28 @@
|
|
|
1
|
+
const TableImporter = require('./TableImporter');
|
|
2
|
+
const {faker} = require('@faker-js/faker');
|
|
3
|
+
const {slugify} = require('@tryghost/string');
|
|
4
|
+
const {blogStartDate} = require('../utils/blog-info');
|
|
5
|
+
|
|
6
|
+
class BenefitsImporter extends TableImporter {
|
|
7
|
+
static table = 'benefits';
|
|
8
|
+
static dependencies = [];
|
|
9
|
+
defaultQuantity = 5;
|
|
10
|
+
|
|
11
|
+
constructor(knex, transaction) {
|
|
12
|
+
super(BenefitsImporter.table, knex, transaction);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
generate() {
|
|
16
|
+
const name = faker.company.catchPhrase();
|
|
17
|
+
const sixMonthsLater = new Date(blogStartDate);
|
|
18
|
+
sixMonthsLater.setMonth(sixMonthsLater.getMonth() + 6);
|
|
19
|
+
return {
|
|
20
|
+
id: this.fastFakeObjectId(),
|
|
21
|
+
name: name,
|
|
22
|
+
slug: `${slugify(name)}-${faker.random.numeric(3)}`,
|
|
23
|
+
created_at: faker.date.between(blogStartDate, sixMonthsLater)
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports = BenefitsImporter;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
const {faker} = require('@faker-js/faker');
|
|
2
|
+
const TableImporter = require('./TableImporter');
|
|
3
|
+
const {luck} = require('../utils/random');
|
|
4
|
+
const generateEvents = require('../utils/event-generator');
|
|
5
|
+
const dateToDatabaseString = require('../utils/database-date');
|
|
6
|
+
|
|
7
|
+
class CommentsImporter extends TableImporter {
|
|
8
|
+
static table = 'comments';
|
|
9
|
+
static dependencies = ['posts'];
|
|
10
|
+
|
|
11
|
+
constructor(knex, transaction) {
|
|
12
|
+
super(CommentsImporter.table, knex, transaction);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async import(quantity) {
|
|
16
|
+
const posts = await this.transaction.select('id', 'published_at').from('posts')
|
|
17
|
+
.where('status', 'published');
|
|
18
|
+
this.members = await this.transaction.select('id', 'created_at').from('members');
|
|
19
|
+
|
|
20
|
+
this.commentsPerPost = quantity ? quantity / posts.length : 10;
|
|
21
|
+
|
|
22
|
+
await this.importForEach(posts, this.commentsPerPost);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
setReferencedModel(model) {
|
|
26
|
+
this.model = model;
|
|
27
|
+
|
|
28
|
+
this.commentIds = [];
|
|
29
|
+
|
|
30
|
+
this.timestamps = generateEvents({
|
|
31
|
+
shape: 'ease-out',
|
|
32
|
+
trend: 'negative',
|
|
33
|
+
// Steady readers login more, readers who lose interest read less overall.
|
|
34
|
+
// ceil because members will all have logged in at least once
|
|
35
|
+
total: faker.datatype.number({min: 0, max: this.commentsPerPost}),
|
|
36
|
+
startTime: new Date(model.published_at),
|
|
37
|
+
endTime: new Date()
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
this.possibleMembers = this.members.filter(member => new Date(member.created_at) < new Date(model.published_at));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
generate() {
|
|
44
|
+
const timestamp = this.timestamps.pop();
|
|
45
|
+
if (!timestamp) {
|
|
46
|
+
// Out of events for this post
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (this.possibleMembers.length === 0) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const isReply = luck(30) && this.commentIds.length > 0; // 30% of comments are replies
|
|
55
|
+
|
|
56
|
+
const event = {
|
|
57
|
+
id: this.fastFakeObjectId(),
|
|
58
|
+
post_id: this.model.id,
|
|
59
|
+
member_id: this.possibleMembers[faker.datatype.number(this.possibleMembers.length - 1)].id,
|
|
60
|
+
parent_id: isReply ? this.commentIds[faker.datatype.number(this.commentIds.length - 1)] : undefined,
|
|
61
|
+
status: 'published',
|
|
62
|
+
created_at: dateToDatabaseString(timestamp),
|
|
63
|
+
updated_at: dateToDatabaseString(timestamp),
|
|
64
|
+
html: `<p>${faker.lorem.sentence().replace(/[&<>"']/g, c => `&#${c.charCodeAt(0)};`)}</p>`
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
this.commentIds.push(event.id);
|
|
68
|
+
|
|
69
|
+
return event;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = CommentsImporter;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const TableImporter = require('./TableImporter');
|
|
2
|
+
const {faker} = require('@faker-js/faker');
|
|
3
|
+
const dateToDatabaseString = require('../utils/database-date');
|
|
4
|
+
|
|
5
|
+
class EmailBatchesImporter extends TableImporter {
|
|
6
|
+
static table = 'email_batches';
|
|
7
|
+
static dependencies = ['emails'];
|
|
8
|
+
|
|
9
|
+
constructor(knex, transaction) {
|
|
10
|
+
super(EmailBatchesImporter.table, knex, transaction);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async import(quantity) {
|
|
14
|
+
const emails = await this.transaction.select('id', 'created_at', 'email_count').from('emails');
|
|
15
|
+
|
|
16
|
+
// 1 batch per 1000 recipients
|
|
17
|
+
await this.importForEach(emails, quantity ?? (() => {
|
|
18
|
+
return Math.ceil(this.model.email_count / 1000);
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
generate() {
|
|
23
|
+
const emailSentDate = new Date(this.model.created_at);
|
|
24
|
+
const latestUpdatedDate = new Date(this.model.created_at);
|
|
25
|
+
latestUpdatedDate.setHours(latestUpdatedDate.getHours() + 1);
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
id: this.fastFakeObjectId(),
|
|
29
|
+
email_id: this.model.id,
|
|
30
|
+
provider_id: `${new Date().toISOString().split('.')[0].replace(/[^0-9]/g, '')}.${faker.datatype.hexadecimal({length: 16, prefix: '', case: 'lower'})}@m.example.com`,
|
|
31
|
+
status: 'submitted', // TODO: introduce failures
|
|
32
|
+
created_at: this.model.created_at,
|
|
33
|
+
updated_at: dateToDatabaseString(faker.date.between(emailSentDate, latestUpdatedDate))
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = EmailBatchesImporter;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const TableImporter = require('./TableImporter');
|
|
2
|
+
const {faker} = require('@faker-js/faker');
|
|
3
|
+
|
|
4
|
+
class EmailRecipientFailuresImporter extends TableImporter {
|
|
5
|
+
static table = 'email_recipient_failures';
|
|
6
|
+
static dependencies = ['email_recipients'];
|
|
7
|
+
|
|
8
|
+
constructor(knex, transaction) {
|
|
9
|
+
super(EmailRecipientFailuresImporter.table, knex, transaction);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async import(quantity) {
|
|
13
|
+
const recipients = await this.transaction
|
|
14
|
+
.select(
|
|
15
|
+
'id',
|
|
16
|
+
'email_id',
|
|
17
|
+
'member_id',
|
|
18
|
+
'failed_at')
|
|
19
|
+
.from('email_recipients')
|
|
20
|
+
.whereNotNull('failed_at');
|
|
21
|
+
|
|
22
|
+
await this.importForEach(recipients, quantity ? quantity / recipients.length : 1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
generate() {
|
|
26
|
+
const errors = [
|
|
27
|
+
{
|
|
28
|
+
severity: 'permanent',
|
|
29
|
+
code: 605,
|
|
30
|
+
enhanced_code: null,
|
|
31
|
+
message: 'Not delivering to previously bounced address'
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
severity: 'permanent',
|
|
35
|
+
code: 451,
|
|
36
|
+
enhanced_code: '4.7.652',
|
|
37
|
+
message: '4.7.652 The mail server [xxx.xxx.xxx.xxx] has exceeded the maximum number of connections.'
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
message: 'No MX for example.com',
|
|
41
|
+
code: 498,
|
|
42
|
+
enhanced_code: null,
|
|
43
|
+
severity: 'permanent'
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
severity: 'temporary',
|
|
47
|
+
code: 552,
|
|
48
|
+
enhanced_code: null,
|
|
49
|
+
message: '5.2.2 <xxxxxxxx@example.com>: user is over quota'
|
|
50
|
+
}
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
const error = faker.helpers.arrayElement(errors);
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
id: this.fastFakeObjectId(),
|
|
57
|
+
email_id: this.model.email_id,
|
|
58
|
+
member_id: this.model.member_id,
|
|
59
|
+
email_recipient_id: this.model.id,
|
|
60
|
+
event_id: faker.random.alphaNumeric(20),
|
|
61
|
+
...error,
|
|
62
|
+
failed_at: this.model.failed_at
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = EmailRecipientFailuresImporter;
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
const TableImporter = require('./TableImporter');
|
|
2
|
+
const {faker} = require('@faker-js/faker');
|
|
3
|
+
const generateEvents = require('../utils/event-generator');
|
|
4
|
+
const dateToDatabaseString = require('../utils/database-date');
|
|
5
|
+
const debug = require('@tryghost/debug')('EmailRecipientsImporter');
|
|
6
|
+
|
|
7
|
+
const emailStatus = {
|
|
8
|
+
delivered: Symbol(),
|
|
9
|
+
opened: Symbol(),
|
|
10
|
+
failed: Symbol(),
|
|
11
|
+
none: Symbol()
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function findFirstHigherIndex(arr, target) {
|
|
15
|
+
let start = 0;
|
|
16
|
+
let end = arr.length - 1;
|
|
17
|
+
let result = -1;
|
|
18
|
+
|
|
19
|
+
while (start <= end) {
|
|
20
|
+
let mid = Math.floor((start + end) / 2);
|
|
21
|
+
|
|
22
|
+
if (arr[mid] >= target) {
|
|
23
|
+
result = mid;
|
|
24
|
+
end = mid - 1; // Continue searching in the left half
|
|
25
|
+
} else {
|
|
26
|
+
start = mid + 1; // Continue searching in the right half
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return result; // Return -1 if no element is higher than target
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
class EmailRecipientsImporter extends TableImporter {
|
|
34
|
+
static table = 'email_recipients';
|
|
35
|
+
static dependencies = ['emails', 'email_batches', 'members', 'members_subscribe_events'];
|
|
36
|
+
|
|
37
|
+
constructor(knex, transaction) {
|
|
38
|
+
super(EmailRecipientsImporter.table, knex, transaction);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async import(quantity) {
|
|
42
|
+
if (quantity === 0) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
const emails = await this.transaction
|
|
48
|
+
.select(
|
|
49
|
+
'id',
|
|
50
|
+
'newsletter_id',
|
|
51
|
+
'email_count',
|
|
52
|
+
'delivered_count',
|
|
53
|
+
'opened_count',
|
|
54
|
+
'failed_count')
|
|
55
|
+
.from('emails');
|
|
56
|
+
this.emails = new Map();
|
|
57
|
+
|
|
58
|
+
for (const email of emails) {
|
|
59
|
+
this.emails.set(email.id, email);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
this.emailBatches = await this.transaction.select('id', 'email_id', 'updated_at').from('email_batches').orderBy('email_id');
|
|
63
|
+
const members = await this.transaction.select('id', 'uuid', 'email', 'name').from('members');
|
|
64
|
+
this.membersSubscribeEvents = await this.transaction.select('id', 'newsletter_id', 'created_at', 'member_id').from('members_subscribe_events').orderBy('created_at', 'asc'); // Order required for better performance in setReferencedModel
|
|
65
|
+
|
|
66
|
+
// Create a map for fast lookups
|
|
67
|
+
this.members = new Map();
|
|
68
|
+
for (const member of members) {
|
|
69
|
+
this.members.set(member.id, member);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Save indexes of each batch for performance (remarkable faster than doing findIndex on each generate call)
|
|
73
|
+
let lastEmailId = null;
|
|
74
|
+
let lastIndex = 0;
|
|
75
|
+
for (const batch of this.emailBatches) {
|
|
76
|
+
if (batch.email_id !== lastEmailId) {
|
|
77
|
+
lastIndex = 0;
|
|
78
|
+
lastEmailId = batch.email_id;
|
|
79
|
+
}
|
|
80
|
+
batch.index = lastIndex;
|
|
81
|
+
lastIndex += 1;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Now reorder by email id
|
|
85
|
+
|
|
86
|
+
debug (`Prepared data for ${this.name} in ${Date.now() - now}ms`);
|
|
87
|
+
|
|
88
|
+
// We use the same event curve for all emails to speed up the generation
|
|
89
|
+
// Spread over 14 days
|
|
90
|
+
this.eventStartTimeUsed = new Date();
|
|
91
|
+
const endTime = new Date(this.eventStartTimeUsed.getTime() + 1000 * 60 * 60 * 24 * 14);
|
|
92
|
+
this.eventCurve = generateEvents({
|
|
93
|
+
shape: 'ease-out',
|
|
94
|
+
trend: 'negative',
|
|
95
|
+
total: 1000,
|
|
96
|
+
startTime: this.eventStartTimeUsed,
|
|
97
|
+
endTime
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
this.membersSubscribeEventsByNewsletterId = new Map();
|
|
101
|
+
this.membersSubscribeEventsCreatedAtsByNewsletterId = new Map();
|
|
102
|
+
for (const memberSubscribeEvent of this.membersSubscribeEvents) {
|
|
103
|
+
if (!this.membersSubscribeEventsByNewsletterId.has(memberSubscribeEvent.newsletter_id)) {
|
|
104
|
+
this.membersSubscribeEventsByNewsletterId.set(memberSubscribeEvent.newsletter_id, []);
|
|
105
|
+
}
|
|
106
|
+
this.membersSubscribeEventsByNewsletterId.get(memberSubscribeEvent.newsletter_id).push(memberSubscribeEvent);
|
|
107
|
+
|
|
108
|
+
if (!this.membersSubscribeEventsCreatedAtsByNewsletterId.has(memberSubscribeEvent.newsletter_id)) {
|
|
109
|
+
this.membersSubscribeEventsCreatedAtsByNewsletterId.set(memberSubscribeEvent.newsletter_id, []);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (!(memberSubscribeEvent.created_at instanceof Date)) {
|
|
113
|
+
// SQLite fix
|
|
114
|
+
memberSubscribeEvent.created_at = new Date(memberSubscribeEvent.created_at);
|
|
115
|
+
}
|
|
116
|
+
this.membersSubscribeEventsCreatedAtsByNewsletterId.get(memberSubscribeEvent.newsletter_id).push(memberSubscribeEvent.created_at.getTime());
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
await this.importForEach(this.emailBatches, quantity ? quantity / emails.length : 1000);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
setReferencedModel(model) {
|
|
123
|
+
this.batch = model;
|
|
124
|
+
this.model = this.emails.get(this.batch.email_id);
|
|
125
|
+
this.batchIndex = this.batch.index;
|
|
126
|
+
|
|
127
|
+
// Shallow clone members list so we can shuffle and modify it
|
|
128
|
+
const earliestOpenTime = new Date(this.batch.updated_at);
|
|
129
|
+
const latestOpenTime = new Date(this.batch.updated_at);
|
|
130
|
+
latestOpenTime.setDate(latestOpenTime.getDate() + 14);
|
|
131
|
+
|
|
132
|
+
// Get all members that were subscribed to this newsletter BEFORE the batch was sent
|
|
133
|
+
// We use binary search to speed up it up
|
|
134
|
+
const lastIndex = findFirstHigherIndex(this.membersSubscribeEventsCreatedAtsByNewsletterId.get(this.model.newsletter_id), earliestOpenTime);
|
|
135
|
+
|
|
136
|
+
this.membersList = this.membersSubscribeEventsByNewsletterId.get(this.model.newsletter_id).slice(0, Math.max(0, lastIndex - 1))
|
|
137
|
+
.slice(this.batchIndex * 1000, (this.batchIndex + 1) * 1000)
|
|
138
|
+
.map(memberSubscribeEvent => memberSubscribeEvent.member_id);
|
|
139
|
+
|
|
140
|
+
this.events = faker.helpers.shuffle(this.eventCurve.slice(0, this.membersList.length));
|
|
141
|
+
this.eventIndex = 0;
|
|
142
|
+
|
|
143
|
+
this.emailMeta = {
|
|
144
|
+
// delievered and not opened
|
|
145
|
+
deliveredCount: this.model.delivered_count - this.model.opened_count,
|
|
146
|
+
openedCount: this.model.opened_count,
|
|
147
|
+
failedCount: this.model.failed_count
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
let offset = this.batchIndex * 1000;
|
|
151
|
+
|
|
152
|
+
// We always first create the failures, then the opened, then the delivered, so we need to remove those from the meta so we don't generate them multiple times
|
|
153
|
+
this.emailMeta = {
|
|
154
|
+
failedCount: Math.max(0, this.emailMeta.failedCount - offset),
|
|
155
|
+
openedCount: Math.max(0, this.emailMeta.openedCount - Math.max(0, offset - this.emailMeta.failedCount)),
|
|
156
|
+
deliveredCount: Math.max(0, this.emailMeta.deliveredCount - Math.max(0, offset - this.emailMeta.failedCount - this.emailMeta.openedCount))
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
generate() {
|
|
161
|
+
let timestamp = this.events.pop();
|
|
162
|
+
if (!timestamp) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// The events are generated for a different time, so we need to move them to the batch time
|
|
167
|
+
timestamp = new Date(timestamp.getTime() - this.eventStartTimeUsed.getTime() + new Date(this.batch.updated_at).getTime());
|
|
168
|
+
|
|
169
|
+
if (timestamp > new Date()) {
|
|
170
|
+
timestamp = new Date();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const memberId = this.membersList[this.events.length];
|
|
174
|
+
const member = this.members.get(memberId);
|
|
175
|
+
|
|
176
|
+
let status = emailStatus.none;
|
|
177
|
+
if (this.emailMeta.failedCount > 0) {
|
|
178
|
+
status = emailStatus.failed;
|
|
179
|
+
this.emailMeta.failedCount -= 1;
|
|
180
|
+
} else if (this.emailMeta.openedCount > 0) {
|
|
181
|
+
status = emailStatus.opened;
|
|
182
|
+
this.emailMeta.openedCount -= 1;
|
|
183
|
+
} else if (this.emailMeta.deliveredCount > 0) {
|
|
184
|
+
status = emailStatus.delivered;
|
|
185
|
+
this.emailMeta.deliveredCount -= 1;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
let deliveredTime;
|
|
189
|
+
if (status === emailStatus.opened) {
|
|
190
|
+
const startDate = this.batch.updated_at;
|
|
191
|
+
const endDate = timestamp;
|
|
192
|
+
deliveredTime = faker.date.between(startDate, endDate);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
// Using sorted ids are much much faster (35% as far as my testing goes) for huge imports
|
|
197
|
+
id: this.fastFakeObjectId(),
|
|
198
|
+
email_id: this.model.id,
|
|
199
|
+
batch_id: this.batch.id,
|
|
200
|
+
member_id: member.id,
|
|
201
|
+
processed_at: dateToDatabaseString(this.batch.updated_at),
|
|
202
|
+
delivered_at: status === emailStatus.opened ? dateToDatabaseString(deliveredTime) : status === emailStatus.delivered ? dateToDatabaseString(timestamp) : null,
|
|
203
|
+
opened_at: status === emailStatus.opened ? dateToDatabaseString(timestamp) : null,
|
|
204
|
+
failed_at: status === emailStatus.failed ? dateToDatabaseString(timestamp) : null,
|
|
205
|
+
member_uuid: member.uuid,
|
|
206
|
+
member_email: member.email,
|
|
207
|
+
member_name: member.name
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
module.exports = EmailRecipientsImporter;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const TableImporter = require('./TableImporter');
|
|
2
|
+
const {faker} = require('@faker-js/faker');
|
|
3
|
+
const generateEvents = require('../utils/event-generator');
|
|
4
|
+
const {luck} = require('../utils/random');
|
|
5
|
+
const dateToDatabaseString = require('../utils/database-date');
|
|
6
|
+
|
|
7
|
+
class EmailsImporter extends TableImporter {
|
|
8
|
+
static table = 'emails';
|
|
9
|
+
static dependencies = ['posts', 'newsletters', 'members_subscribe_events'];
|
|
10
|
+
|
|
11
|
+
constructor(knex, transaction) {
|
|
12
|
+
super(EmailsImporter.table, knex, transaction);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async import(quantity) {
|
|
16
|
+
if (quantity === 0) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const posts = await this.transaction.select('id', 'title', 'published_at').from('posts').where('type', 'post').where('status', 'published').orderBy('published_at', 'desc');
|
|
21
|
+
this.newsletters = await this.transaction.select('id').from('newsletters').orderBy('sort_order');
|
|
22
|
+
this.membersSubscribeEvents = await this.transaction.select('id', 'newsletter_id', 'created_at').from('members_subscribe_events');
|
|
23
|
+
|
|
24
|
+
// Only generate emails for last 25% of posts, and only generate emails for 50% of those
|
|
25
|
+
await this.importForEach(posts.slice(0, Math.ceil(posts.length / 4)), quantity ? quantity / posts.length : 0.5);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
generate() {
|
|
29
|
+
const id = this.fastFakeObjectId();
|
|
30
|
+
|
|
31
|
+
let newsletter;
|
|
32
|
+
if (this.newsletters.length === 0) {
|
|
33
|
+
return null;
|
|
34
|
+
} else if (this.newsletters.length === 1) {
|
|
35
|
+
newsletter = this.newsletters[0];
|
|
36
|
+
} else {
|
|
37
|
+
// Choose between first two newsletters
|
|
38
|
+
newsletter = luck(90)
|
|
39
|
+
// Regular premium
|
|
40
|
+
? this.newsletters[0]
|
|
41
|
+
// Occasional freebie
|
|
42
|
+
: this.newsletters[1];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const timestamp = luck(60)
|
|
46
|
+
? new Date(this.model.published_at)
|
|
47
|
+
: generateEvents({
|
|
48
|
+
shape: 'ease-out',
|
|
49
|
+
trend: 'negative',
|
|
50
|
+
total: 1,
|
|
51
|
+
startTime: new Date(this.model.published_at),
|
|
52
|
+
endTime: new Date()
|
|
53
|
+
})[0];
|
|
54
|
+
|
|
55
|
+
const recipientCount = this.membersSubscribeEvents
|
|
56
|
+
.filter(entry => entry.newsletter_id === newsletter.id)
|
|
57
|
+
.filter(entry => new Date(entry.created_at) < timestamp).length;
|
|
58
|
+
const deliveredCount = Math.ceil(recipientCount * faker.datatype.float({
|
|
59
|
+
max: 1,
|
|
60
|
+
min: 0.9,
|
|
61
|
+
precision: 0.001
|
|
62
|
+
}));
|
|
63
|
+
const openedCount = Math.ceil(deliveredCount * faker.datatype.float({
|
|
64
|
+
max: 0.95,
|
|
65
|
+
min: 0.6,
|
|
66
|
+
precision: 0.001
|
|
67
|
+
}));
|
|
68
|
+
const failedCount = Math.floor((recipientCount - deliveredCount) * faker.datatype.float({
|
|
69
|
+
max: 0.05,
|
|
70
|
+
min: 0,
|
|
71
|
+
precision: 0.001
|
|
72
|
+
}));
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
id,
|
|
76
|
+
uuid: faker.datatype.uuid(),
|
|
77
|
+
post_id: this.model.id,
|
|
78
|
+
status: 'submitted',
|
|
79
|
+
recipient_filter: '', // TODO: Add recipient filter?
|
|
80
|
+
email_count: recipientCount,
|
|
81
|
+
delivered_count: deliveredCount,
|
|
82
|
+
opened_count: openedCount,
|
|
83
|
+
failed_count: failedCount,
|
|
84
|
+
subject: this.model.title,
|
|
85
|
+
source_type: 'html',
|
|
86
|
+
track_opens: true,
|
|
87
|
+
track_clicks: true,
|
|
88
|
+
feedback_enabled: true,
|
|
89
|
+
submitted_at: dateToDatabaseString(timestamp),
|
|
90
|
+
newsletter_id: newsletter.id,
|
|
91
|
+
created_at: dateToDatabaseString(timestamp),
|
|
92
|
+
created_by: 'unused',
|
|
93
|
+
updated_at: dateToDatabaseString(timestamp),
|
|
94
|
+
updated_by: 'unused'
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = EmailsImporter;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const TableImporter = require('./TableImporter');
|
|
2
|
+
const {faker} = require('@faker-js/faker');
|
|
3
|
+
const {slugify} = require('@tryghost/string');
|
|
4
|
+
const {blogStartDate} = require('../utils/blog-info');
|
|
5
|
+
const dateToDatabaseString = require('../utils/database-date');
|
|
6
|
+
|
|
7
|
+
class LabelsImporter extends TableImporter {
|
|
8
|
+
static table = 'labels';
|
|
9
|
+
static dependencies = [];
|
|
10
|
+
defaultQuantity = 10;
|
|
11
|
+
|
|
12
|
+
constructor(knex, transaction) {
|
|
13
|
+
super(LabelsImporter.table, knex, transaction);
|
|
14
|
+
this.generatedNames = new Set();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
generateName() {
|
|
18
|
+
let name;
|
|
19
|
+
do {
|
|
20
|
+
name = `${faker.color.human()} ${faker.name.jobType()}`;
|
|
21
|
+
name = `${name[0].toUpperCase()}${name.slice(1)}`;
|
|
22
|
+
} while (this.generatedNames.has(name));
|
|
23
|
+
this.generatedNames.add(name);
|
|
24
|
+
return name;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
generate() {
|
|
28
|
+
const name = this.generateName();
|
|
29
|
+
return {
|
|
30
|
+
id: this.fastFakeObjectId(),
|
|
31
|
+
name: name,
|
|
32
|
+
slug: `${slugify(name)}`,
|
|
33
|
+
created_at: dateToDatabaseString(blogStartDate),
|
|
34
|
+
created_by: '1',
|
|
35
|
+
updated_at: dateToDatabaseString(blogStartDate),
|
|
36
|
+
updated_by: '1'
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = LabelsImporter;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const TableImporter = require('./TableImporter');
|
|
2
|
+
const {faker} = require('@faker-js/faker');
|
|
3
|
+
const {luck} = require('../utils/random');
|
|
4
|
+
const dateToDatabaseString = require('../utils/database-date');
|
|
5
|
+
|
|
6
|
+
class MembersClickEventsImporter extends TableImporter {
|
|
7
|
+
static table = 'members_click_events';
|
|
8
|
+
static dependencies = ['email_recipients', 'redirects', 'emails'];
|
|
9
|
+
|
|
10
|
+
constructor(knex, transaction) {
|
|
11
|
+
super(MembersClickEventsImporter.table, knex, transaction);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async import(quantity) {
|
|
15
|
+
const emailRecipients = await this.transaction.select('id', 'opened_at', 'email_id', 'member_id').from('email_recipients').whereNotNull('opened_at');
|
|
16
|
+
const redirects = await this.transaction.select('id', 'post_id').from('redirects');
|
|
17
|
+
const emails = await this.transaction.select('id', 'post_id').from('emails');
|
|
18
|
+
this.quantity = quantity ? quantity / emailRecipients.length : 2;
|
|
19
|
+
|
|
20
|
+
// Create maps for faster lookups (this does make a difference for large data generation)
|
|
21
|
+
this.emails = new Map();
|
|
22
|
+
for (const email of emails) {
|
|
23
|
+
this.emails.set(email.id, email);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
this.redirects = new Map();
|
|
27
|
+
for (const redirect of redirects) {
|
|
28
|
+
if (!this.redirects.has(redirect.post_id)) {
|
|
29
|
+
this.redirects.set(redirect.post_id, []);
|
|
30
|
+
}
|
|
31
|
+
this.redirects.get(redirect.post_id).push(redirect);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
await this.importForEach(emailRecipients, this.quantity);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
setReferencedModel(model) {
|
|
38
|
+
this.model = model;
|
|
39
|
+
this.amount = model.opened_at === null ? 0 : luck(40) ? faker.datatype.number({
|
|
40
|
+
min: 0,
|
|
41
|
+
max: this.quantity
|
|
42
|
+
}) : 0;
|
|
43
|
+
const email = this.emails.get(model.email_id);
|
|
44
|
+
this.redirectList = this.redirects.get(email.post_id) ?? [];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
generate() {
|
|
48
|
+
if (this.amount <= 0 || this.redirectList.length === 0 || !this.model.opened_at) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
this.amount -= 1;
|
|
52
|
+
|
|
53
|
+
const openedAt = new Date(this.model.opened_at);
|
|
54
|
+
const laterOn = new Date(openedAt.getTime() + 1000 * 60 * 15);
|
|
55
|
+
const clickTime = faker.date.between(openedAt.getTime(), laterOn.getTime()); //added getTime here because it threw random errors
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
id: this.fastFakeObjectId(),
|
|
59
|
+
member_id: this.model.member_id,
|
|
60
|
+
redirect_id: this.redirectList[this.redirectList.length === 1 ? 0 : (faker.datatype.number({
|
|
61
|
+
min: 0,
|
|
62
|
+
max: this.redirectList.length - 1
|
|
63
|
+
}))].id,
|
|
64
|
+
created_at: dateToDatabaseString(clickTime)
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = MembersClickEventsImporter;
|