WP Manifestindependent plugin directory
manifest / updates / wp-maintenance-toolkit

WP Maintenance Toolkit

افزونهٔ نگهداری وردپرس — بکاپ دیتابیس، پایش آپتایم با هشدار ایمیل و گزارش سلامت ماهانه

★ 0stars

Install

Download source zip No release zip yet

This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/sirdadfar/wp-maintenance-toolkit/archive/refs/heads/main.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

An open-source WordPress plugin for the monthly-maintenance service a freelancer sells to small businesses: database backups, uptime monitoring with email alerts, and a monthly health report.

  • Version: 1.1.0
  • Tested up to WordPress 6.7 (requires 5.8+)
  • Requires PHP 7.4+
  • License: GPL-2.0-or-later
  • No shell_exec, no system mysqldump, no CDN, no build step

This plugin deliberately does a few things well instead of everything badly. Destructive operations stay in human hands.


Table of contents


Features

1. Database backups

  • Pure-PHP, $wpdb-based dumps read in chunks, so memory usage stays bounded on large databases.
  • Chunks are paged by the table's primary key (WHERE pk > last ORDER BY pk LIMIT n), not by OFFSET. A row inserted or deleted between two chunks can therefore never duplicate or skip a row in the dump. Tables without a single-column primary key (rare) fall back to LIMIT/OFFSET.
  • gzip-compressed SQL with CREATE TABLE, INSERT, DROP TABLE IF EXISTS and a standard /*!40101 ... */ header.
  • Tables are ordered parent-first using information_schema foreign keys; the dump sets FOREIGN_KEY_CHECKS=0 and restores it at the end.
  • Stored in wp-content/maintenance-backups/ as db-YYYYmmdd-HHMMSS-{random}.sql.gz.
  • The directory gets an Apache deny .htaccess and an index.php guard. Nginx ignores .htaccess, so the dashboard warns on Nginx and SECURITY.md ships a deny snippet.
  • Only one dump runs at a time (a 15-minute transient lock), so cron and the manual button cannot race on retention or fill the disk.
  • Retention keeps the newest N files (default 7). Only filenames matching this plugin's strict pattern and containing a valid timestamp are eligible for deletion. At least one file is always kept, even with keep=0.
  • Manual "Backup now" button (nonce + manage_options) plus daily/weekly WP-Cron.

2. Uptime monitoring

  • Every 5 minutes, wp_remote_get pings the site's home URL and optionally one extra URL, with a 5-second timeout.
  • A single failure never opens an incident: two consecutive failures are required, which filters out shared-host hiccups and one-off network timeouts.
  • On confirmed downtime, one email per incident is sent and the start is logged. Further failed checks do not duplicate the incident or the email.
  • On recovery, the end time/duration are logged and a recovery email is sent.
  • The incident log is stored in an option and pruned to the last 100 entries.

3. Monthly health report

On the first cron run after a new month starts, an HTML email is sent for the previous calendar month:

  • uptime percentage, incident count and total downtime
  • last successful backup, backup count and total size
  • pending plugin/theme/core updates
  • WordPress and PHP versions
  • free disk space
  • the last 10 PHP error-log lines (only from the absolute path configured in settings)

A manual "Send report" button and wp maint report --send are always available. The report is rendered by the pure function ReportBuilder::build(array $data): array.

4. WP-CLI

wp maint backup --keep=7
wp maint status
wp maint report            # preview
wp maint report --send     # send
wp maint incidents --limit=20

Admin interface

The plugin ships a designed admin experience rather than a default settings form:

  • Persian-first, RTL layout in Vazirmatn with a deliberate Arabic-script fallback stack.
  • Vazirmatn (variable) is bundled in assets/fonts under SIL OFL 1.1 — no Google Fonts or CDN request, so the admin stays fast on filtered networks.
  • Light-first tokens inside wp-admin; the dark glass palette applies automatically under prefers-color-scheme: dark.
  • Glass stat tiles, hairline separators and a single gold accent — no CSS framework, no build step.
  • Dense tables use sticky headers, row hover and data-label cards below 760px.
  • Visible :focus-visible rings, ≥44px targets, aria-live/role="alert" notices, and labelled fields that keep their hints.
  • Styles live in assets/css/admin.css, scoped to .maintkit-wrap / .maintkit-notice so nothing leaks into other admin screens.
  • Full token, component and accessibility notes: docs/design-system.md.

Screenshots

No image files are committed, so the project never ships fabricated or stale screenshots. Suggested shots and a generation guide live in docs/screenshots/README.md:

File Subject
dashboard.png Status dashboard with backup, uptime and report cards
settings.png Settings page
report-email.png Monthly report in a mail client
cli.png wp maint status / wp maint incidents output

Add real captures and update this table; do not create placeholder image binaries.


Installation

Option 1: zip upload

  1. Zip the plugin folder so the archive contains wp-maintenance-toolkit/wp-maintenance-toolkit.php (not a nested folder).
  2. In WordPress: Plugins → Add New → Upload Plugin.
  3. Upload, install and activate.
  4. Open Maintenance Toolkit in the admin menu and save the settings.

Option 2: FTP / SFTP

  1. Upload the wp-maintenance-toolkit directory to wp-content/plugins/.
  2. Verify wp-maintenance-toolkit.php is directly inside that directory.
  3. Activate the plugin from the Plugins screen.

Composer (recommended for development)

cd wp-content/plugins/wp-maintenance-toolkit
COMPOSER_ALLOW_SUPERUSER=1 composer install --no-dev --optimize-autoloader

If vendor/autoload.php is missing, a small built-in PSR-4 fallback keeps the plugin working and an admin notice asks you to run Composer. Production installs should use the optimized autoloader.

Backup directory permissions

The PHP user must be able to write to wp-content/maintenance-backups/. The plugin creates the directory, .htaccess and index.php automatically. On Nginx, .htaccess is ignored; add:

location ^~ /wp-content/maintenance-backups/ {
    deny all;
    return 403;
}

Settings

Admin → Maintenance Toolkit → Settings

Setting Default Notes
Scheduled backups on Enable/disable automatic dumps
Backup schedule weekly daily or weekly
Backups to keep 7 1–60; only plugin-owned files are deleted
Automatic monitoring on Check every 5 minutes
Extra URL empty Optional second probe (e.g. a health endpoint)
Alert email admin email Empty falls back to admin_email
Automatic report on First cron run of each month
PHP error log path empty Local absolute path; only the last 256 KB / 10 lines are read

Relative paths, stream wrappers (http://, ftp://, …) and non-absolute paths are rejected. An unreadable path produces an empty log section, not an error.


WP-CLI

wp maint backup --keep=10      # backup and keep 10 files
wp maint status                # table of backup/monitor/report status
wp maint report                # preview last month's report
wp maint report --send         # send it
wp maint incidents --limit=20  # recent incidents

wp maint status shows WordPress/PHP versions, backup directory and usage, latest backup, monitor state, 30-day uptime, report recipient and log path.


Security notes

  • Every admin page checks manage_options; every state-changing action uses a nonce and goes through admin-post.php.
  • Output is escaped with esc_html, esc_attr, esc_url; numeric input is cast/absint.
  • Every PHP file starts with defined( 'ABSPATH' ) || exit;.
  • Backups are protected by .htaccess + index.php; Nginx needs an explicit deny all.
  • Retention validates filenames against a strict regex and a real calendar timestamp. It never deletes directories, symlinks or files it did not create.
  • Report HTML escapes site names and every log line, so an injected `