Architecture
This page describes the system architecture of the Siyahfy platform, including how requests flow through the system, how the backend is organized, and how key subsystems like authentication and file storage work.
System Design
The platform follows a client-server architecture with multiple Next.js frontend applications communicating with a single Express.js backend API. A separate proxy server handles storefront routing for customer-facing stores.
Frontend Apps
All frontend apps communicate with the backend via REST API calls with JWT authentication.
| App | URL | Port | Purpose |
|---|---|---|---|
| Vendor Dashboard | app.siyahfy.com | 3000 | Store management, products, orders, analytics |
| Theme Editor | editor.siyahfy.com | 3002 | Visual drag-and-drop theme customization |
| Developer Studio | studio.siyahfy.com | 3012 | IDE for theme developers |
| Developer Portal | developer.siyahfy.com | 3000 | App developer dashboard |
| App Store | store.siyahfy.com | 3000 | App marketplace for store owners |
| Marketing Site | siyahfy.com | 3000 | Landing pages, pricing |
Backend & Infrastructure
Storefront Proxy
Customer-facing stores go through the proxy layer:
External Services
| Service | Purpose |
|---|---|
| Razorpay / Cashfree | Payment processing |
| Backblaze B2 | Product images storage |
| Cloudflare R2 | General file storage |
| Gmail SMTP | Transactional emails |
| Firebase | Push notifications, auth |
| Delhivery | Shipping integration |
| WhatsApp API | Marketing messages |
| Google Analytics | Tracking |
Request Flow
Every API request passes through several layers before reaching the route handler. Here is the typical flow for an authenticated request:
Backend Route Organization
The backend organizes routes by domain. All routes are mounted under the /api prefix in index.js. Here is a summary of the major route domains:
| Domain | Route Files | Key Areas |
|---|---|---|
| Admin | admin.js, admin-affiliate.js, admin-plan-assign.js | Platform admin login, settings, affiliate management, plan assignment |
| Vendors | vendors.js, vendor-store.js, vendor-access.js, vendor-affiliate.js, vendor-credits.js | Vendor registration, store creation, access control, affiliate config, credits |
| Products | products.js, bulkupload.js, shopifyBulkUpload.js, catalog.js, inventory.js, product_metafield.js | Product CRUD, bulk upload (CSV/Shopify), catalog management, inventory tracking, metafields |
| Categories | category.js, subcategory.js, dynamic_category.js, categoryStatistics.js | Category tree, subcategories, dynamic filtering, analytics |
| Orders | orders.js, draftorder.js, orderStatistics.js, abandon_checkouts.js | Order management, draft orders, order analytics, abandoned cart recovery |
| Returns & Refunds | refund.js, return.js, cancel.js | Refund processing, return handling, order cancellations |
| Payments | payment-methods.js, razorpay/, codControl.js | Payment method config, Razorpay integration, COD control |
| Collections | collections.js, bulk-collection.js | Product collections, bulk collection operations |
| Marketing | marketing/, whatsapp-marketing.js, discount.js, referral.js | Campaign management, WhatsApp messaging, discount codes, referral programs |
| Content | blogs.js, pages.js, shorts.js, banner.js, banner-v2.js | Blog posts, custom pages, short-form content, banner management |
| Themes | theme.js, theme-editor.js, theme-marketplace.js, store-templates.js, store-menus.js | Theme CRUD, editor API, marketplace, templates, navigation menus |
| Store Layout | home_layout.js, mobile_home_layout.js | Homepage layout builder (desktop and mobile) |
| Files | files.js, export.js, export-store.js, bg-remover.js | File upload/management, data export, store export, AI background removal |
| Users & Auth | userRoles.js, customer.js, studio-auth.js | Role-based access, customer management, studio authentication |
| Developer | developer.js, developer-earnings.js, store-apps.js, app-credits.js | Developer accounts, earnings, app publishing, app credits |
| Affiliate | affiliate-portal.js | Affiliate dashboard, referral tracking, payout management |
| Plans | plans/, addons/ | Subscription plans, add-on features |
| Storefront APIs | apis/ (apis, product, usercategory, menus, usercart, customerapis, rating, collections, checkout, order, wishlist, brands, manage_search, cashfree, razorpay, new-system-user-creation) | Public storefront APIs for customer-facing stores |
| Storefront V2 | template-v2-routes/ (index, customer, menusv2, categoryv2, search, referral_reward, wallet, usercartv2) | Next-generation storefront APIs |
| Mobile App | app_template_routes/ (category, app-api, product) | Native mobile app APIs |
| Infrastructure | cronJob.js, sitemap.js, envDetails.js, testing.js, pdfcreation.js | Scheduled jobs, SEO sitemaps, environment info, testing endpoints, PDF generation |
| Integrations | connect_store/amazon.js, delhivery_partner.js, prefilled-chat.js | Amazon store import, Delhivery shipping, WhatsApp pre-filled chat |
| Showcase | showcase_api/ | Platform showcase/gallery |
Authentication Middleware
The backend uses four authentication middleware functions, each designed for a different user type:
| Middleware | Location | Token Source | Verifies Against | Attaches to req |
|---|---|---|---|---|
authenticate | lib/index.js | Authorization header, tokenSagartech cookie, tokenVendorsSagartech cookie, token cookie | SECRET_KEY env var | req.userId, req.role_id |
authorizeVendor | middleware/index.js | Authorization header (Bearer token) | Hardcoded JWT secret, then looks up api_key in stores table | req.apiKey, req.store_name, req.vendor_id |
authorizeCustomer | middleware/user.js | authorization-customer header or authorization header | SECRET_KEY env var | req.userId |
authenticateAffiliate | routes/affiliate-portal.js | Authorization header or affiliateToken cookie | SECRET_KEY env var | req.affiliateId, req.affiliateEmail |
How authenticate Works
The primary authenticate middleware (lib/index.js) follows this logic:
- Check the
Authorizationheader for a Bearer token - If no header token, parse cookies and check
tokenSagartech, thentokenVendorsSagartech, thentoken - Strip surrounding quotes and redundant “Bearer ” prefixes
- Validate the token matches JWT format (three dot-separated base64 segments)
- Verify the token signature using
jwt.verify(token, process.env.SECRET_KEY) - Check token expiration
- Attach
req.userIdandreq.role_idfrom the decoded payload - Call
next()to pass control to the route handler
How authorizeVendor Works
The authorizeVendor middleware (middleware/index.js) is used for storefront API calls:
- Extract Bearer token from
Authorizationheader - If the token is longer than 20 characters, treat it as a JWT and verify it to extract the
api_keyclaim - If the token is 20 characters or fewer, treat it directly as an API key
- Query the
storestable to find a store whoseapi_keyJSONB array contains a matchingapiKeyvalue - Attach
req.apiKey,req.store_name(slug),req.og_store_name, andreq.vendor_id
Data Flow Example: Customer Places an Order
This sequence shows the complete flow when a customer places an order through a vendor’s storefront:
File Storage Architecture
The platform uses two cloud storage providers for different purposes:
Backblaze B2
- Primary use: Product images, category images, brand logos
- Integration: AWS S3-compatible SDK (
@aws-sdk/client-s3) - Upload flow: Multer receives files on the backend, Sharp processes/resizes images, then the S3 SDK uploads to Backblaze B2
- URL pattern:
https://<bucket>.s3.<region>.backblazeb2.com/<folder>/<filename> - Folders: Organized by type (e.g.,
products/,categories/,brands/)
Cloudflare R2
- Primary use: General file storage, exports, documents
- Integration: AWS S3-compatible SDK with Cloudflare endpoint
- URL pattern:
https://<public-url>.r2.dev/<filename> - Bucket: Single bucket (
siyahfy-files) with path-based organization
Upload Flow
Storefront Proxy Architecture
The backend-store.siyahfy.com service is a reverse proxy that routes incoming storefront requests to the correct Next.js theme server based on the store’s domain or subdomain.
Resolution Flow
- A customer visits
myshop.site.siyahfy.comorcustom-domain.com - The proxy extracts the hostname and determines the store slug
- It queries PostgreSQL for the store’s theme server URL based on the vendor’s subscription plan
- The request is proxied to the correct theme server
- Theme resolution results are cached in memory for 60 seconds
Domain Types
| Pattern | Resolution |
|---|---|
*.site.siyahfy.com | Extract slug from subdomain, query stores table |
Custom domain (e.g., example.com) | Query store_domains table for verified domain |
*.localhost | Slug-based resolution for local development |
Custom Domain Flow
When a vendor connects a custom domain:
- Vendor adds the domain in the dashboard
- Domain record is created in
store_domainstable withverified=false - Vendor configures DNS A record to point to the server IP
- Domain verification is completed and
verifiedis set totrue - Traefik automatically issues a Let’s Encrypt SSL certificate on first HTTPS request
- All subsequent requests are routed through the proxy to the store’s theme server
Database Architecture
PostgreSQL is the sole relational database. Key architectural decisions:
- Multi-tenant by convention: All stores share the same database. Tables use
store_name/store_slug/vendor_idcolumns to scope data to a specific store. - Auto-migrations: The backend runs pending SQL migrations from
backend.siyahfy.com/migrations/on every startup. A_migrationstable tracks which files have been executed. - JSONB usage: Several columns use PostgreSQL JSONB for flexible schema (e.g.,
api_keyinstores, product attributes, theme configuration). - No ORM: All queries are raw SQL via the
pgdriver. The connection pool is configured inconfig/index.js.
Caching Strategy
Redis is used as an optional caching layer:
- Connection: Configured in
config/redis.js, connects toredis://127.0.0.1:6379in development andredis://redis:6379in production - Graceful degradation: The backend starts and functions without Redis. Cache misses simply hit the database directly.
- Storefront proxy: The
backend-store.siyahfy.comproxy uses in-memory caching (60-second TTL) for theme server resolution, independent of Redis.