diff --git a/examples/crash_reporter/backend/README.md b/examples/crash_reporter/backend/README.md index 66f63fe..6e6dd5a 100644 --- a/examples/crash_reporter/backend/README.md +++ b/examples/crash_reporter/backend/README.md @@ -6,7 +6,20 @@ Anonymous crash JSON receiver and web console for ## Requirements - PHP 8.1+ with **PDO** (`pdo_sqlite` for default, `pdo_mysql` for MariaDB) -- `password_hash` / `session` (standard PHP build) +- Extensions: **session**, **json** (usually core), **pdo**, **pdo_sqlite** or **pdo_mysql** + +### Alpine Linux (php-fpm 8.1) + +Minimal `php81-fpm` does **not** include sessions or PDO. Install modules explicitly: + +```sh +apk add php81-fpm php81-session php81-pdo php81-pdo_sqlite php81-sqlite3 +rc-service php-fpm81 restart +# package names may be php82-* on newer Alpine; adjust to your php -v +php -m | grep -E 'session|pdo_sqlite|PDO' +``` + +If `session_name()` is undefined, `php81-session` is missing or FPM was not restarted after install. ## Quick start (SQLite, development) @@ -33,8 +46,10 @@ curl -X POST http://127.0.0.1:8080/api/upload.php \ ## Production (nginx + PHP-FPM) -1. Copy `backend/` to `/var/www/androidcast_crashes/` -2. Point nginx `root` to `.../public` (see `nginx.conf`) +0. Ensure PHP modules above are installed for the FPM SAPI (not only CLI `php -m`). +1. Copy or symlink `backend/` on the VM (e.g. `androidcast_project/backend` → `examples/crash_reporter/backend`). +2. Use **one** `location ^~ /app/androidcast_project/crashes/` with `alias .../backend/public/` (see `nginx.vm.conf`). Do not mix `proxy_*`, `try_files`, and hardcoded `upload.php` in the same block. +3. Set `base_path` in `config.php` to match the URL prefix exactly. 3. Choose DB: - **SQLite (default):** run `sql/schema.sqlite.sql`, set `DB_DRIVER=sqlite` in config - **MariaDB:** create DB, run `sql/schema.mariadb.sql`, set `DB_DRIVER=mysql` diff --git a/examples/crash_reporter/backend/nginx.vm.conf b/examples/crash_reporter/backend/nginx.vm.conf new file mode 100644 index 0000000..247027c --- /dev/null +++ b/examples/crash_reporter/backend/nginx.vm.conf @@ -0,0 +1,62 @@ +# Inner VM nginx — crash reporter behind apps.f0xx.org reverse proxy. +# +# Files: .../androidcast_project/backend/public/ (symlink to examples/crash_reporter/backend/public) +# URL: https://apps.f0xx.org/app/androidcast_project/crashes/ +# PHP: config.php → 'base_path' => '/app/androidcast_project/crashes' +# +# Outer proxy must pass the full URI and Host (your apps.f0xx.org block already does). + +server { + listen 443 ssl; + # server_name _; + + ssl_certificate "/etc/letsencrypt/archive/apps.f0xx.org/fullchain1.pem"; + ssl_certificate_key "/etc/letsencrypt/archive/apps.f0xx.org/privkey1.pem"; + + access_log /var/log/nginx/apps.intra.raptor.org.access_log main; + error_log /var/log/nginx/apps.intra.raptor.org.error_log warn; + + # Do NOT set root to androidcast_project/ — that doubles the /app/androidcast_project/ prefix on disk. + + # --- Crash reporter (only this block handles the app) --- + location ^~ /app/androidcast_project/crashes/ { + alias /var/www/localhost/htdocs/apps/app/androidcast_project/backend/public/; + index index.php; + try_files $uri $uri/ @crash_php; + + location ~ \.php$ { + include /etc/nginx/fastcgi.conf; + fastcgi_pass unix:/run/php-fpm.socket; + # $request_filename is correct when alias ends with / and location ends with / + fastcgi_param SCRIPT_FILENAME $request_filename; + client_max_body_size 4m; + } + } + + location @crash_php { + include /etc/nginx/fastcgi.conf; + fastcgi_pass unix:/run/php-fpm.socket; + fastcgi_param SCRIPT_FILENAME /var/www/localhost/htdocs/apps/app/androidcast_project/backend/public/index.php; + fastcgi_param REQUEST_URI $request_uri; + client_max_body_size 4m; + } + + # Optional: parent path → crashes (stops 400 probes on /app/androidcast_project/) + location = /app/androidcast_project { + return 301 /app/androidcast_project/crashes/; + } + location = /app/androidcast_project/ { + return 301 /app/androidcast_project/crashes/; + } + + # Other PHP on this host (outside crash app) + location ~ \.php$ { + root /var/www/localhost/htdocs/apps/app/androidcast_project; + include /etc/nginx/fastcgi.conf; + fastcgi_pass unix:/run/php-fpm.socket; + } + + location ~ /\. { + deny all; + } +} diff --git a/examples/crash_reporter/backend/src/bootstrap.php b/examples/crash_reporter/backend/src/bootstrap.php index 2f0f29a..7550135 100644 --- a/examples/crash_reporter/backend/src/bootstrap.php +++ b/examples/crash_reporter/backend/src/bootstrap.php @@ -12,6 +12,15 @@ */ declare(strict_types=1); +if (!function_exists('session_start')) { + http_response_code(500); + header('Content-Type: text/plain; charset=utf-8'); + echo "PHP session extension is not loaded.\n" + . "Alpine: apk add php81-session (or php82-session) && rc-service php-fpm81 restart\n" + . "Debian: apt install php-session\n"; + exit(1); +} + $configPath = __DIR__ . '/../config/config.php'; if (!is_file($configPath)) { $configPath = __DIR__ . '/../config/config.example.php';