Split product sitemap and use Farsi category URLs with ids.

sitemap.xml is now an index of main + products; category loc paths use /products/category/{id}/{nameFaSlug} with a public by-id lookup for storefronts.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-23 00:23:30 +03:30
co-authored by Cursor
parent c4367d8eb0
commit ea40ae40d8
13 changed files with 427 additions and 59 deletions
+9
View File
@@ -174,6 +174,15 @@ export class PublicCategoriesController {
return this.service.listPublic(host, query);
}
@Get('by-id/:categoryId')
getById(
@Param('host') host: string,
@Param('categoryId') categoryId: string,
@Query() query: ListCategoriesDto,
) {
return this.service.getPublicById(host, categoryId, query.entityType);
}
@Get('by-slug/:slug')
getBySlug(
@Param('host') host: string,
+26
View File
@@ -87,6 +87,32 @@ export class CategoriesService {
return { category: this.serialize(category) };
}
async getPublicById(host: string, categoryIdRaw: string, entityType?: MediaEntityType) {
const business = await this.tenant.resolveBusinessByDomain(host);
const resolvedType = entityType ?? MediaEntityType.product;
let categoryId: bigint;
try {
categoryId = BigInt(categoryIdRaw);
} catch {
throw new NotFoundException('Category not found');
}
const category = await this.prisma.category.findFirst({
where: {
businessId: business.id,
entityType: resolvedType,
id: categoryId,
isActive: true,
},
});
if (!category) {
throw new NotFoundException('Category not found');
}
return { category: this.serialize(category) };
}
async create(businessIdRaw: string, dto: CreateCategoryDto, actor: AuthUser) {
const businessId = BigInt(businessIdRaw);
await this.assertPermission(businessId, actor.id, 'categories.create');