01 — Infrastructure

Server, PM2, Nginx, SSL, deployment

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

flowchart LR subgraph internet [Internet] User[User] end subgraph server [Linux Server] Nginx[Nginx :443/80] PM2[PM2] subgraph processes [Processes] Backend[Backend :4000] Frontend[Vite dashboard :5173] Public[Vite public-site :5174] Proxy[Cloud SQL Proxy optional] end end subgraph cloud [Google Cloud] MySQL[(Cloud SQL MySQL)] end User --> Nginx Nginx --> Backend Nginx -.->|dev or separate host| Frontend Nginx -.->|dev or separate host| Public PM2 --> processes Backend -->|MYSQL_HOST via proxy| Proxy Proxy --> MySQL Backend -.->|or MYSQL_HOST direct| MySQL

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:

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

AppScriptPortPurpose
cloud-sql-proxycloud-sql-proxy3306Proxy to Google Cloud SQL
pawspos-backend./backend/server.js4000API (cluster, 2 instances)
pawspos-frontendnpx vite --host 0.0.0.0 --port 51735173Dashboard
pawspos-publicnpx vite --host 0.0.0.0 --port 51745174Public 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

PathProxies ToPurpose
/api127.0.0.1:4000Backend API
/auth127.0.0.1:4000Auth endpoints
/signup, /login, /dashboard127.0.0.1:5173Frontend SPA
/public-site/dist or 5174Public 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.

VariableValuesEffect
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

  1. Pull latest code
  2. cd backend && npm install
  3. cd frontend && npm install && npm run build — required when the API serves the dashboard from frontend/dist (includes sw.js and PWA assets)
  4. cd public-site && npm install (if needed) and npm run build
  5. pm2 restart pawspos-backend — picks up new static files from frontend/dist
  6. Restart Vite PM2 apps only if you use them: pm2 restart pawspos-frontend pawspos-public
  7. Verify: curl -s https://staging.gopastearth.com/api/public/test

Infrastructure Scripts

Path: infrastructure/

ScriptPurpose
setup-infrastructure.shFull infrastructure setup
setup-nginx.shNginx config
setup-staging-ssl.shLet's Encrypt SSL
setup-godaddy-ssl.shGoDaddy SSL
extract-godaddy-cert.shExtract cert from GoDaddy
setup-cloud-sql-proxy.shCloud SQL Proxy setup
setup-firewall.shFirewall rules
verify-staging-api.shBackend 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