Unified search algorithm across all lists; per-section record visibility (contacts, deals, activities, leads, finance, tickets, notes, projects)
This commit is contained in:
@@ -4,9 +4,13 @@ import { PassportStrategy } from '@nestjs/passport';
|
||||
import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||
import type { DataVisibility } from '@mento/shared';
|
||||
import { BillingService } from '../billing/billing.service';
|
||||
import { normalizeDataVisibility } from '../common/record-scope';
|
||||
import {
|
||||
type Actor,
|
||||
normalizeDataVisibility,
|
||||
parseVisibilityScopes,
|
||||
} from '../common/record-scope';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { canViewAllRecords } from '@mento/shared';
|
||||
import { DATA_SCOPES, canViewAllRecords } from '@mento/shared';
|
||||
|
||||
export type JwtPayload = {
|
||||
sub: string;
|
||||
@@ -53,7 +57,12 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
isActive: true,
|
||||
organization: { isActive: true },
|
||||
},
|
||||
select: { organizationId: true, role: true, dataVisibility: true },
|
||||
select: {
|
||||
organizationId: true,
|
||||
role: true,
|
||||
dataVisibility: true,
|
||||
dataVisibilityScopes: true,
|
||||
},
|
||||
});
|
||||
if (!membership) {
|
||||
throw new UnauthorizedException();
|
||||
@@ -61,12 +70,26 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
await this.billing.requireActiveForOrganization(membership.organizationId);
|
||||
|
||||
const dataVisibility = normalizeDataVisibility(membership.dataVisibility);
|
||||
const visibleUserIds = await this.resolveVisibleUserIds(
|
||||
membership.organizationId,
|
||||
user.id,
|
||||
membership.role,
|
||||
dataVisibility,
|
||||
);
|
||||
const scopeLevels = parseVisibilityScopes(membership.dataVisibilityScopes);
|
||||
const elevatedRole = canViewAllRecords(membership.role);
|
||||
|
||||
let teamIds: string[] | undefined;
|
||||
const resolve = async (level: DataVisibility): Promise<string[] | null> => {
|
||||
if (elevatedRole || level === 'org') return null;
|
||||
if (level === 'own') return [user.id];
|
||||
teamIds ??= await this.teamPeerIds(membership.organizationId, user.id);
|
||||
return teamIds;
|
||||
};
|
||||
|
||||
const visibleUserIds = await resolve(dataVisibility);
|
||||
const scopes: Actor['scopes'] = {};
|
||||
if (!elevatedRole) {
|
||||
for (const scope of DATA_SCOPES) {
|
||||
const level = scopeLevels[scope];
|
||||
if (!level || level === dataVisibility) continue;
|
||||
scopes[scope] = { dataVisibility: level, visibleUserIds: await resolve(level) };
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
userId: user.id,
|
||||
@@ -74,22 +97,11 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
role: membership.role,
|
||||
dataVisibility,
|
||||
visibleUserIds,
|
||||
scopes,
|
||||
};
|
||||
}
|
||||
|
||||
private async resolveVisibleUserIds(
|
||||
organizationId: string,
|
||||
userId: string,
|
||||
role: string,
|
||||
dataVisibility: DataVisibility,
|
||||
): Promise<string[] | null> {
|
||||
if (canViewAllRecords(role) || dataVisibility === 'org') {
|
||||
return null;
|
||||
}
|
||||
if (dataVisibility === 'own') {
|
||||
return [userId];
|
||||
}
|
||||
|
||||
private async teamPeerIds(organizationId: string, userId: string): Promise<string[]> {
|
||||
const memberships = await this.prisma.teamMember.findMany({
|
||||
where: {
|
||||
userId,
|
||||
|
||||
Reference in New Issue
Block a user