diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index 1c9dc6fc..0191258d 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -351,9 +351,16 @@ jobs: run: | VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - # Final version consistency check before commit + # Re-align all version files (second pass catches anything the build touched) + php /tmp/moko-platform-api/cli/version_set_platform.php \ + --path . --version "$VERSION" --branch main 2>/dev/null || true php /tmp/moko-platform-api/cli/version_check.php --path . --fix 2>/dev/null || true + echo "=== Pre-commit version check ===" + php /tmp/moko-platform-api/cli/version_check.php --path . || true + echo "=== Files changed ===" + git status --short + git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" git config --local user.name "gitea-actions[bot]" git remote set-url origin "https://jmiller:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" diff --git a/README.md b/README.md index 9ca7d56e..cfd372c9 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS - VERSION: 02.13.00 + VERSION: 02.13.01 PATH: /README.md BRIEF: MokoWaaS platform plugin for Joomla --> diff --git a/src/packages/com_mokowaas/mokowaas.xml b/src/packages/com_mokowaas/mokowaas.xml index 12bdc6d7..347d1079 100644 --- a/src/packages/com_mokowaas/mokowaas.xml +++ b/src/packages/com_mokowaas/mokowaas.xml @@ -7,7 +7,7 @@ GPL-3.0-or-later hello@mokoconsulting.tech https://mokoconsulting.tech - 02.13.00 + 02.13.01 Minimal API-only component for MokoWaaS. Provides REST endpoints for site health, cache, updates, and backups. Moko\Component\MokoWaaS\Api diff --git a/src/packages/plg_system_mokowaas/mokowaas.xml b/src/packages/plg_system_mokowaas/mokowaas.xml index 7d72c49d..332cae08 100644 --- a/src/packages/plg_system_mokowaas/mokowaas.xml +++ b/src/packages/plg_system_mokowaas/mokowaas.xml @@ -30,7 +30,7 @@ GNU General Public License version 3 or later; see LICENSE.md hello@mokoconsulting.tech https://mokoconsulting.tech - 02.13.00 + 02.13.01 This plugin rebrands the Joomla system interface with MokoWaaS identity. It applies language overrides and ensures consistent branding across the platform. Moko\Plugin\System\MokoWaaS script.php diff --git a/src/packages/plg_webservices_mokowaas/mokowaas.xml b/src/packages/plg_webservices_mokowaas/mokowaas.xml index 8a042e24..e7a5e26c 100644 --- a/src/packages/plg_webservices_mokowaas/mokowaas.xml +++ b/src/packages/plg_webservices_mokowaas/mokowaas.xml @@ -7,7 +7,7 @@ GPL-3.0-or-later hello@mokoconsulting.tech https://mokoconsulting.tech - 02.13.00 + 02.13.01 Joomla Web Services API routes for MokoWaaS site management — health checks, cache, updates, backups, and site info. Moko\Plugin\WebServices\MokoWaaS diff --git a/src/pkg_mokowaas.xml b/src/pkg_mokowaas.xml index 76381bc7..e054e2f5 100644 --- a/src/pkg_mokowaas.xml +++ b/src/pkg_mokowaas.xml @@ -2,7 +2,7 @@ MokoWaaS mokowaas - 02.13.00 + 02.13.01 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/wiki/api-endpoints.md b/wiki/api-endpoints.md new file mode 100644 index 00000000..310eed25 --- /dev/null +++ b/wiki/api-endpoints.md @@ -0,0 +1,181 @@ +# API Endpoints + +MokoWaaS provides 6 remote management endpoints accessible via query string parameter. All endpoints require HTTPS and Bearer token authentication. + +## Authentication + +All endpoints require the `health_api_token` as a Bearer token in the Authorization header: + +``` +Authorization: Bearer +``` + +The token is auto-generated during plugin installation and stored as a read-only parameter in the plugin configuration. It can also be passed as a `token` query parameter as a fallback. + +Token validation uses `hash_equals()` for timing-safe comparison. If no token is configured, the endpoint returns HTTP 503. An invalid token returns HTTP 401. + +## Endpoints + +### 1. Health Check + +``` +GET /?mokowaas=health +``` + +Runs 16 diagnostic checks and returns a comprehensive health report. See [Health Monitoring](Health-Monitoring) for full documentation of all checks and response format. + +**Response**: JSON object with `status` (`ok`/`degraded`/`error`), `reason`, `timestamp`, `checks`, and `meta`. + +**HTTP Status**: 200 (ok/degraded), 503 (error). + +--- + +### 2. Site Info + +``` +GET /?mokowaas=info +``` + +Returns a compact summary of the Joomla site. + +**Response**: + +| Field | Description | +|---|---| +| `site_name` | Joomla site name | +| `site_url` | Site root URL | +| `joomla_version` | Joomla CMS version | +| `php_version` | PHP version | +| `db_type` | Database driver (e.g. `pdomysql`) | +| `debug` | Whether debug mode is on | +| `sef` | Whether SEF URLs are enabled | +| `caching` | Whether caching is enabled | +| `articles` | Total article count | +| `users` | Total user count | +| `extensions` | Number of enabled extensions | +| `brand` | Configured brand name | +| `plugin_version` | MokoWaaS plugin version | + +--- + +### 3. Remote Install + +``` +POST /?mokowaas=install +Content-Type: application/json + +{"url": "https://example.com/extension.zip"} +``` + +Downloads and installs a Joomla extension from the provided URL. The extension is downloaded to a temporary directory, extracted, and installed using Joomla's installer API. + +**Response**: JSON object with `status`, `extension` name, and `message`. + +**HTTP Status**: 200 (success), 400 (missing URL), 405 (not POST), 500 (install failed). + +--- + +### 4. Update Check + +``` +POST /?mokowaas=update +``` + +Clears the Joomla update cache and triggers a fresh update check via `Updater::findUpdates()`. + +**Response**: + +| Field | Description | +|---|---| +| `status` | `ok` | +| `updates_found` | Number of available updates | +| `message` | Human-readable summary | + +**HTTP Status**: 200 (success), 405 (not POST), 500 (failed). + +--- + +### 5. Cache Clear + +``` +POST /?mokowaas=cache +``` + +Clears the Joomla site cache, admin cache, and PHP OPcache (if available). + +**Response**: + +| Field | Description | +|---|---| +| `status` | `ok` | +| `message` | `Cache cleared` | + +**HTTP Status**: 200 (success), 405 (not POST), 500 (failed). + +--- + +### 6. Backup (Akeeba) + +``` +POST /?mokowaas=backup +Content-Type: application/json + +{"profile": 1} +``` + +Triggers an Akeeba Backup using the specified profile (defaults to profile 1). Requires Akeeba Backup to be installed. + +**Response**: + +| Field | Description | +|---|---| +| `status` | `started` | +| `profile` | Backup profile ID used | +| `message` | `Backup started` | + +**HTTP Status**: 200 (started), 404 (Akeeba not installed), 405 (not POST), 500 (failed), 501 (Akeeba Engine not loadable). + +## Error Responses + +All endpoints return errors in a consistent format: + +```json +{ + "error": "Error description", + "message": "Additional detail (optional)" +} +``` + +### Common Error Codes + +| HTTP Status | Meaning | +|---|---| +| 400 | Bad request (unknown action, missing parameters) | +| 401 | Invalid or missing authentication token | +| 405 | Wrong HTTP method (e.g. GET when POST is required) | +| 500 | Server error during operation | +| 503 | No API token configured | + +## Unknown Actions + +Requesting an unknown action returns HTTP 400 with the list of available actions: + +```json +{ + "error": "Unknown action", + "action": "invalid", + "available": ["health", "install", "update", "cache", "backup", "info"] +} +``` + +## Joomla REST API Routes + +In addition to the query-string endpoints above, MokoWaaS registers standard Joomla API routes via the `plg_webservices_mokowaas` plugin: + +| Route | Controller | +|---|---| +| `GET /api/v1/mokowaas/health` | HealthController | +| `POST /api/v1/mokowaas/cache` | CacheController | +| `POST /api/v1/mokowaas/update` | UpdateController | + +These routes use Joomla's standard API authentication (API token in `X-Joomla-Token` header) and are useful for integrations that already use the Joomla API framework. diff --git a/wiki/configuration.md b/wiki/configuration.md new file mode 100644 index 00000000..c2a9f07e --- /dev/null +++ b/wiki/configuration.md @@ -0,0 +1,94 @@ +# Configuration + +All MokoWaaS settings are managed in the Joomla plugin configuration under **System > Plugins > System - MokoWaaS**. Settings are organized into tabs (fieldsets). + +## Basic (Branding) + +| Parameter | Type | Default | Description | +|---|---|---|---| +| `enable_branding` | Yes/No | Yes | Enable white-label branding (language overrides, logos, colors) | +| `brand_name` | Text | `MokoWaaS` | Brand name displayed throughout the admin interface | +| `company_name` | Text | `Moko Consulting` | Company name used in footers and copyright notices | +| `support_url` | URL | `https://mokoconsulting.tech` | Support link shown on the admin login page and dashboard | + +## WaaS Access + +Controls the master user system that designates a single operator account. + +| Parameter | Type | Default | Description | +|---|---|---|---| +| `enforce_master_user` | Yes/No | Yes | Enable master user enforcement; non-master Super Admins are restricted | +| `master_username` | Text | `mokoconsulting` | Username of the designated master operator | +| `master_email` | Email | `webmaster@mokoconsulting.tech` | Email address of the master user (for verification) | +| `emergency_access` | Yes/No | Yes | Enable emergency access via database password + file-based 2FA | +| `allowed_ips_display` | Display | -- | Read-only display of whitelisted IP addresses for emergency access | + +## Maintenance + +| Parameter | Type | Default | Description | +|---|---|---|---| +| `dev_mode` | Yes/No | No | Disable Joomla caching at runtime (does not modify `configuration.php`) | +| `reset_hits` | Yes/No | No | Reset article hit counters on next admin load | +| `delete_versions` | Yes/No | No | Purge content version history on next admin load | + +## Visual Branding + +| Parameter | Type | Default | Description | +|---|---|---|---| +| `color_primary` | Color | `#1a2744` | Primary brand color (buttons, accents) | +| `color_sidebar` | Color | `#0f1b2d` | Admin sidebar background color | +| `color_header` | Color | `#1a2744` | Admin header bar color | +| `color_link` | Color | `#0051ad` | Link text color | +| `brand_icon` | Text | -- | FontAwesome unicode code point (e.g. `f6d5`) for the brand icon | +| `custom_css` | Textarea | -- | Custom CSS injected into every admin page | + +## Tenant Restrictions + +Controls what non-master Super Admin users can access. + +| Parameter | Type | Default | Description | +|---|---|---|---| +| `restrict_installer` | Yes/No | Yes | Block access to Extension Manager for non-master users | +| `hide_sysinfo` | Yes/No | Yes | Hide System Information page from non-master users | +| `restrict_global_config` | Yes/No | Yes | Block access to Global Configuration for non-master users | +| `restrict_template_editing` | Yes/No | Yes | Prevent non-master users from editing template files | +| `disable_install_url` | Yes/No | Yes | Remove the "Install from URL" tab in Extension Manager | +| `hidden_menu_items` | Textarea | -- | Comma-separated list of admin menu item IDs to hide from non-master users | + +## Site Aliases + +Multi-domain support. See [Site Aliases](Site-Aliases) for full documentation. + +| Parameter | Type | Default | Description | +|---|---|---|---| +| `primary_domain` | Text | -- | The canonical domain for the site (e.g. `waas.dev.mokoconsulting.tech`) | +| `site_aliases` | Subform | -- | Repeatable table of alias domains with per-alias settings | + +Each alias entry contains: + +| Field | Type | Default | Description | +|---|---|---|---| +| `domain` | Text | -- | Alias domain name (e.g. `www.example.com`) | +| `offline` | Yes/No | No | Show offline page for this alias | +| `offline_message` | Textarea | -- | Custom offline message (shown when `offline` is Yes) | +| `robots` | List | `index, follow` | Robots meta directive for this alias | +| `redirect_backend` | Yes/No | Yes | Redirect admin requests on this alias to the primary domain | + +## Diagnostics + +| Parameter | Type | Default | Description | +|---|---|---|---| +| `health_api_token` | Text (read-only) | -- | Auto-generated Bearer token for API authentication. Provisioned on install/update. Cannot be manually edited. | + +## Security + +| Parameter | Type | Default | Description | +|---|---|---|---| +| `force_https` | Yes/No | Yes | Redirect all HTTP requests to HTTPS (301 redirect) | +| `admin_session_timeout` | Number | `60` | Idle timeout in minutes for admin sessions (0 = use Joomla default). Master user is exempt. | +| `password_min_length` | Number | `12` | Minimum password length for user accounts | +| `password_require_uppercase` | Yes/No | Yes | Require at least one uppercase letter | +| `password_require_number` | Yes/No | Yes | Require at least one digit | +| `password_require_special` | Yes/No | Yes | Require at least one special character | +| `upload_allowed_types` | Text | `jpg,jpeg,png,gif,webp,svg,pdf,doc,docx,xls,xlsx` | Comma-separated list of allowed upload file extensions | +| `upload_max_size_mb` | Number | `100` | Maximum upload file size in megabytes | diff --git a/wiki/grafana-integration.md b/wiki/grafana-integration.md new file mode 100644 index 00000000..688810e1 --- /dev/null +++ b/wiki/grafana-integration.md @@ -0,0 +1,127 @@ +# Grafana Integration + +MokoWaaS integrates with a Grafana monitoring stack hosted at `bench.mokoconsulting.tech`. The integration is automatic: on install or update, the plugin sends a heartbeat that provisions a Grafana datasource for the site. + +## Architecture + +``` +MokoWaaS Plugin (Joomla) + | + | POST /api/waas-heartbeat/register + v +Heartbeat Receiver (bench.mokoconsulting.tech) + | + |-- Writes Grafana Infinity datasource YAML + |-- Restarts Grafana to pick up new datasource + |-- Sends ntfy notification (mokowaas-heartbeat topic) + v +Grafana Dashboard + | + | GET /?mokowaas=health (per site, on schedule) + v +Health JSON from each registered site +``` + +## Heartbeat Registration + +### When It Fires + +The heartbeat is sent automatically during: + +- Plugin installation (`postflight` with type `install`) +- Plugin update (`postflight` with type `update`) +- Package installation (via `Pkg_MokowaasInstallerScript::sendHeartbeat()`) + +### Payload + +The plugin sends a POST request to `https://bench.mokoconsulting.tech/api/waas-heartbeat/register` with: + +```json +{ + "site_url": "https://example.com", + "site_name": "Example Site", + "health_token": "", + "action": "register" +} +``` + +Authentication uses a shared secret sent in the `X-MokoWaaS-Key` header. + +### What the Receiver Does + +On receiving a registration request, the heartbeat receiver: + +1. Validates the `X-MokoWaaS-Key` header +2. Generates a unique datasource UID from the site URL +3. Writes a Grafana Infinity datasource YAML file to the Grafana provisioning directory +4. Restarts Grafana to load the new datasource +5. Sends an ntfy notification to the `mokowaas-heartbeat` topic with registration details + +The datasource YAML configures a Grafana Infinity datasource that polls `/?mokowaas=health` on the registered site using the provided Bearer token. + +### Response + +On success (HTTP 200): + +```json +{ + "status": "ok", + "ds_uid": "mokowaas-example-com" +} +``` + +The `ds_uid` is logged in the Joomla admin message queue for reference. + +## Grafana Dashboard + +The MokoWaaS Grafana dashboard is organized into 9 rows covering all health metrics: + +| Row | Panels | +|---|---| +| 1. Overview | Overall status, uptime, plugin version, Joomla version | +| 2. Database | Connectivity, latency, driver, user count | +| 3. Filesystem | Disk space, writable directories, site size | +| 4. Extensions | Extension counts by type, pending updates | +| 5. Backup | Last backup status, age, Akeeba health | +| 6. Security | Admin Tools WAF, SSL certificate, blocked requests | +| 7. Content | Article counts, categories, user activity | +| 8. Infrastructure | Cache status, mail config, scheduled tasks, error log | +| 9. Configuration | SEO settings, template info, config drift | + +Each row contains panels that query the site's Infinity datasource using JSONPath expressions to extract values from the health check response. + +## ntfy Notifications + +Registration events trigger a notification to the `mokowaas-heartbeat` ntfy topic. Notifications include: + +- Site URL +- Site name +- Registration action (new or update) +- Datasource UID + +Subscribe to notifications at `https://ntfy.sh/mokowaas-heartbeat` or use the ntfy app. + +## Troubleshooting + +### Heartbeat failed: connection error + +The receiver at `bench.mokoconsulting.tech` may be unreachable. Check: + +- DNS resolution for `bench.mokoconsulting.tech` +- Outbound HTTPS connectivity from the Joomla server +- Firewall rules allowing outbound port 443 + +Heartbeat failures are logged as warnings in Joomla's log and displayed in the admin message queue. They do not block plugin installation. + +### Datasource not appearing in Grafana + +- Verify the heartbeat completed successfully (check Joomla admin messages after install) +- Check the Grafana provisioning directory on `bench.mokoconsulting.tech` +- Ensure Grafana was restarted after provisioning +- Verify the health endpoint is accessible from the Grafana server + +### Health data not loading in dashboard + +- Confirm the `health_api_token` matches between the plugin configuration and the Grafana datasource +- Test the health endpoint directly: `curl -sk -H "Authorization: Bearer " "https://example.com/?mokowaas=health"` +- Check for SSL certificate issues between the Grafana server and the monitored site diff --git a/wiki/health-endpoint.md b/wiki/health-endpoint.md new file mode 100644 index 00000000..b5f38de8 --- /dev/null +++ b/wiki/health-endpoint.md @@ -0,0 +1,33 @@ +# Health Endpoint + +## Stable Release: 02.01.37 + +16 diagnostic checks via /?mokowaas=health (token-authenticated, HTTPS-only). + +### Checks + +Core: database, filesystem, cache, extensions +Security: backup (Akeeba), security (Admin Tools), SSL certificate +Operations: scheduled tasks, error log, database size, mail +Content: articles, categories, users, sessions, failed logins +Config: SEO, templates, debug mode, force SSL, caching + +### Grafana Dashboard (9 rows) + +Site Overview | Health Metrics | Infrastructure | Backup | Security | SSL/Cron | Content/Users | Mail/SEO/Config | DB/Errors + +### Heartbeat + +Auto-registers with Grafana via bench.mokoconsulting.tech/api/waas-heartbeat/register +ntfy notifications on mokowaas-heartbeat topic + +### Plugin Protection + +Hidden from non-master users, settings blocked, self-healing lock, uninstall blocked. + +--- + +| Field | Value | +|---|---| +| Minimum Version | 02.01.37 | +| Platform | joomla | diff --git a/wiki/health-monitoring.md b/wiki/health-monitoring.md new file mode 100644 index 00000000..9017b450 --- /dev/null +++ b/wiki/health-monitoring.md @@ -0,0 +1,267 @@ +# Health Monitoring + +MokoWaaS includes a built-in health monitoring system that runs 16 diagnostic checks against the Joomla site. Results are returned as a JSON payload via the `/?mokowaas=health` endpoint. + +## Endpoint + +``` +GET https://example.com/?mokowaas=health +Authorization: Bearer +``` + +The `health_api_token` is auto-generated during plugin installation and stored as a read-only plugin parameter. See [API Endpoints](API-Endpoints) for authentication details. + +## Response Structure + +The response includes an overall status, a human-readable reason string, a UTC timestamp, individual check results, and instance metadata. + +| Field | Description | +|---|---| +| `status` | Overall status: `ok`, `degraded`, or `error` | +| `reason` | Human-readable summary of issues (null when status is `ok`) | +| `timestamp` | ISO 8601 UTC timestamp | +| `checks` | Object containing all 16 check results | +| `meta` | Instance metadata (brand, versions, server name) | + +### Status Determination + +- If any check returns `error`, the overall status is `error` and the HTTP status code is **503**. +- If any check returns `degraded` (and none are `error`), the overall status is `degraded` with HTTP **200**. +- Otherwise the overall status is `ok` with HTTP **200**. + +## The 16 Checks + +### 1. database + +Tests database connectivity and query latency. + +| Field | Description | +|---|---| +| `status` | `ok` or `error` | +| `latency_ms` | Query round-trip time in milliseconds | +| `driver` | Database driver name (e.g. `mysqli`, `pdomysql`) | +| `users` | Total user count (sanity check) | + +### 2. filesystem + +Checks writable directories and disk space. + +| Field | Description | +|---|---| +| `status` | `ok`, `degraded` (low disk), or `error` (not writable) | +| `tmp_writable` | Whether `/tmp` is writable | +| `log_writable` | Whether `/administrator/logs` is writable | +| `cache_writable` | Whether `/cache` is writable | +| `free_disk_mb` | Free disk space in MB | +| `total_disk_mb` | Total disk space in MB | +| `site_size_mb` | Estimated site size in MB (images, media, tmp, cache, logs) | + +Degraded when free disk is below 100 MB. Error when required directories are not writable. + +### 3. cache + +Reports Joomla cache configuration. + +| Field | Description | +|---|---| +| `status` | Always `ok` | +| `enabled` | Whether Joomla caching is active | +| `handler` | Cache handler type (e.g. `file`, `redis`) | + +### 4. extensions + +Counts enabled extensions by type and checks for pending updates. + +| Field | Description | +|---|---| +| `status` | `ok` or `degraded` (pending updates) | +| `by_type` | Object with counts per extension type | +| `pending_updates` | Number of available extension updates | + +### 5. backup (Akeeba) + +Checks Akeeba Backup status. + +| Field | Description | +|---|---| +| `status` | `ok`, `degraded`, or `error` | +| `last_status` | Status of the last backup record (`complete`, `fail`, etc.) | +| `days_since` | Days since the last backup | +| `message` | Human-readable backup status | + +Degraded when the last backup is older than 7 days or did not complete successfully. + +### 6. security (Admin Tools) + +Checks Admin Tools WAF status if installed. + +| Field | Description | +|---|---| +| `status` | `ok`, `degraded`, or `error` | +| `waf_enabled` | Whether the Web Application Firewall is active | +| `blocked_24h` | Number of blocked requests in the last 24 hours | + +### 7. ssl + +Checks SSL certificate validity and expiration. + +| Field | Description | +|---|---| +| `status` | `ok`, `degraded`, or `error` | +| `days_left` | Days until certificate expiration | +| `issuer` | Certificate issuer | +| `valid_from` | Certificate start date | +| `valid_to` | Certificate expiration date | + +Degraded when the certificate expires within 30 days. + +### 8. cron (Scheduled Tasks) + +Checks Joomla scheduled task execution. + +| Field | Description | +|---|---| +| `status` | `ok` or `degraded` | +| `total_tasks` | Total number of scheduled tasks | +| `failed_24h` | Tasks that failed in the last 24 hours | + +### 9. errors (Error Log) + +Analyzes recent Joomla error log entries. + +| Field | Description | +|---|---| +| `status` | `ok` or `degraded` | +| `recent_errors` | Count of recent error log entries | +| `last_error` | Most recent error message | + +### 10. db_size + +Reports database size metrics. + +| Field | Description | +|---|---| +| `status` | `ok` or `degraded` | +| `total_mb` | Total database size in MB | +| `tables` | Number of database tables | + +### 11. content + +Reports content statistics. + +| Field | Description | +|---|---| +| `status` | Always `ok` | +| `articles` | Total article count | +| `categories` | Total category count | +| `published` | Number of published articles | +| `unpublished` | Number of unpublished articles | + +### 12. users (User Activity) + +Reports user statistics. + +| Field | Description | +|---|---| +| `status` | Always `ok` | +| `total` | Total user count | +| `active_30d` | Users active in the last 30 days | +| `blocked` | Number of blocked user accounts | + +### 13. mail + +Checks Joomla mail configuration. + +| Field | Description | +|---|---| +| `status` | `ok` or `degraded` | +| `mailer` | Mail handler type (e.g. `smtp`, `mail`, `sendmail`) | +| `from` | Configured sender address | + +### 14. seo + +Checks SEO configuration. + +| Field | Description | +|---|---| +| `status` | `ok` or `degraded` | +| `sef` | Whether SEF URLs are enabled | +| `sef_rewrite` | Whether URL rewriting is enabled | +| `sitemap` | Whether a sitemap is detected | + +### 15. template + +Reports active template information. + +| Field | Description | +|---|---| +| `status` | Always `ok` | +| `site_template` | Active frontend template name | +| `admin_template` | Active admin template name | + +### 16. config (Config Drift) + +Detects configuration anomalies. + +| Field | Description | +|---|---| +| `status` | `ok` or `degraded` | +| `issues` | Array of detected configuration problems | + +Checks for issues such as debug mode enabled in production, error reporting set too high, or default database prefix still in use. + +## Example Response + +```json +{ + "status": "degraded", + "reason": "2 extension updates available; SSL expires in 14 days", + "timestamp": "2026-05-24T12:00:00Z", + "checks": { + "database": { + "status": "ok", + "latency_ms": 1.23, + "driver": "pdomysql", + "users": 5 + }, + "filesystem": { + "status": "ok", + "tmp_writable": true, + "log_writable": true, + "cache_writable": true, + "free_disk_mb": 4500, + "total_disk_mb": 20000, + "site_size_mb": 320 + }, + "ssl": { + "status": "degraded", + "days_left": 14, + "issuer": "Let's Encrypt", + "valid_to": "2026-06-07" + } + }, + "meta": { + "brand": "MokoWaaS", + "plugin_version": "02.03.11", + "joomla_version": "5.2.4", + "php_version": "8.2.20", + "server_name": "Example Site", + "server_time": "2026-05-24T12:00:00Z" + } +} +``` + +(Remaining checks omitted for brevity.) + +## Metadata + +The `meta` object is included in every health response: + +| Field | Description | +|---|---| +| `brand` | Configured brand name | +| `plugin_version` | MokoWaaS plugin version | +| `joomla_version` | Joomla CMS version | +| `php_version` | PHP version | +| `server_name` | Joomla site name | +| `server_time` | Server UTC time | diff --git a/wiki/home.md b/wiki/home.md new file mode 100644 index 00000000..18d918fd --- /dev/null +++ b/wiki/home.md @@ -0,0 +1,52 @@ +# MokoWaaS + +MokoWaaS is a Joomla 5.x / 6.x extension package that provides a configurable white-label identity layer, tenant management, health monitoring, and remote administration API for the MokoWaaS platform. + +Developed by [Moko Consulting](https://mokoconsulting.tech). Licensed under GPL-3.0-or-later. + +## Features + +- **White-label branding** -- customizable brand name, colors, favicon, login page, and admin template theming +- **Master user enforcement** -- designate a single operator account with elevated privileges; restrict other Super Admins +- **Tenant restrictions** -- hide system info, block installer access, restrict global config and template editing, hide menu items +- **Site aliases** -- multi-domain support with per-alias offline mode, robots directives, and backend redirects +- **Health monitoring** -- 16 diagnostic checks exposed via authenticated JSON API +- **Remote management API** -- 6 endpoints for health, info, install, update, cache clear, and backup +- **Grafana integration** -- automatic heartbeat registration with Grafana Infinity datasource provisioning +- **Plugin protection** -- protected flag, self-healing, hidden from non-master users, blocks disable/uninstall +- **Security hardening** -- forced HTTPS, session timeouts, password policy, upload restrictions +- **Emergency access** -- file-based two-factor verification for master user recovery +- **Automatic updates** -- via Joomla update server hosted on Gitea + +## Requirements + +| Requirement | Minimum | +|---|---| +| Joomla | 5.0.0+ (5.x and 6.x supported) | +| PHP | 8.1.0+ | + +## Package Contents + +The `pkg_mokowaas` package installs three extensions: + +| Extension | Type | Purpose | +|---|---|---| +| `plg_system_mokowaas` | System Plugin | Core branding, restrictions, health checks, API endpoints | +| `com_mokowaas` | Component | REST API controllers (health, cache, update) | +| `plg_webservices_mokowaas` | Webservices Plugin | Registers Joomla API routes for `v1/mokowaas/*` | + +## Wiki Pages + +- [Configuration](Configuration) -- All plugin settings organized by tab +- [Health Monitoring](Health-Monitoring) -- The 16 diagnostic checks +- [Site Aliases](Site-Aliases) -- Multi-domain management +- [API Endpoints](API-Endpoints) -- The 6 remote management endpoints +- [Grafana Integration](Grafana-Integration) -- Monitoring dashboard setup +- [Plugin Protection](Plugin-Protection) -- Security measures preventing disable/uninstall +- [Installation](Installation) -- Step-by-step install and upgrade guide + +## Links + +- **Repository**: [MokoWaaS on Gitea](https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS) +- **Update Server**: [updates.xml](https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/raw/branch/main/updates.xml) +- **Author**: [Moko Consulting](https://mokoconsulting.tech) diff --git a/wiki/installation.md b/wiki/installation.md new file mode 100644 index 00000000..dab66569 --- /dev/null +++ b/wiki/installation.md @@ -0,0 +1,100 @@ +# Installation + +MokoWaaS is distributed as a Joomla package (`pkg_mokowaas`) containing three extensions. It can be installed via the standard Joomla Extension Manager. + +## Requirements + +| Requirement | Minimum | +|---|---| +| Joomla | 5.0.0+ | +| PHP | 8.1.0+ | + +The installer checks both requirements during `preflight` and aborts with an error message if either is not met. + +## Method 1: Upload Package File + +1. Download the latest `pkg_mokowaas.zip` from the [Gitea Releases](https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases) page. +2. In Joomla admin, navigate to **System > Install > Extensions**. +3. On the **Upload Package File** tab, drag and drop or browse to select `pkg_mokowaas.zip`. +4. Click **Upload & Install**. +5. Verify the success message. The package installs three extensions: + - `plg_system_mokowaas` (System Plugin) + - `com_mokowaas` (Component) + - `plg_webservices_mokowaas` (Webservices Plugin) + +## Method 2: Install from URL + +1. In Joomla admin, navigate to **System > Install > Extensions**. +2. On the **Install from URL** tab, enter the direct download URL for the package ZIP, e.g.: + ``` + https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/stable/pkg_mokowaas-latest.zip + ``` +3. Click **Install**. +4. Verify the success message. + +## Post-Installation Verification + +After installation, verify the following: + +1. **Plugin enabled**: Navigate to **System > Plugins** and confirm "System - MokoWaaS" is enabled (it is auto-enabled during installation). +2. **Health token generated**: Open the plugin configuration and check the **Diagnostics** tab. The `health_api_token` field should contain an auto-generated token. +3. **Branding applied**: The admin login page and dashboard should reflect MokoWaaS branding (logo, colors, footer text). +4. **Health endpoint**: Test the health endpoint: + ``` + curl -sk -H "Authorization: Bearer " "https://yoursite.com/?mokowaas=health" + ``` +5. **Grafana heartbeat**: Check the admin message queue for a Grafana heartbeat confirmation message. + +## What the Installer Does + +During `postflight`, the installer script performs these operations: + +| Step | Description | +|---|---| +| Enable and protect plugin | Sets `enabled=1`, `protected=1`, `locked=0` in `#__extensions` | +| Install MokoOnyx template | Installs the bundled MokoOnyx site template from the plugin payload, sets it as default | +| Language overrides | Deploys language override files for en-GB and en-US | +| Login support URLs | Updates `mod_loginsupport` to point to Moko Consulting support/docs/news URLs | +| Atum branding | Applies brand colors to the Atum admin template | +| Action log registration | Registers MokoWaaS in the Joomla action log system | +| Health token provisioning | Generates a random API token if one does not exist | +| Heartbeat | Sends a registration heartbeat to the Grafana monitoring receiver | + +## Automatic Updates + +MokoWaaS includes an update server configuration that enables automatic update notifications through Joomla's built-in update system. + +The update server URL is: +``` +https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/raw/branch/main/updates.xml +``` + +When a new version is available: + +1. Joomla's update checker detects the new version from `updates.xml`. +2. A notification appears in the admin dashboard. +3. Navigate to **System > Update > Extensions** to install the update. +4. The `postflight` script runs again, re-applying all configuration steps. + +## Upgrading from Standalone Plugin to Package + +If you previously installed the standalone `plg_system_mokowaas` plugin (before it was packaged as `pkg_mokowaas`), the package installer handles migration automatically: + +1. Install the `pkg_mokowaas.zip` package using either method above. +2. The package installer detects the existing standalone plugin and upgrades it in place. +3. The additional extensions (`com_mokowaas`, `plg_webservices_mokowaas`) are installed alongside. +4. All existing plugin settings are preserved. +5. The `protected` flag is set on all package extensions. + +No manual cleanup of the old standalone plugin is required. + +## Uninstallation + +Uninstallation is restricted to the master user. See [Plugin Protection](Plugin-Protection) for details. + +When the master user uninstalls via the Extension Manager: + +1. Language override files are removed from Joomla's global override directories. +2. Action log registration is cleaned up. +3. An uninstall notification is sent to the monitoring system. +4. The package and all sub-extensions are removed. diff --git a/wiki/plugin-protection.md b/wiki/plugin-protection.md new file mode 100644 index 00000000..c502e6da --- /dev/null +++ b/wiki/plugin-protection.md @@ -0,0 +1,89 @@ +# Plugin Protection + +MokoWaaS uses multiple layers of protection to prevent accidental or unauthorized disabling and uninstallation. The master user retains full control over the plugin at all times. + +## Protection Layers + +### 1. Protected Flag (`protected=1`) + +During installation and on every admin session, MokoWaaS sets the `protected` column to `1` in the `#__extensions` database table for both `mokowaas` and `pkg_mokowaas` entries. + +The Joomla framework itself enforces this flag: protected extensions cannot be disabled or uninstalled through the standard admin interface. + +The `locked` column is set to `0` so the extension can still receive updates and configuration changes. + +### 2. Self-Healing + +The `ensureProtectedFlag()` method runs once per admin session (using a static flag to avoid repeated queries). If the `protected` column has been reset to `0` (e.g., by a database modification), it is automatically restored to `1`. + +This runs in the `protectPlugin()` method, which is called from `onBeforeRender()` on every admin page load. + +### 3. Hidden from Plugin List + +For non-master users, MokoWaaS injects JavaScript on the `com_plugins` and `com_installer` pages that hides any table row containing "mokowaas" or "MokoWaaS". This prevents non-master users from seeing the plugin in the extension list. + +The `hidePluginFromList()` method adds an inline script that runs on `DOMContentLoaded`: + +```javascript +document.querySelectorAll('tr').forEach(function(row) { + var text = row.textContent || ''; + if (text.indexOf('mokowaas') !== -1 || text.indexOf('MokoWaaS') !== -1) { + row.style.display = 'none'; + } +}); +``` + +### 4. onExtensionBeforeSave Interception + +The `onExtensionBeforeSave` event handler intercepts save attempts on the plugin configuration. If a non-master user attempts to save the plugin with `enabled = 0`, the handler: + +1. Displays an error message: "MokoWaaS cannot be disabled." +2. Forces `enabled` back to `1` on the table object +3. Returns `true` to allow the save to proceed (with the corrected value) + +### 5. protectPlugin() -- Uninstall and Disable Blocking + +The `protectPlugin()` method runs on every admin page request and checks for active uninstall or disable attempts: + +**Uninstall blocking**: If the current request is to `com_installer` with task `manage.remove`, and the extension IDs include any MokoWaaS extension, the request is blocked with an error message and a redirect back to the installer manage view. + +**Disable blocking**: If the current request is to `com_plugins` with task `plugins.publish`, and the extension IDs include MokoWaaS, the request is blocked with an error message and a redirect back to the plugins list. + +The `isOurExtension()` helper method checks extension IDs against the database to determine if they belong to MokoWaaS (matching on element name `mokowaas` or `pkg_mokowaas`). + +## Master User Exemption + +All protection checks call `isMasterUser()` first. If the current user is the designated master user (matching the configured `master_username`), all protections are bypassed. The master user can: + +- See MokoWaaS in the plugin and extension lists +- Disable the plugin via the configuration page +- Uninstall the plugin via the Extension Manager +- Modify all plugin settings + +## Installation-Time Protection + +The package installer script (`Pkg_MokowaasInstallerScript`) and the plugin installer script both set `protected=1` during `postflight`: + +- `enableAndLockPlugin()` sets `enabled=1`, `locked=1`, `protected=1` on the system plugin +- `protectExtensions()` sets `protected=1`, `locked=0` on all MokoWaaS extensions (plugin and package) + +This ensures protection is active immediately after installation, before the first admin page load triggers the self-healing logic. + +## Summary of Protection Flow + +``` +Installation + -> postflight sets protected=1, enabled=1 + +Every admin page load (onBeforeRender) + -> protectPlugin() + -> ensureProtectedFlag() (once per session, restores protected=1 if needed) + -> if not master user: + -> block uninstall attempts (com_installer manage.remove) + -> block disable attempts (com_plugins plugins.publish) + -> hidePluginFromList() for non-master users + +Plugin config save (onExtensionBeforeSave) + -> if not master user and enabled=0: + -> force enabled=1, show error +``` diff --git a/wiki/site-aliases.md b/wiki/site-aliases.md new file mode 100644 index 00000000..02479582 --- /dev/null +++ b/wiki/site-aliases.md @@ -0,0 +1,69 @@ +# Site Aliases + +MokoWaaS supports multi-domain configurations through the Site Aliases system. This allows a single Joomla installation to respond to multiple domain names, each with independent settings for offline mode, robots directives, and backend access. + +## Configuration + +Site aliases are configured in the plugin settings under the **Site Aliases** tab. + +### Primary Domain + +Set `primary_domain` to the canonical domain for the site (e.g. `waas.dev.mokoconsulting.tech`). This is the domain that: + +- Serves as the canonical URL for SEO purposes +- Hosts the admin backend (when `redirect_backend` is enabled on aliases) +- Is used in heartbeat registration with Grafana + +### Alias Entries + +Add alias domains using the repeatable subform table. Each alias entry has the following options: + +| Field | Type | Default | Description | +|---|---|---|---| +| `domain` | Text | (required) | The alias domain name, e.g. `www.example.com` | +| `offline` | Yes/No | No | When Yes, visitors to this alias see the offline page | +| `offline_message` | Textarea | -- | Custom message displayed when the alias is offline (only shown when `offline` is Yes) | +| `robots` | List | `index, follow` | Robots meta tag directive for this alias | +| `redirect_backend` | Yes/No | Yes | When Yes, admin URLs (`/administrator`) on this alias redirect to the primary domain | + +### Robots Options + +Each alias can have its own robots directive: + +| Value | Effect | +|---|---| +| `index, follow` | Normal indexing (default) | +| `noindex, follow` | Do not index pages, but follow links | +| `index, nofollow` | Index pages, do not follow links | +| `noindex, nofollow` | Do not index or follow | +| `none` | Equivalent to `noindex, nofollow` | + +## How Canonical URLs Work + +When a request arrives on an alias domain, MokoWaaS: + +1. Matches the `HTTP_HOST` against configured alias domains +2. Applies the alias-specific robots meta tag +3. If the alias is marked offline, renders the offline page with the custom message +4. If `redirect_backend` is enabled and the request is for `/administrator`, issues a 301 redirect to the primary domain's admin +5. Sets the canonical URL to the primary domain equivalent of the current page + +This prevents duplicate content issues when the same site is accessible from multiple domains. + +## Grafana Monitoring for Aliases + +When the plugin sends a heartbeat to the Grafana monitoring receiver, it registers both the primary domain and all alias domains. The monitoring dashboard can then track health status for each domain independently. + +Each alias appears as a separate entry in the Grafana Infinity datasource, pointing to the same health endpoint but accessed via the alias domain. This ensures SSL certificate checks and DNS resolution are validated per-domain. + +## DreamHost Mirror Setup + +For sites hosted on DreamHost, alias domains are typically configured as "mirror" domains in the DreamHost panel: + +1. In DreamHost panel, add the alias domain as a **Mirror** of the primary domain +2. Ensure DNS for the alias domain points to the DreamHost server +3. Add the alias domain to the MokoWaaS Site Aliases configuration +4. Set `redirect_backend` to Yes (recommended) so admin access always uses the primary domain +5. Set `robots` to `noindex, nofollow` if the alias is a staging or preview domain + +DreamHost mirrors serve the same filesystem, so no additional Joomla configuration is needed beyond the MokoWaaS alias entry.