diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..e0c8eff --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,54 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project + +http2pic - PHP website renderer that takes screenshots of URLs and returns them as images. Live at https://http2pic.haschek.at/ + +## Architecture + +**Entry point:** `web/index.php` - PSR-4 router with two paths: +- `/api` - Chrome/Chromium screenshot via Selenium WebDriver (php-webdriver). Takes `url`, `viewport` (WIDTHxHEIGHT), `js` (true/false) params. Connects to `localhost:4444` ChromeDriver, sets window size, disables scrollbars, takes screenshot as PNG. +- default - renders `src/templates/index.html.php` (landing page). + +**Legacy class:** `src/http2pic.class.php` - Old `wkhtmltoimage`-based renderer (deprecated, not used in production). Supports PNG/JPG, viewport, resize, URL reachability check, file caching. + +**Helpers:** `src/helpers.php` - `renderTemplate()`, `addToLog()`, `getUserIP()`. + +**Config:** `src/config.inc.php` - set at build time by `start.sh` from `URL` env var. + +## Running + +**Docker (production):** +``` +docker compose up -d +``` +Services: Caddy (:80), PHP-FPM, ChromeDriver (:4444). Volumes: `./cache` and `./logs`. Configured via `URL` env var. + +**Dev container:** `.devcontainer/` - same stack, run `./devcontainer/start.sh`. Forward port 8080. + +**Quick test:** +``` +php -S localhost:8080 -t web/ +# Then visit http://localhost:8080/api?url= +``` + +## Key files + +| File | Purpose | +|------|---------| +| `web/index.php` | API + template router | +| `src/http2pic.class.php` | Legacy wkhtmltoimage renderer | +| `src/helpers.php` | Template render, logging, IP helper | +| `src/config.inc.php` | Runtime config (URL) | +| `docker/Caddyfile` | Reverse proxy, PHP-FPM, file server | +| `docker/start.sh` | Boots PHP-FPM, ChromeDriver, writes config | +| `docker-compose.yml` | Production compose | + +## Caveats + +- `web/index.php` has a `var_dump($cmd)` debug statement left in `http2pic.class.php:181` - remove before shipping. +- Legacy `http2pic.class.php` has a variable scoping bug: line 109 uses `$url` instead of `$this->params['url']`. +- Cache dir permissions must be `777` (set by `start.sh`). +- ChromeDriver must be running on `localhost:4444` for the API to work. diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index e4b4eca..d6071a2 100755 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -1,4 +1,3 @@ -version: '3.3' services: http2pic: build: @@ -13,6 +12,6 @@ services: - ./logs:/srv/logs environment: - - URL=http://localhostxxx:8080 + - URL=http://localhost:8080 ports: - 8080:80 diff --git a/src/http2pic.class.php b/src/http2pic.class.php index 130ea58..c9d357a 100755 --- a/src/http2pic.class.php +++ b/src/http2pic.class.php @@ -178,11 +178,14 @@ class http2pic $cmd.=' --wait-for-network-idle'; - var_dump($cmd); - $cmd = escapeshellcmd($cmd); - shell_exec($cmd); + $output = []; + $rc = 0; + exec($cmd . ' 2>&1', $output, $rc); $this->params['cmd'] = $cmd; + if ($rc !== 0) { + $this->params['render_error'] = implode("\n", $output); + } $this->postRender(); diff --git a/web/index.php b/web/index.php index ccb5ea1..2f81bbe 100755 --- a/web/index.php +++ b/web/index.php @@ -50,8 +50,9 @@ switch ($url[0]) { $capabilities->setCapability('javascriptEnabled', false); + $driver = null; try { - $driver = RemoteWebDriver::create($serverUrl, $capabilities); + $driver = RemoteWebDriver::create($serverUrl, $capabilities, 30000, 60); $driver->get($target); //hide scroll bars $driver->executeScript('document.body.style.overflow = "hidden";'); @@ -68,18 +69,25 @@ switch ($url[0]) { } $viewportLabel = is_array($viewport) ? implode('x', $viewport) : (string) $viewport; addToLog("$ip\tRequested $target with viewport " . $viewportLabel . " and js " . ($js ? 'enabled' : 'disabled')); + + // take screenshot and send to user + header('Content-Type: image/png'); + echo $driver->takeScreenshot(); } catch (Exception $e) { + // ensure driver is closed to free ChromeDriver memory + if ($driver instanceof \Facebook\WebDriver\Remote\RemoteWebDriver) { + try { $driver->quit(); } catch (Exception $qe) {} + } header('HTTP/1.0 500 Internal Server Error'); addToLog("$ip\tRequested $target but resulted in error:\t" . $e->getMessage()); echo 'Error: ' . $e->getMessage(); exit; + } finally { + if ($driver instanceof \Facebook\WebDriver\Remote\RemoteWebDriver) { + try { $driver->quit(); } catch (Exception $q) {} + } } - // take screenshot and save to file - //header for png - header('Content-Type: image/png'); - echo $driver->takeScreenshot(); - break; default: echo renderTemplate('index.html.php');