Files
backend/src/business-settings/business-settings.service.ts
T
Alireza HassaniandCursor 953b87b616 Add public user-products storefront API and website docs.
Expose published customer listings under /tenants/:host/user-products (list, search, details, technical-info) and document them in the website API pack.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-10 00:19:13 +03:30

188 lines
5.3 KiB
TypeScript

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,
normalizeEnabledBusinessModules,
normalizeHomeCharts,
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');
if (dto.modules !== undefined) {
if (!(await this.permissions.isSuperAdmin(actor.id))) {
throw new ForbiddenException(
'Only super admins can change business modules',
);
}
}
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<BusinessSettings> = {};
if (dto.branding) {
patch.branding = {
primaryColor: normalizeBusinessPrimaryColorId(
dto.branding.primaryColor ?? current.branding.primaryColor,
),
defaultLocale:
dto.branding.defaultLocale ?? current.branding.defaultLocale,
themeMode: dto.branding.themeMode ?? current.branding.themeMode,
};
}
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,
};
}
if (dto.modules) {
patch.modules = {
enabled:
dto.modules.enabled !== undefined
? normalizeEnabledBusinessModules(dto.modules.enabled)
: current.modules.enabled,
charts:
dto.modules.charts !== undefined
? normalizeHomeCharts(dto.modules.charts)
: current.modules.charts,
};
}
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<BusinessSettings> {
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');
}
}
}