mirror of
https://github.com/docmost/docmost.git
synced 2026-08-31 10:55:29 +08:00
filter trash
This commit is contained in:
@@ -213,7 +213,6 @@ export class PageController {
|
|||||||
@Body() pagination: PaginationOptions,
|
@Body() pagination: PaginationOptions,
|
||||||
@AuthUser() user: User,
|
@AuthUser() user: User,
|
||||||
) {
|
) {
|
||||||
//TODO: should space admin see deleted pages they dont have access to?
|
|
||||||
if (deletedPageDto.spaceId) {
|
if (deletedPageDto.spaceId) {
|
||||||
const ability = await this.spaceAbility.createForUser(
|
const ability = await this.spaceAbility.createForUser(
|
||||||
user,
|
user,
|
||||||
@@ -226,6 +225,7 @@ export class PageController {
|
|||||||
|
|
||||||
return this.pageService.getDeletedSpacePages(
|
return this.pageService.getDeletedSpacePages(
|
||||||
deletedPageDto.spaceId,
|
deletedPageDto.spaceId,
|
||||||
|
user.id,
|
||||||
pagination,
|
pagination,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,61 +57,6 @@ export class PageService {
|
|||||||
private eventEmitter: EventEmitter2,
|
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(
|
async findById(
|
||||||
pageId: string,
|
pageId: string,
|
||||||
includeContent?: boolean,
|
includeContent?: boolean,
|
||||||
@@ -760,9 +705,26 @@ export class PageService {
|
|||||||
|
|
||||||
async getDeletedSpacePages(
|
async getDeletedSpacePages(
|
||||||
spaceId: string,
|
spaceId: string,
|
||||||
|
userId: string,
|
||||||
pagination: PaginationOptions,
|
pagination: PaginationOptions,
|
||||||
): Promise<CursorPaginationResult<Page>> {
|
): 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> {
|
async forceDelete(pageId: string, workspaceId: string): Promise<void> {
|
||||||
@@ -820,4 +782,59 @@ export class PageService {
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.pageRepo.removePage(pageId, userId, workspaceId);
|
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')
|
.selectFrom('pages')
|
||||||
.select(['id'])
|
.select(['id'])
|
||||||
.where('id', '=', pageId)
|
.where('id', '=', pageId)
|
||||||
|
.where('deletedAt', 'is', null)
|
||||||
.unionAll((exp) =>
|
.unionAll((exp) =>
|
||||||
exp
|
exp
|
||||||
.selectFrom('pages as p')
|
.selectFrom('pages as p')
|
||||||
.select(['p.id'])
|
.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')
|
.selectFrom('page_descendants')
|
||||||
@@ -197,6 +199,7 @@ export class PageRepo {
|
|||||||
deletedAt: currentDate,
|
deletedAt: currentDate,
|
||||||
})
|
})
|
||||||
.where('id', 'in', pageIds)
|
.where('id', 'in', pageIds)
|
||||||
|
.where('deletedAt', 'is', null)
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
await trx.deleteFrom('shares').where('pageId', 'in', pageIds).execute();
|
await trx.deleteFrom('shares').where('pageId', 'in', pageIds).execute();
|
||||||
|
|||||||
Reference in New Issue
Block a user