mirror of
https://git.meshkee.com/Meshkee/backend.git
synced 2026-08-11 22:30:59 +04:30
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>
188 lines
5.3 KiB
TypeScript
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');
|
|
}
|
|
}
|
|
}
|