Discord: payload_json was missing the attachments array, so uploaded files were orphaned and not shown in the message (Discord API v10 requirement). Mastodon: media_ids[] sent as URL-encoded form was unreliable — switch to JSON body. Also poll /api/v1/media/:id when upload returns 202 (async) to wait until the image is processed before attaching it. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| bin | ||
| config | ||
| data | ||
| lib | ||
| sql | ||
| views | ||
| web | ||
| .gitignore | ||
| CLAUDE.md | ||
| deploylocal.sh | ||
| README.md | ||
| VERSION | ||
HackmanCMS
Lightweight web UI for managing Hexo blogs and other server-side projects.
Features
- Dashboard with project cards, disk-usage badges, recent-activity feed
- Plugin-based project types — drop a PHP file into
lib/project-types/to add a new type - Per-project sidebar with grouped, collapsible navigation; versioning shown in the footer
- Posts editor (Hexo) — Milkdown WYSIWYG with MD-source toggle, in-canvas photos banner from front-matter
photos:, image rendering for relativeimages/...paths, image-paste upload tosource/images/, floating Save (only when dirty), Ctrl/Cmd-S, tab dirty dot, image rewriting kept out of the markdown source so files round-trip cleanly through Hexo - Drafts editor — same Milkdown body editor as posts, with title/slug/folder as inline fields
- Files editor — CodeMirror per file; full-pane height; Edit + Delete from a kebab on each list row; floating Save when dirty
- File browser path-traversal safe; deep-linkable via
?tab=files&path=... - Command runner with whitelisted commands per project type
- Scratchpad (auto-saving notes), recent files panel, disk usage, zip backup (excludes
node_modules,public,.git) - Broken link checker (in Settings tab) — scans posts/pages for dead HTTP links via parallel
curl_multiHEAD requests - Scheduled builds — cron-style triggers for any project command (requires host cron entry)
- Theme manager (Hexo) — list/switch/clone themes, git status + push/pull on each, deep-link to file editor
- Plugin manager (Hexo) — list
hexo-*deps, npm install/uninstall from the UI - Site visit analytics (own tab) — tails the web server's access log into a tiered rollup pipeline (raw 90 days → hourly 365 days → daily forever) with a daily-rotating salt for IP hashes so post-rotation data is anonymized for GDPR purposes. Per-project page with KPIs, current-vs-previous-period chart overlay, hour-of-day distribution, top pages, top referrers, top 404s, status-code mix.
- Tag/category filtering — click a tag/category in the project Dashboard to jump to Posts pre-filtered
- Editor tab persistence — file/post tabs survive sidebar navigation and full reloads
- Global keyboard shortcuts —
?for help,g d/ato navigate,nfor new post /bto trigger generate on project pages,Ctrl/Cmd-Sto save the current editor - Audit log for every state-changing action (file/post/draft writes, command runs, project changes, theme/plugin/git/analytics ops)
- Bootstrap 5.3 dark UI, no client-side build step. Vanilla JS; ESM modules pulled from CDN where needed (Milkdown).
Built-in project types
| Type | Auto-detected by | Type-specific tabs |
|---|---|---|
| Hexo | _config.yml + source/ |
Posts, Config, Run, Themes, Plugins, Git |
| Website | index.html or index.php |
Git |
| Storage | uploads/, files/, storage/ |
Media |
| Generic | fallback | — |
All types share these tabs: Dashboard, Analytics, Files, Notes, Settings.
Requirements
- PHP 8.0+ with
pdo_sqlite,zip, andcurlextensions - Apache (
mod_rewrite) or Nginx with the included.htaccessrewrite rules gitandnpmon PATH (for theme + plugin tabs)- GNU coreutils
du(for disk usage; standard on Debian/Ubuntu/Mint) - System cron (only if scheduled builds or analytics importer are used)
- Modern browser with ES modules support (Milkdown is loaded as ESM)
Quick start
git clone … /opt/hackmancms
php /opt/hackmancms/bin/migrate.php
Configure Apache to serve /opt/hackmancms/web (see CLAUDE.md for the full vhost config).
Visit the app and create your account on first load.
Optional: enable scheduled builds
Schedules created in the UI only fire if a host cron entry runs the dispatcher every minute. Install once, as the same user the web server runs as:
echo "* * * * * /usr/bin/php /opt/hackmancms/bin/run-schedules.php >/dev/null 2>&1" \
| sudo crontab -u www-data -
Optional: enable site-visit analytics
To pull live page-view data into the Analytics tab, set the access-log path under each project's Analytics → "Server-log import setup" panel, then install a host cron entry (as a user that can read the log files):
*/5 * * * * /usr/bin/php /opt/hackmancms/bin/import-site-logs.php
The importer hashes each visitor's IP with a daily-rotating salt, builds hourly + daily rollups, and ages out raw events past 90 days and hourly buckets past 365 days. Daily aggregates are kept forever.
Adding a project type
Create lib/project-types/MyType.php:
<?php
require_once __DIR__ . '/ProjectTypeBase.php';
class MyType extends ProjectTypeBase {
public static function typeSlug(): string { return 'mytype'; }
public static function typeName(): string { return 'My Type'; }
public static function typeIcon(): string { return 'bi-star'; }
public static function tabs(): array {
return ['dashboard', 'analytics', 'files', 'run', 'notes', 'settings'];
}
public static function commands(): array {
return [
['id' => 'build', 'label' => 'Build', 'cmd' => 'make build'],
];
}
public static function detectFromPath(string $path): bool {
return file_exists($path . '/Makefile');
}
}
No other changes needed — it appears in the UI on next load.
License
Internal tool, no license attached.