Use /{id}/{fa-slug} detail URLs in sitemaps and add public by-id lookups.
Default sitemap templates now emit SEO-friendly Farsi path segments with stable CMS ids; storefronts resolve content via new by-id public endpoints. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
1b16e79d3f
commit
7cef24336a
@@ -171,6 +171,35 @@ export class PublicProductsController {
|
||||
return this.service.listPublic(host, query);
|
||||
}
|
||||
|
||||
@Get('by-id/:productId/variations')
|
||||
async getVariationsById(
|
||||
@Param('host') host: string,
|
||||
@Param('productId') productId: string,
|
||||
) {
|
||||
const detail = await this.service.getPublicById(host, productId);
|
||||
return this.variationValuesService.getPublicForProduct(
|
||||
detail.product.businessId,
|
||||
detail.product.id,
|
||||
);
|
||||
}
|
||||
|
||||
@Get('by-id/:productId/technical-info')
|
||||
async getTechnicalInfoById(
|
||||
@Param('host') host: string,
|
||||
@Param('productId') productId: string,
|
||||
) {
|
||||
const detail = await this.service.getPublicById(host, productId);
|
||||
return this.technicalInfoService.getPublicForProduct(
|
||||
detail.product.businessId,
|
||||
detail.product.id,
|
||||
);
|
||||
}
|
||||
|
||||
@Get('by-id/:productId')
|
||||
getById(@Param('host') host: string, @Param('productId') productId: string) {
|
||||
return this.service.getPublicById(host, productId);
|
||||
}
|
||||
|
||||
@Get(':slug/variations')
|
||||
async getVariations(@Param('host') host: string, @Param('slug') slug: string) {
|
||||
const { businessId, productId } = await this.service.assertPublishedProductBySlug(
|
||||
|
||||
@@ -175,6 +175,39 @@ export class ProductsService {
|
||||
throw new NotFoundException('Product not found');
|
||||
}
|
||||
|
||||
return this.serializePublicDetail(businessId, product);
|
||||
}
|
||||
|
||||
async getPublicById(host: string, productIdRaw: string) {
|
||||
const business = await this.tenant.resolveBusinessByDomain(host);
|
||||
const businessId = business.id;
|
||||
let productId: bigint;
|
||||
try {
|
||||
productId = BigInt(productIdRaw);
|
||||
} catch {
|
||||
throw new NotFoundException('Product not found');
|
||||
}
|
||||
|
||||
const product = await this.prisma.product.findFirst({
|
||||
where: {
|
||||
businessId,
|
||||
id: productId,
|
||||
status: ContentStatus.published,
|
||||
},
|
||||
include: productListInclude,
|
||||
});
|
||||
|
||||
if (!product) {
|
||||
throw new NotFoundException('Product not found');
|
||||
}
|
||||
|
||||
return this.serializePublicDetail(businessId, product);
|
||||
}
|
||||
|
||||
private async serializePublicDetail(
|
||||
businessId: bigint,
|
||||
product: Prisma.ProductGetPayload<{ include: typeof productListInclude }>,
|
||||
) {
|
||||
const [serializedList, relatedProducts] = await Promise.all([
|
||||
this.serializePublicListItems(businessId, [product]),
|
||||
this.loadRelatedPublicProducts(businessId, product),
|
||||
|
||||
Reference in New Issue
Block a user