> ## Documentation Index
> Fetch the complete documentation index at: https://docs.spn.wtf/llms.txt
> Use this file to discover all available pages before exploring further.

# Architecture

> SPN v3.0 — PostgreSQL-first hybrid platform design

## Platform Demo

<iframe width="100%" height="400" src="https://www.youtube.com/embed/YOUR_VIDEO_ID" title="SPN Platform — Demo" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen />

***

## Overview

SPN uses a **PostgreSQL-first hybrid approach**. PostgreSQL is the single source of truth for all structured data. Sanity CMS is used exclusively for rich media content (logos, hero images, galleries).

```
┌──────────────────────────────────────────────────────────┐
│                      SPN Platform                         │
│                                                           │
│   Browser / Mobile App                                    │
│         │                                                 │
│         ▼                                                 │
│   ┌─────────────┐        ┌─────────────┐                 │
│   │  React 19   │◄──────►│  Sanity CMS │                 │
│   │  Frontend   │        │  (media)    │                 │
│   └──────┬──────┘        └─────────────┘                 │
│          │                                                │
│          ▼                                                │
│   ┌─────────────┐                                        │
│   │   FastAPI   │◄──── PostgreSQL (Source of Truth)      │
│   │   Backend   │                                        │
│   └──────┬──────┘                                        │
│          │                                                │
│    ┌─────┼──────────┬──────────┐                        │
│    ▼     ▼          ▼          ▼                         │
│  AWS S3  Gemini  WhatsApp   Zoho CRM                     │
│ (Files)  (AI)   (Catalog)   (Leads)                      │
└──────────────────────────────────────────────────────────┘
```

***

## Why PostgreSQL-First?

<CardGroup cols={2}>
  <Card title="No Document Limits" icon="database">
    Sanity free tier caps at 10K documents. PostgreSQL handles millions of records with no limits.
  </Card>

  <Card title="Full-Text Search" icon="magnifying-glass">
    PostgreSQL `tsvector` delivers sub-50ms search across 100K+ records — no external search service needed.
  </Card>

  <Card title="Relational Integrity" icon="link">
    UUID foreign keys with CASCADE ensure referential integrity across companies, users, products, and contacts.
  </Card>

  <Card title="Simpler Integrations" icon="arrow-right">
    One-way pushes to Zoho and WhatsApp — no complex bi-directional syncs to maintain.
  </Card>
</CardGroup>

| Aspect       | Old Approach           | New Approach                 |
| ------------ | ---------------------- | ---------------------------- |
| Company data | Sanity CMS (10K limit) | PostgreSQL (unlimited)       |
| Catalog      | Sanity + DB split      | PostgreSQL only              |
| Search       | Sanity API             | PostgreSQL tsvector (\<50ms) |
| WhatsApp     | Direct API             | XML bulk feed                |
| CRM sync     | Bi-directional         | One-way push                 |

***

## Tech Stack

<AccordionGroup>
  <Accordion title="Backend" icon="server">
    * **Python 3.11** / **FastAPI 0.115**
    * **PostgreSQL 15** + **SQLAlchemy 2.0** (async)
    * **Alembic** — database migrations
    * **Pydantic 2.x** — request/response validation
    * **slowapi** — rate limiting
    * **Docker** — containerised deployment
  </Accordion>

  <Accordion title="Frontend" icon="browser">
    * **React 19** / **TypeScript 5.9**
    * **Vite 7** — build tool
    * **Tailwind CSS 4** — styling
    * **React Router DOM 7** — routing
    * **Axios** — HTTP client with JWT interceptor
  </Accordion>

  <Accordion title="External Services" icon="cloud">
    * **Sanity CMS v3** — rich media content only
    * **AWS S3** — file storage (product images, profile pictures)
    * **Google Gemini AI** — virtual try-on image generation
    * **TalkingShops** — WhatsApp Business API
    * **Zoho CRM** — one-way lead sync
    * **Caddy** — reverse proxy + automatic HTTPS
  </Accordion>
</AccordionGroup>

***

## Database Schema

### Core Tables

| Table              | Purpose                             |
| ------------------ | ----------------------------------- |
| `users`            | User accounts (linked to a company) |
| `companies`        | Supplier/buyer company profiles     |
| `catalog_products` | Product listings                    |
| `contacts`         | Company contact persons             |
| `enquiries`        | Buyer-to-supplier leads             |
| `refresh_tokens`   | JWT refresh token store             |
| `tryon_jobs`       | AI try-on generation jobs           |

All tables use **UUID primary keys** and include `created_at` / `updated_at` timestamps.

### Full-Text Search

```sql theme={null}
-- Weighted search vector (updated on insert/update)
search_vector :=
  setweight(to_tsvector('english', name), 'A') ||
  setweight(to_tsvector('english', category), 'B') ||
  setweight(to_tsvector('english', address_city), 'C');

-- Query
SELECT * FROM companies
WHERE search_vector @@ plainto_tsquery('english', 'textile mumbai')
ORDER BY ts_rank(search_vector, plainto_tsquery('english', 'textile mumbai')) DESC;
```

***

## Integration Patterns

### WhatsApp Catalog

```
CatalogProduct updated
  → FastAPI generates XML feed
  → Meta Commerce Manager polls feed hourly
  → Products appear in WhatsApp Business catalog
```

### Zoho CRM

```
Enquiry created
  → Background task fires
  → POST to Zoho Leads API
  → zoho_lead_id stored on enquiry record
```

### Sanity CMS (Media Only)

```
Company logo/hero uploaded via frontend
  → Stored in AWS S3
  → S3 key pushed to Sanity
  → Frontend fetches rich content from Sanity
  → Structured data always from PostgreSQL
```

***

## Rate Limiting

All endpoints use `slowapi`. Limits are configurable via environment variables.

| Tier      | Default | Applied To                       |
| --------- | ------- | -------------------------------- |
| `AUTH`    | 20/min  | Login, register, password reset  |
| `GENERAL` | 120/min | Authenticated CRUD endpoints     |
| `UPLOAD`  | 10/min  | File uploads                     |
| `PUBLIC`  | 60/min  | Public unauthenticated endpoints |

Set `RATE_LIMIT_ENABLED=false` to disable all limits (useful for testing).

***

## Project Structure

```
spn/
├── backend/
│   ├── app/
│   │   ├── api/v1/endpoints/   # Route handlers (auth, users, companies…)
│   │   ├── models/domain.py    # All SQLAlchemy models
│   │   ├── schemas/            # Pydantic request/response schemas
│   │   ├── services/           # Business logic
│   │   └── core/               # Config, security, rate limiting
│   ├── alembic/                # Database migrations
│   └── tests/                  # pytest test suite
├── frontend/
│   └── src/
│       ├── pages/              # Page components
│       ├── components/         # Reusable UI components
│       ├── contexts/           # AuthContext, EnquiryContext
│       └── utils/              # Axios instance, Sanity client
├── sanity-studio/              # Sanity CMS Studio
├── docs/                       # This documentation
└── docker-compose.yml
```

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Quickstart" icon="rocket" href="/quickstart">
    Run SPN locally in under 5 minutes
  </Card>

  <Card title="API Reference" icon="code" href="/api/overview">
    Explore all REST endpoints
  </Card>
</CardGroup>
