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
@@ -114,9 +114,14 @@ Minimal example:
|
||||
```
|
||||
|
||||
- Include public static routes automatically; exclude login, checkout, cart, account, and admin paths.
|
||||
- Omit `templates` unless this site uses non-default detail URLs (Meshkee defaults: `/products/{slug}`, `/blog/{slug}`, `/portfolios/{slug}`).
|
||||
- After deploy, business owner: **Website → Settings → Sync sitemap config** (imports the manifest once; does not rebuild XML by itself).
|
||||
- Dynamic URLs (products, blogs, portfolios, …) come from the CMS automatically. Hitting `/sitemap.xml` serves a Redis-cached XML that regenerates after CMS publish/update/delete (or when cache expires).
|
||||
- **Canonical detail URLs (Meshkee default for all sites):**
|
||||
- Product: `/products/{id}/{nameFaSlug}` — build `nameFaSlug` from `nameFa` (fallback `title`); resolve page via `GET /tenants/{domain}/products/by-id/{id}` (slug segment is SEO-only; redirect to canonical if it drifts).
|
||||
- Blog: `/blog/{id}/{titleSlug}` — `GET /tenants/{domain}/blogs/by-id/{id}`
|
||||
- Portfolio: `/portfolios/{id}/{titleFaSlug}` — `GET /tenants/{domain}/portfolios/by-id/{id}`
|
||||
- When linking from lists/cards, use the same `{id}/{slug}` shape (slugify Farsi title the same way: spaces → `-`, keep Persian letters).
|
||||
- Omit `templates` in `sitemap-config.json` unless this site uses non-default paths (Meshkee sitemap defaults already use `{id}/{slug}`).
|
||||
- After deploy, business owner: **Website → Settings → Sync sitemap config** (imports static pages; does not rebuild XML by itself).
|
||||
- Dynamic URLs come from the CMS automatically. Hitting `/sitemap.xml` serves a Redis-cached XML that regenerates after CMS publish/update/delete (or when cache expires).
|
||||
|
||||
### On-page SEO (every public page)
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@
|
||||
"SEO"
|
||||
],
|
||||
"summary": "XML sitemap for search engines",
|
||||
"description": "Published products, blogs, and portfolios for this tenant (respects enabled modules), plus static pages and URL templates from the synced website manifest (`GET https://{domain}/meshkee/sitemap-config.json`). Default paths when not synced: `/`, `/products/{slug}`, `/blog/{slug}`, `/portfolios/{slug}`. Proxied from `https://{domain}/sitemap.xml` on the storefront. Cached in Redis; refreshed after CMS writes and manifest sync.",
|
||||
"description": "Published products, blogs, and portfolios for this tenant (respects enabled modules), plus static pages and URL templates from the synced website manifest (`GET https://{domain}/meshkee/sitemap-config.json`). Default detail paths: `/products/{id}/{nameFaSlug}`, `/blog/{id}/{titleSlug}`, `/portfolios/{id}/{titleFaSlug}`. Proxied from `https://{domain}/sitemap.xml` on the storefront. Cached in Redis; refreshed after CMS writes and manifest sync.",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/domain"
|
||||
@@ -514,6 +514,84 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/products/by-id/{productId}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Products"
|
||||
],
|
||||
"summary": "Product by id (preferred for /products/{id}/{nameFaSlug} pages)",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/domain"
|
||||
},
|
||||
{
|
||||
"name": "productId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{ product, relatedProducts } — same shape as product-by-slug"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/products/by-id/{productId}/variations": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Products"
|
||||
],
|
||||
"summary": "Product variations by id",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/domain"
|
||||
},
|
||||
{
|
||||
"name": "productId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Variation tree for the product"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/products/by-id/{productId}/technical-info": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Products"
|
||||
],
|
||||
"summary": "Product technical info by id",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/domain"
|
||||
},
|
||||
{
|
||||
"name": "productId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Technical form + values"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/products/{slug}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -950,6 +1028,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/blogs/by-id/{blogId}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Blogs"
|
||||
],
|
||||
"summary": "Blog by id (preferred for /blog/{id}/{titleSlug} pages)",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/domain"
|
||||
},
|
||||
{
|
||||
"name": "blogId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{ blog }"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/blogs/{slug}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -1277,6 +1381,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/portfolios/by-id/{portfolioId}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Portfolios"
|
||||
],
|
||||
"summary": "Portfolio by id (preferred for /portfolios/{id}/{titleFaSlug} pages)",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/domain"
|
||||
},
|
||||
{
|
||||
"name": "portfolioId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{ portfolio }"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/portfolios/{slug}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
|
||||
Reference in New Issue
Block a user