feat: implement privacy-aware profile visibility and activity status filtering using optional authentication guard

This commit is contained in:
pradeepkumar
2026-03-28 18:46:49 +05:30
parent ba931c350b
commit a71145f6a6
8 changed files with 235 additions and 38 deletions

View File

@@ -3,6 +3,7 @@ import {
NotFoundException,
ConflictException,
BadRequestException,
ForbiddenException,
} from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import {
@@ -12,6 +13,7 @@ import {
SaveFieldValuesDto,
} from './dto';
import { Prisma, Prisma as PrismaTypes, VerificationStatus } from '@prisma/client';
import { ConnectionRequestsService } from '../connection-requests/connection-requests.service';
function generateSlug(firstName: string, lastName: string): string {
const base = `${firstName}-${lastName}`
@@ -24,7 +26,24 @@ function generateSlug(firstName: string, lastName: string): string {
@Injectable()
export class AgentsService {
constructor(private prisma: PrismaService) {}
constructor(
private prisma: PrismaService,
private connectionRequestsService: ConnectionRequestsService,
) {}
/**
* Get profile_visibility setting from user's privacy preferences
*/
private getProfileVisibility(privacyPreferences: any): string {
return privacyPreferences?.privacySettings?.profile_visibility || 'public';
}
/**
* Get activity_status setting from user's privacy preferences
*/
private getActivityStatus(privacyPreferences: any): string {
return privacyPreferences?.privacySettings?.activity_status || 'public';
}
async createProfile(userId: string, dto: CreateAgentProfileDto) {
// Check if user already has an agent profile
@@ -81,7 +100,7 @@ export class AgentsService {
return profile;
}
async getProfileById(profileId: string) {
async getProfileById(profileId: string, requestingUserId?: string) {
const profile = await this.prisma.agentProfile.findUnique({
where: { id: profileId },
include: {
@@ -90,6 +109,7 @@ export class AgentsService {
id: true,
email: true,
avatar: true,
privacyPreferences: true,
},
},
agentType: true,
@@ -100,7 +120,30 @@ export class AgentsService {
throw new NotFoundException('Agent profile not found');
}
return profile;
const visibility = this.getProfileVisibility(profile.user.privacyPreferences);
// Own profile — always visible
if (requestingUserId && requestingUserId === profile.user.id) {
const { privacyPreferences, ...userWithoutPrefs } = profile.user;
return { ...profile, user: userWithoutPrefs };
}
if (visibility === 'private') {
throw new ForbiddenException('This profile is not available');
}
if (visibility === 'connections' && requestingUserId) {
const connected = await this.connectionRequestsService.isConnected(requestingUserId, profile.user.id);
if (!connected) {
throw new ForbiddenException('This profile is only visible to connections');
}
} else if (visibility === 'connections' && !requestingUserId) {
throw new ForbiddenException('This profile is only visible to connections');
}
// Strip privacyPreferences from response
const { privacyPreferences, ...userWithoutPrefs } = profile.user;
return { ...profile, user: userWithoutPrefs };
}
async updateProfile(userId: string, dto: UpdateAgentProfileDto) {
@@ -197,8 +240,17 @@ export class AgentsService {
const skip = (page - 1) * limit;
// Build where clause — only show admin-approved profiles in search
// Exclude agents who set profile_visibility to 'private'
const where: Prisma.AgentProfileWhereInput = {
verificationStatus: 'APPROVED',
NOT: {
user: {
privacyPreferences: {
path: ['privacySettings', 'profile_visibility'],
equals: 'private',
},
},
},
};
if (search) {
@@ -484,6 +536,14 @@ export class AgentsService {
isVerified: true,
isFeatured: true,
verificationStatus: 'APPROVED',
NOT: {
user: {
privacyPreferences: {
path: ['privacySettings', 'profile_visibility'],
equals: 'private',
},
},
},
},
take: limit,
orderBy: [{ averageRating: 'desc' }, { totalReviews: 'desc' }],
@@ -508,6 +568,14 @@ export class AgentsService {
isVerified: true,
totalReviews: { gte: 5 },
verificationStatus: 'APPROVED',
NOT: {
user: {
privacyPreferences: {
path: ['privacySettings', 'profile_visibility'],
equals: 'private',
},
},
},
},
take: limit,
orderBy: [{ averageRating: 'desc' }, { totalReviews: 'desc' }],