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
@@ -13,6 +13,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 { PrismaService } from '../prisma/prisma.service';
import { TenantService } from '../tenant/tenant.service';
import {
@@ -166,7 +171,10 @@ export class InstructionsService {
status,
featured_media_id: featuredMediaId,
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: instructionInclude,
});
@@ -260,10 +268,13 @@ export class InstructionsService {
nextContent.html = dto.descriptionHtml ?? '';
}
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) {
@@ -661,6 +672,7 @@ export class InstructionsService {
categoryId: categoryAssignment?.categoryId.toString() ?? null,
categoryName: categoryAssignment?.category.name ?? '',
tags: Array.isArray(metadata.tags) ? (metadata.tags as string[]) : [],
...readSeoMetaFromMetadata(metadata),
authorId: video.author_id?.toString() ?? null,
author: video.author
? {