Jump to content

ICT:Dashboard autopurge architecture

From Costa Sano MediaWiki

ICT: Dashboard Auto‑Purge Architecture

This page documents why dashboards in this wiki require an automatic purge after saving a Page Forms form, why several intuitive approaches fail, and why the final solution works reliably.

1. The Problem

Dashboards such as Dashboard:Place display data stored in Cargo tables. After saving a form, the updated Cargo data does not appear immediately because:

  • Cargo updates are asynchronous
  • MediaWiki caches parser output
  • APCu/object cache may serve stale HTML
  • Reverse proxies may cache responses
  • Page Forms redirects do not trigger a purge

A reliable, automatic purge mechanism is required.

2. Why Simple Solutions Do Not Work

2.1. HTML <meta> refresh

MediaWiki sanitizes <meta> tags, even inside #tag:html. The tag is displayed as text and never executed.

Conclusion: HTML refresh cannot be used.

2.2. Redirecting to ?action=purge

A redirect to ?action=purge always triggers MediaWiki’s confirmation dialog. This dialog cannot be suppressed via GET requests.

Conclusion: GET‑based purge is never silent.

2.3. Page Forms limitations

Page Forms does not provide hooks or callbacks that run after saving. It cannot trigger a purge.

Conclusion: Page Forms cannot be used for purge logic.

2.4. JavaScript redirect loops

Redirecting to ?action=purge causes:

  1. Purge dialog
  2. Page reload
  3. JavaScript fires again
  4. Loop

Conclusion: Redirect‑based purge is unstable.

2.5. Query parameters being stripped

Reverse proxies, skins, and canonical URL rewrites may strip unknown parameters such as ?purged=1. This prevents loop‑detection.

Conclusion: Only parameters MediaWiki preserves can be used.

3. Requirements for a Working Solution

A correct solution must:

  • purge silently
  • avoid loops
  • work for all dashboards
  • survive reverse proxies
  • be centralized
  • be successor‑friendly

4. The Final, Working Solution

4.1. Purge via the MediaWiki API

Silent purge requires:

  • POST request
  • CSRF token
  • API module

4.2. Use a preserved query parameter

MediaWiki never strips parameters beginning with mw_. We use:

?mw_purged=1

4.3. Centralize logic in MediaWiki:Common.js

One place, all dashboards.

5. The Working Code (Common.js)

$(function () {
    const title = mw.config.get('wgPageName');

    if (title && title.startsWith('Dashboard:')) {

        // Only purge once
        if (!location.search.includes('mw_purged=1')) {

            new mw.Api().get({
                action: 'query',
                meta: 'tokens',
                type: 'csrf'
            }).done(function (data) {

                const token = data.query.tokens.csrftoken;

                new mw.Api().post({
                    action: 'purge',
                    titles: title,
                    token: token
                }).always(function () {

                    // Reload the dashboard normally, marked as purged
                    const url = mw.util.getUrl(title, { mw_purged: 1 });
                    location.replace(url);
                });
            });
        }
    }
});

6. Why This Works

Silent purge

  • API POST + CSRF token
  • No confirmation dialog

No loops

  • mw_purged=1 is preserved
  • JS sees the marker and stops

Works for all dashboards

  • Uses wgPageName

Infrastructure‑safe

  • Survives reverse proxies
  • Survives canonical URL rewrites
  • Survives short URLs

Successor‑friendly

  • One central rule
  • No per‑page hacks

7. Architecture Diagram

The following ASCII diagram shows the complete flow from saving a form to the final dashboard refresh.

                +-----------------------+
                |   User saves form     |
                |   (Page Forms)        |
                +-----------+-----------+
                            |
                            v
                +-----------------------+
                | Page Forms redirect   |
                | to Dashboard:XYZ      |
                +-----------+-----------+
                            |
                            v
                +-----------------------+
                | Dashboard loads       |
                | Common.js executes    |
                +-----------+-----------+
                            |
                            | JS checks:
                            |   - Is this Dashboard:* ?
                            |   - Is mw_purged=1 absent?
                            v
                +-----------------------+
                | Request CSRF token    |
                | via API (GET)         |
                +-----------+-----------+
                            |
                            v
                +-----------------------+
                | Silent purge via API  |
                | (POST + token)        |
                +-----------+-----------+
                            |
                            v
                +-----------------------+
                | Reload dashboard with |
                | ?mw_purged=1 marker   |
                +-----------+-----------+
                            |
                            v
                +-----------------------+
                | Dashboard loads fresh |
                | JS sees mw_purged=1   |
                | -> No purge triggered |
                +-----------------------+

8. Summary

The final solution works because it respects all of MediaWiki’s constraints:

  • Purge must be done via API POST
  • CSRF token is required
  • GET purge always shows a dialog
  • Page Forms cannot trigger purge
  • Unknown query parameters may be stripped
  • Only mw_* parameters survive reliably

This produces a stable, silent, automatic purge mechanism for all dashboards.

9. Failure Mode Diagrams

The following diagrams illustrate each attempted solution, why it seemed plausible, and why it ultimately failed. These are included so future administrators can immediately understand the architectural traps and avoid repeating them.

9.1. Failure Mode 1 — HTML <meta> Refresh (Sanitized)

+---------------------------+
| Dashboard page contains   |
| <meta http-equiv="refresh"> 
+-------------+-------------+
              |
              v
+---------------------------+
| MediaWiki sanitizes HTML  |
| <meta> is escaped         |
| (displayed as text)       |
+-------------+-------------+
              |
              v
+---------------------------+
| No refresh occurs         |
| Dashboard stays stale     |
+---------------------------+

Reason for failure: MediaWiki strips or escapes <meta> tags for security. They never execute.

---

9.2. Failure Mode 2 — Redirect to ?action=purge (Confirmation Dialog)

+---------------------------+
| JS redirects to           |
| Dashboard:XYZ?action=purge
+-------------+-------------+
              |
              v
+---------------------------+
| MediaWiki shows dialog:   |
| "Do you want to purge?"   |
+-------------+-------------+
              |
              v
+---------------------------+
| User must click OK        |
| (not silent)              |
+---------------------------+

Reason for failure: GET-based purge always triggers a confirmation dialog.

---

9.3. Failure Mode 3 — JS Redirect Loop

+---------------------------+
| Dashboard loads           |
| JS sees "not purged"      |
+-------------+-------------+
              |
              v
+---------------------------+
| JS redirects to purge     |
+-------------+-------------+
              |
              v
+---------------------------+
| Purge dialog appears      |
| User clicks OK            |
+-------------+-------------+
              |
              v
+---------------------------+
| Dashboard reloads         |
| URL has no marker         |
| JS thinks "not purged"    |
+-------------+-------------+
              |
              v
+---------------------------+
| JS redirects again        |
| (infinite loop)           |
+---------------------------+

Reason for failure: The dashboard reloads without a marker, so JS purges again.

---

9.4. Failure Mode 4 — Query Parameter Stripping

+---------------------------+
| JS reloads dashboard with |
| ?purged=1                 |
+-------------+-------------+
              |
              v
+---------------------------+
| Reverse proxy / skin      |
| strips unknown parameter  |
| (purged=1 removed)        |
+-------------+-------------+
              |
              v
+---------------------------+
| Dashboard loads as        |
| Dashboard:XYZ             |
| (no marker)               |
+-------------+-------------+
              |
              v
+---------------------------+
| JS purges again           |
| (loop)                    |
+---------------------------+

Reason for failure: Unknown parameters like purged=1 are removed by canonical URL rewriting.

---

9.5. Failure Mode 5 — API Purge Without CSRF Token

+---------------------------+
| JS calls API purge        |
| without CSRF token        |
+-------------+-------------+
              |
              v
+---------------------------+
| MediaWiki rejects purge   |
| "badtoken" or "permission"|
+-------------+-------------+
              |
              v
+---------------------------+
| Dashboard reloads         |
| still stale               |
+-------------+-------------+

Reason for failure: MediaWiki requires a CSRF token for all write operations, including purge.

---

9.6. Failure Mode 6 — API Purge Works, But Loop Continues

+---------------------------+
| JS purges via API         |
| (correct)                 |
+-------------+-------------+
              |
              v
+---------------------------+
| JS reloads dashboard with |
| ?purged=1                 |
+-------------+-------------+
              |
              v
+---------------------------+
| Skin / proxy strips       |
| ?purged=1                 |
+-------------+-------------+
              |
              v
+---------------------------+
| Dashboard loads without   |
| marker                    |
+-------------+-------------+
              |
              v
+---------------------------+
| JS purges again           |
| (loop)                    |
+---------------------------+

Reason for failure: Even with correct API purge, the loop persists if the marker is stripped.

---

9.7. Final Working Architecture (for comparison)

+---------------------------+
| Dashboard loads           |
| JS executes               |
+-------------+-------------+
              |
              | JS checks:
              |   - Is Dashboard:* ?
              |   - Is mw_purged=1 absent?
              v
+---------------------------+
| Request CSRF token        |
| via API (GET)             |
+-------------+-------------+
              |
              v
+---------------------------+
| Silent purge via API      |
| (POST + token)            |
+-------------+-------------+
              |
              v
+---------------------------+
| Reload dashboard with     |
| ?mw_purged=1              |
+-------------+-------------+
              |
              v
+---------------------------+
| Dashboard loads fresh     |
| JS sees mw_purged=1       |
| -> No purge triggered     |
+---------------------------+

Reason for success: mw_purged=1 is preserved by MediaWiki, and API POST purge is silent.