ICT:Dashboard autopurge architecture: Difference between revisions
Created page with "= 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. It is written for future administrators and maintainers of the Costa Sano Research infrastructure. == 1. The Problem == Dashboards such as '''Dashboard:Place''' display data stored in Cargo tables. After saving a form, the updated Car..." |
No edit summary |
||
| (One intermediate revision by the same user not shown) | |||
| Line 1: | Line 1: | ||
= ICT: Dashboard Auto‑Purge Architecture = | = 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 | 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 == | == 1. The Problem == | ||
| Line 14: | Line 13: | ||
* Reverse proxies may cache responses | * Reverse proxies may cache responses | ||
* Page Forms redirects do not trigger a purge | * Page Forms redirects do not trigger a purge | ||
A reliable, automatic purge mechanism is required. | A reliable, automatic purge mechanism is required. | ||
| Line 26: | Line 23: | ||
'''Conclusion:''' HTML refresh cannot be used. | '''Conclusion:''' HTML refresh cannot be used. | ||
=== 2.2. Redirecting to <code>?action=purge</code> === | === 2.2. Redirecting to <code>?action=purge</code> === | ||
A redirect to | A redirect to <code>?action=purge</code> always triggers MediaWiki’s confirmation dialog. | ||
<code>?action=purge</code> | |||
always triggers MediaWiki’s confirmation dialog | |||
This dialog cannot be suppressed via GET requests. | This dialog cannot be suppressed via GET requests. | ||
'''Conclusion:''' GET‑based purge is never silent. | '''Conclusion:''' GET‑based purge is never silent. | ||
=== 2.3. Page Forms limitations === | === 2.3. Page Forms limitations === | ||
Page Forms does not provide | 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 === | === 2.4. JavaScript redirect loops === | ||
Redirecting to <code>?action=purge</code> causes: | |||
# | # Purge dialog | ||
# | # Page reload | ||
# | # JavaScript fires again | ||
# Loop | |||
# | |||
'''Conclusion:''' Redirect‑based purge is unstable. | '''Conclusion:''' Redirect‑based purge is unstable. | ||
=== 2.5. Query parameters being stripped === | === 2.5. Query parameters being stripped === | ||
Reverse proxies, skins, and canonical URL rewrites may strip unknown parameters such as | Reverse proxies, skins, and canonical URL rewrites may strip unknown parameters such as <code>?purged=1</code>. | ||
This prevents loop‑detection. | |||
<code>?purged=1</code> | |||
This | |||
'''Conclusion:''' Only parameters MediaWiki preserves can be used | '''Conclusion:''' Only parameters MediaWiki preserves can be used. | ||
== 3. Requirements for a Working Solution == | == 3. Requirements for a Working Solution == | ||
| Line 84: | Line 56: | ||
A correct solution must: | A correct solution must: | ||
* purge silently | * purge silently | ||
* avoid loops | * avoid loops | ||
* work for all dashboards | * work for all dashboards | ||
* survive reverse proxies | * survive reverse proxies | ||
* be centralized | * be centralized | ||
* be successor‑friendly | * be successor‑friendly | ||
== 4. The Final, Working Solution == | == 4. The Final, Working Solution == | ||
=== 4.1. Purge via the MediaWiki API === | === 4.1. Purge via the MediaWiki API === | ||
Silent purge requires: | |||
* POST request | |||
* CSRF token | |||
* API module | |||
=== 4.2. Use a preserved query parameter === | === 4.2. Use a preserved query parameter === | ||
| Line 111: | Line 77: | ||
<code>?mw_purged=1</code> | <code>?mw_purged=1</code> | ||
=== 4.3. Centralize logic in <code>MediaWiki:Common.js</code> === | === 4.3. Centralize logic in <code>MediaWiki:Common.js</code> === | ||
One place, all dashboards. | |||
== 5. The Working Code (Common.js) == | == 5. The Working Code (Common.js) == | ||
| Line 163: | Line 121: | ||
* API POST + CSRF token | * API POST + CSRF token | ||
* No confirmation dialog | * No confirmation dialog | ||
=== No loops === | === No loops === | ||
* <code>mw_purged=1</code> is preserved | * <code>mw_purged=1</code> is preserved | ||
* JS sees the marker and | * JS sees the marker and stops | ||
=== Works for all dashboards === | === Works for all dashboards === | ||
* Uses <code>wgPageName</code> | * Uses <code>wgPageName</code> | ||
=== Infrastructure‑safe === | === Infrastructure‑safe === | ||
| Line 177: | Line 133: | ||
* Survives canonical URL rewrites | * Survives canonical URL rewrites | ||
* Survives short URLs | * Survives short URLs | ||
=== Successor‑friendly === | === Successor‑friendly === | ||
* One central rule | * One central rule | ||
* No per‑page | * No per‑page hacks | ||
* | |||
== 7. Architecture Diagram == | |||
The following ASCII diagram shows the complete flow from saving a form to the final dashboard refresh. | |||
<pre> | |||
+-----------------------+ | |||
| 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 | | |||
+-----------------------+ | |||
</pre> | |||
== | == 8. Summary == | ||
The final solution works because it respects all of MediaWiki’s constraints: | The final solution works because it respects all of MediaWiki’s constraints: | ||
| Line 195: | Line 200: | ||
* Only <code>mw_*</code> parameters survive reliably | * Only <code>mw_*</code> 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) === | |||
<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.