fix: second version_set_platform pass before release commit #66

Merged
jmiller merged 5 commits from dev into main 2026-05-28 16:15:53 +00:00
15 changed files with 1025 additions and 6 deletions
+8 -1
View File
@@ -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"
+1 -1
View File
@@ -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
-->
+1 -1
View File
@@ -7,7 +7,7 @@
<license>GPL-3.0-or-later</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.13.00</version>
<version>02.13.01</version>
<description>Minimal API-only component for MokoWaaS. Provides REST endpoints for site health, cache, updates, and backups.</description>
<namespace path="api/src">Moko\Component\MokoWaaS\Api</namespace>
<administration>
@@ -30,7 +30,7 @@
<license>GNU General Public License version 3 or later; see LICENSE.md</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.13.00</version>
<version>02.13.01</version>
<description>This plugin rebrands the Joomla system interface with MokoWaaS identity. It applies language overrides and ensures consistent branding across the platform.</description>
<namespace path=".">Moko\Plugin\System\MokoWaaS</namespace>
<scriptfile>script.php</scriptfile>
@@ -7,7 +7,7 @@
<license>GPL-3.0-or-later</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.13.00</version>
<version>02.13.01</version>
<description>Joomla Web Services API routes for MokoWaaS site management — health checks, cache, updates, backups, and site info.</description>
<namespace path="src">Moko\Plugin\WebServices\MokoWaaS</namespace>
<files>
+1 -1
View File
@@ -2,7 +2,7 @@
<extension type="package" method="upgrade">
<name>MokoWaaS</name>
<packagename>mokowaas</packagename>
<version>02.13.00</version>
<version>02.13.01</version>
<creationDate>2026-05-23</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
+181
View File
@@ -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 <health_api_token>
```
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.
+94
View File
@@ -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 |
+127
View File
@@ -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": "<health_api_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 <token>" "https://example.com/?mokowaas=health"`
- Check for SSL certificate issues between the Grafana server and the monitored site
+33
View File
@@ -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 |
+267
View File
@@ -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 <health_api_token>
```
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 |
+52
View File
@@ -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)
+100
View File
@@ -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 <token>" "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.
+89
View File
@@ -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
```
+69
View File
@@ -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.