mirror of
https://git.meshkee.com/Meshkee/backend.git
synced 2026-08-11 22:30:59 +04:30
Add website API docs, SSL api-hosts, and git-only deploy workflow.
Serve public storefront docs at /docs/website, expose api.{domain} hosts for API SSL sync, and require push-then-pull deploys instead of rsync.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
bb59d5e9ba
commit
016cc15bf0
@@ -0,0 +1,43 @@
|
||||
# Meshkee Website API — AI / designer brief
|
||||
|
||||
Copy everything below into a new AI chat when building a Meshkee storefront.
|
||||
|
||||
---
|
||||
|
||||
## System context (paste this)
|
||||
|
||||
You are building a **Meshkee business website (storefront)**. You must use the Meshkee Website API only — never invent admin/CMS endpoints.
|
||||
|
||||
**Canonical docs (always prefer these):**
|
||||
- Hub: https://api.meshkee.com/docs/website
|
||||
- OpenAPI: https://api.meshkee.com/docs/website/openapi.json
|
||||
- Postman: https://api.meshkee.com/docs/website/Meshkee-Website-API.postman_collection.json
|
||||
|
||||
**API base URL:** `https://api.meshkee.com/api/v1`
|
||||
(Optional alias if configured: `https://api.<WEBSITE_DOMAIN>/api/v1` — same backend.)
|
||||
|
||||
**This website’s apex domain:** `<WEBSITE_DOMAIN>`
|
||||
(example: `sanihome.ir` — no `www.`, no `api.`, no `customer.`, no `business.`)
|
||||
|
||||
### Hard rules
|
||||
1. Resolve tenant first: `GET /tenants/<WEBSITE_DOMAIN>` → save `businessId` from `id`.
|
||||
2. All public content uses `/tenants/<WEBSITE_DOMAIN>/...` (no auth).
|
||||
3. Cart, orders, favorites use `/businesses/<businessId>/...` with `Authorization: Bearer <accessToken>`.
|
||||
4. Customer register body must include `"domain": "<WEBSITE_DOMAIN>"`.
|
||||
5. Cell numbers are E.164 (`+98912...`).
|
||||
6. Do not call dashboard/CMS routes (`/businesses/.../products` write APIs, media upload, domain-admin, etc.).
|
||||
|
||||
### 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
|
||||
4. Auth: register/login → store tokens
|
||||
5. Cart checkout with `addressId` or inline `shippingAddress` + `payment`
|
||||
|
||||
If OpenAPI and this brief conflict, **OpenAPI wins**.
|
||||
|
||||
---
|
||||
|
||||
## What to tell each website team
|
||||
|
||||
Replace `<WEBSITE_DOMAIN>` once per project. Everything else is global — same Postman, same OpenAPI, same base URL.
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"id": "meshkee-website-api-global",
|
||||
"name": "Meshkee Website API — Global",
|
||||
"values": [
|
||||
{
|
||||
"key": "baseUrl",
|
||||
"value": "https://api.meshkee.com/api/v1",
|
||||
"type": "default",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"key": "domain",
|
||||
"value": "YOUR_WEBSITE_DOMAIN",
|
||||
"type": "default",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"key": "businessId",
|
||||
"value": "",
|
||||
"type": "default",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"key": "accessToken",
|
||||
"value": "",
|
||||
"type": "secret",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"key": "refreshToken",
|
||||
"value": "",
|
||||
"type": "secret",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"_postman_variable_scope": "environment"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,109 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Meshkee Website API</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #0f1419;
|
||||
--panel: #1a222c;
|
||||
--text: #e8eef4;
|
||||
--muted: #9aa8b5;
|
||||
--accent: #3d9cf0;
|
||||
--line: #2a3542;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: "IBM Plex Sans", "Segoe UI", sans-serif;
|
||||
background: radial-gradient(1200px 600px at 10% -10%, #1b3a57 0%, var(--bg) 55%);
|
||||
color: var(--text);
|
||||
line-height: 1.55;
|
||||
}
|
||||
main {
|
||||
max-width: 760px;
|
||||
margin: 0 auto;
|
||||
padding: 3rem 1.25rem 4rem;
|
||||
}
|
||||
h1 { font-size: 2rem; margin: 0 0 0.5rem; letter-spacing: -0.02em; }
|
||||
h2 { font-size: 1.15rem; margin: 2rem 0 0.75rem; }
|
||||
p, li { color: var(--muted); }
|
||||
strong { color: var(--text); }
|
||||
code {
|
||||
font-family: "IBM Plex Mono", ui-monospace, monospace;
|
||||
background: #0b1015;
|
||||
padding: 0.1rem 0.35rem;
|
||||
border-radius: 4px;
|
||||
color: #cde3f7;
|
||||
font-size: 0.92em;
|
||||
}
|
||||
.panel {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 12px;
|
||||
padding: 1rem 1.1rem;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
a.btn {
|
||||
display: inline-block;
|
||||
margin: 0.35rem 0.5rem 0.35rem 0;
|
||||
padding: 0.65rem 1rem;
|
||||
border-radius: 8px;
|
||||
background: var(--accent);
|
||||
color: #061018;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
a.btn.secondary {
|
||||
background: transparent;
|
||||
color: var(--text);
|
||||
border: 1px solid var(--line);
|
||||
}
|
||||
.eyebrow { color: var(--accent); font-size: 0.85rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<div class="eyebrow">Meshkee · Global storefront contract</div>
|
||||
<h1>Website API</h1>
|
||||
<p>
|
||||
One API for <strong>every</strong> Meshkee business website. Not tied to a single domain.
|
||||
Set your site’s apex host (e.g. <code>sanihome.ir</code>) and reuse the same endpoints.
|
||||
</p>
|
||||
|
||||
<div class="panel">
|
||||
<p style="margin:0 0 0.75rem"><strong>Global links</strong> (share these with designers & AI tools):</p>
|
||||
<a class="btn" href="./openapi.json">OpenAPI JSON</a>
|
||||
<a class="btn secondary" href="./Meshkee-Website-API.postman_collection.json">Download Postman</a>
|
||||
<a class="btn secondary" href="./AI_PROMPT.md">AI prompt</a>
|
||||
</div>
|
||||
|
||||
<h2>Base URL</h2>
|
||||
<p><code>https://api.meshkee.com/api/v1</code></p>
|
||||
<p>Optional per-site alias (same backend): <code>https://api.<domain>/api/v1</code></p>
|
||||
|
||||
<h2>How tenants work</h2>
|
||||
<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>Cart / orders / favorites: <code>/businesses/{businessId}/...</code> + Bearer JWT.</li>
|
||||
</ol>
|
||||
|
||||
<h2>For a new website AI / designer</h2>
|
||||
<ol>
|
||||
<li>Open <a href="./AI_PROMPT.md">AI_PROMPT.md</a> and paste it into the AI chat.</li>
|
||||
<li>Replace <code><WEBSITE_DOMAIN></code> with that site’s apex.</li>
|
||||
<li>Import the Postman collection (set <code>domain</code>, run Resolve tenant).</li>
|
||||
<li>Or feed <code>openapi.json</code> to the AI / codegen tool.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Import Postman</h2>
|
||||
<p>
|
||||
Postman → Import → Link → paste<br />
|
||||
<code>https://api.meshkee.com/docs/website/Meshkee-Website-API.postman_collection.json</code>
|
||||
</p>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,901 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Meshkee Website API",
|
||||
"version": "1.0.0",
|
||||
"description": "Global storefront API for every Meshkee business website.\n\n**Not domain-specific.** Replace `{domain}` with the website apex (e.g. `sanihome.ir`).\n\n**Base URL:** `https://api.meshkee.com/api/v1` (or `https://api.{domain}/api/v1` if that alias is configured).\n\n**Tenant rule:** public content uses `/tenants/{domain}/...`. After login, cart/orders/favorites use `/businesses/{businessId}/...` with Bearer JWT.\n\n**Docs:** https://api.meshkee.com/docs/website"
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://api.meshkee.com/api/v1",
|
||||
"description": "Production (central) — use this for all websites"
|
||||
},
|
||||
{
|
||||
"url": "https://api.{domain}/api/v1",
|
||||
"description": "Optional per-site alias (same backend). {domain} = website apex",
|
||||
"variables": {
|
||||
"domain": {
|
||||
"default": "example.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
{ "name": "Tenant" },
|
||||
{ "name": "Homepage" },
|
||||
{ "name": "Categories" },
|
||||
{ "name": "Products" },
|
||||
{ "name": "Store" },
|
||||
{ "name": "Blogs" },
|
||||
{ "name": "Portfolios" },
|
||||
{ "name": "Comments" },
|
||||
{ "name": "Expert Reviews" },
|
||||
{ "name": "Contact" },
|
||||
{ "name": "Auth" },
|
||||
{ "name": "Addresses" },
|
||||
{ "name": "Cities" },
|
||||
{ "name": "Cart" },
|
||||
{ "name": "Orders" },
|
||||
{ "name": "Favorites" }
|
||||
],
|
||||
"components": {
|
||||
"securitySchemes": {
|
||||
"bearerAuth": {
|
||||
"type": "http",
|
||||
"scheme": "bearer",
|
||||
"bearerFormat": "JWT"
|
||||
}
|
||||
},
|
||||
"parameters": {
|
||||
"domain": {
|
||||
"name": "domain",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "Website apex host only (e.g. sanihome.ir). No www/api/customer/business prefix.",
|
||||
"schema": { "type": "string", "example": "example.com" }
|
||||
},
|
||||
"businessId": {
|
||||
"name": "businessId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "From GET /tenants/{domain} → id",
|
||||
"schema": { "type": "string" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"/tenants/{domain}": {
|
||||
"get": {
|
||||
"tags": ["Tenant"],
|
||||
"summary": "Resolve website domain → business",
|
||||
"parameters": [{ "$ref": "#/components/parameters/domain" }],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Business branding",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": { "type": "string" },
|
||||
"name": { "type": "string" },
|
||||
"nameFa": { "type": "string" },
|
||||
"slug": { "type": "string" },
|
||||
"domain": { "type": "string" },
|
||||
"primaryColor": { "type": "string", "nullable": true },
|
||||
"logoUrl": { "type": "string", "nullable": true },
|
||||
"faviconUrl": { "type": "string", "nullable": true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/website/business-info": {
|
||||
"get": {
|
||||
"tags": ["Homepage"],
|
||||
"summary": "About, contacts, addresses, social",
|
||||
"parameters": [{ "$ref": "#/components/parameters/domain" }],
|
||||
"responses": { "200": { "description": "Business public profile" } }
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/website/sliders": {
|
||||
"get": {
|
||||
"tags": ["Homepage"],
|
||||
"summary": "Homepage sliders + slides",
|
||||
"parameters": [{ "$ref": "#/components/parameters/domain" }],
|
||||
"responses": { "200": { "description": "{ items: Slider[] }" } }
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/website/category-groups": {
|
||||
"get": {
|
||||
"tags": ["Homepage"],
|
||||
"summary": "Homepage category groups",
|
||||
"parameters": [{ "$ref": "#/components/parameters/domain" }],
|
||||
"responses": { "200": { "description": "{ items: CategoryGroup[] }" } }
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/website/brand-groups": {
|
||||
"get": {
|
||||
"tags": ["Homepage"],
|
||||
"summary": "Homepage brand groups",
|
||||
"parameters": [{ "$ref": "#/components/parameters/domain" }],
|
||||
"responses": { "200": { "description": "{ items: BrandGroup[] }" } }
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/store-specials": {
|
||||
"get": {
|
||||
"tags": ["Homepage", "Store"],
|
||||
"summary": "Active store specials",
|
||||
"parameters": [{ "$ref": "#/components/parameters/domain" }],
|
||||
"responses": { "200": { "description": "{ items: StoreSpecial[] }" } }
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/categories": {
|
||||
"get": {
|
||||
"tags": ["Categories"],
|
||||
"summary": "Public categories",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/domain" },
|
||||
{
|
||||
"name": "entityType",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["product", "blog", "portfolio"],
|
||||
"default": "product"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": { "200": { "description": "{ items: Category[] }" } }
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/products": {
|
||||
"get": {
|
||||
"tags": ["Products"],
|
||||
"summary": "List published products",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/domain" },
|
||||
{ "name": "page", "in": "query", "schema": { "type": "integer" } },
|
||||
{ "name": "pageSize", "in": "query", "schema": { "type": "integer", "default": 12 } },
|
||||
{ "name": "name", "in": "query", "schema": { "type": "string" } },
|
||||
{ "name": "categoryId", "in": "query", "schema": { "type": "string" } },
|
||||
{ "name": "brandId", "in": "query", "schema": { "type": "string" } },
|
||||
{ "name": "tag", "in": "query", "schema": { "type": "string" } },
|
||||
{ "name": "inStore", "in": "query", "schema": { "type": "boolean" } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ items, total, page, pageSize }" } }
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/products/{slug}": {
|
||||
"get": {
|
||||
"tags": ["Products"],
|
||||
"summary": "Product by slug",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/domain" },
|
||||
{ "name": "slug", "in": "path", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ product }" } }
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/products/{slug}/variations": {
|
||||
"get": {
|
||||
"tags": ["Products"],
|
||||
"summary": "Product variation options",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/domain" },
|
||||
{ "name": "slug", "in": "path", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ variations }" } }
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/products/{slug}/technical-info": {
|
||||
"get": {
|
||||
"tags": ["Products"],
|
||||
"summary": "Product technical specs",
|
||||
"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"],
|
||||
"summary": "List sellable variants",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/domain" },
|
||||
{ "name": "page", "in": "query", "schema": { "type": "integer" } },
|
||||
{ "name": "pageSize", "in": "query", "schema": { "type": "integer", "default": 20 } },
|
||||
{ "name": "categoryId", "in": "query", "schema": { "type": "string" } },
|
||||
{ "name": "brandId", "in": "query", "schema": { "type": "string" } },
|
||||
{ "name": "productId", "in": "query", "schema": { "type": "string" } },
|
||||
{ "name": "name", "in": "query", "schema": { "type": "string" } },
|
||||
{ "name": "inStock", "in": "query", "schema": { "type": "boolean" } },
|
||||
{ "name": "isFestival", "in": "query", "schema": { "type": "boolean" } },
|
||||
{ "name": "minPrice", "in": "query", "schema": { "type": "number" } },
|
||||
{ "name": "maxPrice", "in": "query", "schema": { "type": "number" } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ items, total, page, pageSize }" } }
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/store-items/by-product/{productId}": {
|
||||
"get": {
|
||||
"tags": ["Store"],
|
||||
"summary": "Variants for one product",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/domain" },
|
||||
{ "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ storeItem }" } }
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/store-items/{variantId}": {
|
||||
"get": {
|
||||
"tags": ["Store"],
|
||||
"summary": "One variant",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/domain" },
|
||||
{ "name": "variantId", "in": "path", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ variant }" } }
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/blogs": {
|
||||
"get": {
|
||||
"tags": ["Blogs"],
|
||||
"summary": "List published blogs",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/domain" },
|
||||
{ "name": "page", "in": "query", "schema": { "type": "integer" } },
|
||||
{ "name": "pageSize", "in": "query", "schema": { "type": "integer", "default": 12 } },
|
||||
{ "name": "type", "in": "query", "schema": { "type": "string", "enum": ["news", "article", "blog"] } },
|
||||
{ "name": "categoryId", "in": "query", "schema": { "type": "string" } },
|
||||
{ "name": "title", "in": "query", "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ items, total, page, pageSize }" } }
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/blogs/{slug}": {
|
||||
"get": {
|
||||
"tags": ["Blogs"],
|
||||
"summary": "Blog by slug",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/domain" },
|
||||
{ "name": "slug", "in": "path", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ blog }" } }
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/blogs/{blogId}/comments": {
|
||||
"get": {
|
||||
"tags": ["Blogs", "Comments"],
|
||||
"summary": "Approved blog comments",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/domain" },
|
||||
{ "name": "blogId", "in": "path", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ items }" } }
|
||||
},
|
||||
"post": {
|
||||
"tags": ["Blogs", "Comments"],
|
||||
"summary": "Submit blog comment",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/domain" },
|
||||
{ "name": "blogId", "in": "path", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["authorName", "text"],
|
||||
"properties": {
|
||||
"authorName": { "type": "string" },
|
||||
"authorEmail": { "type": "string" },
|
||||
"text": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": { "201": { "description": "{ comment, message }" } }
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/portfolios": {
|
||||
"get": {
|
||||
"tags": ["Portfolios"],
|
||||
"summary": "List published portfolios",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/domain" },
|
||||
{ "name": "page", "in": "query", "schema": { "type": "integer" } },
|
||||
{ "name": "pageSize", "in": "query", "schema": { "type": "integer", "default": 12 } },
|
||||
{ "name": "categoryId", "in": "query", "schema": { "type": "string" } },
|
||||
{ "name": "title", "in": "query", "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ items, total, page, pageSize }" } }
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/portfolios/{slug}": {
|
||||
"get": {
|
||||
"tags": ["Portfolios"],
|
||||
"summary": "Portfolio by slug",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/domain" },
|
||||
{ "name": "slug", "in": "path", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ portfolio }" } }
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/portfolios/{portfolioId}/comments": {
|
||||
"get": {
|
||||
"tags": ["Portfolios", "Comments"],
|
||||
"summary": "Approved portfolio comments",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/domain" },
|
||||
{ "name": "portfolioId", "in": "path", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ items }" } }
|
||||
},
|
||||
"post": {
|
||||
"tags": ["Portfolios", "Comments"],
|
||||
"summary": "Submit portfolio comment",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/domain" },
|
||||
{ "name": "portfolioId", "in": "path", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["authorName", "text"],
|
||||
"properties": {
|
||||
"authorName": { "type": "string" },
|
||||
"authorEmail": { "type": "string" },
|
||||
"text": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": { "201": { "description": "{ comment, message }" } }
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/comments": {
|
||||
"get": {
|
||||
"tags": ["Comments"],
|
||||
"summary": "List approved comments for any entity",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/domain" },
|
||||
{
|
||||
"name": "entityType",
|
||||
"in": "query",
|
||||
"required": true,
|
||||
"schema": { "type": "string", "enum": ["product", "blog", "portfolio"] }
|
||||
},
|
||||
{ "name": "entityId", "in": "query", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ items }" } }
|
||||
},
|
||||
"post": {
|
||||
"tags": ["Comments"],
|
||||
"summary": "Submit comment (product/blog/portfolio)",
|
||||
"parameters": [{ "$ref": "#/components/parameters/domain" }],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["entityType", "entityId", "authorName", "text"],
|
||||
"properties": {
|
||||
"entityType": { "type": "string", "enum": ["product", "blog", "portfolio"] },
|
||||
"entityId": { "type": "string" },
|
||||
"authorName": { "type": "string" },
|
||||
"authorEmail": { "type": "string" },
|
||||
"text": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": { "201": { "description": "{ comment, message }" } }
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/expert-reviews": {
|
||||
"get": {
|
||||
"tags": ["Expert Reviews"],
|
||||
"summary": "Approved expert reviews for a product",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/domain" },
|
||||
{ "name": "productId", "in": "query", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ items }" } }
|
||||
},
|
||||
"post": {
|
||||
"tags": ["Expert Reviews"],
|
||||
"summary": "Submit expert review",
|
||||
"parameters": [{ "$ref": "#/components/parameters/domain" }],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["productId", "authorName", "rate", "positivePoints", "negativePoints", "text"],
|
||||
"properties": {
|
||||
"productId": { "type": "string" },
|
||||
"authorName": { "type": "string" },
|
||||
"authorEmail": { "type": "string" },
|
||||
"rate": { "type": "integer", "minimum": 1, "maximum": 10 },
|
||||
"positivePoints": { "type": "array", "items": { "type": "string" } },
|
||||
"negativePoints": { "type": "array", "items": { "type": "string" } },
|
||||
"text": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": { "201": { "description": "{ review, message }" } }
|
||||
}
|
||||
},
|
||||
"/tenants/{domain}/contact-submissions": {
|
||||
"post": {
|
||||
"tags": ["Contact"],
|
||||
"summary": "Contact form",
|
||||
"parameters": [{ "$ref": "#/components/parameters/domain" }],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["title", "name", "text"],
|
||||
"properties": {
|
||||
"title": { "type": "string" },
|
||||
"name": { "type": "string" },
|
||||
"email": { "type": "string" },
|
||||
"cellNumber": { "type": "string" },
|
||||
"text": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": { "201": { "description": "{ submission, message }" } }
|
||||
}
|
||||
},
|
||||
"/auth/register": {
|
||||
"post": {
|
||||
"tags": ["Auth"],
|
||||
"summary": "Register customer on a website",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["cellNumber", "password", "firstName", "lastName", "domain"],
|
||||
"properties": {
|
||||
"cellNumber": { "type": "string", "description": "E.164 e.g. +98912..." },
|
||||
"password": { "type": "string", "minLength": 8 },
|
||||
"firstName": { "type": "string" },
|
||||
"lastName": { "type": "string" },
|
||||
"email": { "type": "string" },
|
||||
"domain": { "type": "string", "description": "Same website apex as {domain}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": { "201": { "description": "{ user, accessToken, refreshToken, registeredBusiness }" } }
|
||||
}
|
||||
},
|
||||
"/auth/login": {
|
||||
"post": {
|
||||
"tags": ["Auth"],
|
||||
"summary": "Login",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["cellNumber", "password"],
|
||||
"properties": {
|
||||
"cellNumber": { "type": "string" },
|
||||
"password": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": { "200": { "description": "{ user, accessToken, refreshToken }" } }
|
||||
}
|
||||
},
|
||||
"/auth/refresh": {
|
||||
"post": {
|
||||
"tags": ["Auth"],
|
||||
"summary": "Refresh tokens",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["refreshToken"],
|
||||
"properties": { "refreshToken": { "type": "string" } }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": { "200": { "description": "{ user, accessToken, refreshToken }" } }
|
||||
}
|
||||
},
|
||||
"/auth/me": {
|
||||
"get": {
|
||||
"tags": ["Auth"],
|
||||
"summary": "Current user",
|
||||
"security": [{ "bearerAuth": [] }],
|
||||
"responses": { "200": { "description": "{ user }" } }
|
||||
}
|
||||
},
|
||||
"/auth/profile": {
|
||||
"patch": {
|
||||
"tags": ["Auth"],
|
||||
"summary": "Update profile",
|
||||
"security": [{ "bearerAuth": [] }],
|
||||
"responses": { "200": { "description": "{ message, user }" } }
|
||||
}
|
||||
},
|
||||
"/auth/change-password": {
|
||||
"post": {
|
||||
"tags": ["Auth"],
|
||||
"summary": "Change password",
|
||||
"security": [{ "bearerAuth": [] }],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["currentPassword", "newPassword"],
|
||||
"properties": {
|
||||
"currentPassword": { "type": "string" },
|
||||
"newPassword": { "type": "string", "minLength": 8 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": { "200": { "description": "{ message }" } }
|
||||
}
|
||||
},
|
||||
"/auth/send-otp": {
|
||||
"post": {
|
||||
"tags": ["Auth"],
|
||||
"summary": "Send OTP SMS",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["cellNumber"],
|
||||
"properties": { "cellNumber": { "type": "string" } }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": { "200": { "description": "{ enabled, message, expiresInSeconds? }" } }
|
||||
}
|
||||
},
|
||||
"/auth/verify-otp": {
|
||||
"post": {
|
||||
"tags": ["Auth"],
|
||||
"summary": "Verify OTP",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["cellNumber", "code"],
|
||||
"properties": {
|
||||
"cellNumber": { "type": "string" },
|
||||
"code": { "type": "string", "minLength": 6, "maxLength": 6 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": { "200": { "description": "{ enabled, verified, message }" } }
|
||||
}
|
||||
},
|
||||
"/auth/addresses": {
|
||||
"get": {
|
||||
"tags": ["Addresses"],
|
||||
"summary": "List my shipping addresses",
|
||||
"security": [{ "bearerAuth": [] }],
|
||||
"responses": { "200": { "description": "{ items }" } }
|
||||
},
|
||||
"post": {
|
||||
"tags": ["Addresses"],
|
||||
"summary": "Create address",
|
||||
"security": [{ "bearerAuth": [] }],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["province", "city", "address"],
|
||||
"properties": {
|
||||
"label": { "type": "string" },
|
||||
"province": { "type": "string" },
|
||||
"city": { "type": "string" },
|
||||
"address": { "type": "string" },
|
||||
"postalCode": { "type": "string" },
|
||||
"landline": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": { "201": { "description": "{ address }" } }
|
||||
}
|
||||
},
|
||||
"/auth/addresses/{addressId}": {
|
||||
"patch": {
|
||||
"tags": ["Addresses"],
|
||||
"summary": "Update address",
|
||||
"security": [{ "bearerAuth": [] }],
|
||||
"parameters": [
|
||||
{ "name": "addressId", "in": "path", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ address }" } }
|
||||
},
|
||||
"delete": {
|
||||
"tags": ["Addresses"],
|
||||
"summary": "Delete address",
|
||||
"security": [{ "bearerAuth": [] }],
|
||||
"parameters": [
|
||||
{ "name": "addressId", "in": "path", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ message }" } }
|
||||
}
|
||||
},
|
||||
"/cities": {
|
||||
"get": {
|
||||
"tags": ["Cities"],
|
||||
"summary": "Location tree (countries / provinces / cities)",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "level",
|
||||
"in": "query",
|
||||
"schema": { "type": "string", "enum": ["country", "province", "city"] }
|
||||
},
|
||||
{ "name": "parentId", "in": "query", "schema": { "type": "string" } },
|
||||
{ "name": "parentSlug", "in": "query", "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ items }" } }
|
||||
}
|
||||
},
|
||||
"/cities/{cityId}": {
|
||||
"get": {
|
||||
"tags": ["Cities"],
|
||||
"summary": "Get one location node",
|
||||
"parameters": [
|
||||
{ "name": "cityId", "in": "path", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ city }" } }
|
||||
}
|
||||
},
|
||||
"/businesses/{businessId}/cart": {
|
||||
"get": {
|
||||
"tags": ["Cart"],
|
||||
"summary": "Get cart",
|
||||
"security": [{ "bearerAuth": [] }],
|
||||
"parameters": [{ "$ref": "#/components/parameters/businessId" }],
|
||||
"responses": { "200": { "description": "{ cart }" } }
|
||||
},
|
||||
"delete": {
|
||||
"tags": ["Cart"],
|
||||
"summary": "Clear cart",
|
||||
"security": [{ "bearerAuth": [] }],
|
||||
"parameters": [{ "$ref": "#/components/parameters/businessId" }],
|
||||
"responses": { "200": { "description": "{ message, cart }" } }
|
||||
}
|
||||
},
|
||||
"/businesses/{businessId}/cart/items": {
|
||||
"post": {
|
||||
"tags": ["Cart"],
|
||||
"summary": "Add variant to cart",
|
||||
"security": [{ "bearerAuth": [] }],
|
||||
"parameters": [{ "$ref": "#/components/parameters/businessId" }],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["storeItemVariantId"],
|
||||
"properties": {
|
||||
"storeItemVariantId": { "type": "string" },
|
||||
"quantity": { "type": "integer", "minimum": 1, "default": 1 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": { "201": { "description": "{ message, cart }" } }
|
||||
}
|
||||
},
|
||||
"/businesses/{businessId}/cart/items/{itemId}": {
|
||||
"patch": {
|
||||
"tags": ["Cart"],
|
||||
"summary": "Update cart line quantity",
|
||||
"security": [{ "bearerAuth": [] }],
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/businessId" },
|
||||
{ "name": "itemId", "in": "path", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["quantity"],
|
||||
"properties": { "quantity": { "type": "integer", "minimum": 1 } }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": { "200": { "description": "{ message, cart }" } }
|
||||
},
|
||||
"delete": {
|
||||
"tags": ["Cart"],
|
||||
"summary": "Remove cart line",
|
||||
"security": [{ "bearerAuth": [] }],
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/businessId" },
|
||||
{ "name": "itemId", "in": "path", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ message, cart }" } }
|
||||
}
|
||||
},
|
||||
"/businesses/{businessId}/cart/checkout": {
|
||||
"post": {
|
||||
"tags": ["Cart"],
|
||||
"summary": "Checkout → create order",
|
||||
"security": [{ "bearerAuth": [] }],
|
||||
"parameters": [{ "$ref": "#/components/parameters/businessId" }],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["payment"],
|
||||
"properties": {
|
||||
"addressId": { "type": "string" },
|
||||
"shippingAddress": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"province": { "type": "string" },
|
||||
"city": { "type": "string" },
|
||||
"address": { "type": "string" },
|
||||
"postalCode": { "type": "string" },
|
||||
"landline": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"customerNotes": { "type": "string" },
|
||||
"payment": {
|
||||
"type": "object",
|
||||
"required": ["type"],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["pos", "cash", "transfer", "e_payment_gate"]
|
||||
},
|
||||
"posType": { "type": "string" },
|
||||
"transferAccount": { "type": "string" },
|
||||
"transferRefNumber": { "type": "string" },
|
||||
"gatewayType": { "type": "string" },
|
||||
"notes": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": { "201": { "description": "{ message, order }" } }
|
||||
}
|
||||
},
|
||||
"/businesses/{businessId}/orders": {
|
||||
"get": {
|
||||
"tags": ["Orders"],
|
||||
"summary": "My orders",
|
||||
"security": [{ "bearerAuth": [] }],
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/businessId" },
|
||||
{ "name": "page", "in": "query", "schema": { "type": "integer" } },
|
||||
{ "name": "pageSize", "in": "query", "schema": { "type": "integer", "default": 20 } },
|
||||
{
|
||||
"name": "status",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["pending", "confirmed", "processing", "shipped", "delivered", "cancelled"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": { "200": { "description": "{ items, total, page, pageSize }" } }
|
||||
}
|
||||
},
|
||||
"/businesses/{businessId}/orders/{orderId}": {
|
||||
"get": {
|
||||
"tags": ["Orders"],
|
||||
"summary": "My order detail",
|
||||
"security": [{ "bearerAuth": [] }],
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/businessId" },
|
||||
{ "name": "orderId", "in": "path", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ order }" } }
|
||||
}
|
||||
},
|
||||
"/businesses/{businessId}/favorites": {
|
||||
"get": {
|
||||
"tags": ["Favorites"],
|
||||
"summary": "List favorites",
|
||||
"security": [{ "bearerAuth": [] }],
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/businessId" },
|
||||
{ "name": "page", "in": "query", "schema": { "type": "integer" } },
|
||||
{ "name": "pageSize", "in": "query", "schema": { "type": "integer", "default": 20 } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ items, total, page, pageSize }" } }
|
||||
},
|
||||
"post": {
|
||||
"tags": ["Favorites"],
|
||||
"summary": "Add favorite",
|
||||
"security": [{ "bearerAuth": [] }],
|
||||
"parameters": [{ "$ref": "#/components/parameters/businessId" }],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["productId"],
|
||||
"properties": { "productId": { "type": "string" } }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": { "201": { "description": "{ favorite, message }" } }
|
||||
}
|
||||
},
|
||||
"/businesses/{businessId}/favorites/{productId}": {
|
||||
"delete": {
|
||||
"tags": ["Favorites"],
|
||||
"summary": "Remove favorite",
|
||||
"security": [{ "bearerAuth": [] }],
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/businessId" },
|
||||
{ "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": { "200": { "description": "{ message }" } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user