mirror of
https://github.com/turnstonelabs/turnstone.git
synced 2026-08-24 04:44:47 -06:00
7968f1b361
* feat: auto-invalidate JWT and static assets on version upgrade
Add a `ver` claim (major.minor) to user-facing JWTs so tokens from
previous versions are rejected after upgrade, triggering re-login.
Service tokens are excluded for rolling-deployment safety. Tokens
without a `ver` claim (pre-upgrade) are accepted for backward compat.
Inject `?v={__version__}` query strings into static asset URLs at
startup so browsers fetch fresh JS/CSS after any release. Vendored
libraries (KaTeX, Highlight.js, etc.) are skipped since they already
carry version numbers in directory paths. HTML responses now include
`Cache-Control: no-cache` to ensure browsers always revalidate.
Frontend detects upgrade-specific 401s and shows a contextual subtitle
("The server was updated — please sign in again"), then performs a full
page reload after re-auth to load the new versioned assets.
* refactor: address PR review — public API name, single decode, idempotent regex
Rename _version_slot() → jwt_version_slot() to make the cross-module
import explicit rather than relying on a private name.
Move version gating from validate_jwt() into check_request() via a new
AuthResult.token_version field. This eliminates the double JWT decode
that occurred on version-mismatch detection — the token is now decoded
once and the version compared afterward.
Guard version_html() regex against double-apply by excluding URLs that
already contain a query string ([^"?]+ instead of [^"]+).
* feat: structured version_mismatch code, ETag, cross-tab auth sync
Add structured "code": "version_mismatch" field to the 401 response
so the frontend detects upgrade-triggered re-auth without string
matching on the error message.
Add ETag headers to HTML index responses (server, console, and proxied
node UI). Combined with Cache-Control: no-cache, browsers send
conditional GETs and receive 304 between upgrades, saving bandwidth.
Add BroadcastChannel-based cross-tab auth sync so logging in on one
tab dismisses the login modal on all other tabs (and vice-versa for
logout).
Add a reminder to the vendored JS update script about the
version_html() regex lookahead.
* fix: remove unused import in test_web_helpers
117 lines
4.3 KiB
Python
117 lines
4.3 KiB
Python
"""Tests for turnstone.core.web_helpers — version_html() cache-busting."""
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
class TestVersionHtml:
|
|
def test_app_css_gets_version(self):
|
|
from turnstone.core.web_helpers import version_html
|
|
|
|
html = '<link rel="stylesheet" href="/shared/base.css">'
|
|
result = version_html(html)
|
|
assert "?v=" in result
|
|
assert "/shared/base.css?v=" in result
|
|
|
|
def test_app_js_gets_version(self):
|
|
from turnstone.core.web_helpers import version_html
|
|
|
|
html = '<script src="/static/app.js"></script>'
|
|
result = version_html(html)
|
|
assert "/static/app.js?v=" in result
|
|
|
|
def test_shared_js_gets_version(self):
|
|
from turnstone.core.web_helpers import version_html
|
|
|
|
html = '<script src="/shared/utils.js"></script>'
|
|
result = version_html(html)
|
|
assert "/shared/utils.js?v=" in result
|
|
|
|
def test_vendored_katex_skipped(self):
|
|
from turnstone.core.web_helpers import version_html
|
|
|
|
html = '<link rel="stylesheet" href="/shared/katex-0.16.44/katex.min.css">'
|
|
result = version_html(html)
|
|
assert result == html # unchanged
|
|
|
|
def test_vendored_hljs_skipped(self):
|
|
from turnstone.core.web_helpers import version_html
|
|
|
|
html = '<script src="/shared/hljs-11.11.1/highlight.min.js"></script>'
|
|
result = version_html(html)
|
|
assert result == html # unchanged
|
|
|
|
def test_vendored_mermaid_skipped(self):
|
|
from turnstone.core.web_helpers import version_html
|
|
|
|
html = '<script src="/shared/mermaid-11.14.0/mermaid.min.js"></script>'
|
|
result = version_html(html)
|
|
assert result == html # unchanged
|
|
|
|
def test_vendored_hls_skipped(self):
|
|
from turnstone.core.web_helpers import version_html
|
|
|
|
html = '<script src="/shared/hls-1.6.15/hls.min.js"></script>'
|
|
result = version_html(html)
|
|
assert result == html # unchanged
|
|
|
|
def test_external_urls_not_modified(self):
|
|
from turnstone.core.web_helpers import version_html
|
|
|
|
html = (
|
|
'<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono" rel="stylesheet">'
|
|
)
|
|
result = version_html(html)
|
|
assert result == html # unchanged
|
|
|
|
def test_docs_link_not_modified(self):
|
|
from turnstone.core.web_helpers import version_html
|
|
|
|
html = '<a href="/docs#/System:%20Settings" target="_blank">docs</a>'
|
|
result = version_html(html)
|
|
assert result == html # unchanged
|
|
|
|
def test_multiple_tags(self):
|
|
from turnstone import __version__
|
|
from turnstone.core.web_helpers import version_html
|
|
|
|
html = (
|
|
'<link rel="stylesheet" href="/shared/base.css">\n'
|
|
'<link rel="stylesheet" href="/shared/katex-0.16.44/katex.min.css">\n'
|
|
'<link rel="stylesheet" href="/static/style.css">\n'
|
|
'<script src="/shared/utils.js"></script>\n'
|
|
'<script src="/shared/hljs-11.11.1/highlight.min.js"></script>\n'
|
|
'<script src="/static/app.js"></script>'
|
|
)
|
|
result = version_html(html)
|
|
assert f'/shared/base.css?v={__version__}"' in result
|
|
assert f'/static/style.css?v={__version__}"' in result
|
|
assert f'/shared/utils.js?v={__version__}"' in result
|
|
assert f'/static/app.js?v={__version__}"' in result
|
|
# Vendored libs unchanged
|
|
assert '/shared/katex-0.16.44/katex.min.css"' in result
|
|
assert '/shared/hljs-11.11.1/highlight.min.js"' in result
|
|
|
|
def test_version_matches_package(self):
|
|
from turnstone import __version__
|
|
from turnstone.core.web_helpers import version_html
|
|
|
|
html = '<script src="/static/app.js"></script>'
|
|
result = version_html(html)
|
|
assert f"?v={__version__}" in result
|
|
|
|
def test_double_apply_is_idempotent(self):
|
|
from turnstone.core.web_helpers import version_html
|
|
|
|
html = '<script src="/static/app.js"></script>'
|
|
once = version_html(html)
|
|
twice = version_html(once)
|
|
assert once == twice
|
|
assert twice.count("?v=") == 1
|
|
|
|
def test_existing_query_string_preserved(self):
|
|
from turnstone.core.web_helpers import version_html
|
|
|
|
html = '<script src="/static/app.js?foo=bar"></script>'
|
|
result = version_html(html)
|
|
assert result == html # unchanged — already has query string
|