01 — Infrastructure
CEO Summary
Infrastructure is the foundation that keeps the platform running. We use a Linux server (e.g. Vultr) with Nginx for SSL and routing, PM2 for process management, and Google Cloud SQL for the database. Business value: Uptime directly impacts tenant operations and revenue. Key metrics: Uptime %, response time, PM2 process health. If this breaks: All tenant operations stop — no POS, no signup, no API.
Architecture
MySQL path: Use either Cloud SQL Proxy (PM2 cloud-sql-proxy + MYSQL_HOST=127.0.0.1) or a direct host/IP (VPC, authorized network, etc.). A stopped proxy process in PM2 is normal when the deployment no longer uses it.
Dashboard in production: backend/server.js serves the built SPA from frontend/dist (run npm run build in frontend/). PM2 pawspos-frontend is an optional Vite dev server for local/staging workflows, not the only way to ship the dashboard.
Overview
The platform runs on a Linux server with:
- Nginx — Reverse proxy, SSL termination, routing
- PM2 — Process manager for Node.js apps
- Cloud SQL Proxy — Optional; tunnels to Google Cloud SQL when
MYSQL_HOST=127.0.0.1and proxy is running - Backend — Express API (port 4000); in production often also serves
frontend/diststatic assets - Frontend (Vite) — Dev server (port 5173) when run via PM2 or
npm run dev - Public Site (Vite) — Dev server (port 5174) or built
public-site/distper Nginx config
Directory Structure
/home/pawspos/app/
├── backend/ # Express API
├── frontend/ # Dashboard SPA (React + Vite)
├── public-site/ # Marketing/signup (React + Vite)
├── bridge/ # Node.js bridge (legacy)
├── bridge-win/ # Node.js bridge Windows
├── bridge-win-csharp/ # C#/.NET 8 Windows bridge
├── infrastructure/ # PM2, Nginx, SSL scripts
├── documentation/ # OAO documentation
├── logs/ # PM2 logs
├── sessions/ # Express session files
└── sqldumps/ # DB backups
PM2 Configuration
File: infrastructure/ecosystem.production.config.js
| App | Script | Port | Purpose |
|---|---|---|---|
| cloud-sql-proxy | cloud-sql-proxy | 3306 | Proxy to Google Cloud SQL |
| pawspos-backend | ./backend/server.js | 4000 | API (cluster, 2 instances) |
| pawspos-frontend | npx vite --host 0.0.0.0 --port 5173 | 5173 | Dashboard |
| pawspos-public | npx vite --host 0.0.0.0 --port 5174 | 5174 | Public site |
PM2 Commands
cd /home/pawspos/app
pm2 start infrastructure/ecosystem.production.config.js
pm2 status
pm2 restart all
pm2 restart pawspos-backend pawspos-frontend pawspos-public
pm2 logs pawspos-backend --lines 50
pm2 save
pm2 startup systemd # Persist across reboots
Logs
/home/pawspos/app/logs/ — backend-error.log, backend-out.log, frontend-*, public-site-*, cloud-sql-proxy-*
Nginx
Staging: infrastructure/nginx-staging.conf
Production: infrastructure/nginx.conf, infrastructure/nginx-ssl.conf
| Path | Proxies To | Purpose |
|---|---|---|
| /api | 127.0.0.1:4000 | Backend API |
| /auth | 127.0.0.1:4000 | Auth endpoints |
| /signup, /login, /dashboard | 127.0.0.1:5173 | Frontend SPA |
| / | public-site/dist or 5174 | Public site |
SSL
Let's Encrypt or GoDaddy certs. Scripts: setup-staging-ssl.sh, setup-godaddy-ssl.sh. Certs: /etc/nginx/ssl/ or /etc/letsencrypt/live/
Environment Variables (Backend)
Node loads dotenv from the process working directory (PM2 cwd is usually /home/pawspos/app), so production often uses a root .env beside backend/. You can also duplicate keys in backend/.env for cd backend && npm run dev. PM2 env in ecosystem.production.config.js overrides where set. Critical MySQL variables include MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DATABASE, plus MYSQL_MASTER_DATABASE and MYSQL_TENANT_DB_PREFIX for multi-DB resolution — see 02 — Databases.
Tip: If shell scripts source backend/.env and MySQL fails with strange “port” errors, the file may have Windows CRLF line endings — use LF-only or dos2unix backend/.env.
NODE_ENV=production
PORT=4000
MYSQL_HOST=127.0.0.1 # or direct Cloud SQL IP if not using proxy
MYSQL_PORT=3306
MYSQL_DATABASE=ppos-cloudfresh-dev
MYSQL_MASTER_DATABASE=ppos-cloudfresh-dev
MYSQL_TENANT_DB_PREFIX=dev-
MYSQL_ANALYTICS_DB_PREFIX=dev-analytics-
CORS_ORIGIN=https://staging.gopastearth.com,...
SESSION_SECRET=...
APP_BASE_URL=https://staging.gopastearth.com
API_BASE_URL=https://staging.gopastearth.com/api
Disabling public signup (tenant provisioning)
To stop new self-service signups (and the automatic creation of master rows plus three MySQL databases per tenant), set an environment flag. Business value: avoids surprise Cloud SQL / storage cost and load while staging or hardening the product. Existing tenants are unaffected; login and APIs keep working.
| Variable | Values | Effect |
|---|---|---|
DISABLE_PUBLIC_SIGNUP |
1, true, yes, on (case-insensitive) |
Blocks POST /api/public/signup (403 JSON, or redirect with ?error= when ?redirect=1). Replaces GET /api/public/signup-form HTML with a short “unavailable” page. |
DISABLE_PUBLIC_SIGNUP_MESSAGE |
Optional string | Custom user-facing text (returned in JSON and on the HTML fallback page). |
SPA behavior: GET /api/public/signup-status returns { signupOpen, message }. Dashboard and public-site /signup pages call this and show a “registration closed” screen when signupOpen is false. Rebuild frontends after changing copy-only UX; the API gate is enough for security.
Re-enable: Remove the variable or set it to 0 / empty, then pm2 restart pawspos-backend --update-env.
Verify:
curl -s https://staging.gopastearth.com/api/public/signup-status
# Expect: {"signupOpen":false,"message":"..."} when disabled
curl -s -o /dev/null -w "%{http_code}" -X POST https://staging.gopastearth.com/api/public/signup \
-H "Content-Type: application/json" -d '{}'
# Expect: 403 when disabled
Code: backend/routes/public.signup.routes.js. Documented in backend/.env.example; optional commented line in infrastructure/ecosystem.production.config.js. Related: 03 — Backend, Appendix: Data Flows (signup), Glossary.
Deployment Steps
- Pull latest code
cd backend && npm installcd frontend && npm install && npm run build— required when the API serves the dashboard fromfrontend/dist(includessw.jsand PWA assets)cd public-site && npm install(if needed) andnpm run buildpm2 restart pawspos-backend— picks up new static files fromfrontend/dist- Restart Vite PM2 apps only if you use them:
pm2 restart pawspos-frontend pawspos-public - Verify:
curl -s https://staging.gopastearth.com/api/public/test
Infrastructure Scripts
Path: infrastructure/
| Script | Purpose |
|---|---|
| setup-infrastructure.sh | Full infrastructure setup |
| setup-nginx.sh | Nginx config |
| setup-staging-ssl.sh | Let's Encrypt SSL |
| setup-godaddy-ssl.sh | GoDaddy SSL |
| extract-godaddy-cert.sh | Extract cert from GoDaddy |
| setup-cloud-sql-proxy.sh | Cloud SQL Proxy setup |
| setup-firewall.sh | Firewall rules |
| verify-staging-api.sh | Backend health, Nginx, PM2 status |
Infrastructure Documentation
README.md, QUICK-START.md, HOSTING-SETUP.md, STAGING-SETUP.md, SSL-SETUP.md, SSL-REQUIREMENTS.md, FIREWALL-SETUP.md, DATABASE-CONNECTION-SETUP.md