Fix e-payment settings payload, cart race, and media/content helpers.

Stop returning passwordSet (forbidNonWhitelisted), upsert carts safely, and harden related media/product/workshop flows.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-25 16:41:00 +03:30
co-authored by Cursor
parent ad6f9dcf1d
commit 20cc0bbd78
12 changed files with 220 additions and 42 deletions
+33
View File
@@ -12,6 +12,7 @@ import {
import { AuthUser } from '../auth/auth.types';
import { PermissionsService } from '../auth/permissions.service';
import { BusinessSettingsService } from '../business-settings/business-settings.service';
import { MediaService } from '../media/media.service';
import { PrismaService } from '../prisma/prisma.service';
import { SitemapService } from '../sitemap/sitemap.service';
import { TenantService } from '../tenant/tenant.service';
@@ -56,6 +57,7 @@ export class PortfoliosService {
private readonly tenant: TenantService,
private readonly businessSettings: BusinessSettingsService,
private readonly sitemap: SitemapService,
private readonly mediaService: MediaService,
) {}
async list(businessIdRaw: string, query: ListPortfoliosDto, actor: AuthUser) {
@@ -285,6 +287,13 @@ export class PortfoliosService {
? await this.nextSortOrder(businessId)
: undefined;
const previousGalleryIds = await this.mediaService.galleryMediaIds(
businessId,
MediaEntityType.portfolio,
portfolioId,
);
let nextGalleryIds: bigint[] | undefined;
const updated = await this.prisma.$transaction(async (tx) => {
const data: Prisma.portfoliosUncheckedUpdateInput = {
slug,
@@ -353,6 +362,7 @@ export class PortfoliosService {
businessId,
dto.galleryMediaIds,
);
nextGalleryIds = galleryMediaIds;
await this.syncGalleryAttachments(
tx,
businessId,
@@ -366,6 +376,21 @@ export class PortfoliosService {
await this.sitemap.invalidateForBusiness(businessId);
const dropped: bigint[] = [];
if (nextGalleryIds) {
dropped.push(
...this.mediaService.idsNotIn(previousGalleryIds, nextGalleryIds),
);
}
if (
featuredMediaId !== undefined &&
existing.featured_media_id != null &&
existing.featured_media_id !== featuredMediaId
) {
dropped.push(existing.featured_media_id);
}
await this.mediaService.deleteUnused(businessId, dropped);
return {
message: 'Portfolio updated successfully',
portfolio: await this.serializePortfolio(updated),
@@ -389,6 +414,13 @@ export class PortfoliosService {
throw new NotFoundException('Portfolio not found');
}
const mediaIds = await this.mediaService.allLinkedMediaIds(
businessId,
MediaEntityType.portfolio,
portfolioId,
existing.featured_media_id,
);
await this.prisma.$transaction([
this.prisma.comment.deleteMany({
where: {
@@ -415,6 +447,7 @@ export class PortfoliosService {
]);
await this.sitemap.invalidateForBusiness(businessId);
await this.mediaService.deleteUnused(businessId, mediaIds);
return { message: 'Portfolio deleted successfully' };
}