mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-08-05 02:45:28 +00:00
fix(login): render the profile's active theme on the login page
The pre-auth login page cannot call /api/prefs/theme (401) and a freshly opened device has nothing in localStorage, so it always fell back to the built-in default palette instead of the theme the user set in-app. Inject the profile's active theme into the login page at render time so it matches the in-app theme on any device, including a first visit. Scoped to single-user instances (no way to know whose theme to show before sign-in); localStorage and the default :root remain fallbacks. The injected JSON is escaped so it cannot break out of the surrounding <script>. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
d8a2059df8
commit
cf0cc4dc0f
2 changed files with 42 additions and 1 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
# src/app_helpers.py
|
# src/app_helpers.py
|
||||||
import base64
|
import base64
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
@ -9,6 +10,37 @@ from starlette.requests import Request
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _login_theme_json() -> str:
|
||||||
|
"""Active profile theme as a script-safe JSON literal for the login page.
|
||||||
|
|
||||||
|
The pre-auth login page can't fetch /api/prefs/theme (401) and a fresh
|
||||||
|
device has nothing in localStorage, so without this it always renders the
|
||||||
|
built-in default. We inject the profile's active theme so login matches
|
||||||
|
the theme set in-app. Only a single-user instance is handled — with more
|
||||||
|
than one user there's no way to know whose theme to show before sign-in,
|
||||||
|
so we return "null" and let the client fall back to localStorage/default.
|
||||||
|
Returns the string "null" (a valid JS literal) on any miss or error.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from src.constants import USER_PREFS_FILE
|
||||||
|
with open(USER_PREFS_FILE, "r", encoding="utf-8") as f:
|
||||||
|
users = (json.load(f) or {}).get("_users", {})
|
||||||
|
if len(users) != 1:
|
||||||
|
return "null"
|
||||||
|
theme = next(iter(users.values())).get("theme")
|
||||||
|
if not isinstance(theme, dict) or not theme.get("colors"):
|
||||||
|
return "null"
|
||||||
|
# Escape so the JSON can't break out of the surrounding <script>.
|
||||||
|
return (
|
||||||
|
json.dumps(theme)
|
||||||
|
.replace("<", "\\u003c")
|
||||||
|
.replace(">", "\\u003e")
|
||||||
|
.replace("&", "\\u0026")
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
return "null"
|
||||||
|
|
||||||
def read_if_exists(path: str) -> str:
|
def read_if_exists(path: str) -> str:
|
||||||
"""Read file if it exists, return empty string otherwise."""
|
"""Read file if it exists, return empty string otherwise."""
|
||||||
try:
|
try:
|
||||||
|
|
@ -46,6 +78,10 @@ def serve_html_with_nonce(request: Request, file_path: str) -> HTMLResponse:
|
||||||
raise HTTPException(500, "Internal server error")
|
raise HTTPException(500, "Internal server error")
|
||||||
nonce = getattr(request.state, "csp_nonce", "")
|
nonce = getattr(request.state, "csp_nonce", "")
|
||||||
html = html.replace("{{CSP_NONCE}}", nonce)
|
html = html.replace("{{CSP_NONCE}}", nonce)
|
||||||
|
# Only the login page carries this placeholder; skip the prefs read for
|
||||||
|
# every other template (index, backgrounds) that doesn't need it.
|
||||||
|
if "{{LOGIN_THEME}}" in html:
|
||||||
|
html = html.replace("{{LOGIN_THEME}}", _login_theme_json())
|
||||||
return HTMLResponse(html)
|
return HTMLResponse(html)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,12 @@
|
||||||
};
|
};
|
||||||
var THEME_DEFAULT_INTENSITY = { midnight:0.5, terminal:0.8, organs:0.65 };
|
var THEME_DEFAULT_INTENSITY = { midnight:0.5, terminal:0.8, organs:0.65 };
|
||||||
try {
|
try {
|
||||||
var t = JSON.parse(localStorage.getItem('odysseus-theme'));
|
// Prefer the profile's active theme injected by the server (so the login
|
||||||
|
// page matches the in-app theme on ANY device, even a first visit with an
|
||||||
|
// empty localStorage). Falls back to this device's localStorage, then to
|
||||||
|
// the default :root palette below. The server substitutes a theme object
|
||||||
|
// or the literal null in the parenthesized slot on the next line.
|
||||||
|
var t = ({{LOGIN_THEME}}) || JSON.parse(localStorage.getItem('odysseus-theme'));
|
||||||
var s = document.documentElement.style;
|
var s = document.documentElement.style;
|
||||||
if (t && t.colors) {
|
if (t && t.colors) {
|
||||||
var c = t.colors;
|
var c = t.colors;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue