- Set protected=1, locked=0 on MokoWaaS extensions via package script
- Self-healing: plugin checks and restores protected flag each session
- Block non-master disable via plugin list toggle (plugins.publish)
- Block non-master uninstall via installer manage
- Joomla framework natively enforces protected status (greys out toggles)
- Master users can still manage settings and updates
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- manifest.xml: package-type plugin → package
- Canonical URL injection for alias domains (prevents SEO duplication)
- Heartbeat registration for alias domains (each alias gets Grafana datasource)
- Package script.php: enable plugins on every install/update, heartbeat on postflight
- Remove accidentally committed profile.ps1 and TODO.md
- Add profile.ps1 and TODO.md to .gitignore
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Instead of rendering a custom HTML page, set config('offline', 1) at
onAfterRoute so Joomla renders the site template's offline.php layout.
Custom offline_message is passed via config if set.
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Removed primaryHost check from getCurrentAlias() — just look up
current host in aliases list directly
- Handle Joomla subform stdClass→array conversion
- Strip trailing slashes from alias domains
- Moved handleSiteAlias() to onAfterRoute (client type resolved)
- Use http_response_code(503) + die() for offline page
- Cast offline value to string for comparison
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Uri::root() returns the current request domain, so redirecting from an
alias to Uri::root()/administrator redirects back to the alias. Added
primary_domain field to Site Aliases tab and getPrimaryHost() method
that checks: plugin setting → $live_site → alias exclusion fallback.
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Read plugin_version from manifest XML instead of hardcoding
- Hide MokoWaaS from plugin/installer list for non-master users
- Block non-master uninstall and disable attempts
- No self-healing lock — master users can still disable if needed
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Restructure MokoWaaS from a standalone system plugin into a Joomla
package containing:
- plg_system_mokowaas — existing system plugin (moved to packages/)
- com_mokowaas — minimal API-only component with health, cache, and
update controllers for Joomla Web Services API
- plg_webservices_mokowaas — registers /api/v1/mokowaas/* routes
Package script auto-enables both plugins on fresh install.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Alias domains were creating separate Grafana datasources with unique
UIDs, causing provisioning failures (duplicate UID) when Grafana
restarts. Only the primary domain should be registered.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move site aliases from a comma-separated text field in Diagnostics to its
own tab using Joomla's subform repeatable-table layout. Each alias entry
now supports: domain, offline toggle with custom message, robots meta
directive, and backend redirect to primary domain. Frontend stays on the
alias domain while admin requests can be redirected to the primary.
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The receiver returns 'updated' when re-registering an existing site,
but the code only accepted 'registered', causing false 'HTTP 200 — Unknown' warnings.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The postflight still had the old Grafana API code with obfuscated tokens,
causing 403 RBAC errors on install/update. Now uses the heartbeat receiver
at bench.mokoconsulting.tech/api/waas-heartbeat/register.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New API endpoints (all token-authenticated, HTTPS-only):
?mokowaas=health — 16 diagnostic checks (GET)
?mokowaas=install — install extension from URL (POST)
?mokowaas=update — trigger Joomla update check (POST)
?mokowaas=cache — clear all caches + opcache (POST)
?mokowaas=backup — trigger Akeeba Backup (POST)
?mokowaas=info — compact site summary (GET)
Also adds:
- Centralized API router with shared token auth
- site_aliases config field for multi-domain support
- Each alias registers its own Grafana datasource
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reconstructed from git history after cascade revert wiped dev:
- 16 health checks (database, filesystem, cache, extensions, backup,
security, SSL, cron, errors, db_size, content, users, mail, SEO,
template, config)
- Heartbeat receiver provisioning (replaces Grafana API)
- Multi-domain support via site_aliases config field
- Each alias domain registers its own Grafana datasource
- Human-readable reason field for degraded status
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reports total_disk_mb and site_size_mb (images, media, tmp, cache, logs).
Shows in Infrastructure table on Grafana dashboard.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New health checks:
- backup: last backup date/status/size, days since, total count, 7d count
- security: Admin Tools WAF status, blocked requests 24h/7d
Degraded reasons:
- No backups found
- Last backup older than 7 days
- Last backup failed/incomplete
Dashboard updated with Backup and Security rows.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Status 'degraded' now includes reason like '3 extension updates available'
or 'Low disk space: 45 MB free'. Shows in dashboard Site Info table.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Injects JS on com_plugins that removes the MokoWaaS row from the
plugin table. Combined with the edit/save block, non-master users
cannot see, edit, or save the plugin settings.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Non-master users blocked from editing MokoWaaS plugin config
- isOurPlugin() helper checks extension_id against our plugin
- Blocks both edit view and save task for non-master users
- Renamed bare 'Gitea' references to 'MokoGitea' in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- enforceLocked() runs every page load — re-enables, re-locks, re-protects
if someone tampers with the database flags
- preflight() blocks uninstall attempts with error message
- Logs tampering attempts to mokowaas log category
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove all Grafana API code (630 lines), obfuscated tokens, SA tokens,
ensureGrafanaPlugin, provisionGrafanaDatasource, buildDashboardModel.
Replace with simple HTTP POST to heartbeat receiver on bench server.
Receiver writes Grafana provisioning YAML and restarts Grafana container.
No API tokens or RBAC permissions needed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New service account token with correct RBAC permissions
- script.php postflight now shows success/failure messages to admin
- Logs all heartbeat attempts with HTTP code and cURL errors
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add CURLOPT_SSL_VERIFYPEER=false for shared hosting environments
- Add CURLOPT_FOLLOWLOCATION to handle redirects
- Log all Grafana heartbeat attempts with HTTP code and cURL errors
- Helps debug provisioning failures on DreamHost and similar hosts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
API key and URL stored as XOR-encoded base64 constants. Deobfuscated
at runtime only when needed. Prevents plain-text grep discovery.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Health endpoint always enabled when plugin is installed
- Grafana URL and API key hardcoded as constants
- Removed enable_health_endpoint, grafana_url, grafana_api_key from config UI
- Token still auto-generated and shown as read-only
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>