# Deploy dashboards (Debian VPS) Stack: static Vite builds → Nginx → Let's Encrypt (HTTP-01). Host: `45.149.76.52` · App path: `/opt/meshkee/dashboards` · Web root: `/var/www/meshkee/{super-admin,business,customer}` | Host | App | |------|-----| | `manage.meshkee.com` | Super Admin | | `business.` | Business dashboard | | `customer.` | Customer dashboard | API: `https://api.meshkee.com/api/v1` (separate server). ## New tenant SSL (automatic) 1. Add business + domain in Super Admin (apex, e.g. `sanihome.ir`). The API writes Arvan records on that zone (`@` ANAME → `ns.meshkee.com`, `www` CNAME → the apex, plus `business` / `customer` / `api` CNAMEs to Meshkee hosts). The domain must already exist in Arvan. 2. In Super Admin → **Websites** → **Sync SSL now** (or wait for cron ~2h) Manual on the VPS: `sudo /opt/meshkee/dashboards/deploy/ssl-sync.sh` Cron (root): ``` 0 */2 * * * /opt/meshkee/dashboards/deploy/ssl-sync.sh >> /var/log/meshkee-ssl-sync.log 2>&1 ``` ### SSL sync agent (on-demand from Super Admin) Small Node agent on the dashboards VPS; Nest calls it after the button is clicked. 1. Files under `/opt/meshkee/dashboards/deploy/`: `ssl-sync.sh`, `ssl-issue-tenant.sh`, `ssl-sync-agent.mjs`, `meshkee-ssl-sync-agent.service`, `meshkee-dashboard-certs-map.conf`, `nginx-dashboards-ssl.conf` 2. Make scripts executable: `chmod +x /opt/meshkee/dashboards/deploy/ssl-sync.sh /opt/meshkee/dashboards/deploy/ssl-issue-tenant.sh` 3. Install nginx cert map (http context): `cp /opt/meshkee/dashboards/deploy/meshkee-dashboard-certs-map.conf /etc/nginx/conf.d/` — business/customer server blocks must use `$meshkee_dashboard_ssl_cert` / `$meshkee_dashboard_ssl_key` (see `nginx-dashboards-ssl.conf`) 4. Agent env `/etc/meshkee/ssl-sync-agent.env`: ``` SSL_SYNC_AGENT_TOKEN= SSL_SYNC_SCRIPT=/opt/meshkee/dashboards/deploy/ssl-sync.sh SSL_TENANT_SCRIPT=/opt/meshkee/dashboards/deploy/ssl-issue-tenant.sh PORT=9051 BIND=0.0.0.0 ``` 5. Install + start: ```bash cp /opt/meshkee/dashboards/deploy/meshkee-ssl-sync-agent.service /etc/systemd/system/ systemctl daemon-reload systemctl enable --now meshkee-ssl-sync-agent ufw allow from 185.164.72.119 to any port 9051 proto tcp comment 'SSL sync agent from API' ``` 5. API `.env`: ``` SSL_SYNC_AGENT_URL=http://45.149.76.52:9051/ssl-sync SSL_SYNC_AGENT_TOKEN= ``` Endpoint used by the UI: `POST /api/v1/domains/ssl-sync` (super-admin JWT) — runs **full** `ssl-sync.sh` (all dashboard hosts + shared `meshkee-dashboards` cert). Use for cron and the global **Sync SSL** button. Agent body: | Action | Body | |--------|------| | Global sync (cron / Sync SSL) | `{ "wait": false }` or `{ "wait": true }` | | Per-row Issue SSL (dashboard hosts only) | `{ "wait": true, "tenantApex": "example.com" }` → runs `ssl-issue-tenant.sh` for `business.example.com` + `customer.example.com` only | Per-row **Issue SSL**: `POST /api/v1/domains/:id/issue-ssl` probes apex, www, business, and customer. Storefront (apex/www) is issued on the **websites** VM for that domain only. Dashboard hosts use the per-tenant script above — other tenants are not validated. ## Redeploy frontends From your laptop (rsync source tree, then build on server): ```bash rsync -az --delete \ --exclude node_modules --exclude .git --exclude '**/dist' --exclude '.env' \ -e 'ssh -i ~/.ssh/id_ed25519' \ ./ root@45.149.76.52:/opt/meshkee/dashboards/ ssh root@45.149.76.52 'cd /opt/meshkee/dashboards && npm ci && npm run build && \ rsync -a --delete apps/super-admin/dist/ /var/www/meshkee/super-admin/ && \ rsync -a --delete apps/business/dist/ /var/www/meshkee/business/ && \ rsync -a --delete apps/customer/dist/ /var/www/meshkee/customer/ && \ find /var/www/meshkee -type f -exec chmod a+r {} + && \ find /var/www/meshkee -type d -exec chmod a+rx {} +' ``` > **Note:** Source files like `favicon.png` must be world-readable (`644`). If they are `700`, Nginx returns **403** and browsers fall back to a default icon (often the Vite lightning bolt). Build env on server (`apps/*/.env`): - All: `VITE_API_BASE_URL=https://api.meshkee.com/api/v1` - Super Admin: `VITE_ADMIN_DOMAIN=manage.meshkee.com` - Business/Customer: leave `VITE_BUSINESS_DOMAIN` unset ## Backend SSL sync (API server) Env on API (`/opt/meshkee/app/.env`): ``` SSL_SYNC_TOKEN= DASHBOARD_ADMIN_HOST=manage.meshkee.com ``` Endpoint: `GET /api/v1/internal/ssl/hosts` → `{ "hosts": ["manage.meshkee.com", "business.…", "customer.…"] }`