mirror of
https://github.com/docmost/docmost.git
synced 2026-05-17 23:14:07 +08:00
filter trash
This commit is contained in:
@@ -213,7 +213,6 @@ export class PageController {
|
||||
@Body() pagination: PaginationOptions,
|
||||
@AuthUser() user: User,
|
||||
) {
|
||||
//TODO: should space admin see deleted pages they dont have access to?
|
||||
if (deletedPageDto.spaceId) {
|
||||
const ability = await this.spaceAbility.createForUser(
|
||||
user,
|
||||
@@ -226,6 +225,7 @@ export class PageController {
|
||||
|
||||
return this.pageService.getDeletedSpacePages(
|
||||
deletedPageDto.spaceId,
|
||||
user.id,
|
||||
pagination,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,61 +57,6 @@ export class PageService {
|
||||
private eventEmitter: EventEmitter2,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Filters a list of pages to only those accessible to the user while maintaining tree integrity.
|
||||
* A page is included only if:
|
||||
* 1. The user has access to it
|
||||
* 2. Its parent is also included (or it's the root page)
|
||||
* This ensures that if a middle page is inaccessible, its entire subtree is excluded.
|
||||
*/
|
||||
private async filterAccessibleTreePages<T extends { id: string; parentPageId: string | null }>(
|
||||
pages: T[],
|
||||
rootPageId: string,
|
||||
userId: string,
|
||||
): Promise<T[]> {
|
||||
if (pages.length === 0) return [];
|
||||
|
||||
const pageIds = pages.map((p) => p.id);
|
||||
const accessiblePages =
|
||||
await this.pagePermissionRepo.filterAccessiblePageIdsWithPermissions(
|
||||
pageIds,
|
||||
userId,
|
||||
);
|
||||
const accessibleSet = new Set(accessiblePages.map((p) => p.id));
|
||||
|
||||
// Build a map for quick lookup
|
||||
const pageMap = new Map(pages.map((p) => [p.id, p]));
|
||||
|
||||
// Prune: include a page only if it's accessible AND its parent chain to root is included
|
||||
const includedIds = new Set<string>();
|
||||
|
||||
// Process pages in a way that ensures parents are processed before children
|
||||
// We do this by iterating until no more pages can be added
|
||||
let changed = true;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
for (const page of pages) {
|
||||
if (includedIds.has(page.id)) continue;
|
||||
if (!accessibleSet.has(page.id)) continue;
|
||||
|
||||
// Root page: include if accessible
|
||||
if (page.id === rootPageId) {
|
||||
includedIds.add(page.id);
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Non-root: include if parent is already included
|
||||
if (page.parentPageId && includedIds.has(page.parentPageId)) {
|
||||
includedIds.add(page.id);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pages.filter((p) => includedIds.has(p.id));
|
||||
}
|
||||
|
||||
async findById(
|
||||
pageId: string,
|
||||
includeContent?: boolean,
|
||||
@@ -760,9 +705,26 @@ export class PageService {
|
||||
|
||||
async getDeletedSpacePages(
|
||||
spaceId: string,
|
||||
userId: string,
|
||||
pagination: PaginationOptions,
|
||||
): Promise<CursorPaginationResult<Page>> {
|
||||
return this.pageRepo.getDeletedPagesInSpace(spaceId, pagination);
|
||||
const result = await this.pageRepo.getDeletedPagesInSpace(
|
||||
spaceId,
|
||||
pagination,
|
||||
);
|
||||
|
||||
if (result.items.length > 0) {
|
||||
const pageIds = result.items.map((p) => p.id);
|
||||
const accessiblePages =
|
||||
await this.pagePermissionRepo.filterAccessiblePageIdsWithPermissions(
|
||||
pageIds,
|
||||
userId,
|
||||
);
|
||||
const accessibleSet = new Set(accessiblePages.map((p) => p.id));
|
||||
result.items = result.items.filter((p) => accessibleSet.has(p.id));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async forceDelete(pageId: string, workspaceId: string): Promise<void> {
|
||||
@@ -820,4 +782,59 @@ export class PageService {
|
||||
): Promise<void> {
|
||||
await this.pageRepo.removePage(pageId, userId, workspaceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters a list of pages to only those accessible to the user while maintaining tree integrity.
|
||||
* A page is included only if:
|
||||
* 1. The user has access to it
|
||||
* 2. Its parent is also included (or it's the root page)
|
||||
* This ensures that if a middle page is inaccessible, its entire subtree is excluded.
|
||||
*/
|
||||
private async filterAccessibleTreePages<T extends { id: string; parentPageId: string | null }>(
|
||||
pages: T[],
|
||||
rootPageId: string,
|
||||
userId: string,
|
||||
): Promise<T[]> {
|
||||
if (pages.length === 0) return [];
|
||||
|
||||
const pageIds = pages.map((p) => p.id);
|
||||
const accessiblePages =
|
||||
await this.pagePermissionRepo.filterAccessiblePageIdsWithPermissions(
|
||||
pageIds,
|
||||
userId,
|
||||
);
|
||||
const accessibleSet = new Set(accessiblePages.map((p) => p.id));
|
||||
|
||||
// Build a map for quick lookup
|
||||
const pageMap = new Map(pages.map((p) => [p.id, p]));
|
||||
|
||||
// Prune: include a page only if it's accessible AND its parent chain to root is included
|
||||
const includedIds = new Set<string>();
|
||||
|
||||
// Process pages in a way that ensures parents are processed before children
|
||||
// We do this by iterating until no more pages can be added
|
||||
let changed = true;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
for (const page of pages) {
|
||||
if (includedIds.has(page.id)) continue;
|
||||
if (!accessibleSet.has(page.id)) continue;
|
||||
|
||||
// Root page: include if accessible
|
||||
if (page.id === rootPageId) {
|
||||
includedIds.add(page.id);
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Non-root: include if parent is already included
|
||||
if (page.parentPageId && includedIds.has(page.parentPageId)) {
|
||||
includedIds.add(page.id);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pages.filter((p) => includedIds.has(p.id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,11 +175,13 @@ export class PageRepo {
|
||||
.selectFrom('pages')
|
||||
.select(['id'])
|
||||
.where('id', '=', pageId)
|
||||
.where('deletedAt', 'is', null)
|
||||
.unionAll((exp) =>
|
||||
exp
|
||||
.selectFrom('pages as p')
|
||||
.select(['p.id'])
|
||||
.innerJoin('page_descendants as pd', 'pd.id', 'p.parentPageId'),
|
||||
.innerJoin('page_descendants as pd', 'pd.id', 'p.parentPageId')
|
||||
.where('p.deletedAt', 'is', null),
|
||||
),
|
||||
)
|
||||
.selectFrom('page_descendants')
|
||||
@@ -197,6 +199,7 @@ export class PageRepo {
|
||||
deletedAt: currentDate,
|
||||
})
|
||||
.where('id', 'in', pageIds)
|
||||
.where('deletedAt', 'is', null)
|
||||
.execute();
|
||||
|
||||
await trx.deleteFrom('shares').where('pageId', 'in', pageIds).execute();
|
||||
|
||||
Reference in New Issue
Block a user