Files
docmost/apps/server/src/database/repos/workspace/workspace.repo.ts
2025-05-09 16:44:33 +01:00

160 lines
4.0 KiB
TypeScript

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<keyof Workspaces> = [
'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<Workspace> {
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<Workspace> {
return await this.db
.selectFrom('workspaces')
.selectAll()
.orderBy('createdAt', 'asc')
.limit(1)
.executeTakeFirst();
}
async findByHostname(hostname: string): Promise<Workspace> {
return await this.db
.selectFrom('workspaces')
.selectAll()
.where(sql`LOWER(hostname)`, '=', sql`LOWER(${hostname})`)
.executeTakeFirst();
}
async hostnameExists(
hostname: string,
trx?: KyselyTransaction,
): Promise<boolean> {
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<Workspace> {
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<Workspace> {
const db = dbOrTx(this.db, trx);
return db
.insertInto('workspaces')
.values(insertableWorkspace)
.returning(this.baseFields)
.executeTakeFirst();
}
async count(): Promise<number> {
const { count } = await this.db
.selectFrom('workspaces')
.select((eb) => eb.fn.count('id').as('count'))
.executeTakeFirst();
return count as number;
}
withMemberCount(eb: ExpressionBuilder<DB, 'workspaces'>) {
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<number> {
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;
}
}