Skip to content

Architecture — Foot Factory

System Overview

graph TB
    subgraph Clients
        C[dot-foot-factory-client\nfactory floor app]
        A[dot-foot-factory-admin\nsuper admin dashboard]
    end

    subgraph API
        API[dot-foot-factory-api\nNode.js / Express / Mongoose]
    end

    subgraph Data
        MONGO[(MongoDB Atlas\nprimary data store)]
        REDIS[(Upstash Redis\nplan limits cache)]
    end

    subgraph Platform
        PA[dot-portal-api\nPortal API]
        SB[Supabase Auth]
        CR[(Central Registry\nSupabase PostgreSQL)]
    end

    C -->|Bearer JWT| API
    A -->|Bearer JWT| API
    API -->|supabase.auth.getUser| SB
    API -->|plan limits| PA
    PA -->|tenant status| CR
    API --> MONGO
    API --> REDIS

1. API Service (dot-foot-factory-api)

  • URL: https://foot-factory-api.dotevolve.net
  • Stack: Node.js, Express v5, Mongoose, MongoDB Atlas, Heroku
  • Auth: Supabase JWT validation (no custom tokens)

Key Middleware

Middleware File Responsibility
protect controllers/authController.js Validates Supabase JWT, checks tenant status, attaches req.user
tenantMiddleware middleware/tenantMiddleware.js Injects { tenantId: activeTenantId } into all Mongoose queries
requireServiceSecret middleware/requireServiceSecret.js Guards internal endpoints with x-service-secret header

protect Middleware Flow

flowchart TD
    A[Incoming Request] --> B{Has Bearer JWT?}
    B -->|No| C[401 Unauthorized]
    B -->|Yes| D[supabase.auth.getUser token]
    D --> E{Valid?}
    E -->|No| F[401 Unauthorized]
    E -->|Yes| G[Extract app_metadata]
    G --> H{Tenant status?}
    H -->|suspended| I[403 Forbidden]
    H -->|pending_payment| J[402 Payment Required]
    H -->|active| K[Attach req.user]
    K --> L{Profile doc exists?}
    L -->|No| M[Create Profile_Doc lazily]
    M --> N[Next middleware]
    L -->|Yes| N

External Dependencies

dot-foot-factory-api
  ├── MongoDB Atlas          (primary data store)
  ├── Upstash Redis          (plan limits cache — ioredis TLS)
  ├── Supabase Auth          (JWT validation)
  ├── Supabase PostgreSQL    (tenant status checks via Central Registry)
  └── dot-portal-api         (plan limits, CORS origins — internal HTTP)

2. Factory Floor App (dot-foot-factory-client)

  • URL: https://foot-factory.dotevolve.net
  • Stack: React, Vite, TypeScript, Tailwind CSS, Redux Toolkit, Vercel

Dynamic Rendering

On login, TenantConfigContext fetches GET /api/v1/tenant-config/:tenantId. The config drives:

  • Article forms — loops over articleAttributes to render dynamic fields
  • Job creation — loops over jobSteps to render step assignment inputs

3. Admin Dashboard (dot-foot-factory-admin)

  • URL: https://foot-factory-admin.dotevolve.net
  • Stack: React, Vite, TypeScript, Tailwind CSS, Vercel

SSO Entry Point

Accepts ?token=<jwt>&tenantId=<id> at /auth/sso. Validates JWT via Supabase, sets session, redirects to dashboard.

What Was Removed (Central Portal Migration)

Removed Reason
vercelClient.js Domain provisioning moved to dot-portal-api
cloudflareClient.js Domain provisioning moved to dot-portal-api
domainProvisioning.js Domain provisioning moved to dot-portal-api
models/planModel.js Plans moved to Supabase plans table
controllers/planController.js Plans managed via Portal API
models/GlobalConfig.js CORS origins derived from Central Registry
controllers/globalConfigController.js CORS managed by Portal API
signToken, signRefreshToken Replaced by Supabase Auth
User.password, User.passwordResetToken Managed by Supabase Auth

Multi-Tenancy Model

Foot Factory uses shared database, shared collection multi-tenancy. All tenant data lives in the same MongoDB collections, isolated by tenantId.

graph LR
    JWT[JWT\napp_metadata.activeTenantId] --> MW[tenantMiddleware]
    MW -->|injects tenantId| Q1[Worker.find]
    MW -->|injects tenantId| Q2[Job.find]
    MW -->|injects tenantId| Q3[Article.find]
    MW -->|injects tenantId| Q4[Ledger.find]
    SA[Super Admin] -->|skipTenantFilter: true| Q5[Cross-tenant queries]