Add website favicon API and manual favicon profile updates.
Expose GET /tenants/{domain}/website/favicon for storefront tab icons, allow faviconMediaId on profile PATCH, and sync website API docs.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
6ce6f959ac
commit
ead16080b8
@@ -30,16 +30,23 @@ You are building a **Meshkee business website (storefront)**. You must use the M
|
||||
|
||||
### Typical bootstrap sequence
|
||||
1. `GET /tenants/{domain}` → branding + `businessId` + `specialProductsSource` (`product` or `store_item`)
|
||||
2. Homepage: business-info, **static-images**, sliders, category-groups, brand-groups, store-specials (`source` repeats the tenant setting; items are store listings when `source` is `store_item`)
|
||||
3. Catalog: categories, products (`GET /products/{slug}` includes `relatedProducts`: same category then same brand, in-stock first), store-items (`name` instant search: in-stock first, then `updatedAt`), **user-products** (customer stock listings). Portfolios list newest first (`sortOrder` desc, then `publishedAt` / `createdAt` desc); each portfolio may include nullable `projectUrl` (external website link). List filters: products/blogs/portfolios/videos accept `?tag=` (exact match on `metadata.tags`).
|
||||
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`
|
||||
2. `GET /tenants/{domain}/website/favicon` → `faviconUrl` for `<link rel="icon">` / Next.js `metadata.icons` (falls back to `logoUrl` when no dedicated favicon). Also returns `logoUrl`, `logoDarkUrl`, `hasDedicatedFavicon`.
|
||||
3. Homepage: business-info, **static-images**, sliders, category-groups, brand-groups, store-specials (`source` repeats the tenant setting; items are store listings when `source` is `store_item`)
|
||||
4. Catalog: categories, products (`GET /products/{slug}` includes `relatedProducts`: same category then same brand, in-stock first), store-items (`name` instant search: in-stock first, then `updatedAt`), **user-products** (customer stock listings). Portfolios list newest first (`sortOrder` desc, then `publishedAt` / `createdAt` desc); each portfolio may include nullable `projectUrl` (external website link). List filters: products/blogs/portfolios/videos accept `?tag=` (exact match on `metadata.tags`).
|
||||
5. 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).
|
||||
6. Cart checkout with `addressId` or inline `shippingAddress` + `payment`
|
||||
- For online pay: `payment.type = "e_payment_gate"`, `gatewayType` (e.g. `"mellat"` or `"zarinpal"`), and absolute `returnUrl`
|
||||
- Response includes `payment.redirect` `{ method, url, fields }` — POST/redirect shopper to the bank
|
||||
- Meshkee registers the bank `callback_url` on the **store apex** (`https://YOUR_WEBSITE_DOMAIN/meshkee/payments/{gateway}/callback`), which nginx proxies to the API. ZarinPal/Mellat domain checks must match the store domain, not `api.meshkee.com`.
|
||||
- After verify, API redirects the browser to `returnUrl?status=success|failed&orderId=…`
|
||||
- Enabled gateways: `GET /tenants/{domain}` → `ePayment`, or `GET /businesses/{businessId}/payments/methods`
|
||||
|
||||
### Favicon + logos
|
||||
- `GET /tenants/{domain}/website/favicon` → `{ faviconUrl, logoUrl, logoDarkUrl, hasDedicatedFavicon }`
|
||||
- Use `faviconUrl` in layout metadata (Next.js: `icons: [{ url: faviconUrl, type: 'image/png' }]` when set).
|
||||
- When `hasDedicatedFavicon` is false, `faviconUrl` equals the light `logoUrl` — still safe to use as tab icon.
|
||||
- Header/footer logos: `logoUrl` on light backgrounds, `logoDarkUrl` on dark (fallback to `logoUrl` in CSS when null).
|
||||
|
||||
### 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)
|
||||
|
||||
@@ -1494,6 +1494,13 @@
|
||||
"url": "{{baseUrl}}/tenants/{{domain}}/website/business-info"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Get favicon + logos (website)",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "{{baseUrl}}/tenants/{{domain}}/website/favicon"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "List category groups (website)",
|
||||
"request": {
|
||||
|
||||
@@ -92,6 +92,15 @@
|
||||
<li>Cart / orders / favorites: <code>/businesses/{businessId}/...</code> + Bearer JWT.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Branding (favicon + logos)</h2>
|
||||
<p>
|
||||
<code>GET /tenants/{domain}/website/favicon</code> returns
|
||||
<code>faviconUrl</code>, <code>logoUrl</code>, <code>logoDarkUrl</code>, and
|
||||
<code>hasDedicatedFavicon</code>. Use <code>faviconUrl</code> for the browser tab icon
|
||||
(Next.js <code>metadata.icons</code>). When no dedicated favicon is uploaded,
|
||||
<code>faviconUrl</code> falls back to <code>logoUrl</code>.
|
||||
</p>
|
||||
|
||||
<h2>User products (customer listings)</h2>
|
||||
<p>
|
||||
Marketplace-style stock listings created by customers. Public read-only under
|
||||
|
||||
@@ -176,7 +176,8 @@
|
||||
},
|
||||
"faviconUrl": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
"nullable": true,
|
||||
"description": "Tab icon URL. Dedicated favicon when uploaded; otherwise same as logoUrl."
|
||||
},
|
||||
"specialProductsSource": {
|
||||
"type": "string",
|
||||
@@ -332,7 +333,99 @@
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Business public profile"
|
||||
"description": "Business public profile",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": { "type": "string" },
|
||||
"name": { "type": "string" },
|
||||
"nameFa": { "type": "string" },
|
||||
"about": { "type": "string" },
|
||||
"vision": { "type": "string" },
|
||||
"logoUrl": { "type": "string", "nullable": true },
|
||||
"logoDarkUrl": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "Logo for dark backgrounds; null when not set."
|
||||
},
|
||||
"faviconUrl": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "Dedicated favicon when uploaded; otherwise same as logoUrl."
|
||||
},
|
||||
"emails": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"phoneNumbers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": { "type": "string", "enum": ["landline", "cell"] },
|
||||
"number": { "type": "string" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"socialMedia": { "type": "object" },
|
||||
"addresses": { "type": "array", "items": { "type": "object" } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/website/favicon": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Homepage"
|
||||
],
|
||||
"summary": "Favicon and logo URLs for site chrome",
|
||||
"description": "Use `faviconUrl` for `<link rel=\"icon\">` or Next.js `metadata.icons`. When no dedicated favicon is uploaded, `faviconUrl` equals `logoUrl`. Prefer this endpoint over tenant resolve when you only need branding assets.",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/domain"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Branding asset URLs",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"faviconUrl": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "Use for browser tab icon. Falls back to logoUrl when no dedicated favicon exists."
|
||||
},
|
||||
"logoUrl": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "Light-theme logo."
|
||||
},
|
||||
"logoDarkUrl": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "Dark-theme logo; null when not set."
|
||||
},
|
||||
"hasDedicatedFavicon": {
|
||||
"type": "boolean",
|
||||
"description": "True when the business uploaded a separate favicon (not auto-derived from logo)."
|
||||
}
|
||||
},
|
||||
"required": ["faviconUrl", "logoUrl", "logoDarkUrl", "hasDedicatedFavicon"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Unknown domain"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user