mirror of
https://git.meshkee.com/Meshkee/backend.git
synced 2026-08-12 06:40:58 +04:30
Add public user-products storefront API and website docs.
Expose published customer listings under /tenants/:host/user-products (list, search, details, technical-info) and document them in the website API pack. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
158523df7b
commit
953b87b616
@@ -31,10 +31,17 @@ You are building a **Meshkee business website (storefront)**. You must use the M
|
||||
### Typical bootstrap sequence
|
||||
1. `GET /tenants/{domain}` → branding + `businessId`
|
||||
2. Homepage: business-info, sliders, category-groups, brand-groups, store-specials
|
||||
3. Catalog: categories, products, store-items
|
||||
3. Catalog: categories, products, store-items, **user-products** (customer stock listings)
|
||||
4. Auth: register/login → store tokens. Optional: `POST /auth/send-otp` then `POST /auth/login-otp` (passwordless) or `POST /auth/reset-password` (forgot password). `POST /auth/verify-otp` only marks the cell verified (no tokens).
|
||||
5. Cart checkout with `addressId` or inline `shippingAddress` + `payment`
|
||||
|
||||
### User products (customer listings)
|
||||
Public marketplace listings owned by customers — not catalog `products`.
|
||||
- `GET /tenants/{domain}/user-products` — list published (`name`/`q`, `categoryId`, `cityId`, `countryId`, `condition`, `promoted`, pagination)
|
||||
- `GET /tenants/{domain}/user-products/{slug}` — details + gallery
|
||||
- `GET /tenants/{domain}/user-products/{slug}/technical-info` — category form + values
|
||||
Use product categories from `GET /tenants/{domain}/categories?entityType=product` for filters. Creating/editing listings is customer-dashboard only (`/businesses/.../my-user-products`), not website-facing.
|
||||
|
||||
If OpenAPI and this brief conflict, **OpenAPI wins**.
|
||||
|
||||
---
|
||||
|
||||
@@ -33,6 +33,14 @@
|
||||
"key": "productSlug",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "userProductId",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "userProductSlug",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "blogId",
|
||||
"value": ""
|
||||
@@ -1101,6 +1109,133 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "User Products",
|
||||
"item": [
|
||||
{
|
||||
"name": "List published user products",
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"type": "text/javascript",
|
||||
"exec": [
|
||||
"if (pm.response.code === 200) {",
|
||||
" const json = pm.response.json();",
|
||||
" if (json.items?.[0]?.id) pm.collectionVariables.set('userProductId', json.items[0].id);",
|
||||
" if (json.items?.[0]?.slug) pm.collectionVariables.set('userProductSlug', json.items[0].slug);",
|
||||
"}"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/tenants/{{domain}}/user-products?page=1&pageSize=12",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"tenants",
|
||||
"{{domain}}",
|
||||
"user-products"
|
||||
],
|
||||
"query": [
|
||||
{
|
||||
"key": "page",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"key": "pageSize",
|
||||
"value": "12"
|
||||
},
|
||||
{
|
||||
"key": "name",
|
||||
"value": "",
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"key": "q",
|
||||
"value": "",
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"key": "categoryId",
|
||||
"value": "{{categoryId}}",
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"key": "cityId",
|
||||
"value": "",
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"key": "countryId",
|
||||
"value": "",
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"key": "condition",
|
||||
"value": "new",
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"key": "promoted",
|
||||
"value": "true",
|
||||
"disabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Search user products",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/tenants/{{domain}}/user-products?q=boiler&page=1&pageSize=12",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"tenants",
|
||||
"{{domain}}",
|
||||
"user-products"
|
||||
],
|
||||
"query": [
|
||||
{
|
||||
"key": "q",
|
||||
"value": "boiler"
|
||||
},
|
||||
{
|
||||
"key": "page",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"key": "pageSize",
|
||||
"value": "12"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Get user product by slug",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "{{baseUrl}}/tenants/{{domain}}/user-products/{{userProductSlug}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Get user product technical info by slug",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "{{baseUrl}}/tenants/{{domain}}/user-products/{{userProductSlug}}/technical-info"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Store Items",
|
||||
"item": [
|
||||
|
||||
@@ -88,10 +88,17 @@
|
||||
<ol>
|
||||
<li>Variable <code>domain</code> = website apex only (no <code>www</code>/<code>api</code>/<code>customer</code>/<code>business</code>).</li>
|
||||
<li><code>GET /tenants/{domain}</code> → <code>businessId</code>.</li>
|
||||
<li>Public pages: <code>/tenants/{domain}/...</code> (no auth).</li>
|
||||
<li>Public pages: <code>/tenants/{domain}/...</code> (no auth) — products, <strong>user-products</strong>, blogs, portfolios, store-items, etc.</li>
|
||||
<li>Cart / orders / favorites: <code>/businesses/{businessId}/...</code> + Bearer JWT.</li>
|
||||
</ol>
|
||||
|
||||
<h2>User products (customer listings)</h2>
|
||||
<p>
|
||||
Marketplace-style stock listings created by customers. Public read-only under
|
||||
<code>/tenants/{domain}/user-products</code> (list / details / technical-info).
|
||||
See OpenAPI tag <strong>User Products</strong>.
|
||||
</p>
|
||||
|
||||
<h2>For a new website AI / designer</h2>
|
||||
<ol>
|
||||
<li>Open <a href="/docs/website/AI_PROMPT.md">AI_PROMPT.md</a> and paste it into the AI chat.</li>
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
{ "name": "Homepage" },
|
||||
{ "name": "Categories" },
|
||||
{ "name": "Products" },
|
||||
{ "name": "User Products" },
|
||||
{ "name": "Store" },
|
||||
{ "name": "Blogs" },
|
||||
{ "name": "Portfolios" },
|
||||
@@ -210,6 +211,66 @@
|
||||
"responses": { "200": { "description": "{ form, values }" } }
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/user-products": {
|
||||
"get": {
|
||||
"tags": ["User Products"],
|
||||
"summary": "List published customer / stock listings",
|
||||
"description": "Public marketplace-style listings created by customers (user products). Only `status=published`. Search with `name` or `q` (title/description). Filter by category, city, country, condition, or promoted.",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/domain" },
|
||||
{ "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 } },
|
||||
{ "name": "pageSize", "in": "query", "schema": { "type": "integer", "default": 12 } },
|
||||
{ "name": "name", "in": "query", "description": "Search title/description (alias of q)", "schema": { "type": "string" } },
|
||||
{ "name": "q", "in": "query", "description": "Search title/description (alias of name)", "schema": { "type": "string" } },
|
||||
{ "name": "categoryId", "in": "query", "schema": { "type": "string" } },
|
||||
{ "name": "cityId", "in": "query", "schema": { "type": "string" } },
|
||||
{ "name": "countryId", "in": "query", "schema": { "type": "string" } },
|
||||
{
|
||||
"name": "condition",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["new", "stock", "needs_repair", "scrap"]
|
||||
}
|
||||
},
|
||||
{ "name": "promoted", "in": "query", "schema": { "type": "boolean" } }
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{ items: UserProductListItem[], total, page, pageSize }. Each item includes id, slug, titleFa/titleEn, price, priceCurrency, condition, city/country names, imageUrl, category*, promoted, publishedAt."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/user-products/{slug}": {
|
||||
"get": {
|
||||
"tags": ["User Products"],
|
||||
"summary": "User product details by slug",
|
||||
"description": "Full published listing: location IDs, gallery images (`images`, `galleryMediaIds`), technical field values, delivery/technical notes.",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/domain" },
|
||||
{ "name": "slug", "in": "path", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{ product } with gallery (`images`: [{ mediaId, url }]), featuredMediaId, technicalValues, countryId, cityId, countrySlug"
|
||||
},
|
||||
"404": { "description": "Not found or not published" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/user-products/{slug}/technical-info": {
|
||||
"get": {
|
||||
"tags": ["User Products"],
|
||||
"summary": "User product technical form + values",
|
||||
"description": "Category technical form schema plus the listing’s submitted values (same shape as dashboard technical values).",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/domain" },
|
||||
{ "name": "slug", "in": "path", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ form, values }" } }
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/store-items": {
|
||||
"get": {
|
||||
"tags": ["Store"],
|
||||
|
||||
Reference in New Issue
Block a user