The nginx config written by install.sh proxied waiter.*, manager.* and the IP default_server without proxy_http_version 1.1 or Upgrade/Connection headers, so /api/ws/connect never upgraded and live events (new orders, KDS status, chat, phone calls) never reached waiters or the manager. The repo's nginx-proxy/nginx.conf had the same gap on the manager block. Both configs now use a $connection_upgrade map, 1h read/send timeouts and proxy_buffering off (SSE) on every proxied location. Verified with nginx -t and a header-echo upstream: old config strips Upgrade, new one forwards it. Existing sites: copy the new install.sh, re-run it, restart the proxy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
213 lines
8.1 KiB
Bash
213 lines
8.1 KiB
Bash
#!/bin/bash
|
|
# Xenia POS — first-time install script
|
|
# Run this on the client machine before starting the stack.
|
|
# Usage: bash install.sh
|
|
|
|
set -e
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
|
|
echo "=== Xenia POS Install ==="
|
|
echo ""
|
|
|
|
# ── 1. Create required directories ───────────────────────────────────────────
|
|
echo "[ 1/5 ] Creating directories..."
|
|
mkdir -p "$SCRIPT_DIR/data"
|
|
mkdir -p "$SCRIPT_DIR/certs"
|
|
mkdir -p "$SCRIPT_DIR/nginx-proxy"
|
|
mkdir -p /opt/xenia/data
|
|
touch /opt/xenia/logo.png 2>/dev/null || true
|
|
|
|
# ── 2. Create .env from .env.example if missing ───────────────────────────────
|
|
echo "[ 2/5 ] Configuring environment..."
|
|
|
|
if [ ! -f "$SCRIPT_DIR/.env" ]; then
|
|
cp "$SCRIPT_DIR/.env.example" "$SCRIPT_DIR/.env"
|
|
echo ""
|
|
echo " A .env file has been created from .env.example."
|
|
echo " You must fill in SITE_ID, SITE_KEY, and SECRET_KEY before starting."
|
|
echo ""
|
|
echo " Get SITE_ID and SITE_KEY from: https://xenia-admin.bonamin.gr"
|
|
echo " Generate SECRET_KEY with: openssl rand -hex 32"
|
|
echo ""
|
|
|
|
read -rp " Enter SITE_ID: " INPUT_SITE_ID
|
|
read -rp " Enter SITE_KEY: " INPUT_SITE_KEY
|
|
read -rp " Enter SECRET_KEY (leave blank to auto-generate): " INPUT_SECRET_KEY
|
|
|
|
if [ -z "$INPUT_SECRET_KEY" ]; then
|
|
INPUT_SECRET_KEY=$(openssl rand -hex 32)
|
|
echo " Generated SECRET_KEY: $INPUT_SECRET_KEY"
|
|
fi
|
|
|
|
sed -i "s/^SITE_ID=.*/SITE_ID=${INPUT_SITE_ID}/" "$SCRIPT_DIR/.env"
|
|
sed -i "s/^SITE_KEY=.*/SITE_KEY=${INPUT_SITE_KEY}/" "$SCRIPT_DIR/.env"
|
|
sed -i "s/^SECRET_KEY=.*/SECRET_KEY=${INPUT_SECRET_KEY}/" "$SCRIPT_DIR/.env"
|
|
|
|
echo ""
|
|
echo " .env written. Review it at: $SCRIPT_DIR/.env"
|
|
echo ""
|
|
else
|
|
echo " .env already exists — skipping."
|
|
fi
|
|
|
|
# ── 3. Write nginx-proxy/nginx.conf ──────────────────────────────────────────
|
|
echo "[ 3/5 ] Writing nginx proxy config..."
|
|
cat > "$SCRIPT_DIR/nginx-proxy/nginx.conf" << 'EOF'
|
|
# Generated by install.sh — keep in sync with nginx-proxy/nginx.conf in the repo.
|
|
# Every proxied location must forward WebSocket upgrades (/api/ws/connect) and
|
|
# must not buffer (SSE), otherwise live events never reach waiters / KDS.
|
|
map $http_upgrade $connection_upgrade {
|
|
default upgrade;
|
|
'' close;
|
|
}
|
|
|
|
server {
|
|
listen 80;
|
|
return 301 https://$host$request_uri;
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl;
|
|
server_name waiter.*;
|
|
|
|
ssl_certificate /etc/nginx/certs/cert.pem;
|
|
ssl_certificate_key /etc/nginx/certs/key.pem;
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
|
|
|
location / {
|
|
proxy_pass http://waiter_pwa:80;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection $connection_upgrade;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_read_timeout 3600s;
|
|
proxy_send_timeout 3600s;
|
|
proxy_buffering off;
|
|
}
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl;
|
|
server_name manager.*;
|
|
|
|
ssl_certificate /etc/nginx/certs/cert.pem;
|
|
ssl_certificate_key /etc/nginx/certs/key.pem;
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
|
|
|
location / {
|
|
proxy_pass http://manager_dashboard:80;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection $connection_upgrade;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_read_timeout 3600s;
|
|
proxy_send_timeout 3600s;
|
|
proxy_buffering off;
|
|
}
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl default_server;
|
|
|
|
ssl_certificate /etc/nginx/certs/cert.pem;
|
|
ssl_certificate_key /etc/nginx/certs/key.pem;
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
|
|
|
location /api/ {
|
|
proxy_pass http://backend:8000;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection $connection_upgrade;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_read_timeout 3600s;
|
|
proxy_send_timeout 3600s;
|
|
proxy_buffering off;
|
|
}
|
|
|
|
location / {
|
|
proxy_pass http://waiter_pwa:80;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection $connection_upgrade;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_read_timeout 3600s;
|
|
proxy_send_timeout 3600s;
|
|
proxy_buffering off;
|
|
}
|
|
}
|
|
EOF
|
|
|
|
# ── 4. SSL certificates ───────────────────────────────────────────────────────
|
|
echo "[ 4/5 ] Checking SSL certificates..."
|
|
|
|
if [ -f "$SCRIPT_DIR/certs/cert.pem" ] && [ -f "$SCRIPT_DIR/certs/key.pem" ]; then
|
|
echo " Certificates already exist — skipping."
|
|
else
|
|
echo ""
|
|
echo " No certificates found in certs/"
|
|
echo ""
|
|
echo " DNS requirement:"
|
|
echo " Two subdomains must point to this machine's IP:"
|
|
echo " waiter.YOURDOMAIN → this machine's IP"
|
|
echo " manager.YOURDOMAIN → this machine's IP"
|
|
echo " The waiter domain should also be registered in the sysadmin"
|
|
echo " panel as the 'Waiter Domain' so phones get the QR code."
|
|
echo ""
|
|
echo " Option A — Let's Encrypt (recommended):"
|
|
echo " sudo apt install certbot"
|
|
echo " sudo certbot certonly --manual --preferred-challenges dns \\"
|
|
echo " -d waiter.YOURDOMAIN -d manager.YOURDOMAIN"
|
|
echo " sudo cp /etc/letsencrypt/live/waiter.YOURDOMAIN/fullchain.pem certs/cert.pem"
|
|
echo " sudo cp /etc/letsencrypt/live/waiter.YOURDOMAIN/privkey.pem certs/key.pem"
|
|
echo ""
|
|
echo " Option B — Self-signed / mkcert (local testing only):"
|
|
echo " sudo apt install mkcert libnss3-tools"
|
|
echo " mkcert -install"
|
|
echo " mkcert -cert-file certs/cert.pem -key-file certs/key.pem \\"
|
|
echo " waiter.YOURDOMAIN manager.YOURDOMAIN"
|
|
echo ""
|
|
echo " Add certs then run: docker compose up -d"
|
|
echo ""
|
|
fi
|
|
|
|
# ── 5. Logo ───────────────────────────────────────────────────────────────────
|
|
echo "[ 5/5 ] Checking logo..."
|
|
if [ ! -s "$SCRIPT_DIR/logo.png" ]; then
|
|
echo " WARNING: logo.png not found or is empty."
|
|
echo " Place your restaurant logo at: $SCRIPT_DIR/logo.png"
|
|
touch "$SCRIPT_DIR/logo.png"
|
|
fi
|
|
|
|
# ── Done ─────────────────────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "=== Setup complete ==="
|
|
echo ""
|
|
|
|
if [ -f "$SCRIPT_DIR/certs/cert.pem" ] && [ -f "$SCRIPT_DIR/certs/key.pem" ]; then
|
|
echo "Starting stack..."
|
|
docker compose -f "$SCRIPT_DIR/docker-compose.yml" up -d
|
|
echo ""
|
|
echo "Done! Services running."
|
|
echo " Waiter app: https://waiter.YOURDOMAIN"
|
|
echo " Manager app: https://manager.YOURDOMAIN"
|
|
else
|
|
echo "Add SSL certificates to certs/ then run:"
|
|
echo " docker compose up -d"
|
|
fi
|