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>
4.4 KiB
4.4 KiB
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 atry_filesfallback. - 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 to750or700, which blocks the web user from traversing into the app.sudo chmod 755 /home/wwwonce is usually enough./var/wwwdoesn't have this problem (always world-readable by default).
First-time install
# 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)
<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)
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:
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:
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:
# 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.
rsync -avz --delete --exclude='data/' --exclude='config.php' \
~/dev/att-site-healthcheck/server/ \
user@vps:/home/www/healthcheck/
Quick health check
curl https://healthcheck-history.example.com/
# → {"ok":true,"service":"att-site-healthcheck-server","version":"..."}