UNPKG

@wepublish/api-db-mongodb

Version:

We.publish Database adapter for mongoDB

1,028 lines (928 loc) 32.6 kB
import {Db} from 'mongodb' import {CollectionName, DBInvoice, DBPaymentMethod, DBUser} from './db/schema' import { ArticleBlock, BlockType, CreateSettingArgs, PageBlock, PaymentProviderCustomer, SettingName, Subscription, SubscriptionDeactivationReason } from '@wepublish/api' import {slugify} from './utility' export interface Migration { readonly version: number migrate(adapter: Db, locale: string): Promise<void> } const SessionDocumentTTL = 60 * 60 * 24 // 24h export const Migrations: Migration[] = [ { version: 0, async migrate(db, locale) { const migrations = await db.createCollection(CollectionName.Migrations, {strict: true}) await migrations.createIndex({name: 1}, {unique: true}) const users = await db.createCollection(CollectionName.Users, { strict: true }) await users.createIndex({email: 1}, {unique: true}) const sessions = await db.createCollection(CollectionName.Sessions, { strict: true }) await sessions.createIndex({userID: 1}) await sessions.createIndex({token: 1}, {unique: true}) await sessions.createIndex({expiresAt: 1}, {expireAfterSeconds: SessionDocumentTTL}) const navigations = await db.createCollection(CollectionName.Navigations, {strict: true}) await navigations.createIndex({createdAt: -1}) await navigations.createIndex({modifiedAt: -1}) await navigations.createIndex({name: 1}) await navigations.createIndex({key: 1}, {unique: true}) const authors = await db.createCollection(CollectionName.Authors, {strict: true}) await authors.createIndex({createdAt: -1}) await authors.createIndex({modifiedAt: -1}) await authors.createIndex({name: 1}) await authors.createIndex({slug: 1}, {unique: true}) const images = await db.createCollection(CollectionName.Images, {strict: true}) await images.createIndex({createdAt: -1}) await images.createIndex({modifiedAt: -1}) await images.createIndex({title: 1}) await images.createIndex({tags: 1}, {collation: {locale, strength: 2}}) const articles = await db.createCollection(CollectionName.Articles, { strict: true }) await articles.createIndex({createdAt: -1}) await articles.createIndex({modifiedAt: -1}) await articles.createIndex({'published.publishedAt': -1}) await articles.createIndex({'published.updatedAt': -1}) await articles.createIndex({'pending.publishAt': -1}) await articles.createIndex({'draft.tags': 1}, {collation: {locale, strength: 2}}) await articles.createIndex({'pending.tags': 1}, {collation: {locale, strength: 2}}) await articles.createIndex({'published.tags': 1}, {collation: {locale, strength: 2}}) await db.createCollection(CollectionName.ArticlesHistory, { strict: true }) const pages = await db.createCollection(CollectionName.Pages, { strict: true }) await pages.createIndex({createdAt: -1}) await pages.createIndex({modifiedAt: -1}) await pages.createIndex({'published.publishedAt': -1}) await pages.createIndex({'published.updatedAt': -1}) await pages.createIndex({'pending.publishAt': -1}) await pages.createIndex({'draft.tags': 1}, {collation: {locale, strength: 2}}) await pages.createIndex({'pending.tags': 1}, {collation: {locale, strength: 2}}) await pages.createIndex({'published.tags': 1}, {collation: {locale, strength: 2}}) await db.createCollection(CollectionName.PagesHistory, { strict: true }) } }, { // Fix incorrect migration index. Add user roles, Add name and roleIDs to users. version: 1, async migrate(db, locale) { const migrations = db.collection(CollectionName.Migrations) await migrations.dropIndex('name_1') await migrations.createIndex({version: 1}, {unique: true}) const userRoles = await db.createCollection(CollectionName.UserRoles, { strict: true }) await userRoles.createIndex({name: 1}, {unique: true}) await userRoles.insertMany([ { _id: 'admin', createdAt: new Date(), modifiedAt: new Date(), systemRole: true, name: 'Admin', description: 'Administrator Role', permissionIDs: [] }, { _id: 'editor', createdAt: new Date(), modifiedAt: new Date(), systemRole: true, name: 'Editor', description: 'Editor Role', permissionIDs: [] } ]) const user = db.collection(CollectionName.Users) await user.updateMany({}, [ { $set: { name: '$email', roleIDs: ['admin'] } } ]) } }, { // Add peering and token collections and migrate ArticleTeaserGridBlock to TeaserGridBlock. version: 2, async migrate(db) { const userRoles = db.collection(CollectionName.UserRoles) await userRoles.insertOne({ _id: 'peer', createdAt: new Date(), modifiedAt: new Date(), systemRole: true, name: 'Peer', description: 'Peer Role', permissionIDs: [] }) await db.createCollection(CollectionName.PeerProfiles, {strict: true}) const peers = await db.createCollection(CollectionName.Peers, {strict: true}) await peers.createIndex({slug: 1}, {unique: true}) const tokens = await db.createCollection(CollectionName.Tokens, { strict: true }) await tokens.createIndex({name: 1}, {unique: true}) const filter = { $or: [ {'draft.blocks.type': 'articleTeaserGrid'}, {'published.blocks.type': 'articleTeaserGrid'}, {'pending.blocks.type': 'articleTeaserGrid'} ] } function mapArticleTeaserGridBlock(block: any) { if (block.type === 'articleTeaserGrid') { return { type: 'teaserGrid', teasers: block.teasers.map((teaser: any) => teaser ? { type: 'article', style: 'default', articleID: teaser.articleID } : null ), numColumns: block.numColumns } } return block } const articles = db.collection(CollectionName.Articles) const migrationArticles = await articles.find(filter).toArray() for (const article of migrationArticles) { if (article.draft) { article.draft.blocks = article.draft.blocks.map(mapArticleTeaserGridBlock) } if (article.pending) { article.pending.blocks = article.pending.blocks.map(mapArticleTeaserGridBlock) } if (article.published) { article.published.blocks = article.published.blocks.map(mapArticleTeaserGridBlock) } await articles.findOneAndReplace({_id: article._id}, article) } const pages = db.collection(CollectionName.Pages) const migrationPages = await pages.find(filter).toArray() for (const page of migrationPages) { if (page.draft) { page.draft.blocks = page.draft.blocks.map(mapArticleTeaserGridBlock) } if (page.pending) { page.pending.blocks = page.pending.blocks.map(mapArticleTeaserGridBlock) } if (page.published) { page.published.blocks = page.published.blocks.map(mapArticleTeaserGridBlock) } await pages.findOneAndReplace({_id: page._id}, page) } } }, { // Add peering and token collections and migrate ArticleTeaserGridBlock to TeaserGridBlock. version: 3, async migrate(db) { const articles = db.collection(CollectionName.Articles) const migrationArticles = await articles.find().toArray() for (const article of migrationArticles) { if (article.draft) { article.draft.properties = [] } if (article.pending) { article.pending.properties = [] } if (article.published) { article.published.properties = [] } await articles.findOneAndReplace({_id: article._id}, article) } const pages = db.collection(CollectionName.Pages) const migrationPages = await pages.find().toArray() for (const page of migrationPages) { if (page.draft) { page.draft.properties = [] } if (page.pending) { page.pending.properties = [] } if (page.published) { page.published.properties = [] } await pages.findOneAndReplace({_id: page._id}, page) } } }, { // Add RTE to page break block if not exists in articles and pages version: 4, async migrate(db) { await db.collection(CollectionName.Articles).updateMany( {'draft.blocks': {$elemMatch: {type: 'linkPageBreak', richText: {$exists: false}}}}, { $set: { 'draft.blocks.$[elem].richText': [{children: [{text: ''}], type: 'paragraph'}] } }, {arrayFilters: [{'elem.type': 'linkPageBreak'}]} ) await db.collection(CollectionName.Articles).updateMany( { 'published.blocks': { $elemMatch: {type: 'linkPageBreak', richText: {$exists: false}} } }, { $set: {'published.blocks.$[elem].richText': [{children: [{text: ''}], type: 'paragraph'}]} }, {arrayFilters: [{'elem.type': 'linkPageBreak'}]} ) await db.collection(CollectionName.Articles).updateMany( { 'pending.blocks': {$elemMatch: {type: 'linkPageBreak', richText: {$exists: false}}} }, {$set: {'pending.blocks.$[elem].richText': [{children: [{text: ''}], type: 'paragraph'}]}}, {arrayFilters: [{'elem.type': 'linkPageBreak'}]} ) // Add RTE to page break block if not exists in pages await db.collection(CollectionName.Pages).updateMany( {'draft.blocks': {$elemMatch: {type: 'linkPageBreak', richText: {$exists: false}}}}, { $set: { 'draft.blocks.$[elem].richText': [{children: [{text: ''}], type: 'paragraph'}] } }, {arrayFilters: [{'elem.type': 'linkPageBreak'}]} ) await db.collection(CollectionName.Pages).updateMany( { 'published.blocks': { $elemMatch: {type: 'linkPageBreak', richText: {$exists: false}} } }, { $set: {'published.blocks.$[elem].richText': [{children: [{text: ''}], type: 'paragraph'}]} }, {arrayFilters: [{'elem.type': 'linkPageBreak'}]} ) await db.collection(CollectionName.Pages).updateMany( {'pending.blocks': {$elemMatch: {type: 'linkPageBreak', richText: {$exists: false}}}}, { $set: {'pending.blocks.$[elem].richText': [{children: [{text: ''}], type: 'paragraph'}]} }, {arrayFilters: [{'elem.type': 'linkPageBreak'}]} ) } }, { // Add hideButton false to pageBreakBlocks version: 5, async migrate(db) { await db.collection(CollectionName.Articles).updateMany( {'draft.blocks': {$elemMatch: {type: 'linkPageBreak', hideButton: {$exists: false}}}}, { $set: { 'draft.blocks.$[elem].hideButton': false } }, {arrayFilters: [{'elem.type': 'linkPageBreak'}]} ) await db.collection(CollectionName.Articles).updateMany( { 'published.blocks': { $elemMatch: {type: 'linkPageBreak', hideButton: {$exists: false}} } }, { $set: {'published.blocks.$[elem].hideButton': false} }, {arrayFilters: [{'elem.type': 'linkPageBreak'}]} ) await db.collection(CollectionName.Articles).updateMany( { 'pending.blocks': {$elemMatch: {type: 'linkPageBreak', hideButton: {$exists: false}}} }, {$set: {'pending.blocks.$[elem].hideButton': false}}, {arrayFilters: [{'elem.type': 'linkPageBreak'}]} ) // Add RTE to page break block if not exists in pages await db.collection(CollectionName.Pages).updateMany( {'draft.blocks': {$elemMatch: {type: 'linkPageBreak', hideButton: {$exists: false}}}}, { $set: { 'draft.blocks.$[elem].hideButton': false } }, {arrayFilters: [{'elem.type': 'linkPageBreak'}]} ) await db.collection(CollectionName.Pages).updateMany( { 'published.blocks': { $elemMatch: {type: 'linkPageBreak', hideButton: {$exists: false}} } }, { $set: {'published.blocks.$[elem].hideButton': false} }, {arrayFilters: [{'elem.type': 'linkPageBreak'}]} ) await db.collection(CollectionName.Pages).updateMany( {'pending.blocks': {$elemMatch: {type: 'linkPageBreak', hideButton: {$exists: false}}}}, { $set: {'pending.blocks.$[elem].hideButton': false} }, {arrayFilters: [{'elem.type': 'linkPageBreak'}]} ) } }, { // Add hide author property to article. version: 6, async migrate(db) { await db.collection(CollectionName.Articles).updateMany( { pending: {$ne: null}, 'pending.hideAuthor': {$exists: false} }, {$set: {'pending.hideAuthor': false}} ) await db.collection(CollectionName.Articles).updateMany( { published: {$ne: null}, 'published.hideAuthor': {$exists: false} }, {$set: {'published.hideAuthor': false}} ) await db.collection(CollectionName.Articles).updateMany( { draft: {$ne: null}, 'draft.hideAuthor': {$exists: false} }, {$set: {'draft.hideAuthor': false}} ) } }, { // Add Call To Action Details to Peer Profile. version: 7, async migrate(db) { await db.collection(CollectionName.PeerProfiles).updateMany( { callToActionURL: {$exists: false}, callToActionText: {$exists: false} }, { $set: { callToActionURL: '', callToActionText: [] } } ) } }, { // Add social media metatags to article. version: 8, async migrate(db) { await db.collection(CollectionName.Articles).updateMany( { pending: {$ne: null}, 'pending.socialMediaAuthorIDs': {$exists: false} }, { $set: { 'pending.socialMediaAuthorIDs': [] } } ) await db.collection(CollectionName.Articles).updateMany( { published: {$ne: null}, 'published.socialMediaAuthorIDs': {$exists: false} }, { $set: { 'published.socialMediaAuthorIDs': [] } } ) await db.collection(CollectionName.Articles).updateMany( { draft: {$ne: null}, 'draft.socialMediaAuthorIDs': {$exists: false} }, { $set: { 'draft.socialMediaAuthorIDs': [] } } ) } }, { // Add new collection MailLog version: 9, async migrate(db) { const mailLogs = await db.createCollection(CollectionName.MailLog, {strict: true}) await mailLogs.createIndex({subject: 1}) } }, { // Add MemberPlan Collection and PaymentMethod Collection version: 10, async migrate(db, locale) { const memberPlans = await db.createCollection(CollectionName.MemberPlans, { strict: true }) await memberPlans.createIndex({name: 1}) await memberPlans.createIndex({slug: 1}, {unique: true}) const paymentMethod = await db.createCollection(CollectionName.PaymentMethods, { strict: true }) await paymentMethod.createIndex({name: 1}) await paymentMethod.createIndex({paymentAdapter: 1}) const users = db.collection(CollectionName.Users) await users.createIndex({'subscription.memberPlanId': 1}) await users.updateMany( { paymentProviderCustomers: {$exists: false} }, {$set: {paymentProviderCustomers: {}}} ) await users.updateMany( { active: {$exists: false} }, {$set: {active: true}} ) await users.updateMany( { lastLogin: {$exists: false} }, {$set: {lastLogin: null}} ) await users.updateMany( { properties: {$exists: false} }, {$set: {properties: []}} ) const invoices = await db.createCollection(CollectionName.Invoices, { strict: true }) await invoices.createIndex({mail: 1}) const payments = await db.createCollection(CollectionName.Payments, { strict: true }) await payments.createIndex({intentID: 1}) } }, { // Add Commenting Table. version: 11, async migrate(db) { const comments = await db.createCollection(CollectionName.Comments, { strict: true }) await comments.createIndex({createdAt: -1}) await comments.createIndex({'revisions.createdAt': -1}) } }, { // Make slug for published pages unique version: 12, async migrate(db, locale) { const pages = await db.collection(CollectionName.Pages) await pages.createIndex( {'published.slug': 1}, { collation: {locale, strength: 2}, unique: true, partialFilterExpression: {'published.slug': {$exists: true}} } ) } }, { // Rename street field in address to address version: 13, async migrate(db, locale) { const users = await db.collection(CollectionName.Users) await users.updateMany( { 'address.street': {$exists: true} }, { $rename: {'address.street': 'address.streetAddress'} } ) } }, { // Add emailVerifiedAt and oauth2Accounts to user model version: 14, async migrate(db, locale) { const users = await db.collection(CollectionName.Users) await users.updateMany( { emailVerifiedAt: {$exists: false} }, {$set: {emailVerifiedAt: null}} ) await users.updateMany( { oauth2Accounts: {$exists: false} }, {$set: {oauth2Accounts: []}} ) } }, { // Set paymentProviderCustomers to an empty array version: 15, async migrate(db, locale) { const users = await db.collection(CollectionName.Users) const allUsers: DBUser[] = await users.find({}).toArray() for (const user of allUsers) { const paymentProvidersCustomersArray: PaymentProviderCustomer[] = [] const paymentProviderCustomers = Object.keys(user.paymentProviderCustomers) paymentProviderCustomers.forEach(ppc => { interface OldPaymentProviderCustomer extends PaymentProviderCustomer { customerID: never id: PaymentProviderCustomer['customerID'] } const userPPC = user.paymentProviderCustomers[+ppc] as OldPaymentProviderCustomer paymentProvidersCustomersArray.push({ paymentProviderID: ppc, customerID: userPPC.id }) }) await users.updateOne( {_id: user._id}, {$set: {paymentProviderCustomers: paymentProvidersCustomersArray}} ) } } }, { // add slug to existing paymentMethods version: 16, async migrate(db, locale) { const paymentMethods = await db.collection(CollectionName.PaymentMethods) const allPaymentMethods: DBPaymentMethod[] = await paymentMethods.find({}).toArray() for (const paymentMethod of allPaymentMethods) { const slug = slugify(paymentMethod.name) await paymentMethods.updateOne({_id: paymentMethod._id}, {$set: {slug}}) } } }, { // migrate existing deactivated subscriptions version: 17, async migrate(db, locale) { const users = await db.collection(CollectionName.Users) await users.updateMany( { $and: [ {'subscription.deactivatedAt': {$exists: true}}, {'subscription.deactivatedAt': null} ] }, [ { $set: { 'subscription.deactivation': null } }, { $unset: ['subscription.deactivatedAt'] } ] ) await users.updateMany( { $and: [ {'subscription.deactivatedAt': {$exists: true}}, {'subscription.deactivatedAt': {$ne: null}} ] }, [ { $set: { 'subscription.deactivation.date': '$subscription.deactivatedAt', 'subscription.deactivation.reason': SubscriptionDeactivationReason.None } }, { $unset: ['subscription.deactivatedAt'] } ] ) } }, { // migrate existing deactivated subscriptions version: 18, async migrate(db, locale) { // Values required to execute migration const TEMP_USER_PREFIX = '__temp_' const removePrefixTempUser = function removePrefixTempUser(userID: string): string { return userID.replace(TEMP_USER_PREFIX, '') } // 1. move subscription from user object into new subscription collection const users = await db.collection(CollectionName.Users) const userWithSubscriptions = await users.find({subscription: {$exists: true}}).toArray() const newSubscriptions: Omit<Subscription, 'id'>[] = [] const OLD_TEMP_USER_REGEX = /^temp_[0-9]+_/ for (const user of userWithSubscriptions) { if (!user.subscription) continue // create correct temp user reference id const userId = OLD_TEMP_USER_REGEX.test(user.email) ? `${TEMP_USER_PREFIX}${user._id}` : user._id newSubscriptions.push({ userID: userId, createdAt: user.createdAt, modifiedAt: user.modifiedAt, memberPlanID: user.subscription.memberPlanID, paymentPeriodicity: user.subscription.paymentPeriodicity, monthlyAmount: user.subscription.monthlyAmount, autoRenew: user.subscription.autoRenew, startsAt: user.subscription.startsAt, paidUntil: user.subscription.paidUntil, periods: user.subscription.periods, paymentMethodID: user.subscription.paymentMethodID, properties: [], deactivation: user.subscription.deactivation }) } const subscriptions = await db.createCollection(CollectionName.Subscriptions, { strict: true }) if (newSubscriptions.length > 0) await subscriptions.insertMany(newSubscriptions) // 2. create new invoices const invoices = await db.collection(CollectionName.Invoices) const allInvoices = await invoices.find({}).toArray() const allSubscriptions = await subscriptions.find({}).toArray() const newInvoices: DBInvoice[] = [] for (const invoice of allInvoices) { const userID = invoice.userID const subscription = allSubscriptions.find( subscription => removePrefixTempUser(subscription.userID) === userID ) if (!subscription) { continue } newInvoices.push({ _id: invoice._id, createdAt: invoice.createdAt, modifiedAt: invoice.modifiedAt, subscriptionID: subscription._id, description: invoice.description, canceledAt: invoice.canceledAt, dueAt: invoice.dueAt, items: invoice.items, mail: invoice.mail, paidAt: invoice.paidAt, sentReminderAt: invoice.sentReminderAt }) } // inform We.Publish operators to remove this manually await invoices.rename('invoices.bak') const emptyInvoices = await db.createCollection(CollectionName.Invoices, { strict: true }) if (newInvoices.length > 0) await emptyInvoices.insertMany(newInvoices) // 3. remove subscription object from user collection await users.updateMany( {subscription: {$exists: true}}, { $unset: {subscription: ''} } ) // 4. split existing user collection into new temp.user and user collection const tempUserQuery = {email: {$regex: OLD_TEMP_USER_REGEX}} const tempUsers = await users.find(tempUserQuery).toArray() const newTempUserCollection = await db.createCollection('temp.users', { strict: true }) if (tempUsers.length) { // remove old temp_ prefix from emails in the new temp.user collection for (const tempUser of tempUsers) { tempUser.email = tempUser.email.replace(OLD_TEMP_USER_REGEX, '') } await newTempUserCollection.insertMany(tempUsers) // 5. delete migrated temp users from user collection await users.deleteMany(tempUserQuery) } } }, { // rename image => source to link and author to source; This should make code and data consistent version: 19, async migrate(db, locale) { const images = await db.collection(CollectionName.Images) await images.updateMany({}, {$rename: {source: 'link'}}) await images.updateMany({}, {$rename: {author: 'source'}}) } }, { // change embed block properties width and height from number to string version: 20, async migrate(db) { const articles = db.collection(CollectionName.Articles) const migrationArticles = await articles.find().toArray() for (const article of migrationArticles) { if (article.draft) { article.draft.blocks.forEach((block: ArticleBlock) => { if (block.type === BlockType.Embed) { block.height = String(block.height) block.width = String(block.width) } }) } if (article.published) { article.published.blocks.forEach((block: ArticleBlock) => { if (block.type === BlockType.Embed) { block.height = String(block.height) block.width = String(block.width) } }) } if (article.pending) { article.pending.blocks.forEach((block: ArticleBlock) => { if (block.type === BlockType.Embed) { block.height = String(block.height) block.width = String(block.width) } }) } await articles.findOneAndReplace({_id: article._id}, article) } const pages = db.collection(CollectionName.Pages) const migrationPages = await pages.find().toArray() for (const page of migrationPages) { if (page.draft) { page.draft.blocks.forEach((block: PageBlock) => { if (block.type === BlockType.Embed) { block.height = String(block.height) block.width = String(block.width) } }) } if (page.published) { page.published.blocks.forEach((block: PageBlock) => { if (block.type === BlockType.Embed) { block.height = String(block.height) block.width = String(block.width) } }) } if (page.pending) { page.pending.blocks.forEach((block: PageBlock) => { if (block.type === BlockType.Embed) { block.height = String(block.height) block.width = String(block.width) } }) } await pages.findOneAndReplace({_id: page._id}, page) } } }, { version: 21, async migrate(db, locale) { const invoices = db.collection(CollectionName.Invoices) await invoices.updateMany({}, {$set: {manuallySetAsPaidByUserId: undefined}}) } }, { // Rename unused temp user collection. For operators to remove manually since the collection not used anymore. version: 22, async migrate(db) { const collections = await db.listCollections().toArray() if (collections.includes('temp.users')) { const tempUser = await db.collection('temp.users') await tempUser.rename('temp.users.bak') } } }, { // Try to migrate email addressees of users version: 23, async migrate(db) { const userCollection = await db.collection(CollectionName.Users) const subscriptionCollection = await db.collection(CollectionName.Subscriptions) const sessionCollection = await db.collection(CollectionName.Sessions) const users = await userCollection.find().toArray() // inform We.Publish operators to remove this manually await userCollection.rename('23-users.bak') const emptyUsers = await db.createCollection(CollectionName.Users, { strict: true }) type List = { userId: string email: string } const listSanitizedUsers: List[] = [] for (const user of users) { user.email = user.email.toLowerCase() const duplicatedMail = listSanitizedUsers.find(element => element.email === user.email) // If already a user with normalized mail exist merge them if (duplicatedMail) { // Update userID in subscription table const subscriptions = await subscriptionCollection.find({userID: user._id}).toArray() for (const subscription of subscriptions) { subscription.properties.push({ key: 'UserIDChangedByMigration23', value: subscription.userID, public: false }) subscription.userID = duplicatedMail.userId await subscriptionCollection.replaceOne({_id: subscription._id}, subscription, { upsert: true }) } // Update userID in session table const sessions = await sessionCollection.find({userID: user._id}).toArray() for (const session of sessions) { session.user = duplicatedMail.userId await sessionCollection.replaceOne({_id: session._id}, session, {upsert: true}) } } else { // If user is unique update mail of user await emptyUsers.insertOne(user) listSanitizedUsers.push({ userId: user._id, email: user.email }) } } } }, { // add settings category version: 24, async migrate(db, locale) { const settingsDoc = await db.createCollection(CollectionName.Settings, { strict: true }) const peeringTimeout: CreateSettingArgs<number> = { name: SettingName.PEERING_TIMEOUT_MS, value: 3000, settingRestriction: {minValue: 1000, maxValue: 10000} } const allowAnonCommenting: CreateSettingArgs<boolean> = { name: SettingName.ALLOW_GUEST_COMMENTING, value: false, settingRestriction: {allowedValues: {boolChoice: true}} } const sendLoginJWTExpires: CreateSettingArgs<number> = { name: SettingName.SEND_LOGIN_JWT_EXPIRES_MIN, value: 10080, settingRestriction: {minValue: 1, maxValue: 10080} } const resetPwdExpires: CreateSettingArgs<number> = { name: SettingName.RESET_PASSWORD_JWT_EXPIRES_MIN, value: 1440, settingRestriction: {minValue: 1, maxValue: 10080} } const invoiceReminderFreq: CreateSettingArgs<number> = { name: SettingName.INVOICE_REMINDER_FREQ, value: 3, settingRestriction: {minValue: 0, maxValue: 30} } const invoiceReminderTries: CreateSettingArgs<number> = { name: SettingName.INVOICE_REMINDER_MAX_TRIES, value: 5, settingRestriction: {minValue: 0, maxValue: 10} } await settingsDoc.insertMany([ peeringTimeout, allowAnonCommenting, sendLoginJWTExpires, resetPwdExpires, invoiceReminderFreq, invoiceReminderTries ]) } } ] export const LatestMigration = Migrations[Migrations.length - 1]