mirror of
https://github.com/Shadowfita/docmost.git
synced 2025-11-24 05:31:19 +10:00
updates and fixes
* seo friendly urls * custom client serve-static module * database fixes * fix recent pages * other fixes
This commit is contained in:
154
apps/server/src/database/repos/group/group-user.repo.ts
Normal file
154
apps/server/src/database/repos/group/group-user.repo.ts
Normal file
@ -0,0 +1,154 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { InjectKysely } from 'nestjs-kysely';
|
||||
import { KyselyDB, KyselyTransaction } from '@docmost/db/types/kysely.types';
|
||||
import { dbOrTx, executeTx } from '@docmost/db/utils';
|
||||
import { GroupUser, InsertableGroupUser } from '@docmost/db/types/entity.types';
|
||||
import { PaginationOptions } from '../../pagination/pagination-options';
|
||||
import { executeWithPagination } from '@docmost/db/pagination/pagination';
|
||||
import { GroupRepo } from '@docmost/db/repos/group/group.repo';
|
||||
import { UserRepo } from '@docmost/db/repos/user/user.repo';
|
||||
|
||||
@Injectable()
|
||||
export class GroupUserRepo {
|
||||
constructor(
|
||||
@InjectKysely() private readonly db: KyselyDB,
|
||||
private readonly groupRepo: GroupRepo,
|
||||
private readonly userRepo: UserRepo,
|
||||
) {}
|
||||
|
||||
async getGroupUserById(
|
||||
userId: string,
|
||||
groupId: string,
|
||||
trx?: KyselyTransaction,
|
||||
) {
|
||||
const db = dbOrTx(this.db, trx);
|
||||
return db
|
||||
.selectFrom('groupUsers')
|
||||
.selectAll()
|
||||
.where('userId', '=', userId)
|
||||
.where('groupId', '=', groupId)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
async insertGroupUser(
|
||||
insertableGroupUser: InsertableGroupUser,
|
||||
trx?: KyselyTransaction,
|
||||
): Promise<GroupUser> {
|
||||
const db = dbOrTx(this.db, trx);
|
||||
return db
|
||||
.insertInto('groupUsers')
|
||||
.values(insertableGroupUser)
|
||||
.returningAll()
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
async getGroupUsersPaginated(groupId: string, pagination: PaginationOptions) {
|
||||
let query = this.db
|
||||
.selectFrom('groupUsers')
|
||||
.innerJoin('users', 'users.id', 'groupUsers.userId')
|
||||
.selectAll('users')
|
||||
.where('groupId', '=', groupId)
|
||||
.orderBy('createdAt', 'asc');
|
||||
|
||||
if (pagination.query) {
|
||||
query = query.where((eb) =>
|
||||
eb('users.name', 'ilike', `%${pagination.query}%`),
|
||||
);
|
||||
}
|
||||
|
||||
const result = await executeWithPagination(query, {
|
||||
page: pagination.page,
|
||||
perPage: pagination.limit,
|
||||
});
|
||||
|
||||
result.items.map((user) => {
|
||||
delete user.password;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async addUserToGroup(
|
||||
userId: string,
|
||||
groupId: string,
|
||||
workspaceId: string,
|
||||
trx?: KyselyTransaction,
|
||||
): Promise<void> {
|
||||
await executeTx(
|
||||
this.db,
|
||||
async (trx) => {
|
||||
const group = await this.groupRepo.findById(groupId, workspaceId, {
|
||||
trx,
|
||||
});
|
||||
if (!group) {
|
||||
throw new NotFoundException('Group not found');
|
||||
}
|
||||
|
||||
const user = await this.userRepo.findById(userId, workspaceId, {
|
||||
trx: trx,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new NotFoundException('User not found');
|
||||
}
|
||||
|
||||
const groupUserExists = await this.getGroupUserById(
|
||||
userId,
|
||||
groupId,
|
||||
trx,
|
||||
);
|
||||
|
||||
if (groupUserExists) {
|
||||
throw new BadRequestException(
|
||||
'User is already a member of this group',
|
||||
);
|
||||
}
|
||||
|
||||
await this.insertGroupUser(
|
||||
{
|
||||
userId,
|
||||
groupId,
|
||||
},
|
||||
trx,
|
||||
);
|
||||
},
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
async addUserToDefaultGroup(
|
||||
userId: string,
|
||||
workspaceId: string,
|
||||
trx?: KyselyTransaction,
|
||||
): Promise<void> {
|
||||
await executeTx(
|
||||
this.db,
|
||||
async (trx) => {
|
||||
const defaultGroup = await this.groupRepo.getDefaultGroup(
|
||||
workspaceId,
|
||||
trx,
|
||||
);
|
||||
await this.insertGroupUser(
|
||||
{
|
||||
userId,
|
||||
groupId: defaultGroup.id,
|
||||
},
|
||||
trx,
|
||||
);
|
||||
},
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
async delete(userId: string, groupId: string): Promise<void> {
|
||||
await this.db
|
||||
.deleteFrom('groupUsers')
|
||||
.where('userId', '=', userId)
|
||||
.where('groupId', '=', groupId)
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
148
apps/server/src/database/repos/group/group.repo.ts
Normal file
148
apps/server/src/database/repos/group/group.repo.ts
Normal file
@ -0,0 +1,148 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectKysely } from 'nestjs-kysely';
|
||||
import { KyselyDB, KyselyTransaction } from '@docmost/db/types/kysely.types';
|
||||
import { dbOrTx } from '@docmost/db/utils';
|
||||
import {
|
||||
Group,
|
||||
InsertableGroup,
|
||||
UpdatableGroup,
|
||||
} from '@docmost/db/types/entity.types';
|
||||
import { ExpressionBuilder, sql } from 'kysely';
|
||||
import { PaginationOptions } from '../../pagination/pagination-options';
|
||||
import { DB } from '@docmost/db/types/db';
|
||||
import { executeWithPagination } from '@docmost/db/pagination/pagination';
|
||||
import { DefaultGroup } from '../../../core/group/dto/create-group.dto';
|
||||
|
||||
@Injectable()
|
||||
export class GroupRepo {
|
||||
constructor(@InjectKysely() private readonly db: KyselyDB) {}
|
||||
|
||||
async findById(
|
||||
groupId: string,
|
||||
workspaceId: string,
|
||||
opts?: { includeMemberCount?: boolean; trx?: KyselyTransaction },
|
||||
): Promise<Group> {
|
||||
const db = dbOrTx(this.db, opts?.trx);
|
||||
return db
|
||||
.selectFrom('groups')
|
||||
.selectAll('groups')
|
||||
.$if(opts?.includeMemberCount, (qb) => qb.select(this.withMemberCount))
|
||||
.where('id', '=', groupId)
|
||||
.where('workspaceId', '=', workspaceId)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
async findByName(
|
||||
groupName: string,
|
||||
workspaceId: string,
|
||||
opts?: { includeMemberCount?: boolean; trx?: KyselyTransaction },
|
||||
): Promise<Group> {
|
||||
const db = dbOrTx(this.db, opts?.trx);
|
||||
return db
|
||||
.selectFrom('groups')
|
||||
.selectAll('groups')
|
||||
.$if(opts?.includeMemberCount, (qb) => qb.select(this.withMemberCount))
|
||||
.where(sql`LOWER(name)`, '=', sql`LOWER(${groupName})`)
|
||||
.where('workspaceId', '=', workspaceId)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
async update(
|
||||
updatableGroup: UpdatableGroup,
|
||||
groupId: string,
|
||||
workspaceId: string,
|
||||
): Promise<void> {
|
||||
await this.db
|
||||
.updateTable('groups')
|
||||
.set(updatableGroup)
|
||||
.where('id', '=', groupId)
|
||||
.where('workspaceId', '=', workspaceId)
|
||||
.execute();
|
||||
}
|
||||
|
||||
async insertGroup(
|
||||
insertableGroup: InsertableGroup,
|
||||
trx?: KyselyTransaction,
|
||||
): Promise<Group> {
|
||||
const db = dbOrTx(this.db, trx);
|
||||
return db
|
||||
.insertInto('groups')
|
||||
.values(insertableGroup)
|
||||
.returningAll()
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
async getDefaultGroup(
|
||||
workspaceId: string,
|
||||
trx: KyselyTransaction,
|
||||
): Promise<Group> {
|
||||
const db = dbOrTx(this.db, trx);
|
||||
return (
|
||||
db
|
||||
.selectFrom('groups')
|
||||
.selectAll()
|
||||
// .select((eb) => this.withMemberCount(eb))
|
||||
.where('isDefault', '=', true)
|
||||
.where('workspaceId', '=', workspaceId)
|
||||
.executeTakeFirst()
|
||||
);
|
||||
}
|
||||
|
||||
async createDefaultGroup(
|
||||
workspaceId: string,
|
||||
opts?: { userId?: string; trx?: KyselyTransaction },
|
||||
): Promise<Group> {
|
||||
const { userId, trx } = opts;
|
||||
const insertableGroup: InsertableGroup = {
|
||||
name: DefaultGroup.EVERYONE,
|
||||
isDefault: true,
|
||||
creatorId: userId,
|
||||
workspaceId: workspaceId,
|
||||
};
|
||||
|
||||
return this.insertGroup(insertableGroup, trx);
|
||||
}
|
||||
|
||||
async getGroupsPaginated(workspaceId: string, pagination: PaginationOptions) {
|
||||
let query = this.db
|
||||
.selectFrom('groups')
|
||||
.selectAll('groups')
|
||||
.select((eb) => this.withMemberCount(eb))
|
||||
.where('workspaceId', '=', workspaceId)
|
||||
.orderBy('memberCount', 'desc')
|
||||
.orderBy('createdAt', 'asc');
|
||||
|
||||
if (pagination.query) {
|
||||
query = query.where((eb) =>
|
||||
eb('name', 'ilike', `%${pagination.query}%`).or(
|
||||
'description',
|
||||
'ilike',
|
||||
`%${pagination.query}%`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const result = executeWithPagination(query, {
|
||||
page: pagination.page,
|
||||
perPage: pagination.limit,
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
withMemberCount(eb: ExpressionBuilder<DB, 'groups'>) {
|
||||
return eb
|
||||
.selectFrom('groupUsers')
|
||||
.select((eb) => eb.fn.countAll().as('count'))
|
||||
.whereRef('groupUsers.groupId', '=', 'groups.id')
|
||||
.as('memberCount');
|
||||
}
|
||||
|
||||
async delete(groupId: string, workspaceId: string): Promise<void> {
|
||||
await this.db
|
||||
.deleteFrom('groups')
|
||||
.where('id', '=', groupId)
|
||||
.where('workspaceId', '=', workspaceId)
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user