ghost 5.115.1 → 5.116.1
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.1.tgz → tryghost-api-framework-5.116.1.tgz} +0 -0
- package/components/tryghost-constants-5.116.1.tgz +0 -0
- package/components/tryghost-custom-fonts-5.116.1.tgz +0 -0
- package/components/{tryghost-custom-theme-settings-service-5.115.1.tgz → tryghost-custom-theme-settings-service-5.116.1.tgz} +0 -0
- package/components/{tryghost-domain-events-5.115.1.tgz → tryghost-domain-events-5.116.1.tgz} +0 -0
- package/components/{tryghost-donations-5.115.1.tgz → tryghost-donations-5.116.1.tgz} +0 -0
- package/components/tryghost-email-addresses-5.116.1.tgz +0 -0
- package/components/tryghost-email-service-5.116.1.tgz +0 -0
- package/components/tryghost-email-suppression-list-5.116.1.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.116.1.tgz +0 -0
- package/components/tryghost-i18n-5.116.1.tgz +0 -0
- package/components/tryghost-job-manager-5.116.1.tgz +0 -0
- package/components/tryghost-link-replacer-5.116.1.tgz +0 -0
- package/components/tryghost-magic-link-5.116.1.tgz +0 -0
- package/components/{tryghost-member-attribution-5.115.1.tgz → tryghost-member-attribution-5.116.1.tgz} +0 -0
- package/components/tryghost-member-events-5.116.1.tgz +0 -0
- package/components/tryghost-members-api-5.116.1.tgz +0 -0
- package/components/tryghost-members-csv-5.116.1.tgz +0 -0
- package/components/{tryghost-members-offers-5.115.1.tgz → tryghost-members-offers-5.116.1.tgz} +0 -0
- package/components/{tryghost-milestones-5.115.1.tgz → tryghost-milestones-5.116.1.tgz} +0 -0
- package/components/{tryghost-mw-error-handler-5.115.1.tgz → tryghost-mw-error-handler-5.116.1.tgz} +0 -0
- package/components/tryghost-mw-vhost-5.116.1.tgz +0 -0
- package/components/{tryghost-post-events-5.115.1.tgz → tryghost-post-events-5.116.1.tgz} +0 -0
- package/components/{tryghost-post-revisions-5.115.1.tgz → tryghost-post-revisions-5.116.1.tgz} +0 -0
- package/components/tryghost-posts-service-5.116.1.tgz +0 -0
- package/components/{tryghost-prometheus-metrics-5.115.1.tgz → tryghost-prometheus-metrics-5.116.1.tgz} +0 -0
- package/components/tryghost-security-5.116.1.tgz +0 -0
- package/components/{tryghost-tiers-5.115.1.tgz → tryghost-tiers-5.116.1.tgz} +0 -0
- package/components/tryghost-webmentions-5.116.1.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 +6 -11
- package/content/themes/source/partials/feature-image.hbs +2 -2
- package/core/boot.js +0 -42
- package/core/built/admin/assets/admin-x-activitypub/admin-x-activitypub.js +24764 -24129
- package/core/built/admin/assets/admin-x-demo/admin-x-demo.js +1 -1
- package/core/built/admin/assets/admin-x-demo/{index-15df2af5.mjs → index-a9601514.mjs} +3 -3
- package/core/built/admin/assets/admin-x-demo/{modals-8ca61d78.mjs → modals-c1789d04.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{CodeEditorView-d2e6872f.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-8e8821e5.mjs → index-84580c3a.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-f5cb3db3.mjs → index-f744cab7.mjs} +49 -35
- package/core/built/admin/assets/admin-x-settings/{modals-e8ae4d46.mjs → modals-d9ca60c5.mjs} +1198 -1192
- package/core/built/admin/assets/chunk.524.cb72a86e19c9ffd6172e.js +35 -0
- package/core/built/admin/assets/chunk.582.4f4d38ffe79fbdbd26f7.js +37 -0
- package/core/built/admin/assets/{chunk.874.461cb3cf5b6b36915f8c.js → chunk.713.e9027c0cc3c56110f5da.js} +125 -98
- package/core/built/admin/assets/{ghost-df7b9558260aa27d18b195ee895b487d.js → ghost-03b64c086f3c60cabc85fe7a7e2b640a.js} +144 -145
- 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 +71082 -7533
- 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 +6 -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/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/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 +1 -1
- 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/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 +1 -1
- package/core/server/services/auth/session/session-service.js +15 -5
- package/core/server/services/custom-redirects/index.js +1 -1
- package/core/server/services/email-analytics/EmailAnalyticsProviderMailgun.js +1 -1
- 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/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/InMemoryMailEventRepository.js +2 -2
- package/core/server/services/mail-events/InMemoryMailEventRepository.ts +1 -1
- package/core/server/services/members-events/LastSeenAtUpdater.js +1 -1
- package/core/server/services/offers/service.js +1 -1
- 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/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/web/api/endpoints/admin/app.js +1 -21
- package/core/server/web/api/middleware/version-match.js +41 -0
- package/core/shared/labs.js +2 -2
- package/package.json +87 -104
- package/tsconfig.tsbuildinfo +1 -1
- package/yarn.lock +1470 -1540
- package/components/tryghost-adapter-cache-redis-5.115.1.tgz +0 -0
- package/components/tryghost-adapter-manager-5.115.1.tgz +0 -0
- package/components/tryghost-announcement-bar-settings-5.115.1.tgz +0 -0
- package/components/tryghost-constants-5.115.1.tgz +0 -0
- package/components/tryghost-custom-fonts-5.115.1.tgz +0 -0
- package/components/tryghost-data-generator-5.115.1.tgz +0 -0
- package/components/tryghost-email-addresses-5.115.1.tgz +0 -0
- package/components/tryghost-email-content-generator-5.115.1.tgz +0 -0
- package/components/tryghost-email-events-5.115.1.tgz +0 -0
- package/components/tryghost-email-service-5.115.1.tgz +0 -0
- package/components/tryghost-email-suppression-list-5.115.1.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.115.1.tgz +0 -0
- package/components/tryghost-ghost-5.115.1.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.115.1.tgz +0 -0
- package/components/tryghost-i18n-5.115.1.tgz +0 -0
- package/components/tryghost-importer-handler-content-files-5.115.1.tgz +0 -0
- package/components/tryghost-in-memory-repository-5.115.1.tgz +0 -0
- package/components/tryghost-job-manager-5.115.1.tgz +0 -0
- package/components/tryghost-link-redirects-5.115.1.tgz +0 -0
- package/components/tryghost-link-replacer-5.115.1.tgz +0 -0
- package/components/tryghost-magic-link-5.115.1.tgz +0 -0
- package/components/tryghost-mailgun-client-5.115.1.tgz +0 -0
- package/components/tryghost-member-events-5.115.1.tgz +0 -0
- package/components/tryghost-members-api-5.115.1.tgz +0 -0
- package/components/tryghost-members-csv-5.115.1.tgz +0 -0
- package/components/tryghost-members-payments-5.115.1.tgz +0 -0
- package/components/tryghost-minifier-5.115.1.tgz +0 -0
- package/components/tryghost-mw-version-match-5.115.1.tgz +0 -0
- package/components/tryghost-mw-vhost-5.115.1.tgz +0 -0
- package/components/tryghost-posts-service-5.115.1.tgz +0 -0
- package/components/tryghost-recommendations-5.115.1.tgz +0 -0
- package/components/tryghost-security-5.115.1.tgz +0 -0
- package/components/tryghost-slack-notifications-5.115.1.tgz +0 -0
- package/components/tryghost-webmentions-5.115.1.tgz +0 -0
- package/core/built/admin/assets/chunk.524.2439684964c164c598ab.js +0 -35
- package/core/built/admin/assets/chunk.582.bf5a2bbb2c4eb69ef1e7.js +0 -37
- package/core/built/admin/assets/ghost-327b17ea23cb8c89bd7e6a51e18e8506.css +0 -1
- package/core/built/admin/assets/ghost-dark-f30a597ac19632a118939492591c531b.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,90 @@
|
|
|
1
|
+
const _ = require('lodash');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
class ImporterContentFileHandler {
|
|
5
|
+
/** @property {'media' | 'files' | 'images'} */
|
|
6
|
+
type;
|
|
7
|
+
|
|
8
|
+
/** @property {string[]} */
|
|
9
|
+
directories;
|
|
10
|
+
|
|
11
|
+
/** @property {string[]} */
|
|
12
|
+
extensions;
|
|
13
|
+
|
|
14
|
+
/** @property {string[]} */
|
|
15
|
+
contentTypes;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Holds path to the destination content directory
|
|
19
|
+
* @property {string} */
|
|
20
|
+
#contentPath;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
*
|
|
24
|
+
* @param {Object} deps dependencies
|
|
25
|
+
* @param {'media' | 'files' | 'images'} deps.type type of content file
|
|
26
|
+
* @param {string[]} deps.extensions file extensions to search for
|
|
27
|
+
* @param {boolean} [deps.ignoreRootFolderFiles] whether to ignore files in the root folder
|
|
28
|
+
* @param {string[]} deps.contentTypes content types to search for
|
|
29
|
+
* @param {string[]} deps.directories directories to search for content files
|
|
30
|
+
* @param {string} deps.contentPath path to the destination content directory
|
|
31
|
+
* @param {Object} deps.storage storage adapter instance
|
|
32
|
+
* @param {object} deps.urlUtils urlUtils instance
|
|
33
|
+
*/
|
|
34
|
+
constructor(deps) {
|
|
35
|
+
this.type = deps.type;
|
|
36
|
+
this.directories = deps.directories;
|
|
37
|
+
this.extensions = deps.extensions;
|
|
38
|
+
this.contentTypes = deps.contentTypes;
|
|
39
|
+
this.ignoreRootFolderFiles = deps.ignoreRootFolderFiles;
|
|
40
|
+
this.storage = deps.storage;
|
|
41
|
+
this.#contentPath = deps.contentPath;
|
|
42
|
+
this.urlUtils = deps.urlUtils;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async loadFile(files, baseDir) {
|
|
46
|
+
const baseDirRegex = baseDir ? new RegExp('^' + baseDir + '/') : new RegExp('');
|
|
47
|
+
|
|
48
|
+
const contentFilesFolderRegexes = _.map(this.storage.staticFileURLPrefix.split('/'), function (dir) {
|
|
49
|
+
return new RegExp('^' + dir + '/');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (this.ignoreRootFolderFiles) {
|
|
53
|
+
files = _.filter(files, function (file) {
|
|
54
|
+
return file.name.indexOf('/') !== -1;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// normalize the directory structure
|
|
59
|
+
const filesContentPath = this.#contentPath;
|
|
60
|
+
files = _.map(files, function (file) {
|
|
61
|
+
const noBaseDir = file.name.replace(baseDirRegex, '');
|
|
62
|
+
let noGhostDirs = noBaseDir;
|
|
63
|
+
|
|
64
|
+
_.each(contentFilesFolderRegexes, function (regex) {
|
|
65
|
+
noGhostDirs = noGhostDirs.replace(regex, '');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
file.originalPath = noBaseDir;
|
|
69
|
+
file.name = noGhostDirs;
|
|
70
|
+
file.targetDir = path.join(filesContentPath, path.dirname(noGhostDirs));
|
|
71
|
+
return file;
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const self = this;
|
|
75
|
+
return Promise.all(files.map(function (contentFile) {
|
|
76
|
+
return self.storage.getUniqueFileName(contentFile, contentFile.targetDir).then(function (targetFilename) {
|
|
77
|
+
contentFile.newPath = self.urlUtils.urlJoin(
|
|
78
|
+
'/',
|
|
79
|
+
self.urlUtils.getSubdir(),
|
|
80
|
+
self.storage.staticFileURLPrefix,
|
|
81
|
+
path.relative(filesContentPath, targetFilename)
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
return contentFile;
|
|
85
|
+
});
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = ImporterContentFileHandler;
|
|
@@ -11,7 +11,7 @@ const debug = require('@tryghost/debug')('import-manager');
|
|
|
11
11
|
const logging = require('@tryghost/logging');
|
|
12
12
|
const errors = require('@tryghost/errors');
|
|
13
13
|
const ImageHandler = require('./handlers/image');
|
|
14
|
-
const ImporterContentFileHandler = require('
|
|
14
|
+
const ImporterContentFileHandler = require('./handlers/ImporterContentFileHandler');
|
|
15
15
|
const RevueHandler = require('./handlers/revue');
|
|
16
16
|
const JSONHandler = require('./handlers/json');
|
|
17
17
|
const MarkdownHandler = require('./handlers/markdown');
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs/promises');
|
|
3
|
+
const JsonImporter = require('./utils/JsonImporter');
|
|
4
|
+
const {getProcessRoot} = require('@tryghost/root-utils');
|
|
5
|
+
const topologicalSort = require('./utils/topological-sort');
|
|
6
|
+
const {faker} = require('@faker-js/faker');
|
|
7
|
+
const {faker: americanFaker} = require('@faker-js/faker/locale/en_US');
|
|
8
|
+
const crypto = require('crypto');
|
|
9
|
+
const {Buffer} = require('node:buffer');
|
|
10
|
+
const DatabaseInfo = require('@tryghost/database-info');
|
|
11
|
+
const errors = require('@tryghost/errors');
|
|
12
|
+
const importers = require('./importers').reduce((acc, val) => {
|
|
13
|
+
acc[val.table] = val;
|
|
14
|
+
return acc;
|
|
15
|
+
}, {});
|
|
16
|
+
|
|
17
|
+
class DataGenerator {
|
|
18
|
+
/**
|
|
19
|
+
*
|
|
20
|
+
* @param {object} options
|
|
21
|
+
* @param {Record<string,number>} [options.quantities] Pass in custom amounts for specific tables
|
|
22
|
+
* @param {number} [options.seed] If you pass the same seed, the same data will be generated if you used the same options too and if the data generation code remained the same.
|
|
23
|
+
*/
|
|
24
|
+
constructor({
|
|
25
|
+
knex,
|
|
26
|
+
tables,
|
|
27
|
+
schemaTables,
|
|
28
|
+
clearDatabase = false,
|
|
29
|
+
baseDataPack = '',
|
|
30
|
+
baseUrl,
|
|
31
|
+
logger,
|
|
32
|
+
printDependencies,
|
|
33
|
+
withDefault,
|
|
34
|
+
seed,
|
|
35
|
+
quantities = {},
|
|
36
|
+
useTransaction = true
|
|
37
|
+
}) {
|
|
38
|
+
this.knex = knex;
|
|
39
|
+
this.tableList = tables || [];
|
|
40
|
+
this.schemaTables = schemaTables;
|
|
41
|
+
this.willClearData = clearDatabase;
|
|
42
|
+
this.useBaseDataPack = baseDataPack !== '';
|
|
43
|
+
this.baseDataPack = baseDataPack;
|
|
44
|
+
this.baseUrl = baseUrl;
|
|
45
|
+
this.logger = logger;
|
|
46
|
+
this.withDefault = withDefault;
|
|
47
|
+
this.printDependencies = printDependencies;
|
|
48
|
+
this.seed = seed;
|
|
49
|
+
this.quantities = quantities;
|
|
50
|
+
this.useTransaction = useTransaction;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
sortTableList() {
|
|
54
|
+
// Add missing dependencies
|
|
55
|
+
for (const table of this.tableList) {
|
|
56
|
+
table.importer = importers[table.name];
|
|
57
|
+
|
|
58
|
+
// eslint-disable-next-line no-unused-vars
|
|
59
|
+
table.dependencies = Object.entries(this.schemaTables[table.name]).reduce((acc, [_col, data]) => {
|
|
60
|
+
if (data.references) {
|
|
61
|
+
const referencedTable = data.references.split('.')[0];
|
|
62
|
+
// The ghost_subscriptions_id property has a foreign key to the subscriptions table, but we don't use that table yet atm, so don't add it as a dependency
|
|
63
|
+
if (!acc.includes(referencedTable) && referencedTable !== 'subscriptions') {
|
|
64
|
+
acc.push(referencedTable);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return acc;
|
|
68
|
+
}, table.importer.dependencies);
|
|
69
|
+
|
|
70
|
+
for (const dependency of table.dependencies) {
|
|
71
|
+
if (!this.tableList.find(t => t.name === dependency)) {
|
|
72
|
+
this.tableList.push({
|
|
73
|
+
name: dependency,
|
|
74
|
+
importer: importers[dependency]
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Order to ensure dependencies are created before dependants
|
|
81
|
+
this.tableList = topologicalSort(this.tableList);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* TODO: This needs to reverse through all dependency chains to clear data from all tables
|
|
86
|
+
* @param {import('knex/types').Knex.Transaction} transaction
|
|
87
|
+
*/
|
|
88
|
+
async clearData(transaction) {
|
|
89
|
+
const tables = this.tableList.map(t => t.name).reverse();
|
|
90
|
+
|
|
91
|
+
// TODO: Remove this once we import posts_meta
|
|
92
|
+
tables.unshift('posts_meta');
|
|
93
|
+
|
|
94
|
+
// Clear data from any tables that are being imported
|
|
95
|
+
for (const table of tables) {
|
|
96
|
+
this.logger.debug(`Clearing table ${table}`);
|
|
97
|
+
|
|
98
|
+
if (table === 'roles_users') {
|
|
99
|
+
await transaction(table).del().whereNot('user_id', '1');
|
|
100
|
+
} else if (table === 'users') {
|
|
101
|
+
// Avoid deleting the admin user
|
|
102
|
+
await transaction(table).del().whereNot('id', '1');
|
|
103
|
+
} else {
|
|
104
|
+
await transaction(table).truncate();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async importBasePack(transaction) {
|
|
110
|
+
let baseDataPack = this.baseDataPack;
|
|
111
|
+
if (!path.isAbsolute(this.baseDataPack)) {
|
|
112
|
+
baseDataPack = path.join(getProcessRoot(), baseDataPack);
|
|
113
|
+
}
|
|
114
|
+
let baseData = {};
|
|
115
|
+
try {
|
|
116
|
+
baseData = JSON.parse((await fs.readFile(baseDataPack)).toString());
|
|
117
|
+
this.logger.info('Read base data pack');
|
|
118
|
+
} catch (error) {
|
|
119
|
+
this.logger.error('Failed to read data pack: ', error);
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
this.logger.info('Starting base data import');
|
|
124
|
+
const jsonImporter = new JsonImporter(transaction);
|
|
125
|
+
|
|
126
|
+
// Clear settings table
|
|
127
|
+
await transaction('settings').del();
|
|
128
|
+
|
|
129
|
+
// Hard-coded for order
|
|
130
|
+
const tablesToImport = [
|
|
131
|
+
'newsletters',
|
|
132
|
+
'posts',
|
|
133
|
+
'tags',
|
|
134
|
+
'products',
|
|
135
|
+
'benefits',
|
|
136
|
+
'products_benefits',
|
|
137
|
+
'stripe_products',
|
|
138
|
+
'stripe_prices',
|
|
139
|
+
'settings',
|
|
140
|
+
'custom_theme_settings'
|
|
141
|
+
];
|
|
142
|
+
for (const table of tablesToImport) {
|
|
143
|
+
this.logger.info(`Importing content for table ${table} from base data pack`);
|
|
144
|
+
await jsonImporter.import({
|
|
145
|
+
name: table,
|
|
146
|
+
data: baseData[table]
|
|
147
|
+
});
|
|
148
|
+
const tableIndex = this.tableList.findIndex(t => t.name === table);
|
|
149
|
+
if (tableIndex !== -1) {
|
|
150
|
+
this.tableList.splice(tableIndex, 1);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
this.logger.info('Completed base data import');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async importData() {
|
|
158
|
+
const start = Date.now();
|
|
159
|
+
|
|
160
|
+
// Add default tables if none are specified
|
|
161
|
+
if (this.tableList.length === 0) {
|
|
162
|
+
this.tableList = Object.keys(importers).map(name => ({name}));
|
|
163
|
+
} else if (this.withDefault) {
|
|
164
|
+
// Add default tables to the end of the list
|
|
165
|
+
const defaultTables = Object.keys(importers).map(name => ({name}));
|
|
166
|
+
for (const table of defaultTables) {
|
|
167
|
+
if (!this.tableList.find(t => t.name === table.name)) {
|
|
168
|
+
this.tableList.push(table);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Error if we have an unknown table
|
|
174
|
+
for (const table of this.tableList) {
|
|
175
|
+
if (importers[table.name] === undefined) {
|
|
176
|
+
throw new errors.IncorrectUsageError({message: `Unknown table: ${table.name}`});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
this.sortTableList();
|
|
181
|
+
|
|
182
|
+
if (this.printDependencies) {
|
|
183
|
+
this.logger.info('Table dependencies:');
|
|
184
|
+
for (const table of this.tableList) {
|
|
185
|
+
this.logger.info(`\t${table.name}: ${table.dependencies.join(', ')}`);
|
|
186
|
+
}
|
|
187
|
+
process.exit(0);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (this.useTransaction) {
|
|
191
|
+
await this.knex.transaction(async (transaction) => {
|
|
192
|
+
if (!DatabaseInfo.isSQLite(this.knex)) {
|
|
193
|
+
await transaction.raw('SET autocommit=0;');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
await this.#run(transaction);
|
|
197
|
+
}, {isolationLevel: 'read committed'});
|
|
198
|
+
} else {
|
|
199
|
+
await this.#run(this.knex);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
this.logger.info(`Completed data import in ${((Date.now() - start) / 1000).toFixed(1)}s`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async #run(transaction) {
|
|
206
|
+
if (!DatabaseInfo.isSQLite(this.knex)) {
|
|
207
|
+
if (process.env.DISABLE_FAST_IMPORT) {
|
|
208
|
+
await transaction.raw('SET FOREIGN_KEY_CHECKS=0;');
|
|
209
|
+
await transaction.raw('SET unique_checks=0;');
|
|
210
|
+
} else {
|
|
211
|
+
await transaction.raw('ALTER INSTANCE DISABLE INNODB REDO_LOG;');
|
|
212
|
+
await transaction.raw('SET FOREIGN_KEY_CHECKS=0;');
|
|
213
|
+
await transaction.raw('SET unique_checks=0;');
|
|
214
|
+
await transaction.raw('SET GLOBAL local_infile=1;');
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (this.willClearData) {
|
|
219
|
+
await this.clearData(transaction);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (this.useBaseDataPack) {
|
|
223
|
+
await this.importBasePack(transaction);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Set quantities for tables
|
|
227
|
+
for (const table of this.tableList) {
|
|
228
|
+
if (this.quantities[table.name] !== undefined) {
|
|
229
|
+
table.quantity = this.quantities[table.name];
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const cryptoRandomBytes = crypto.randomBytes;
|
|
234
|
+
|
|
235
|
+
if (this.seed) {
|
|
236
|
+
// The probality distributions library uses crypto.randomBytes, which we can't seed, so we need to override it
|
|
237
|
+
crypto.randomBytes = (size) => {
|
|
238
|
+
const buffer = Buffer.alloc(size);
|
|
239
|
+
for (let i = 0; i < size; i++) {
|
|
240
|
+
buffer[i] = Math.floor(faker.datatype.number({min: 0, max: 255}));
|
|
241
|
+
}
|
|
242
|
+
return buffer;
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
for (const table of this.tableList) {
|
|
248
|
+
if (this.seed) {
|
|
249
|
+
// We reset the seed for every table, so the chosen tables don't affect the data and changes in one importer don't affect the others
|
|
250
|
+
faker.seed(this.seed);
|
|
251
|
+
americanFaker.seed(this.seed);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Add all common options to every importer, whether they use them or not
|
|
255
|
+
const tableImporter = new table.importer(this.knex, transaction, {
|
|
256
|
+
baseUrl: this.baseUrl
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
const amount = table.quantity ?? tableImporter.defaultQuantity;
|
|
260
|
+
this.logger.info('Importing content for table', table.name, amount ? `(${amount} records)` : '');
|
|
261
|
+
|
|
262
|
+
await tableImporter.import(table.quantity ?? undefined);
|
|
263
|
+
}
|
|
264
|
+
} finally {
|
|
265
|
+
if (this.seed) {
|
|
266
|
+
// Revert crypto.randomBytes to the original function
|
|
267
|
+
crypto.randomBytes = cryptoRandomBytes;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Finalise all tables - uses new table importer objects to avoid keeping all data in memory
|
|
272
|
+
for (const table of this.tableList) {
|
|
273
|
+
const tableImporter = new table.importer(this.knex, transaction, {
|
|
274
|
+
baseUrl: this.baseUrl
|
|
275
|
+
});
|
|
276
|
+
await tableImporter.finalise();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Re-enable the redo log because it's a persisted global
|
|
280
|
+
// Leaving it disabled can break the database in the event of an unexpected shutdown
|
|
281
|
+
// See https://dev.mysql.com/doc/refman/8.0/en/innodb-redo-log.html#innodb-disable-redo-logging
|
|
282
|
+
if (!DatabaseInfo.isSQLite(this.knex) && !process.env.DISABLE_FAST_IMPORT) {
|
|
283
|
+
await transaction.raw('ALTER INSTANCE ENABLE INNODB REDO_LOG;');
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
module.exports = DataGenerator;
|
|
@@ -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;
|