ICT:Dashboard autopurge architecture: Difference between revisions
No edit summary |
No edit summary |
||
| Line 201: | Line 201: | ||
This produces a stable, silent, automatic purge mechanism for all dashboards. | 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) === | |||
<pre> | |||
+---------------------------+ | |||
| Dashboard page contains | | |||
| <meta http-equiv="refresh"> | |||
+-------------+-------------+ | |||
| | |||
v | |||
+---------------------------+ | |||
| MediaWiki sanitizes HTML | | |||
| <meta> is escaped | | |||
| (displayed as text) | | |||
+-------------+-------------+ | |||
| | |||
v | |||
+---------------------------+ | |||
| No refresh occurs | | |||
| Dashboard stays stale | | |||
+---------------------------+ | |||
</pre> | |||
'''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) === | |||
<pre> | |||
+---------------------------+ | |||
| JS redirects to | | |||
| Dashboard:XYZ?action=purge | |||
+-------------+-------------+ | |||
| | |||
v | |||
+---------------------------+ | |||
| MediaWiki shows dialog: | | |||
| "Do you want to purge?" | | |||
+-------------+-------------+ | |||
| | |||
v | |||
+---------------------------+ | |||
| User must click OK | | |||
| (not silent) | | |||
+---------------------------+ | |||
</pre> | |||
'''Reason for failure:''' | |||
GET-based purge always triggers a confirmation dialog. | |||
--- | |||
=== 9.3. Failure Mode 3 — JS Redirect Loop === | |||
<pre> | |||
+---------------------------+ | |||
| 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) | | |||
+---------------------------+ | |||
</pre> | |||
'''Reason for failure:''' | |||
The dashboard reloads without a marker, so JS purges again. | |||
--- | |||
=== 9.4. Failure Mode 4 — Query Parameter Stripping === | |||
<pre> | |||
+---------------------------+ | |||
| 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) | | |||
+---------------------------+ | |||
</pre> | |||
'''Reason for failure:''' | |||
Unknown parameters like <code>purged=1</code> are removed by canonical URL rewriting. | |||
--- | |||
=== 9.5. Failure Mode 5 — API Purge Without CSRF Token === | |||
<pre> | |||
+---------------------------+ | |||
| JS calls API purge | | |||
| without CSRF token | | |||
+-------------+-------------+ | |||
| | |||
v | |||
+---------------------------+ | |||
| MediaWiki rejects purge | | |||
| "badtoken" or "permission"| | |||
+-------------+-------------+ | |||
| | |||
v | |||
+---------------------------+ | |||
| Dashboard reloads | | |||
| still stale | | |||
+-------------+-------------+ | |||
</pre> | |||
'''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 === | |||
<pre> | |||
+---------------------------+ | |||
| 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) | | |||
+---------------------------+ | |||
</pre> | |||
'''Reason for failure:''' | |||
Even with correct API purge, the loop persists if the marker is stripped. | |||
--- | |||
=== 9.7. Final Working Architecture (for comparison) === | |||
<pre> | |||
+---------------------------+ | |||
| 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 | | |||
+---------------------------+ | |||
</pre> | |||
'''Reason for success:''' | |||
<code>mw_purged=1</code> is preserved by MediaWiki, and API POST purge is silent. | |||
Latest revision as of 21:40, 14 February 2026
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:
- Purge dialog
- Page reload
- JavaScript fires again
- 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=1is 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.