Compare commits
6 Commits
v2.0.7
...
4a548f50e7
| Author | SHA1 | Date | |
|---|---|---|---|
| 4a548f50e7 | |||
| faea2b0899 | |||
| 427fa24565 | |||
| 086e7c7a77 | |||
| 181bed4449 | |||
| 6e0795bbdf |
54
CLAUDE.md
Normal file
54
CLAUDE.md
Normal file
@@ -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=<target>
|
||||
```
|
||||
|
||||
## 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.
|
||||
@@ -1,4 +1,3 @@
|
||||
version: '3.3'
|
||||
services:
|
||||
http2pic:
|
||||
build:
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
version: '3.3'
|
||||
services:
|
||||
http2pic:
|
||||
image: gitea.haschek.at/haschek-solutions/http2pic:2
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
- ./cache:/srv/cache
|
||||
- ./logs:/srv/logs
|
||||
environment:
|
||||
- URL=http://localhost:8080
|
||||
ports:
|
||||
|
||||
@@ -20,6 +20,6 @@ _buildConfig() {
|
||||
echo ""
|
||||
}
|
||||
|
||||
_buildConfig > src/inc/config.inc.php
|
||||
_buildConfig > /srv/src/config.inc.php
|
||||
|
||||
caddy run --config /etc/caddy/Caddyfile
|
||||
1
src/.gitignore
vendored
1
src/.gitignore
vendored
@@ -1 +1,2 @@
|
||||
vendor/
|
||||
config.inc.php
|
||||
3
src/config.inc.php.php
Executable file
3
src/config.inc.php.php
Executable file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
define('URL','http://localhost:8080'); //no trailing slash
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -30,7 +30,12 @@ switch ($url[0]) {
|
||||
}
|
||||
$ip = getUserIP();
|
||||
|
||||
$viewport = $_REQUEST['viewport'];
|
||||
$viewport = $_REQUEST['viewport'] ?: '1024x768';
|
||||
if (!preg_match('/^\d+x\d+$/', $viewport)) {
|
||||
header('HTTP/1.0 400 Bad Request');
|
||||
echo 'Invalid viewport format. Use WIDTHxHEIGHT (e.g., 1024x768)';
|
||||
exit;
|
||||
}
|
||||
$js = $_REQUEST['js'] == 'false' ? false : true;
|
||||
|
||||
$serverUrl = 'http://localhost:4444';
|
||||
@@ -45,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";');
|
||||
@@ -58,21 +64,30 @@ switch ($url[0]) {
|
||||
$viewport = explode('x', $viewport);
|
||||
$driver->manage()->window()->setSize(new \Facebook\WebDriver\WebDriverDimension($viewport[0], $viewport[1]));
|
||||
} else {
|
||||
$driver->manage()->window()->setSize(new \Facebook\WebDriver\WebDriverDimension(1024, 768));
|
||||
$viewport = [1024, 768];
|
||||
$driver->manage()->window()->setSize(new \Facebook\WebDriver\WebDriverDimension($viewport[0], $viewport[1]));
|
||||
}
|
||||
addToLog("$ip\tRequested $target with viewport " . implode('x', $viewport) . " and js " . ($js ? 'enabled' : 'disabled'));
|
||||
$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');
|
||||
|
||||
Reference in New Issue
Block a user