# Debian 12 App Server Setup Panduan ini untuk menyiapkan server kosong Debian 12 sebagai app server QRIS Soundbox Platform. - App domain: `sms.bizone.id` - App user: `qrisapp` - App directory: `/opt/qris-soundbox` - Env file: `/etc/qris-soundbox/qris-soundbox.env` - Local app port: `3000` - MQTT broker: `broker.bizone.id` Broker MQTT boleh berada di server lain. Dokumen ini fokus ke server aplikasi. ## 1. DNS Pastikan `sms.bizone.id` sudah mengarah ke public IP server app. ```bash dig +short sms.bizone.id curl -4 ifconfig.me ``` ## 2. Base Packages Jalankan sebagai root atau user sudo. ```bash sudo apt update sudo apt upgrade -y sudo apt install -y \ ca-certificates \ curl \ gnupg \ git \ build-essential \ nginx \ certbot \ python3-certbot-nginx \ postgresql \ postgresql-contrib \ ufw ``` ## 3. Node.js Gunakan Node.js 22 untuk runtime production. ```bash curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - sudo apt install -y nodejs node -v npm -v ``` ## 4. Firewall ```bash sudo ufw allow OpenSSH sudo ufw allow 'Nginx Full' sudo ufw enable sudo ufw status ``` Jangan buka port `3000` ke internet. Traffic publik masuk lewat Nginx. ## 5. App User Buat user khusus untuk menjalankan service. ```bash sudo adduser --system --group --home /opt/qris-soundbox qrisapp sudo install -d -o qrisapp -g qrisapp -m 750 /opt/qris-soundbox sudo install -d -o root -g qrisapp -m 750 /etc/qris-soundbox sudo install -d -o qrisapp -g qrisapp -m 750 /var/lib/qris-soundbox/exports ``` Untuk deploy via git/rsync, operator boleh masuk dengan user sudo biasa, lalu copy release ke `/opt/qris-soundbox` dan set ownership ke `qrisapp:qrisapp`. ## 6. PostgreSQL Buat database dan user production. ```bash sudo -u postgres psql ``` Di prompt `psql`: ```sql CREATE USER qris_app WITH PASSWORD '5174e2c2fb3f8424806d1e5a4ca873a3dd33aace06a7b99c49f1bed9d1f42c4a'; CREATE DATABASE qris_soundbox_platform OWNER qris_app; GRANT ALL PRIVILEGES ON DATABASE qris_soundbox_platform TO qris_app; \q ``` ## 7. Deploy Code Contoh deploy awal via git: ```bash sudo -u qrisapp git clone https://git.iptek.co/wirabasalamah/Qris-Soundbox.git /opt/qris-soundbox sudo -u qrisapp bash -lc 'cd /opt/qris-soundbox && npm ci' sudo -u qrisapp bash -lc 'cd /opt/qris-soundbox && npm run typecheck' sudo -u qrisapp bash -lc 'cd /opt/qris-soundbox && npm run build' ``` Jika deploy dari artifact, extract/copy artifact ke `/opt/qris-soundbox`, lalu: ```bash sudo chown -R qrisapp:qrisapp /opt/qris-soundbox sudo -u qrisapp bash -lc 'cd /opt/qris-soundbox && npm ci' sudo -u qrisapp bash -lc 'cd /opt/qris-soundbox && npm run build' ``` Catatan: command `npm run typecheck`, `npm run db:migrate`, dan sebagian smoke script membutuhkan dev dependency. Untuk server staging/pilot awal, gunakan `npm ci` penuh agar semua command operasional tersedia. Service production tetap menjalankan hasil build lewat `npm run start:dist`. ## 8. Environment Buat env production: ```bash sudo nano /etc/qris-soundbox/qris-soundbox.env ``` Template awal: ```env NODE_ENV=production PORT=3000 TRUST_PROXY=true JSON_BODY_LIMIT=1mb LOG_FORMAT=json LOG_LEVEL=info ADMIN_AUTH_ALLOW_LEGACY_TOKEN=false ADMIN_DEV_LOGIN_ENABLED=false ADMIN_SESSION_SECRET=CHANGE_ME_LONG_RANDOM_ADMIN_SESSION_SECRET ADMIN_SESSION_TTL_SECONDS=28800 MERCHANT_AUTH_ALLOW_LEGACY_TOKEN=false MERCHANT_DEV_LOGIN_ENABLED=false MERCHANT_SESSION_SECRET=CHANGE_ME_LONG_RANDOM_MERCHANT_SESSION_SECRET MERCHANT_SESSION_TTL_SECONDS=28800 DEVICE_AUTH_ALLOW_LEGACY_TOKEN=false TRACE_HEADER=x-request-id IDEMPOTENCY_TTL_MS=300000 INTEGRATION_WEBHOOK_SECRET=CHANGE_ME_LONG_RANDOM_WEBHOOK_SECRET MQTT_PUBLISH_MODE=broker MQTT_BROKER_URL=mqtts://broker.bizone.id:8883 MQTT_USERNAME=qris-backend MQTT_PASSWORD=CHANGE_ME_MQTT_BACKEND_PASSWORD MQTT_CLIENT_ID=qris-platform-backend-prod MQTT_CONNECT_TIMEOUT_MS=5000 MQTT_SUBSCRIBE_ENABLED=true MQTT_SUBSCRIBE_TOPICS=devices/+/uplink/# MQTT_PUBLISH_FORCE_FAIL_ALL=false MQTT_PUBLISH_FORCE_FAIL_DEVICE_IDS= MQTT_PUBLISH_DEFAULT_RETRY_INTERVAL_MS=15000 QF100_MQTT_BROKER_HOST=broker.bizone.id QF100_MQTT_BROKER_PORT=8883 QF100_MQTT_USERNAME=qris-backend QF100_MQTT_PASSWORD=CHANGE_ME_MQTT_BACKEND_PASSWORD QF100_MQTT_KEEP_ALIVE_SECONDS=60 DYNAMIC_QR_EXPIRY_SCHEDULER_ENABLED=true DYNAMIC_QR_EXPIRY_SWEEP_INTERVAL_MS=60000 DYNAMIC_QR_EXPIRY_SWEEP_LIMIT=100 EXPORT_WORKER_ENABLED=true EXPORT_WORKER_INTERVAL_MS=2000 EXPORT_WORKER_BATCH_SIZE=2 EXPORT_JOB_STALE_RUNNING_MS=900000 EXPORT_SETTLEMENT_ADJUSTMENT_MAX_ROWS=5000 EXPORT_STORAGE_DIR=/var/lib/qris-soundbox/exports EXPORT_RETENTION_DAYS=30 RATE_LIMIT_ENABLED=true RATE_LIMIT_LOGIN_WINDOW_MS=60000 RATE_LIMIT_LOGIN_MAX=20 RATE_LIMIT_DEVICE_WINDOW_MS=60000 RATE_LIMIT_DEVICE_MAX=600 RATE_LIMIT_ADMIN_WRITE_WINDOW_MS=60000 RATE_LIMIT_ADMIN_WRITE_MAX=300 FINANCE_PLATFORM_FEE_BPS=70 SETTLEMENT_ADJUSTMENT_REQUIRE_APPROVAL=true PGHOST=127.0.0.1 PGPORT=5432 PGUSER=qris_app PGPASSWORD=CHANGE_ME_STRONG_DB_PASSWORD PGDATABASE=qris_soundbox_platform ``` Lock permission: ```bash sudo chown root:qrisapp /etc/qris-soundbox/qris-soundbox.env sudo chmod 640 /etc/qris-soundbox/qris-soundbox.env ``` ## 9. Database Migration ```bash cd /opt/qris-soundbox sudo -u qrisapp env $(sudo cat /etc/qris-soundbox/qris-soundbox.env | xargs) npm run db:migrate ``` Jika env berisi karakter spesial yang membuat `xargs` bermasalah, gunakan systemd service sementara atau jalankan lewat shell yang melakukan `set -a; source ...; set +a`. ```bash cd /opt/qris-soundbox sudo -u qrisapp bash -lc 'set -a; source /etc/qris-soundbox/qris-soundbox.env; set +a; npm run db:migrate' ``` ## 10. Systemd Service Buat service: ```bash sudo nano /etc/systemd/system/qris-soundbox.service ``` Isi: ```ini [Unit] Description=QRIS Soundbox Platform After=network-online.target postgresql.service Wants=network-online.target [Service] Type=simple User=qrisapp Group=qrisapp WorkingDirectory=/opt/qris-soundbox EnvironmentFile=/etc/qris-soundbox/qris-soundbox.env ExecStart=/usr/bin/npm run start:dist Restart=always RestartSec=5 NoNewPrivileges=true PrivateTmp=true ProtectSystem=full ReadWritePaths=/opt/qris-soundbox /var/lib/qris-soundbox [Install] WantedBy=multi-user.target ``` Start service: ```bash sudo systemctl daemon-reload sudo systemctl enable qris-soundbox sudo systemctl start qris-soundbox sudo systemctl status qris-soundbox --no-pager journalctl -u qris-soundbox -f ``` ## 11. Nginx Buat config: ```bash sudo nano /etc/nginx/sites-available/sms.bizone.id ``` Isi: ```nginx server { listen 80; listen [::]:80; server_name sms.bizone.id; client_max_body_size 1m; location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; 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; } } ``` Enable: ```bash sudo ln -s /etc/nginx/sites-available/sms.bizone.id /etc/nginx/sites-enabled/sms.bizone.id sudo nginx -t sudo systemctl reload nginx ``` ## 12. TLS Certificate ```bash sudo certbot --nginx -d sms.bizone.id sudo certbot renew --dry-run ``` ## 13. Create Production Users ```bash cd /opt/qris-soundbox sudo -u qrisapp bash -lc 'set -a; source /etc/qris-soundbox/qris-soundbox.env; set +a; npm run admin:create-user -- --email --name --role admin --password ' sudo -u qrisapp bash -lc 'set -a; source /etc/qris-soundbox/qris-soundbox.env; set +a; npm run merchant:create-user -- --merchant --email --name --role owner --password ' ``` ## 14. Verification ```bash curl -fsS https://sms.bizone.id/health curl -fsS https://sms.bizone.id/health/deep curl -I https://sms.bizone.id/ui ``` Run production preflight from server: ```bash cd /opt/qris-soundbox sudo -u qrisapp bash -lc 'set -a; source /etc/qris-soundbox/qris-soundbox.env; set +a; npm run deploy:check-env' sudo -u qrisapp bash -lc 'set -a; source /etc/qris-soundbox/qris-soundbox.env; set +a; npm run smoke:mqtt-real' ``` Run full smoke only on staging/controlled environment because it creates and cleans test data: ```bash sudo -u qrisapp bash -lc 'set -a; source /etc/qris-soundbox/qris-soundbox.env; set +a; npm run smoke:e2e' sudo -u qrisapp bash -lc 'set -a; source /etc/qris-soundbox/qris-soundbox.env; set +a; npm run ui:qa' ``` ## 15. QF100 Notes For QF100 devices, app config server URL should point to: ```text https://sms.bizone.id/speaker/dev-config ``` The config response will point the device to MQTT broker: ```text broker.bizone.id:8883 ``` If the firmware still has MQTT TLS disabled, either patch firmware TLS or prepare a restricted non-TLS pilot listener. Do not expose unrestricted non-TLS MQTT to the internet. ## 16. Routine Ops Useful commands: ```bash sudo systemctl status qris-soundbox --no-pager journalctl -u qris-soundbox -n 200 --no-pager sudo nginx -t sudo systemctl reload nginx ``` Backup: ```bash cd /opt/qris-soundbox sudo -u qrisapp bash -lc 'set -a; source /etc/qris-soundbox/qris-soundbox.env; set +a; npm run backup:production -- --out /var/backups/qris --include-mosquitto' ```