Use id+slug paths for user products and count their visits.

Align storefront/sitemap URLs with catalog products, add by-id public APIs, and include user-product detail views in the product visit chart.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-09-15 15:35:46 +03:30
co-authored by Cursor
parent 3aef62c1b8
commit 6517877bed
19 changed files with 392 additions and 86 deletions
+10 -3
View File
@@ -111,9 +111,16 @@ export function resolveSitemapConfig(
DEFAULT_SITEMAP_PATH_TEMPLATES.instruction,
workshop:
stored?.templates?.workshop ?? DEFAULT_SITEMAP_PATH_TEMPLATES.workshop,
userProduct:
stored?.templates?.userProduct ??
DEFAULT_SITEMAP_PATH_TEMPLATES.userProduct,
userProduct: resolveUserProductTemplate(stored?.templates?.userProduct),
},
};
}
/** Prefer id+slug paths; upgrade legacy slug-only defaults still stored on tenants. */
function resolveUserProductTemplate(stored: string | undefined): string {
const raw = stored?.trim();
if (!raw || raw === '/user-products/{slug}') {
return DEFAULT_SITEMAP_PATH_TEMPLATES.userProduct;
}
return raw;
}
+1 -1
View File
@@ -11,7 +11,7 @@ export const DEFAULT_SITEMAP_PATH_TEMPLATES = {
video: '/videos/{slug}',
instruction: '/instruction/{slug}',
workshop: '/workshops/{slug}',
userProduct: '/user-products/{slug}',
userProduct: '/user-products/{id}/{slug}',
} as const;
export const WEBSITE_SITEMAP_CONFIG_PATH = '/meshkee/sitemap-config.json';
+14 -6
View File
@@ -479,19 +479,27 @@ export class SitemapService {
},
select: {
id: true,
slug: true,
title: true,
content: true,
updatedAt: true,
},
orderBy: [{ updatedAt: 'desc' }, { id: 'desc' }],
});
return rows.map((row) =>
this.toEntry(baseUrl, template, {
return rows.map((row) => {
const content = this.asRecord(row.content);
const titleEn =
typeof content.titleEn === 'string' ? content.titleEn.trim() : '';
const pathSlug = slugifyForUrl(
row.title?.trim() || titleEn,
'user-product',
);
return this.toEntry(baseUrl, template, {
id: row.id,
slug: row.slug,
slug: pathSlug,
updatedAt: row.updatedAt,
}),
);
});
});
}
private toEntry(