Compare commits

..

3 Commits

Author SHA1 Message Date
Philipinho bda7e0099c full implementation 2026-05-10 17:54:43 +01:00
Philipinho 0369e727de Merge branch 'main' into feat/labels 2026-05-09 17:11:37 +01:00
Philipinho aa657e7bad feat: labels (WIP) 2026-05-09 17:06:38 +01:00
3 changed files with 0 additions and 34 deletions
@@ -1,21 +0,0 @@
export function LogTiming() {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor,
) {
const original = descriptor.value;
descriptor.value = async function (...args: any[]) {
const start = performance.now();
try {
return await original.apply(this, args);
} finally {
const ms = performance.now() - start;
console.log(
`[perm-timing] ${target.constructor.name}.${propertyKey} ${ms.toFixed(2)}ms`,
);
}
};
return descriptor;
};
}
@@ -13,12 +13,10 @@ import {
SpaceCaslSubject, SpaceCaslSubject,
} from '../interfaces/space-ability.type'; } from '../interfaces/space-ability.type';
import { findHighestUserSpaceRole } from '@docmost/db/repos/space/utils'; import { findHighestUserSpaceRole } from '@docmost/db/repos/space/utils';
import { LogTiming } from '../../../common/helpers/log-timing';
@Injectable() @Injectable()
export default class SpaceAbilityFactory { export default class SpaceAbilityFactory {
constructor(private readonly spaceMemberRepo: SpaceMemberRepo) {} constructor(private readonly spaceMemberRepo: SpaceMemberRepo) {}
@LogTiming()
async createForUser(user: User, spaceId: string) { async createForUser(user: User, spaceId: string) {
const userSpaceRoles = await this.spaceMemberRepo.getUserSpaceRoles( const userSpaceRoles = await this.spaceMemberRepo.getUserSpaceRoles(
user.id, user.id,
@@ -17,7 +17,6 @@ import {
executeWithCursorPagination, executeWithCursorPagination,
} from '@docmost/db/pagination/cursor-pagination'; } from '@docmost/db/pagination/cursor-pagination';
import { PagePermissionMember } from './types/page-permission.types'; import { PagePermissionMember } from './types/page-permission.types';
import { LogTiming } from '../../../common/helpers/log-timing';
export { PagePermissionMember } from './types/page-permission.types'; export { PagePermissionMember } from './types/page-permission.types';
@@ -361,7 +360,6 @@ export class PagePermissionRepo {
/** /**
* Check if user can access a page by verifying they have permission on ALL restricted ancestors. * Check if user can access a page by verifying they have permission on ALL restricted ancestors.
*/ */
@LogTiming()
async canUserAccessPage(userId: string, pageId: string): Promise<boolean> { async canUserAccessPage(userId: string, pageId: string): Promise<boolean> {
const deniedAncestor = await this.db const deniedAncestor = await this.db
.withRecursive('ancestors', (qb) => .withRecursive('ancestors', (qb) =>
@@ -406,7 +404,6 @@ export class PagePermissionRepo {
* - array_agg(role ORDER BY depth)[1]: role on the nearest restricted ancestor * - array_agg(role ORDER BY depth)[1]: role on the nearest restricted ancestor
* - Zero rows (no restricted ancestors): both NULL → defer to space permissions (true) * - Zero rows (no restricted ancestors): both NULL → defer to space permissions (true)
*/ */
@LogTiming()
async canUserEditPage( async canUserEditPage(
userId: string, userId: string,
pageId: string, pageId: string,
@@ -463,7 +460,6 @@ export class PagePermissionRepo {
* - canAccess: user has permission on all restricted ancestors (always true if no restrictions) * - canAccess: user has permission on all restricted ancestors (always true if no restrictions)
* - canEdit: user has writer on nearest restricted ancestor (always true if no restrictions) * - canEdit: user has writer on nearest restricted ancestor (always true if no restrictions)
*/ */
@LogTiming()
async getUserPageAccessLevel( async getUserPageAccessLevel(
userId: string, userId: string,
pageId: string, pageId: string,
@@ -674,7 +670,6 @@ export class PagePermissionRepo {
* Returns page IDs with their permission level (canEdit). * Returns page IDs with their permission level (canEdit).
* Single query implementation for efficiency. * Single query implementation for efficiency.
*/ */
@LogTiming()
async filterAccessiblePageIds(opts: { async filterAccessiblePageIds(opts: {
pageIds: string[]; pageIds: string[];
userId: string; userId: string;
@@ -752,7 +747,6 @@ export class PagePermissionRepo {
return results.map((r) => r.id); return results.map((r) => r.id);
} }
@LogTiming()
async filterAccessiblePageIdsWithPermissions( async filterAccessiblePageIdsWithPermissions(
pageIds: string[], pageIds: string[],
userId: string, userId: string,
@@ -883,7 +877,6 @@ export class PagePermissionRepo {
* Check if a page or any of its ancestors has restrictions. * Check if a page or any of its ancestors has restrictions.
* Used to determine if page-level permission checks are needed. * Used to determine if page-level permission checks are needed.
*/ */
@LogTiming()
async hasRestrictedAncestor(pageId: string): Promise<boolean> { async hasRestrictedAncestor(pageId: string): Promise<boolean> {
const result = await this.db const result = await this.db
.withRecursive('ancestors', (qb) => .withRecursive('ancestors', (qb) =>
@@ -910,7 +903,6 @@ export class PagePermissionRepo {
* Check if any page in a space has restrictions. * Check if any page in a space has restrictions.
* Used as a quick check to skip heavy permission filtering when no restrictions exist. * Used as a quick check to skip heavy permission filtering when no restrictions exist.
*/ */
@LogTiming()
async hasRestrictedPagesInSpace(spaceId: string): Promise<boolean> { async hasRestrictedPagesInSpace(spaceId: string): Promise<boolean> {
const result = await this.db const result = await this.db
.selectNoFrom((eb) => .selectNoFrom((eb) =>
@@ -932,7 +924,6 @@ export class PagePermissionRepo {
* Given a list of parent page IDs, return which ones have at least one accessible child. * Given a list of parent page IDs, return which ones have at least one accessible child.
* Efficient batch query for sidebar hasChildren calculation. * Efficient batch query for sidebar hasChildren calculation.
*/ */
@LogTiming()
async getParentIdsWithAccessibleChildren( async getParentIdsWithAccessibleChildren(
parentIds: string[], parentIds: string[],
userId: string, userId: string,
@@ -1009,7 +1000,6 @@ export class PagePermissionRepo {
* Used to filter pages from public shares - if a page is restricted, it and all its * Used to filter pages from public shares - if a page is restricted, it and all its
* children should be hidden. * children should be hidden.
*/ */
@LogTiming()
async getRestrictedSubtreeIds(rootPageId: string): Promise<string[]> { async getRestrictedSubtreeIds(rootPageId: string): Promise<string[]> {
const results = await this.db const results = await this.db
.withRecursive('descendants', (qb) => .withRecursive('descendants', (qb) =>
@@ -1071,7 +1061,6 @@ export class PagePermissionRepo {
* access the page (have permission on ALL restricted ancestors). * access the page (have permission on ALL restricted ancestors).
* Returns all userIds if the page has no restricted ancestors. * Returns all userIds if the page has no restricted ancestors.
*/ */
@LogTiming()
async getUserIdsWithPageAccess( async getUserIdsWithPageAccess(
pageId: string, pageId: string,
userIds: string[], userIds: string[],