Add Schema.org JSON-LD, SEO meta overrides, and resilient bulk SMS.

Expose Organization/LocalBusiness and Product/BlogPosting jsonLd, optional seoMeta fields on content/categories, and continue mass SMS after per-recipient failures.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-09-09 23:30:24 +03:30
co-authored by Cursor
parent db305c58a4
commit aaf7a83742
33 changed files with 1395 additions and 188 deletions
+17 -5
View File
@@ -12,6 +12,11 @@ import {
import { AuthUser } from '../auth/auth.types';
import { PermissionsService } from '../auth/permissions.service';
import { BusinessSettingsService } from '../business-settings/business-settings.service';
import {
applySeoMetaToMetadata,
normalizeSeoMetaInput,
readSeoMetaFromMetadata,
} from '../common/seo-meta.util';
import { MediaService } from '../media/media.service';
import { PrismaService } from '../prisma/prisma.service';
import { SitemapService } from '../sitemap/sitemap.service';
@@ -166,7 +171,10 @@ export class PortfoliosService {
featured_media_id: featuredMediaId,
sort_order: sortOrder,
published_at: status === ContentStatus.published ? new Date() : null,
metadata: this.buildMetadata(dto.tags) as Prisma.InputJsonValue,
metadata: applySeoMetaToMetadata(
this.buildMetadata(dto.tags),
normalizeSeoMetaInput(dto.seoMetaTitle, dto.seoMetaDescription),
) as Prisma.InputJsonValue,
},
include: portfolioInclude,
});
@@ -265,10 +273,13 @@ export class PortfoliosService {
nextContent.html = dto.mainTextHtml ?? '';
}
const nextMetadata = { ...existingMetadata };
if (dto.tags !== undefined) {
nextMetadata.tags = dto.tags;
}
const nextMetadata = applySeoMetaToMetadata(
{
...existingMetadata,
...(dto.tags !== undefined ? { tags: dto.tags } : {}),
},
normalizeSeoMetaInput(dto.seoMetaTitle, dto.seoMetaDescription),
);
let publishedAt: Date | null | undefined = undefined;
if (dto.status !== undefined) {
@@ -786,6 +797,7 @@ export class PortfoliosService {
categoryId: categoryAssignment?.categoryId.toString() ?? null,
categoryName: categoryAssignment?.category.name ?? '',
tags: Array.isArray(metadata.tags) ? (metadata.tags as string[]) : [],
...readSeoMetaFromMetadata(metadata),
titleImageUrl:
portfolio.media?.publicUrl ??
galleryAttachments[0]?.media.publicUrl ??