import { ForbiddenException, Injectable, NotFoundException, } from '@nestjs/common'; import { AuthUser } from '../auth/auth.types'; import { PermissionsService } from '../auth/permissions.service'; import { PrismaService } from '../prisma/prisma.service'; import { BusinessSettings } from './business-settings.types'; import { mergeBusinessSettings, normalizeBusinessSettings, toPrismaJson, } from './business-settings.util'; import { UpdateBusinessSettingsDto } from './dto/update-business-settings.dto'; import { normalizeBusinessPrimaryColorId } from './business-primary-colors'; @Injectable() export class BusinessSettingsService { constructor( private readonly prisma: PrismaService, private readonly permissions: PermissionsService, ) {} async get(businessIdRaw: string, actor: AuthUser) { const businessId = BigInt(businessIdRaw); await this.assertPermission(businessId, actor.id, 'business.read'); const business = await this.prisma.business.findUnique({ where: { id: businessId }, select: { id: true, settings: true }, }); if (!business) { throw new NotFoundException('Business not found'); } return { businessId: business.id.toString(), settings: normalizeBusinessSettings(business.settings), }; } async update( businessIdRaw: string, dto: UpdateBusinessSettingsDto, actor: AuthUser, ) { const businessId = BigInt(businessIdRaw); await this.assertPermission(businessId, actor.id, 'business.update'); const business = await this.prisma.business.findUnique({ where: { id: businessId }, select: { id: true, settings: true }, }); if (!business) { throw new NotFoundException('Business not found'); } const current = normalizeBusinessSettings(business.settings); const patch: Partial = {}; if (dto.branding) { patch.branding = { primaryColor: normalizeBusinessPrimaryColorId( dto.branding.primaryColor ?? current.branding.primaryColor, ), defaultLocale: dto.branding.defaultLocale ?? current.branding.defaultLocale, }; } if (dto.dashboard) { patch.dashboard = { comments: { autoApprove: dto.dashboard.comments?.autoApprove ?? current.dashboard.comments.autoApprove, }, expertReviews: { autoApprove: dto.dashboard.expertReviews?.autoApprove ?? current.dashboard.expertReviews.autoApprove, }, }; } if (dto.store) { patch.store = { onlineSellEnabled: dto.store.onlineSellEnabled ?? current.store.onlineSellEnabled, orderProcessSteps: dto.store.orderProcessSteps ? normalizeBusinessSettings({ store: { orderProcessSteps: dto.store.orderProcessSteps }, }).store.orderProcessSteps : current.store.orderProcessSteps, }; } const next = mergeBusinessSettings(current, patch); const updated = await this.prisma.business.update({ where: { id: businessId }, data: { settings: toPrismaJson(next) }, select: { id: true }, }); return { businessId: updated.id.toString(), settings: next, }; } async getNormalizedSettings(businessId: bigint): Promise { const business = await this.prisma.business.findUnique({ where: { id: businessId }, select: { settings: true }, }); if (!business) { throw new NotFoundException('Business not found'); } return normalizeBusinessSettings(business.settings); } async isCommentsAutoApprove(businessId: bigint) { const settings = await this.getNormalizedSettings(businessId); return settings.dashboard.comments.autoApprove; } async isExpertReviewsAutoApprove(businessId: bigint) { const settings = await this.getNormalizedSettings(businessId); return settings.dashboard.expertReviews.autoApprove; } async isOnlineSellEnabled(businessId: bigint) { const settings = await this.getNormalizedSettings(businessId); return settings.store.onlineSellEnabled; } async getOrderProcessSteps(businessId: bigint) { const settings = await this.getNormalizedSettings(businessId); return settings.store.orderProcessSteps; } private async assertPermission( businessId: bigint, userId: bigint, permission: string, ) { const allowed = await this.permissions.hasBusinessPermission( userId, businessId, permission, ); if (!allowed) { throw new ForbiddenException('Insufficient permissions'); } } }