import { Injectable } from '@nestjs/common'; import { InjectKysely } from 'nestjs-kysely'; import { KyselyDB, KyselyTransaction } from '../../types/kysely.types'; import { dbOrTx } from '../../utils'; import { InsertableWorkspace, UpdatableWorkspace, Workspace, } from '@docmost/db/types/entity.types'; import { ExpressionBuilder, sql } from 'kysely'; import { DB, Workspaces } from '@docmost/db/types/db'; @Injectable() export class WorkspaceRepo { public baseFields: Array = [ 'id', 'name', 'description', 'logo', 'hostname', 'customDomain', 'settings', 'defaultRole', 'emailDomains', 'defaultSpaceId', 'createdAt', 'updatedAt', 'deletedAt', 'stripeCustomerId', 'status', 'billingEmail', 'trialEndAt', 'enforceSso', 'plan', ]; constructor(@InjectKysely() private readonly db: KyselyDB) {} async findById( workspaceId: string, opts?: { withLock?: boolean; withMemberCount?: boolean; withLicenseKey?: boolean; trx?: KyselyTransaction; }, ): Promise { const db = dbOrTx(this.db, opts?.trx); let query = db .selectFrom('workspaces') .select(this.baseFields) .where('id', '=', workspaceId); if (opts?.withMemberCount) { query = query.select(this.withMemberCount); } if (opts?.withLicenseKey) { query = query.select('licenseKey'); } if (opts?.withLock && opts?.trx) { query = query.forUpdate(); } return query.executeTakeFirst(); } async findFirst(): Promise { return await this.db .selectFrom('workspaces') .selectAll() .orderBy('createdAt', 'asc') .limit(1) .executeTakeFirst(); } async findByHostname(hostname: string): Promise { return await this.db .selectFrom('workspaces') .selectAll() .where(sql`LOWER(hostname)`, '=', sql`LOWER(${hostname})`) .executeTakeFirst(); } async hostnameExists( hostname: string, trx?: KyselyTransaction, ): Promise { if (hostname?.length < 1) return false; const db = dbOrTx(this.db, trx); let { count } = await db .selectFrom('workspaces') .select((eb) => eb.fn.count('id').as('count')) .where(sql`LOWER(hostname)`, '=', sql`LOWER(${hostname})`) .executeTakeFirst(); count = count as number; return count != 0; } async updateWorkspace( updatableWorkspace: UpdatableWorkspace, workspaceId: string, trx?: KyselyTransaction, ): Promise { const db = dbOrTx(this.db, trx); return db .updateTable('workspaces') .set({ ...updatableWorkspace, updatedAt: new Date() }) .where('id', '=', workspaceId) .returning(this.baseFields) .executeTakeFirst(); } async insertWorkspace( insertableWorkspace: InsertableWorkspace, trx?: KyselyTransaction, ): Promise { const db = dbOrTx(this.db, trx); return db .insertInto('workspaces') .values(insertableWorkspace) .returning(this.baseFields) .executeTakeFirst(); } async count(): Promise { const { count } = await this.db .selectFrom('workspaces') .select((eb) => eb.fn.count('id').as('count')) .executeTakeFirst(); return count as number; } withMemberCount(eb: ExpressionBuilder) { return eb .selectFrom('users') .select((eb) => eb.fn.countAll().as('count')) .where('users.deactivatedAt', 'is', null) .where('users.deletedAt', 'is', null) .whereRef('users.workspaceId', '=', 'workspaces.id') .as('memberCount'); } async getActiveUserCount(workspaceId: string): Promise { const users = await this.db .selectFrom('users') .select(['id', 'deactivatedAt', 'deletedAt']) .where('workspaceId', '=', workspaceId) .execute(); const activeUsers = users.filter( (user) => user.deletedAt === null && user.deactivatedAt === null, ); return activeUsers.length; } }