diff --git a/apps/client/src/features/file-task/services/file-task-service.ts b/apps/client/src/features/file-task/services/file-task-service.ts index ffccbaae..dba3e5cd 100644 --- a/apps/client/src/features/file-task/services/file-task-service.ts +++ b/apps/client/src/features/file-task/services/file-task-service.ts @@ -1,5 +1,7 @@ import api from "@/lib/api-client"; import { IFileTask } from "@/features/file-task/types/file-task.types.ts"; +import { IPagination, QueryParams } from "@/lib/types.ts"; +import { IApiKey } from "@/ee/api-key"; export async function getFileTaskById(fileTaskId: string): Promise { const req = await api.post("/file-tasks/info", { @@ -8,7 +10,10 @@ export async function getFileTaskById(fileTaskId: string): Promise { return req.data; } -export async function getFileTasks(): Promise { - const req = await api.post("/file-tasks"); +export async function getFileTasks( + params?: QueryParams, +): Promise> { + const req = await api.post("/file-tasks", { ...params }); return req.data; } + diff --git a/apps/server/src/core/search/search.service.ts b/apps/server/src/core/search/search.service.ts index 29508797..376f5c9f 100644 --- a/apps/server/src/core/search/search.service.ts +++ b/apps/server/src/core/search/search.service.ts @@ -74,16 +74,13 @@ export class SearchService { queryResults = queryResults.where('spaceId', '=', searchParams.spaceId); } else if (opts.userId && !searchParams.spaceId) { // only search spaces the user is a member of - const userSpaceIds = await this.spaceMemberRepo.getUserSpaceIds( - opts.userId, - ); - if (userSpaceIds.length > 0) { - queryResults = queryResults - .where('spaceId', 'in', userSpaceIds) - .where('workspaceId', '=', opts.workspaceId); - } else { - return []; - } + queryResults = queryResults + .where( + 'spaceId', + 'in', + this.spaceMemberRepo.getUserSpaceIdsQuery(opts.userId), + ) + .where('workspaceId', '=', opts.workspaceId); } else if (searchParams.shareId && !searchParams.spaceId && !opts.userId) { // search in shares const shareId = searchParams.shareId; diff --git a/apps/server/src/database/repos/page/page.repo.ts b/apps/server/src/database/repos/page/page.repo.ts index 3b948a48..f2b27abb 100644 --- a/apps/server/src/database/repos/page/page.repo.ts +++ b/apps/server/src/database/repos/page/page.repo.ts @@ -293,24 +293,18 @@ export class PageRepo { } async getRecentPages(userId: string, pagination: PaginationOptions) { - const userSpaceIds = await this.spaceMemberRepo.getUserSpaceIds(userId); - const query = this.db .selectFrom('pages') .select(this.baseFields) .select((eb) => this.withSpace(eb)) - .where('spaceId', 'in', userSpaceIds) + .where('spaceId', 'in', this.spaceMemberRepo.getUserSpaceIdsQuery(userId)) .where('deletedAt', 'is', null) .orderBy('updatedAt', 'desc'); - const hasEmptyIds = userSpaceIds.length === 0; - const result = executeWithPagination(query, { + return executeWithPagination(query, { page: pagination.page, perPage: pagination.limit, - hasEmptyIds, }); - - return result; } async getDeletedPagesInSpace(spaceId: string, pagination: PaginationOptions) { diff --git a/apps/server/src/database/repos/share/share.repo.ts b/apps/server/src/database/repos/share/share.repo.ts index c2943c07..3cf4ab3b 100644 --- a/apps/server/src/database/repos/share/share.repo.ts +++ b/apps/server/src/database/repos/share/share.repo.ts @@ -137,25 +137,19 @@ export class ShareRepo { } async getShares(userId: string, pagination: PaginationOptions) { - const userSpaceIds = await this.spaceMemberRepo.getUserSpaceIds(userId); - const query = this.db .selectFrom('shares') .select(this.baseFields) .select((eb) => this.withPage(eb)) .select((eb) => this.withSpace(eb, userId)) .select((eb) => this.withCreator(eb)) - .where('spaceId', 'in', userSpaceIds) + .where('spaceId', 'in', this.spaceMemberRepo.getUserSpaceIdsQuery(userId)) .orderBy('updatedAt', 'desc'); - const hasEmptyIds = userSpaceIds.length === 0; - const result = executeWithPagination(query, { + return executeWithPagination(query, { page: pagination.page, perPage: pagination.limit, - hasEmptyIds, }); - - return result; } withPage(eb: ExpressionBuilder) { diff --git a/apps/server/src/database/repos/space/space-member.repo.ts b/apps/server/src/database/repos/space/space-member.repo.ts index 0850c5e1..64e4ba2c 100644 --- a/apps/server/src/database/repos/space/space-member.repo.ts +++ b/apps/server/src/database/repos/space/space-member.repo.ts @@ -209,34 +209,33 @@ export class SpaceMemberRepo { return roles; } - async getUserSpaceIds(userId: string): Promise { - const membership = await this.db + getUserSpaceIdsQuery(userId: string) { + return this.db .selectFrom('spaceMembers') .innerJoin('spaces', 'spaces.id', 'spaceMembers.spaceId') - .select(['spaces.id']) + .select('spaces.id') .where('userId', '=', userId) .union( this.db .selectFrom('spaceMembers') .innerJoin('groupUsers', 'groupUsers.groupId', 'spaceMembers.groupId') .innerJoin('spaces', 'spaces.id', 'spaceMembers.spaceId') - .select(['spaces.id']) + .select('spaces.id') .where('groupUsers.userId', '=', userId), - ) - .execute(); + ); + } + async getUserSpaceIds(userId: string): Promise { + const membership = await this.getUserSpaceIdsQuery(userId).execute(); return membership.map((space) => space.id); } async getUserSpaces(userId: string, pagination: PaginationOptions) { - const userSpaceIds = await this.getUserSpaceIds(userId); - let query = this.db .selectFrom('spaces') .selectAll() .select((eb) => [this.spaceRepo.withMemberCount(eb)]) - //.where('workspaceId', '=', workspaceId) - .where('id', 'in', userSpaceIds) + .where('id', 'in', this.getUserSpaceIdsQuery(userId)) .orderBy('createdAt', 'asc'); if (pagination.query) { @@ -253,14 +252,9 @@ export class SpaceMemberRepo { ); } - const hasEmptyIds = userSpaceIds.length === 0; - - const result = executeWithPagination(query, { + return executeWithPagination(query, { page: pagination.page, perPage: pagination.limit, - hasEmptyIds, }); - - return result; } } diff --git a/apps/server/src/ee b/apps/server/src/ee index 075761c2..b55f6f5d 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit 075761c2d9bcae7adcc3de4b1c5b8f8c3b315878 +Subproject commit b55f6f5d236a40e0ebe831d4a07320137589f2f5 diff --git a/apps/server/src/integrations/import/file-task.controller.ts b/apps/server/src/integrations/import/file-task.controller.ts index 305779b4..096cd5aa 100644 --- a/apps/server/src/integrations/import/file-task.controller.ts +++ b/apps/server/src/integrations/import/file-task.controller.ts @@ -10,46 +10,59 @@ import { } from '@nestjs/common'; import SpaceAbilityFactory from '../../core/casl/abilities/space-ability.factory'; import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard'; -import { User } from '@docmost/db/types/entity.types'; +import { User, Workspace } from '@docmost/db/types/entity.types'; import { SpaceCaslAction, SpaceCaslSubject, } from '../../core/casl/interfaces/space-ability.type'; +import { + WorkspaceCaslAction, + WorkspaceCaslSubject, +} from '../../core/casl/interfaces/workspace-ability.type'; +import WorkspaceAbilityFactory from '../../core/casl/abilities/workspace-ability.factory'; +import { AuthWorkspace } from '../../common/decorators/auth-workspace.decorator'; import { InjectKysely } from 'nestjs-kysely'; import { KyselyDB } from '@docmost/db/types/kysely.types'; import { AuthUser } from '../../common/decorators/auth-user.decorator'; import { FileTaskIdDto } from './dto/file-task-dto'; import { SpaceMemberRepo } from '@docmost/db/repos/space/space-member.repo'; +import { PaginationOptions } from '@docmost/db/pagination/pagination-options'; +import { executeWithPagination } from '@docmost/db/pagination/pagination'; @Controller('file-tasks') export class FileTaskController { constructor( - private readonly spaceMemberRepo: SpaceMemberRepo, private readonly spaceAbility: SpaceAbilityFactory, + private readonly workspaceAbility: WorkspaceAbilityFactory, + private readonly spaceMemberRepo: SpaceMemberRepo, @InjectKysely() private readonly db: KyselyDB, ) {} @UseGuards(JwtAuthGuard) @HttpCode(HttpStatus.OK) @Post() - async getFileTasks(@AuthUser() user: User) { - const userSpaceIds = await this.spaceMemberRepo.getUserSpaceIds(user.id); - - if (!userSpaceIds || userSpaceIds.length === 0) { - return []; + async getFileTasks( + @Body() pagination: PaginationOptions, + @AuthUser() user: User, + @AuthWorkspace() workspace: Workspace, + ) { + const ability = this.workspaceAbility.createForUser(user, workspace); + if ( + ability.cannot(WorkspaceCaslAction.Manage, WorkspaceCaslSubject.Settings) + ) { + throw new ForbiddenException(); } - const fileTasks = await this.db + const query = this.db .selectFrom('fileTasks') .selectAll() - .where('spaceId', 'in', userSpaceIds) - .execute(); + .where('spaceId', 'in', this.spaceMemberRepo.getUserSpaceIdsQuery(user.id)) + .orderBy('createdAt', 'desc'); - if (!fileTasks) { - throw new NotFoundException('File task not found'); - } - - return fileTasks; + return executeWithPagination(query, { + page: pagination.page, + perPage: pagination.limit, + }); } @UseGuards(JwtAuthGuard)