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
articleAttributesto render dynamic fields - Job creation — loops over
jobStepsto 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]