feat: favorites (#2103)

* feat: favorites and templates(ee)

* rename migrations

* fix sidebar

* cleanup tabs

* fix

* turn off templates

* cleanup

* uuid validation
This commit is contained in:
Philip Okugbe
2026-04-12 22:06:25 +01:00
committed by GitHub
parent 57efb91bd3
commit d42091ccb1
90 changed files with 4557 additions and 187 deletions
@@ -0,0 +1,11 @@
import { IsOptional, IsUUID } from 'class-validator';
export class CreatedByUserDto {
@IsOptional()
@IsUUID()
userId?: string;
@IsOptional()
@IsUUID()
spaceId?: string;
}
@@ -1,5 +1,4 @@
import { IsOptional, IsString, IsUUID } from 'class-validator';
import { SpaceIdDto } from './page.dto';
export class SidebarPageDto {
@IsOptional()
@@ -35,6 +35,7 @@ import {
import SpaceAbilityFactory from '../casl/abilities/space-ability.factory';
import { PageRepo } from '@docmost/db/repos/page/page.repo';
import { RecentPageDto } from './dto/recent-page.dto';
import { CreatedByUserDto } from './dto/created-by-user.dto';
import { DuplicatePageDto } from './dto/duplicate-page.dto';
import { DeletedPageDto } from './dto/deleted-page.dto';
import {
@@ -336,6 +337,29 @@ export class PageController {
return this.pageService.getRecentPages(user.id, pagination);
}
@HttpCode(HttpStatus.OK)
@Post('created-by-user')
async getCreatedByPages(
@Body() dto: CreatedByUserDto,
@Body() pagination: PaginationOptions,
@AuthUser() user: User,
) {
const targetUserId = dto.userId ?? user.id;
if (dto.spaceId) {
const ability = await this.spaceAbility.createForUser(
user,
dto.spaceId,
);
if (ability.cannot(SpaceCaslAction.Read, SpaceCaslSubject.Page)) {
throw new ForbiddenException();
}
}
return this.pageService.getCreatedByPages(targetUserId, user.id, pagination, dto.spaceId);
}
@HttpCode(HttpStatus.OK)
@Post('trash')
async getDeletedPages(
@@ -300,7 +300,7 @@ export class PageService {
}
const result = await executeWithCursorPagination(query, {
perPage: 200,
perPage: pagination.limit,
cursor: pagination.cursor,
beforeCursor: pagination.beforeCursor,
fields: [
@@ -856,6 +856,33 @@ export class PageService {
return result;
}
async getCreatedByPages(
creatorId: string,
requestingUserId: string,
pagination: PaginationOptions,
spaceId?: string,
): Promise<CursorPaginationResult<Page>> {
const result = await this.pageRepo.getCreatedByPages(
creatorId,
requestingUserId,
pagination,
spaceId,
);
if (result.items.length > 0) {
const pageIds = result.items.map((p) => p.id);
const accessibleIds =
await this.pagePermissionRepo.filterAccessiblePageIds({
pageIds,
userId: requestingUserId,
});
const accessibleSet = new Set(accessibleIds);
result.items = result.items.filter((p) => accessibleSet.has(p.id));
}
return result;
}
async getDeletedSpacePages(
spaceId: string,
userId: string,