Files
dashboards/docs/DEPLOY.md
T
Alireza HassaniandCursor 0212148a73 Ensure per-row SSL for apex, business.*, and customer.* hosts.
Lock icon probes live TLS and issues only missing certs; toast z-index sits above modals; ssl-sync agent supports blocking wait.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-09 16:32:05 +03:30

3.4 KiB

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.<apex> Business dashboard
customer.<apex> 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)
  2. Add Arvan A records (DNS-only): business + customer → dashboards VPS IP
  3. In Super Admin → WebsitesSync 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-sync-agent.mjs, meshkee-ssl-sync-agent.service
  2. Make the script executable: chmod +x /opt/meshkee/dashboards/deploy/ssl-sync.sh
  3. Agent env /etc/meshkee/ssl-sync-agent.env:
SSL_SYNC_AGENT_TOKEN=<secret>
SSL_SYNC_SCRIPT=/opt/meshkee/dashboards/deploy/ssl-sync.sh
PORT=9051
BIND=0.0.0.0
  1. Install + start:
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'
  1. API .env:
SSL_SYNC_AGENT_URL=http://45.149.76.52:9051/ssl-sync
SSL_SYNC_AGENT_TOKEN=<same secret>

Endpoint used by the UI: POST /api/v1/domains/ssl-sync (super-admin JWT).

Body on the agent (optional): { "wait": true } — run ssl-sync.sh and wait for exit (used by per-row Ensure SSL). Default is fire-and-forget 202.

Per-row Ensure SSL: POST /api/v1/domains/:id/issue-ssl probes host, business.host, customer.host and issues only failures.

Redeploy frontends

From your laptop (rsync source tree, then build on server):

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=<secret>
DASHBOARD_ADMIN_HOST=manage.meshkee.com

Endpoint: GET /api/v1/internal/ssl/hosts{ "hosts": ["manage.meshkee.com", "business.…", "customer.…"] }