UNPKG

@wepublish/api-db-mongodb

Version:

We.publish Database adapter for mongoDB

209 lines (180 loc) 6.67 kB
import {MongoClient, Db} from 'mongodb' import {DBAdapter, SortOrder} from '@wepublish/api' import {Migrations, LatestMigration} from './migration' import {generateID} from './utility' import {MongoDBUserAdapter} from './db/user' import {MongoDBPeerAdapter} from './db/peer' import {MongoDBSessionAdapter} from './db/session' import {MongoDBAuthorAdapter} from './db/author' import {MongoDBNavigationAdapter} from './db/navigation' import {MongoDBImageAdapter} from './db/image' import {MongoDBTokenAdapter} from './db/token' import {DefaultSessionTTL, DefaultBcryptHashCostFactor} from './db/defaults' import {MongoDBCommentAdapter} from './db/comment' import {MongoDBArticleAdapter} from './db/article' import {MongoDBPageAdapter} from './db/page' import {DBMigration, CollectionName} from './db/schema' import {MongoDBUserRoleAdapter} from './db/userRole' import {MongoDBMemberPlanAdapter} from './db/memberPlan' import {MongoDBPaymentMethodAdapter} from './db/paymentMethod' import {MongoDBInvoiceAdapter} from './db/invoice' import {MongoDBPaymentAdapter} from './db/payment' import {MongoDBMailLogAdapter} from './db/mailLog' import {MongoDBSubscriptionAdapter} from './db/subscription' import {MongoDBSettingAdapter} from './db/setting' export interface MongoDBAdabterCommonArgs { readonly sessionTTL?: number readonly bcryptHashCostFactor?: number } export interface MongoDBAdapterConnectArgs extends MongoDBAdabterCommonArgs { readonly url: string readonly locale: string } export interface MongoDBAdapterInitializeArgs extends MongoDBAdabterCommonArgs { readonly url: string readonly locale: string readonly seed?: (adapter: MongoDBAdapter) => Promise<void> } export interface InitializationResult { readonly migrated?: { readonly from?: number readonly to: number } } interface MongoDBAdapterArgs extends MongoDBAdabterCommonArgs { readonly locale: string readonly client: MongoClient readonly db: Db } export class MongoDBAdapter implements DBAdapter { readonly sessionTTL: number readonly bcryptHashCostFactor: number readonly locale: string readonly client: MongoClient readonly db: Db readonly peer: MongoDBPeerAdapter readonly user: MongoDBUserAdapter readonly userRole: MongoDBUserRoleAdapter readonly subscription: MongoDBSubscriptionAdapter readonly session: MongoDBSessionAdapter readonly token: MongoDBTokenAdapter readonly navigation: MongoDBNavigationAdapter readonly author: MongoDBAuthorAdapter readonly image: MongoDBImageAdapter readonly comment: MongoDBCommentAdapter readonly article: MongoDBArticleAdapter readonly page: MongoDBPageAdapter readonly memberPlan: MongoDBMemberPlanAdapter readonly paymentMethod: MongoDBPaymentMethodAdapter readonly invoice: MongoDBInvoiceAdapter readonly payment: MongoDBPaymentAdapter readonly mailLog: MongoDBMailLogAdapter readonly setting: MongoDBSettingAdapter // Init // ==== private constructor({ sessionTTL = DefaultSessionTTL, bcryptHashCostFactor = DefaultBcryptHashCostFactor, locale, client, db }: MongoDBAdapterArgs) { this.sessionTTL = sessionTTL this.bcryptHashCostFactor = bcryptHashCostFactor this.locale = locale this.client = client this.db = db this.peer = new MongoDBPeerAdapter(db) this.user = new MongoDBUserAdapter(db, bcryptHashCostFactor, locale) this.userRole = new MongoDBUserRoleAdapter(db, locale) this.subscription = new MongoDBSubscriptionAdapter(db, locale) this.session = new MongoDBSessionAdapter(db, this.user, this.userRole, sessionTTL) this.token = new MongoDBTokenAdapter(db) this.navigation = new MongoDBNavigationAdapter(db) this.comment = new MongoDBCommentAdapter(db, locale) this.author = new MongoDBAuthorAdapter(db, locale) this.image = new MongoDBImageAdapter(db, locale) this.article = new MongoDBArticleAdapter(db, locale) this.page = new MongoDBPageAdapter(db, locale) this.memberPlan = new MongoDBMemberPlanAdapter(db, locale) this.paymentMethod = new MongoDBPaymentMethodAdapter(db) this.invoice = new MongoDBInvoiceAdapter(db, locale) this.payment = new MongoDBPaymentAdapter(db, locale) this.mailLog = new MongoDBMailLogAdapter(db, locale) this.setting = new MongoDBSettingAdapter(db) } static createMongoClient(url: string): Promise<MongoClient> { return MongoClient.connect(url, { pkFactory: { createPk() { return generateID() } }, useNewUrlParser: true, useUnifiedTopology: true }) } static async connect({ sessionTTL = DefaultSessionTTL, bcryptHashCostFactor = DefaultBcryptHashCostFactor, url, locale }: MongoDBAdapterConnectArgs) { const client = await this.createMongoClient(url) const db = client.db() const migrationState = await this.getDBMigrationState(db) if (migrationState?.version !== LatestMigration.version) { throw new Error( 'Database is not initialized or out of date, call `initialize` to intialize/migrate database.' ) } return new MongoDBAdapter({ sessionTTL, bcryptHashCostFactor, client, db, locale }) } static async getDBMigrationState(db: Db): Promise<DBMigration | null> { const result = await db .collection<DBMigration>(CollectionName.Migrations) .findOne({}, {sort: {createdAt: SortOrder.Descending}}) return result } static async initialize({ sessionTTL = DefaultSessionTTL, bcryptHashCostFactor = DefaultBcryptHashCostFactor, url, locale, seed }: MongoDBAdapterInitializeArgs): Promise<InitializationResult> { const client = await this.createMongoClient(url) const db = client.db() const migrationState = await this.getDBMigrationState(db) if (migrationState?.version === LatestMigration.version) { return {} } const index = Migrations.findIndex(migration => migration.version === migrationState?.version) const remainingMigrations = Migrations.slice(index + 1) for (const migration of remainingMigrations) { await migration.migrate(db, locale) await db.collection<DBMigration>(CollectionName.Migrations).insertOne({ version: migration.version, createdAt: new Date() }) } if (!migrationState) { const adapter = await this.connect({sessionTTL, bcryptHashCostFactor, url, locale}) await seed?.(adapter) await adapter.client.close() } await client.close() return { migrated: { from: migrationState?.version, to: LatestMigration.version } } } }