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,39 @@
|
|
|
1
|
+
const TableImporter = require('./TableImporter');
|
|
2
|
+
const {faker} = require('@faker-js/faker');
|
|
3
|
+
const {luck} = require('../utils/random');
|
|
4
|
+
|
|
5
|
+
class MembersLabelsImporter extends TableImporter {
|
|
6
|
+
static table = 'members_labels';
|
|
7
|
+
static dependencies = ['labels', 'members'];
|
|
8
|
+
|
|
9
|
+
constructor(knex, transaction, {labels}) {
|
|
10
|
+
super(MembersLabelsImporter.table, knex, transaction);
|
|
11
|
+
this.labels = labels;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async import(quantity) {
|
|
15
|
+
const members = await this.transaction.select('id').from('members');
|
|
16
|
+
this.labels = await this.transaction.select('id').from('labels');
|
|
17
|
+
|
|
18
|
+
await this.importForEach(members, quantity ? quantity / members.length : 1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
generate() {
|
|
22
|
+
if (luck(90)) {
|
|
23
|
+
// 90% of members don't have labels
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// TODO: Ensure we don't generate the same member label twice
|
|
27
|
+
return {
|
|
28
|
+
id: this.fastFakeObjectId(),
|
|
29
|
+
member_id: this.model.id,
|
|
30
|
+
label_id: this.labels[faker.datatype.number({
|
|
31
|
+
min: 0,
|
|
32
|
+
max: this.labels.length - 1
|
|
33
|
+
})].id,
|
|
34
|
+
sort_order: 0
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = MembersLabelsImporter;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const TableImporter = require('./TableImporter');
|
|
2
|
+
const {luck} = require('../utils/random');
|
|
3
|
+
const generateEvents = require('../utils/event-generator');
|
|
4
|
+
const dateToDatabaseString = require('../utils/database-date');
|
|
5
|
+
|
|
6
|
+
class MembersLoginEventsImporter extends TableImporter {
|
|
7
|
+
static table = 'members_login_events';
|
|
8
|
+
static dependencies = ['members'];
|
|
9
|
+
|
|
10
|
+
constructor(knex, transaction) {
|
|
11
|
+
super(MembersLoginEventsImporter.table, knex, transaction);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async import(quantity) {
|
|
15
|
+
if (quantity === 0) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let offset = 0;
|
|
20
|
+
let limit = 100000;
|
|
21
|
+
|
|
22
|
+
// eslint-disable-next-line no-constant-condition
|
|
23
|
+
while (true) {
|
|
24
|
+
const members = await this.transaction.select('id', 'created_at').from('members').limit(limit).offset(offset);
|
|
25
|
+
|
|
26
|
+
if (members.length === 0) {
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
await this.importForEach(members, quantity ? quantity / members.length : 5);
|
|
31
|
+
|
|
32
|
+
offset += limit;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
setReferencedModel(model) {
|
|
37
|
+
this.model = model;
|
|
38
|
+
|
|
39
|
+
const endDate = new Date();
|
|
40
|
+
const daysBetween = Math.ceil((endDate.valueOf() - new Date(model.created_at).valueOf()) / (1000 * 60 * 60 * 24));
|
|
41
|
+
|
|
42
|
+
// Assuming most people either subscribe and lose interest, or maintain steady readership
|
|
43
|
+
const shape = luck(40) ? 'ease-out' : 'flat';
|
|
44
|
+
this.timestamps = generateEvents({
|
|
45
|
+
shape,
|
|
46
|
+
trend: 'negative',
|
|
47
|
+
// Steady readers login more, readers who lose interest read less overall.
|
|
48
|
+
// ceil because members will all have logged in at least once
|
|
49
|
+
total: Math.min(5, shape === 'flat' ? Math.ceil(daysBetween / 3) : Math.ceil(daysBetween / 7)),
|
|
50
|
+
startTime: new Date(model.created_at),
|
|
51
|
+
endTime: endDate
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
generate() {
|
|
56
|
+
const timestamp = this.timestamps.pop();
|
|
57
|
+
if (!timestamp) {
|
|
58
|
+
// Out of events for this user
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
id: this.fastFakeObjectId(),
|
|
63
|
+
created_at: dateToDatabaseString(timestamp),
|
|
64
|
+
member_id: this.model.id
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = MembersLoginEventsImporter;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const TableImporter = require('./TableImporter');
|
|
2
|
+
|
|
3
|
+
class MembersNewslettersImporter extends TableImporter {
|
|
4
|
+
static table = 'members_newsletters';
|
|
5
|
+
static dependencies = ['members_subscribe_events'];
|
|
6
|
+
|
|
7
|
+
constructor(knex, transaction) {
|
|
8
|
+
super(MembersNewslettersImporter.table, knex, transaction);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async import(quantity) {
|
|
12
|
+
let offset = 0;
|
|
13
|
+
let limit = 100000;
|
|
14
|
+
|
|
15
|
+
// eslint-disable-next-line no-constant-condition
|
|
16
|
+
while (true) {
|
|
17
|
+
const membersSubscribeEvents = await this.transaction.select('member_id', 'newsletter_id').from('members_subscribe_events').limit(limit).offset(offset);
|
|
18
|
+
|
|
19
|
+
if (membersSubscribeEvents.length === 0) {
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
await this.importForEach(membersSubscribeEvents, quantity ? quantity / membersSubscribeEvents.length : 1);
|
|
24
|
+
|
|
25
|
+
offset += limit;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
generate() {
|
|
30
|
+
return {
|
|
31
|
+
id: this.fastFakeObjectId(),
|
|
32
|
+
member_id: this.model.member_id,
|
|
33
|
+
newsletter_id: this.model.newsletter_id
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = MembersNewslettersImporter;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const TableImporter = require('./TableImporter');
|
|
2
|
+
|
|
3
|
+
class MembersPaidSubscriptionEventsImporter extends TableImporter {
|
|
4
|
+
static table = 'members_paid_subscription_events';
|
|
5
|
+
static dependencies = ['members_stripe_customers_subscriptions'];
|
|
6
|
+
|
|
7
|
+
constructor(knex, transaction) {
|
|
8
|
+
super(MembersPaidSubscriptionEventsImporter.table, knex, transaction);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async import() {
|
|
12
|
+
let offset = 0;
|
|
13
|
+
let limit = 1000;
|
|
14
|
+
|
|
15
|
+
// eslint-disable-next-line no-constant-condition
|
|
16
|
+
while (true) {
|
|
17
|
+
const subscriptions = await this.transaction.select('id', 'customer_id', 'plan_currency', 'plan_amount', 'created_at', 'plan_id', 'status', 'cancel_at_period_end', 'current_period_end').from('members_stripe_customers_subscriptions').limit(limit).offset(offset);
|
|
18
|
+
|
|
19
|
+
if (subscriptions.length === 0) {
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
const membersStripeCustomers = await this.transaction.select('id', 'member_id', 'customer_id').from('members_stripe_customers').whereIn('customer_id', subscriptions.map(subscription => subscription.customer_id));
|
|
23
|
+
|
|
24
|
+
this.membersStripeCustomers = new Map();
|
|
25
|
+
for (const customer of membersStripeCustomers) {
|
|
26
|
+
this.membersStripeCustomers.set(customer.customer_id, customer);
|
|
27
|
+
}
|
|
28
|
+
await this.importForEach(subscriptions, 2);
|
|
29
|
+
|
|
30
|
+
offset += limit;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
setReferencedModel(model) {
|
|
35
|
+
this.model = model;
|
|
36
|
+
this.count = 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
isActiveSubscriptionStatus(status) {
|
|
40
|
+
return ['active', 'trialing', 'unpaid', 'past_due'].includes(status);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getStatus(modelToCheck) {
|
|
44
|
+
const status = modelToCheck.status;
|
|
45
|
+
const canceled = modelToCheck.cancel_at_period_end;
|
|
46
|
+
|
|
47
|
+
if (status === 'canceled') {
|
|
48
|
+
return 'expired';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (canceled) {
|
|
52
|
+
return 'canceled';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (this.isActiveSubscriptionStatus(status)) {
|
|
56
|
+
return 'active';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return 'inactive';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
generate() {
|
|
63
|
+
this.count += 1;
|
|
64
|
+
|
|
65
|
+
const isActive = this.isActiveSubscriptionStatus(this.model.status);
|
|
66
|
+
if (this.count > 1 && isActive) {
|
|
67
|
+
// We only need one event, because the MRR is still here
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (this.model.status === 'incomplete' || this.model.status === 'incomplete_expired') {
|
|
72
|
+
// Not a paid subscription
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const memberCustomer = this.membersStripeCustomers.get(this.model.customer_id);
|
|
77
|
+
const isMonthly = this.model.plan_interval === 'month';
|
|
78
|
+
|
|
79
|
+
// Note that we need to recalculate the MRR, because it will be zero for inactive subscrptions
|
|
80
|
+
const mrr = isMonthly ? this.model.plan_amount : Math.floor(this.model.plan_amount / 12);
|
|
81
|
+
|
|
82
|
+
// todo: implement + MRR and -MRR in case of inactive subscriptions
|
|
83
|
+
return {
|
|
84
|
+
id: this.fastFakeObjectId(),
|
|
85
|
+
// TODO: Support expired / updated / cancelled events too
|
|
86
|
+
type: this.count === 1 ? 'created' : this.getStatus(this.model),
|
|
87
|
+
member_id: memberCustomer.member_id,
|
|
88
|
+
subscription_id: this.model.id,
|
|
89
|
+
from_plan: this.count === 1 ? null : this.model.plan_id,
|
|
90
|
+
to_plan: this.count === 1 ? this.model.plan_id : null,
|
|
91
|
+
currency: this.model.plan_currency,
|
|
92
|
+
source: 'stripe',
|
|
93
|
+
mrr_delta: this.count === 1 ? mrr : -mrr,
|
|
94
|
+
created_at: this.count === 1 ? this.model.created_at : this.model.current_period_end
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = MembersPaidSubscriptionEventsImporter;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const {faker} = require('@faker-js/faker');
|
|
2
|
+
const TableImporter = require('./TableImporter');
|
|
3
|
+
const {luck} = require('../utils/random');
|
|
4
|
+
const dateToDatabaseString = require('../utils/database-date');
|
|
5
|
+
|
|
6
|
+
class MembersProductsImporter extends TableImporter {
|
|
7
|
+
static table = 'members_products';
|
|
8
|
+
static dependencies = ['products', 'members'];
|
|
9
|
+
|
|
10
|
+
constructor(knex, transaction) {
|
|
11
|
+
super(MembersProductsImporter.table, knex, transaction);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async import(quantity) {
|
|
15
|
+
const members = await this.transaction.select('id').from('members').whereNot('status', 'free');
|
|
16
|
+
this.products = await this.transaction.select('id').from('products').whereNot('type', 'fee');
|
|
17
|
+
|
|
18
|
+
await this.importForEach(members, quantity ? quantity / members.length : 1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
getProduct() {
|
|
22
|
+
if (this.products.length > 1) {
|
|
23
|
+
return luck(10) ? this.products[2]
|
|
24
|
+
: luck(50) ? this.products[1]
|
|
25
|
+
: this.products[0];
|
|
26
|
+
} else {
|
|
27
|
+
return this.products[0];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
generate() {
|
|
32
|
+
return {
|
|
33
|
+
id: this.fastFakeObjectId(),
|
|
34
|
+
member_id: this.model.id,
|
|
35
|
+
product_id: this.getProduct().id,
|
|
36
|
+
sort_order: 0,
|
|
37
|
+
expiry_at: this.model.status === 'paid' ? null : (luck(50) ? null : dateToDatabaseString(faker.date.future()))
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = MembersProductsImporter;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const TableImporter = require('./TableImporter');
|
|
2
|
+
const {faker} = require('@faker-js/faker');
|
|
3
|
+
const dateToDatabaseString = require('../utils/database-date');
|
|
4
|
+
|
|
5
|
+
class MembersStatusEventsImporter extends TableImporter {
|
|
6
|
+
static table = 'members_status_events';
|
|
7
|
+
static dependencies = ['members'];
|
|
8
|
+
|
|
9
|
+
constructor(knex, transaction) {
|
|
10
|
+
super(MembersStatusEventsImporter.table, knex, transaction);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async import(quantity) {
|
|
14
|
+
let offset = 0;
|
|
15
|
+
let limit = 100000;
|
|
16
|
+
|
|
17
|
+
// eslint-disable-next-line no-constant-condition
|
|
18
|
+
while (true) {
|
|
19
|
+
const members = await this.transaction.select('id', 'created_at', 'status').from('members').limit(limit).offset(offset);
|
|
20
|
+
|
|
21
|
+
if (members.length === 0) {
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
await this.importForEach(members, quantity ? quantity / members.length : 2);
|
|
26
|
+
offset += limit;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
setReferencedModel(model) {
|
|
31
|
+
this.events = [{
|
|
32
|
+
id: this.fastFakeObjectId(),
|
|
33
|
+
member_id: model.id,
|
|
34
|
+
from_status: null,
|
|
35
|
+
to_status: 'free',
|
|
36
|
+
created_at: dateToDatabaseString(model.created_at)
|
|
37
|
+
}];
|
|
38
|
+
if (model.status !== 'free') {
|
|
39
|
+
this.events.push({
|
|
40
|
+
id: this.fastFakeObjectId(),
|
|
41
|
+
member_id: model.id,
|
|
42
|
+
from_status: 'free',
|
|
43
|
+
to_status: model.status,
|
|
44
|
+
created_at: dateToDatabaseString(faker.date.between(new Date(model.created_at), new Date()))
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
generate() {
|
|
50
|
+
const event = this.events.pop();
|
|
51
|
+
if (!event) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
return event;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = MembersStatusEventsImporter;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const {faker} = require('@faker-js/faker');
|
|
2
|
+
const TableImporter = require('./TableImporter');
|
|
3
|
+
|
|
4
|
+
class MembersStripeCustomersImporter extends TableImporter {
|
|
5
|
+
static table = 'members_stripe_customers';
|
|
6
|
+
static dependencies = ['members'];
|
|
7
|
+
|
|
8
|
+
constructor(knex, transaction) {
|
|
9
|
+
super(MembersStripeCustomersImporter.table, knex, transaction);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async import(quantity) {
|
|
13
|
+
if (quantity === 0) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let offset = 0;
|
|
18
|
+
let limit = 100000;
|
|
19
|
+
|
|
20
|
+
// eslint-disable-next-line no-constant-condition
|
|
21
|
+
while (true) {
|
|
22
|
+
const members = await this.transaction.select('id', 'name', 'email', 'created_at', 'status').from('members').limit(limit).offset(offset);
|
|
23
|
+
|
|
24
|
+
if (members.length === 0) {
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
await this.importForEach(members, quantity ? quantity / members.length : 1);
|
|
29
|
+
offset += limit;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
generate() {
|
|
34
|
+
if (this.model.status !== 'paid') {
|
|
35
|
+
// Only 30% of free members should have a stripe customer = have had a subscription in the past or tried to subscribe
|
|
36
|
+
// The number should increase the older the member is
|
|
37
|
+
|
|
38
|
+
const daysSinceMemberCreated = Math.floor((new Date() - new Date(this.model.created_at)) / (1000 * 60 * 60 * 24));
|
|
39
|
+
const shouldHaveStripeCustomer = faker.datatype.number({min: 0, max: 100}) < Math.max(Math.min(daysSinceMemberCreated / 60, 15), 2);
|
|
40
|
+
|
|
41
|
+
if (!shouldHaveStripeCustomer) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
id: this.fastFakeObjectId(),
|
|
48
|
+
member_id: this.model.id,
|
|
49
|
+
customer_id: `cus_${faker.random.alphaNumeric(14, {
|
|
50
|
+
casing: 'mixed'
|
|
51
|
+
})}`,
|
|
52
|
+
name: this.model.name,
|
|
53
|
+
email: this.model.email,
|
|
54
|
+
created_at: this.model.created_at,
|
|
55
|
+
created_by: 'unused'
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = MembersStripeCustomersImporter;
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
const {faker} = require('@faker-js/faker');
|
|
2
|
+
const TableImporter = require('./TableImporter');
|
|
3
|
+
const dateToDatabaseString = require('../utils/database-date');
|
|
4
|
+
const generateEvents = require('../utils/event-generator');
|
|
5
|
+
const {luck} = require('../utils/random');
|
|
6
|
+
|
|
7
|
+
class MembersStripeCustomersSubscriptionsImporter extends TableImporter {
|
|
8
|
+
static table = 'members_stripe_customers_subscriptions';
|
|
9
|
+
static dependencies = ['members', 'members_products', 'members_stripe_customers', 'products', 'stripe_products', 'stripe_prices'];
|
|
10
|
+
|
|
11
|
+
constructor(knex, transaction) {
|
|
12
|
+
super(MembersStripeCustomersSubscriptionsImporter.table, knex, transaction);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async import() {
|
|
16
|
+
let offset = 0;
|
|
17
|
+
let limit = 5000;
|
|
18
|
+
this.products = await this.transaction.select('id', 'name').from('products').whereNot('type', 'free');
|
|
19
|
+
this.stripeProducts = await this.transaction.select('id', 'product_id', 'stripe_product_id').from('stripe_products');
|
|
20
|
+
this.stripePrices = await this.transaction.select('id', 'nickname', 'stripe_product_id', 'stripe_price_id', 'amount', 'interval', 'currency').from('stripe_prices');
|
|
21
|
+
|
|
22
|
+
// eslint-disable-next-line no-constant-condition
|
|
23
|
+
while (true) {
|
|
24
|
+
const membersStripeCustomers = await this.transaction.select('id', 'member_id', 'customer_id').from('members_stripe_customers').limit(limit).offset(offset);
|
|
25
|
+
|
|
26
|
+
if (membersStripeCustomers.length === 0) {
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
this.members = await this.transaction.select('id', 'status', 'created_at').from('members').whereIn('id', membersStripeCustomers.map(m => m.member_id));
|
|
31
|
+
|
|
32
|
+
if (this.members.length === 0) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const membersProducts = await this.transaction.select('member_id', 'product_id').from('members_products').whereIn('member_id', this.members.map(member => member.id));
|
|
37
|
+
//const membersStripeCustomers = await this.transaction.select('id', 'member_id', 'customer_id').from('members_stripe_customers').whereIn('member_id', this.members.map(member => member.id));
|
|
38
|
+
|
|
39
|
+
this.membersStripeCustomers = new Map();
|
|
40
|
+
for (const customer of membersStripeCustomers) {
|
|
41
|
+
this.membersStripeCustomers.set(customer.member_id, customer);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.membersProducts = new Map();
|
|
45
|
+
|
|
46
|
+
for (const product of membersProducts) {
|
|
47
|
+
this.membersProducts.set(product.member_id, product);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
await this.importForEach(this.members, 1.2);
|
|
51
|
+
|
|
52
|
+
offset += limit;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
setReferencedModel(model) {
|
|
57
|
+
this.model = model;
|
|
58
|
+
this.count = 0;
|
|
59
|
+
this.lastSubscriptionStart = null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
generate() {
|
|
63
|
+
this.count += 1;
|
|
64
|
+
|
|
65
|
+
const member = this.model;
|
|
66
|
+
const customer = this.membersStripeCustomers.get(this.model.id);
|
|
67
|
+
|
|
68
|
+
if (!customer) {
|
|
69
|
+
// This is a requirement, so skip if we don't have a customer
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (this.count > 1 && member.status !== 'paid') {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const memberProduct = this.membersProducts.get(this.model.id);
|
|
78
|
+
let ghostProduct = memberProduct ? this.products.find(product => product.id === memberProduct.product_id) : null;
|
|
79
|
+
|
|
80
|
+
// Whether we should create a valid subscription or not
|
|
81
|
+
// We'll only create one valid subscription for each member if they are currently paid
|
|
82
|
+
let createValid = this.count === 1 && member.status === 'paid';
|
|
83
|
+
|
|
84
|
+
if (!ghostProduct) {
|
|
85
|
+
// Generate canceled, incomplete, incomplete_expired or unpaid subscriptions
|
|
86
|
+
// Choose a random paid product
|
|
87
|
+
ghostProduct = faker.helpers.arrayElement(this.products);
|
|
88
|
+
createValid = false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const isMonthly = luck(70);
|
|
92
|
+
const stripeProduct = this.stripeProducts.find(product => product.product_id === ghostProduct.id);
|
|
93
|
+
const stripePrice = this.stripePrices.find((price) => {
|
|
94
|
+
return price.stripe_product_id === stripeProduct.stripe_product_id &&
|
|
95
|
+
(isMonthly ? price.interval === 'month' : price.interval === 'year');
|
|
96
|
+
});
|
|
97
|
+
const mrr = createValid ? (isMonthly ? stripePrice.amount : Math.floor(stripePrice.amount / 12)) : 0;
|
|
98
|
+
|
|
99
|
+
const referenceEndDate = this.lastSubscriptionStart ?? new Date();
|
|
100
|
+
|
|
101
|
+
if (!createValid) {
|
|
102
|
+
if (isMonthly) {
|
|
103
|
+
referenceEndDate.setMonth(referenceEndDate.getMonth() - 1);
|
|
104
|
+
} else {
|
|
105
|
+
referenceEndDate.setFullYear(referenceEndDate.getFullYear() - 1);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (referenceEndDate < member.created_at) {
|
|
110
|
+
// Not possible to create an invalid subscription here
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const [startDate] = generateEvents({
|
|
115
|
+
total: 1,
|
|
116
|
+
trend: 'negative',
|
|
117
|
+
startTime: new Date(member.created_at),
|
|
118
|
+
endTime: referenceEndDate,
|
|
119
|
+
shape: 'ease-out'
|
|
120
|
+
});
|
|
121
|
+
this.lastSubscriptionStart = startDate;
|
|
122
|
+
const endDate = new Date(startDate);
|
|
123
|
+
|
|
124
|
+
if (createValid) {
|
|
125
|
+
// End date should be in the future
|
|
126
|
+
|
|
127
|
+
if (isMonthly) {
|
|
128
|
+
endDate.setFullYear(new Date().getFullYear());
|
|
129
|
+
endDate.setMonth(new Date().getMonth());
|
|
130
|
+
if (endDate < new Date()) {
|
|
131
|
+
endDate.setMonth(endDate.getMonth() + 1);
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
endDate.setFullYear(new Date().getFullYear());
|
|
135
|
+
if (endDate < new Date()) {
|
|
136
|
+
endDate.setFullYear(endDate.getFullYear() + 1);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
// End date should be in the past
|
|
141
|
+
if (isMonthly) {
|
|
142
|
+
// What is the month difference between startDate and now? Pick a random number in between
|
|
143
|
+
const monthDiff = (new Date().getFullYear() - startDate.getFullYear()) * 12 + (new Date().getMonth() - startDate.getMonth());
|
|
144
|
+
if (monthDiff === 0) {
|
|
145
|
+
// Not possible to create an invalid subscription here
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const randomMonthDiff = faker.datatype.number({min: 1, max: monthDiff});
|
|
150
|
+
endDate.setMonth(startDate.getMonth() + randomMonthDiff);
|
|
151
|
+
} else {
|
|
152
|
+
// What is the year difference between startDate and now? Pick a random number in between
|
|
153
|
+
const yearDiff = new Date().getFullYear() - startDate.getFullYear();
|
|
154
|
+
|
|
155
|
+
if (yearDiff === 0) {
|
|
156
|
+
// Not possible to create an invalid subscription here
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const randomYearDiff = faker.datatype.number({min: 1, max: yearDiff});
|
|
160
|
+
|
|
161
|
+
endDate.setFullYear(startDate.getFullYear() + randomYearDiff);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Simulate some different statusses here:
|
|
166
|
+
// - active, not ending (cancel_at_period_end = false)
|
|
167
|
+
// - active, ending (cancel_at_period_end = true)
|
|
168
|
+
// - canceled -> current_period_end can be in both past or present, cancel_at_period_end can be both true or false
|
|
169
|
+
// - incomplete_expired -> user tried to pay but 3D secure expired
|
|
170
|
+
// - incomplete -> waiting on 3D secure
|
|
171
|
+
// - trialing -> need to set trial_end_at to a date in the future
|
|
172
|
+
// - past_due -> last paymet failed, but subscription still active until tried a couple of times
|
|
173
|
+
// - unpaid -> all payment attempts failed - but keep the subscription active (special setting in Stripe)
|
|
174
|
+
|
|
175
|
+
const validStatusses = new Array(10).fill({
|
|
176
|
+
status: 'active',
|
|
177
|
+
cancel_at_period_end: false
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Trialing only possible when the startDate > 1 month ago
|
|
181
|
+
const monthAgo = new Date();
|
|
182
|
+
|
|
183
|
+
if (!isMonthly) {
|
|
184
|
+
// Year ago
|
|
185
|
+
monthAgo.setFullYear(monthAgo.getFullYear() - 1);
|
|
186
|
+
} else {
|
|
187
|
+
// Month ago
|
|
188
|
+
monthAgo.setMonth(monthAgo.getMonth() - 1);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (startDate > monthAgo) {
|
|
192
|
+
validStatusses.push({
|
|
193
|
+
status: 'trialing',
|
|
194
|
+
cancel_at_period_end: false,
|
|
195
|
+
trial_end_at: dateToDatabaseString(endDate),
|
|
196
|
+
trial_start_at: dateToDatabaseString(startDate)
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Past due only possible if startDate < 1 month ago
|
|
201
|
+
if (startDate < monthAgo) {
|
|
202
|
+
validStatusses.push({
|
|
203
|
+
status: 'past_due',
|
|
204
|
+
cancel_at_period_end: false
|
|
205
|
+
});
|
|
206
|
+
validStatusses.push({
|
|
207
|
+
status: 'unpaid',
|
|
208
|
+
cancel_at_period_end: false
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const invalidStatusses = [
|
|
213
|
+
{
|
|
214
|
+
status: 'canceled',
|
|
215
|
+
cancel_at_period_end: true
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
status: 'canceled',
|
|
219
|
+
cancel_at_period_end: false
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
status: 'incomplete_expired',
|
|
223
|
+
cancel_at_period_end: false
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
status: 'incomplete',
|
|
227
|
+
cancel_at_period_end: false
|
|
228
|
+
}
|
|
229
|
+
];
|
|
230
|
+
|
|
231
|
+
const status = createValid ? faker.helpers.arrayElement(validStatusses) : faker.helpers.arrayElement(invalidStatusses);
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
id: this.fastFakeObjectId(),
|
|
235
|
+
customer_id: customer.customer_id,
|
|
236
|
+
subscription_id: `sub_${faker.random.alphaNumeric(14)}`,
|
|
237
|
+
stripe_price_id: stripePrice.stripe_price_id,
|
|
238
|
+
start_date: dateToDatabaseString(startDate),
|
|
239
|
+
created_at: dateToDatabaseString(startDate),
|
|
240
|
+
created_by: 'unused',
|
|
241
|
+
mrr,
|
|
242
|
+
plan_id: stripeProduct.stripe_product_id,
|
|
243
|
+
plan_nickname: `${ghostProduct.name} - ${stripePrice.nickname}`,
|
|
244
|
+
plan_interval: stripePrice.interval,
|
|
245
|
+
plan_amount: stripePrice.amount,
|
|
246
|
+
plan_currency: stripePrice.currency,
|
|
247
|
+
|
|
248
|
+
// Defaults
|
|
249
|
+
status: 'active',
|
|
250
|
+
cancel_at_period_end: false,
|
|
251
|
+
current_period_end: dateToDatabaseString(endDate),
|
|
252
|
+
|
|
253
|
+
// Override
|
|
254
|
+
...status
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
module.exports = MembersStripeCustomersSubscriptionsImporter;
|