Android Cast — crash reporter backend deployment (checklist) ================================================================ PUBLIC URL (example) https://apps.f0xx.org/app/androidcast_project/crashes/ Upload API: .../crashes/api/upload.php FILES ON VM Deploy backend so web root is: .../androidcast_project/backend/public/ Symlink is fine, e.g.: backend -> android_cast/examples/crash_reporter/backend/ config: backend/config/config.php (copy from config.example.php) DB (SQLite default): backend/data/crashes.sqlite Writable dirs: backend/data/ backend/storage/ ALPINE + PHP-FPM 8.1 (required modules) apk add php81-fpm php81-session php81-pdo php81-pdo_sqlite php81-sqlite3 rc-service php-fpm81 restart Verify FPM (not only CLI): php-fpm81 -m | grep -E 'session|pdo_sqlite|PDO' If "session_name() undefined" -> php81-session missing or FPM not restarted INIT DB (SQLite) mkdir -p backend/data backend/storage sqlite3 backend/data/crashes.sqlite < backend/sql/schema.sqlite.sql chown -R www-data:www-data backend/data backend/storage # user = php-fpm pool user config.php base_path MUST match URL prefix exactly: 'base_path' => '/app/androidcast_project/crashes' (empty base_path only for local: php -S -t public) INNER NGINX (VM behind reverse proxy) - ONE location for the app prefix only: location ^~ /app/androidcast_project/crashes/ { alias .../backend/public/; try_files + nested .php -> php-fpm socket } - See: examples/crash_reporter/backend/nginx.vm.conf - DO NOT mix proxy_*, try_files, and fastcgi in one broken /app/androidcast_project/ block - DO NOT set root = androidcast_project/ for crash URLs (doubles path on disk) - Optional redirects: /app/androidcast_project/ -> /app/androidcast_project/crashes/ - fastcgi_pass: unix:/run/php-fpm.socket (match your install) - client_max_body_size 4m (upload JSON) - Every adb/php-fpm call: use artc0 VM) - proxy_pass with full URI preserved - proxy_set_header Host $host; - proxy_set_header X-Forwarded-Proto $scheme; - TLS terminate at outer nginx ANDROID APP (device settings.json) "crash": { "enabled": "true", "upload_url": "https://apps.f0xx.org/app/androidcast_project/crashes/api/upload.php", "prune_after_upload": "true", "upload_interval": "5m" } Enable "Send anonymous crash logs" in app settings. SMOKE TESTS curl -vk -H 'Host: apps.f0xx.org' \ https://127.0.0.1/app/androidcast_project/crashes/ curl -vk -H 'Host: apps.f0xx.org' -H 'Content-Type: application/json' \ -d '{"schema_version":1,"report_id":"test","generated_at_epoch_ms":1,"crash_type":"java","fingerprint":"x","device":{},"app":{}}' \ https://127.0.0.1/app/androidcast_project/crashes/api/upload.php Web UI login (change in prod): admin / admin COMMON MISTAKES - 400 on GET /app/androidcast_project/ only -> health check wrong path; add redirect or ignore - 500 session_name() -> install php81-session for FPM - Location /crashes/ without /app/androidcast_project/ prefix -> no match behind full URI proxy - "Primary script unknown" in nginx error log: php-fpm got a SCRIPT_FILENAME that does not exist on disk usual cause: fastcgi.conf + alias + try_files (wrong path) fix: examples/crash_reporter/backend/nginx.vm.conf (explicit paths, fastcgi_params) verify: ls .../backend/public/index.php compare: php-fpm pool user can read that file - Pending reports deleted before pull: prune_after_upload uploads then deletes on device CURL WHERE 127.0.0.1 tests only ON the VM (loopback on that machine) From laptop use: curl -vk https://apps.f0xx.org/app/androidcast_project/crashes/ -H Host only needed when URL is 127.0.0.1 but vhost is apps.f0xx.org PULL REPORTS FROM DEVICE (laptop) ./pull_stats.sh (also pulls log_capture/crash_reports//) PROD HARDENING (when ready) - Change admin password (users table password_hash) - TLS only, restrict /api/upload.php by firewall if desired - MariaDB: use sql/schema.mariadb.sql + db.driver mysql in config.php - nginx/php-fpm error logs: check on 500, not only access log REF IN REPO examples/crash_reporter/backend/README.md examples/crash_reporter/backend/nginx.vm.conf docs/CRASH_REPORTER.md