Adds a PHP/SQLite history server in server/ and refactors the plugin to write every session change through it. Healthcheck history now survives plugin uninstall and groups across dev + live URLs for the same engagement via an editable site_key (defaults to the normalised host). Server (server/): - Front controller + hand-rolled autoloader, no framework, no composer - SQLite default DSN; swap to MySQL by changing config.php - Schema: healthchecks (PK id, UNIQUE (site_key, started_at)) + step_updates (PK (healthcheck_id, step_id)) + request_log; auto-migration runner - 8 endpoints: POST/GET/PUT healthchecks, PUT/GET step rows, GET step history with exclude_id, GET /sites (recent), GET /step-counts (badge data) - Bearer auth via hash_equals; HTTPS expected (plugin enforces client-side) - DEPLOY.md with Apache/nginx vhosts, Let's Encrypt, SQLite backup cron, and the /home/www/ perm gotcha - dev-router.php works around PHP -S 405-ing dotted uniqid paths Plugin: - ATT_HC_Api HTTP client reads ATT_HC_API_URL/ATT_HC_API_KEY constants from wp-config.php; refuses non-HTTPS with a loopback dev exception - ATT_HC_Session is now write-through: every start/update_step/finish/ set_autocheck POSTs or PUTs to the server first, then updates the local WP option cache. No drift possible — failures throw ATT_HC_Api_Exception - previous() now reads from /healthchecks?include=steps and reconstructs; the old att_hc_previous_session local option is gone - ATT_HC_Session::resume(id) hydrates a server session into the local cache - Start screen: editable site_key (defaults to normalise_site_url()), datalist of recent engagements, table of in-progress sessions for the chosen key with Resume buttons. Double-click guard on start + resume handlers short-circuits if a session is already active - Per-step <details> disclosure shows "Previous notes (N)" badge from /step-counts; lazy-loads detail rows on first expand via admin-ajax, caches via data-loaded, resets on error so user can retry - All admin handlers catch ATT_HC_Api_Exception and surface via att_hc_api_error transient → admin notice - Hard config-error gate at the top of the admin page blocks the UI when ATT_HC_API_URL/ATT_HC_API_KEY are missing or malformed Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
150 lines
4.4 KiB
Markdown
150 lines
4.4 KiB
Markdown
# Deploying the ATT Healthcheck Server
|
|
|
|
Single-tenant PHP/SQLite app. Tiny — runs comfortably on the smallest VPS tier.
|
|
|
|
## Requirements
|
|
|
|
- PHP **8.1+** with `pdo`, `pdo_sqlite`, `json`, `mbstring` (all standard).
|
|
- Apache + `mod_rewrite`, or nginx with a `try_files` fallback.
|
|
- HTTPS terminating in front of PHP. Plain HTTP is technically accepted, but the plugin client will refuse to talk to non-`https://` endpoints.
|
|
|
|
## Layout on the server
|
|
|
|
Anywhere works — every path inside the app is resolved relative to its own files.
|
|
Example using `/home/www/healthcheck`:
|
|
|
|
```
|
|
/home/www/healthcheck/
|
|
├── public/ ← docroot
|
|
│ ├── .htaccess
|
|
│ └── index.php
|
|
├── src/
|
|
├── migrations/
|
|
├── data/ ← SQLite file lives here (writable by web user)
|
|
├── config.php ← deployment-specific, NOT in git
|
|
└── config.php.example
|
|
```
|
|
|
|
> **Perm gotcha for `/home/...` locations:** home directories often default to
|
|
> `750` or `700`, which blocks the web user from traversing into the app.
|
|
> `sudo chmod 755 /home/www` once is usually enough. `/var/www` doesn't have
|
|
> this problem (always world-readable by default).
|
|
|
|
## First-time install
|
|
|
|
```sh
|
|
# As you, on the server:
|
|
sudo mkdir -p /home/www/healthcheck
|
|
sudo chown $USER:www-data /home/www/healthcheck
|
|
sudo chmod 755 /home/www # see perm gotcha above
|
|
|
|
# From your dev box:
|
|
rsync -avz --exclude='data/' --exclude='config.php' \
|
|
~/dev/att-site-healthcheck/server/ \
|
|
user@vps:/home/www/healthcheck/
|
|
|
|
# Back on the server:
|
|
cd /home/www/healthcheck
|
|
cp config.php.example config.php
|
|
php -r 'echo bin2hex(random_bytes(32)), PHP_EOL;' # generate api_key
|
|
$EDITOR config.php # paste it in
|
|
|
|
mkdir -p data
|
|
sudo chown -R www-data:www-data data/ config.php
|
|
sudo chmod 640 config.php
|
|
sudo chmod 770 data/
|
|
```
|
|
|
|
## Apache vhost (example)
|
|
|
|
```apache
|
|
<VirtualHost *:443>
|
|
ServerName healthcheck-history.example.com
|
|
DocumentRoot /home/www/healthcheck/public
|
|
|
|
<Directory /home/www/healthcheck/public>
|
|
AllowOverride All
|
|
Require all granted
|
|
</Directory>
|
|
|
|
# Pass Authorization header through to PHP — Apache strips it by default with FPM.
|
|
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
|
|
|
|
SSLEngine on
|
|
SSLCertificateFile /etc/letsencrypt/live/healthcheck-history.example.com/fullchain.pem
|
|
SSLCertificateKeyFile /etc/letsencrypt/live/healthcheck-history.example.com/privkey.pem
|
|
</VirtualHost>
|
|
```
|
|
|
|
## nginx (example)
|
|
|
|
```nginx
|
|
server {
|
|
listen 443 ssl http2;
|
|
server_name healthcheck-history.example.com;
|
|
root /home/www/healthcheck/public;
|
|
index index.php;
|
|
|
|
location / {
|
|
try_files $uri /index.php?$query_string;
|
|
}
|
|
|
|
location ~ \.php$ {
|
|
include fastcgi_params;
|
|
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
|
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
|
fastcgi_param HTTP_AUTHORIZATION $http_authorization;
|
|
}
|
|
|
|
ssl_certificate /etc/letsencrypt/live/healthcheck-history.example.com/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/healthcheck-history.example.com/privkey.pem;
|
|
}
|
|
```
|
|
|
|
## TLS
|
|
|
|
Use Let's Encrypt + certbot. Example:
|
|
|
|
```sh
|
|
sudo certbot --apache -d healthcheck-history.example.com
|
|
# or for nginx:
|
|
sudo certbot --nginx -d healthcheck-history.example.com
|
|
```
|
|
|
|
## Configure the plugin to talk to it
|
|
|
|
In `wp-config.php` on each WordPress install:
|
|
|
|
```php
|
|
define('ATT_HC_API_URL', 'https://healthcheck-history.example.com');
|
|
define('ATT_HC_API_KEY', '<the long random string from config.php>');
|
|
```
|
|
|
|
## Backups
|
|
|
|
The SQLite DB is a single file. Snapshot it on a cron:
|
|
|
|
```cron
|
|
# Daily at 03:15, rotate 14 days. SQLite-safe online backup.
|
|
15 3 * * * /usr/bin/sqlite3 /home/www/healthcheck/data/att_hc.sqlite ".backup '/var/backups/att-hc/att_hc-$(date +\%F).sqlite'" && find /var/backups/att-hc -name 'att_hc-*.sqlite' -mtime +14 -delete
|
|
```
|
|
|
|
Pull those backups offsite with `rsync` / `rclone` to whatever you already use.
|
|
|
|
## Updating
|
|
|
|
Just rsync the source again — migrations run on first request after deploy.
|
|
|
|
```sh
|
|
rsync -avz --delete --exclude='data/' --exclude='config.php' \
|
|
~/dev/att-site-healthcheck/server/ \
|
|
user@vps:/home/www/healthcheck/
|
|
```
|
|
|
|
## Quick health check
|
|
|
|
```sh
|
|
curl https://healthcheck-history.example.com/
|
|
# → {"ok":true,"service":"att-site-healthcheck-server","version":"..."}
|
|
```
|