mirror of
https://github.com/docmost/docmost.git
synced 2026-09-11 07:56:54 +08:00
refactor(favorites): move favorite filtering into the repository query
This commit is contained in:
@@ -48,9 +48,9 @@ export class FavoriteController {
|
|||||||
|
|
||||||
await this.favoriteService.addFavorite(user.id, workspace.id, {
|
await this.favoriteService.addFavorite(user.id, workspace.id, {
|
||||||
type: dto.type,
|
type: dto.type,
|
||||||
pageId: dto.pageId,
|
pageId: dto.type === 'page' ? dto.pageId : undefined,
|
||||||
spaceId: dto.type === 'space' ? resolved.spaceId : undefined,
|
spaceId: dto.type === 'space' ? resolved.spaceId : undefined,
|
||||||
templateId: dto.templateId,
|
templateId: dto.type === 'template' ? dto.templateId : undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,14 +6,12 @@ import {
|
|||||||
import { PaginationOptions } from '@docmost/db/pagination/pagination-options';
|
import { PaginationOptions } from '@docmost/db/pagination/pagination-options';
|
||||||
import { InsertableFavorite } from '@docmost/db/types/entity.types';
|
import { InsertableFavorite } from '@docmost/db/types/entity.types';
|
||||||
import { PagePermissionRepo } from '@docmost/db/repos/page/page-permission.repo';
|
import { PagePermissionRepo } from '@docmost/db/repos/page/page-permission.repo';
|
||||||
import { SpaceMemberRepo } from '@docmost/db/repos/space/space-member.repo';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class FavoriteService {
|
export class FavoriteService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly favoriteRepo: FavoriteRepo,
|
private readonly favoriteRepo: FavoriteRepo,
|
||||||
private readonly pagePermissionRepo: PagePermissionRepo,
|
private readonly pagePermissionRepo: PagePermissionRepo,
|
||||||
private readonly spaceMemberRepo: SpaceMemberRepo,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async getFavoriteIds(
|
async getFavoriteIds(
|
||||||
@@ -43,12 +41,6 @@ export class FavoriteService {
|
|||||||
result.items = result.items.filter((id) => accessibleSet.has(id));
|
result.items = result.items.filter((id) => accessibleSet.has(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === FavoriteType.SPACE) {
|
|
||||||
const userSpaceIds = await this.spaceMemberRepo.getUserSpaceIds(userId);
|
|
||||||
const spaceSet = new Set(userSpaceIds);
|
|
||||||
result.items = result.items.filter((id) => spaceSet.has(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,9 +103,6 @@ export class FavoriteService {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const userSpaceIds = await this.spaceMemberRepo.getUserSpaceIds(userId);
|
|
||||||
const spaceSet = new Set(userSpaceIds);
|
|
||||||
|
|
||||||
const pageFavorites = result.items.filter(
|
const pageFavorites = result.items.filter(
|
||||||
(f) => f.type === FavoriteType.PAGE && f.pageId,
|
(f) => f.type === FavoriteType.PAGE && f.pageId,
|
||||||
);
|
);
|
||||||
@@ -129,19 +118,11 @@ export class FavoriteService {
|
|||||||
accessiblePageSet = new Set(accessibleIds);
|
accessiblePageSet = new Set(accessibleIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
result.items = result.items.filter((f) => {
|
result.items = result.items.filter(
|
||||||
if (f.type === FavoriteType.PAGE) {
|
(f) =>
|
||||||
return f.pageId && accessiblePageSet?.has(f.pageId);
|
f.type !== FavoriteType.PAGE ||
|
||||||
}
|
(f.pageId && accessiblePageSet?.has(f.pageId)),
|
||||||
if (f.type === FavoriteType.SPACE) {
|
);
|
||||||
return f.spaceId && spaceSet.has(f.spaceId);
|
|
||||||
}
|
|
||||||
if (f.type === FavoriteType.TEMPLATE) {
|
|
||||||
const templateSpaceId = (f as any).template?.spaceId;
|
|
||||||
return !templateSpaceId || spaceSet.has(templateSpaceId);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { jsonObjectFrom } from 'kysely/helpers/postgres';
|
|||||||
import { ExpressionBuilder, SelectQueryBuilder, sql } from 'kysely';
|
import { ExpressionBuilder, SelectQueryBuilder, sql } from 'kysely';
|
||||||
import { DB } from '@docmost/db/types/db';
|
import { DB } from '@docmost/db/types/db';
|
||||||
import { dbOrTx } from '@docmost/db/utils';
|
import { dbOrTx } from '@docmost/db/utils';
|
||||||
|
import { SpaceMemberRepo } from '@docmost/db/repos/space/space-member.repo';
|
||||||
|
|
||||||
export const FavoriteType = {
|
export const FavoriteType = {
|
||||||
PAGE: 'page',
|
PAGE: 'page',
|
||||||
@@ -19,7 +20,10 @@ export type FavoriteType = (typeof FavoriteType)[keyof typeof FavoriteType];
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class FavoriteRepo {
|
export class FavoriteRepo {
|
||||||
constructor(@InjectKysely() private readonly db: KyselyDB) {}
|
constructor(
|
||||||
|
@InjectKysely() private readonly db: KyselyDB,
|
||||||
|
private readonly spaceMemberRepo: SpaceMemberRepo,
|
||||||
|
) {}
|
||||||
|
|
||||||
async insert(favorite: InsertableFavorite): Promise<Favorite | undefined> {
|
async insert(favorite: InsertableFavorite): Promise<Favorite | undefined> {
|
||||||
try {
|
try {
|
||||||
@@ -82,6 +86,8 @@ export class FavoriteRepo {
|
|||||||
.where('favorites.workspaceId', '=', workspaceId)
|
.where('favorites.workspaceId', '=', workspaceId)
|
||||||
.where('favorites.type', '=', type);
|
.where('favorites.type', '=', type);
|
||||||
|
|
||||||
|
query = this.applyMembershipFilter(query, userId);
|
||||||
|
|
||||||
if (spaceId) {
|
if (spaceId) {
|
||||||
query = this.applySpaceFilter(query, type, spaceId);
|
query = this.applySpaceFilter(query, type, spaceId);
|
||||||
}
|
}
|
||||||
@@ -113,6 +119,8 @@ export class FavoriteRepo {
|
|||||||
.where('favorites.userId', '=', userId)
|
.where('favorites.userId', '=', userId)
|
||||||
.where('favorites.workspaceId', '=', workspaceId);
|
.where('favorites.workspaceId', '=', workspaceId);
|
||||||
|
|
||||||
|
query = this.applyMembershipFilter(query, userId);
|
||||||
|
|
||||||
if (type) {
|
if (type) {
|
||||||
query = query.where('favorites.type', '=', type);
|
query = query.where('favorites.type', '=', type);
|
||||||
}
|
}
|
||||||
@@ -155,7 +163,7 @@ export class FavoriteRepo {
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (userIds.length === 0) return;
|
if (userIds.length === 0) return;
|
||||||
|
|
||||||
const { trx } = opts;
|
const { trx } = opts ?? {};
|
||||||
const db = dbOrTx(this.db, trx);
|
const db = dbOrTx(this.db, trx);
|
||||||
|
|
||||||
const usersWithAccess = db
|
const usersWithAccess = db
|
||||||
@@ -174,7 +182,24 @@ export class FavoriteRepo {
|
|||||||
await db
|
await db
|
||||||
.deleteFrom('favorites')
|
.deleteFrom('favorites')
|
||||||
.where('userId', 'in', userIds)
|
.where('userId', 'in', userIds)
|
||||||
.where('spaceId', '=', spaceId)
|
.where((eb) =>
|
||||||
|
eb.or([
|
||||||
|
eb('spaceId', '=', spaceId),
|
||||||
|
eb(
|
||||||
|
'pageId',
|
||||||
|
'in',
|
||||||
|
eb.selectFrom('pages').select('id').where('spaceId', '=', spaceId),
|
||||||
|
),
|
||||||
|
eb(
|
||||||
|
'templateId',
|
||||||
|
'in',
|
||||||
|
eb
|
||||||
|
.selectFrom('templates')
|
||||||
|
.select('id')
|
||||||
|
.where('spaceId', '=', spaceId),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
)
|
||||||
.where('userId', 'not in', usersWithAccess)
|
.where('userId', 'not in', usersWithAccess)
|
||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
@@ -194,6 +219,46 @@ export class FavoriteRepo {
|
|||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private applyMembershipFilter<Q extends SelectQueryBuilder<any, any, any>>(
|
||||||
|
query: Q,
|
||||||
|
userId: string,
|
||||||
|
): Q {
|
||||||
|
const spaceIds = this.spaceMemberRepo.getUserSpaceIdsQuery(userId);
|
||||||
|
return query.where((eb: any) =>
|
||||||
|
eb.or([
|
||||||
|
eb.and([
|
||||||
|
eb('favorites.type', '=', FavoriteType.SPACE),
|
||||||
|
eb('favorites.spaceId', 'in', spaceIds),
|
||||||
|
]),
|
||||||
|
eb.and([
|
||||||
|
eb('favorites.type', '=', FavoriteType.PAGE),
|
||||||
|
eb.exists(
|
||||||
|
eb
|
||||||
|
.selectFrom('pages')
|
||||||
|
.select(sql`1`.as('one'))
|
||||||
|
.whereRef('pages.id', '=', 'favorites.pageId')
|
||||||
|
.where('pages.spaceId', 'in', spaceIds),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
eb.and([
|
||||||
|
eb('favorites.type', '=', FavoriteType.TEMPLATE),
|
||||||
|
eb.exists(
|
||||||
|
eb
|
||||||
|
.selectFrom('templates')
|
||||||
|
.select(sql`1`.as('one'))
|
||||||
|
.whereRef('templates.id', '=', 'favorites.templateId')
|
||||||
|
.where((e: any) =>
|
||||||
|
e.or([
|
||||||
|
e('templates.spaceId', 'is', null),
|
||||||
|
e('templates.spaceId', 'in', spaceIds),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
) as Q;
|
||||||
|
}
|
||||||
|
|
||||||
private applySpaceFilter<Q extends SelectQueryBuilder<any, any, any>>(
|
private applySpaceFilter<Q extends SelectQueryBuilder<any, any, any>>(
|
||||||
query: Q,
|
query: Q,
|
||||||
type: FavoriteType | undefined,
|
type: FavoriteType | undefined,
|
||||||
@@ -239,7 +304,8 @@ export class FavoriteRepo {
|
|||||||
'pages.isBase',
|
'pages.isBase',
|
||||||
'pages.spaceId',
|
'pages.spaceId',
|
||||||
])
|
])
|
||||||
.whereRef('pages.id', '=', 'favorites.pageId'),
|
.whereRef('pages.id', '=', 'favorites.pageId')
|
||||||
|
.where(sql.ref('favorites.type'), '=', FavoriteType.PAGE),
|
||||||
).as('page');
|
).as('page');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,8 +335,8 @@ export class FavoriteRepo {
|
|||||||
.select(['spaces.id', 'spaces.name', 'spaces.slug', 'spaces.logo'])
|
.select(['spaces.id', 'spaces.name', 'spaces.slug', 'spaces.logo'])
|
||||||
.where(({ or, ref }) =>
|
.where(({ or, ref }) =>
|
||||||
or([
|
or([
|
||||||
sql<boolean>`${ref('spaces.id')} = ${ref('favorites.spaceId')}`,
|
sql<boolean>`${ref('favorites.type')} = ${FavoriteType.SPACE} and ${ref('spaces.id')} = ${ref('favorites.spaceId')}`,
|
||||||
sql<boolean>`${ref('spaces.id')} = (SELECT pages.space_id FROM pages WHERE pages.id = ${ref('favorites.pageId')})`,
|
sql<boolean>`${ref('favorites.type')} = ${FavoriteType.PAGE} and ${ref('spaces.id')} = (SELECT pages.space_id FROM pages WHERE pages.id = ${ref('favorites.pageId')})`,
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
).as('space');
|
).as('space');
|
||||||
@@ -287,7 +353,8 @@ export class FavoriteRepo {
|
|||||||
'templates.icon',
|
'templates.icon',
|
||||||
'templates.spaceId',
|
'templates.spaceId',
|
||||||
])
|
])
|
||||||
.whereRef('templates.id', '=', 'favorites.templateId'),
|
.whereRef('templates.id', '=', 'favorites.templateId')
|
||||||
|
.where(sql.ref('favorites.type'), '=', FavoriteType.TEMPLATE),
|
||||||
).as('template');
|
).as('template');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
Submodule apps/server/src/ee updated: 039bd87f8a...5b873a53c8
Reference in New Issue
Block a user