Compare commits
7 Commits
9d7f2669df
...
3e3fbed470
| Author | SHA1 | Date | |
|---|---|---|---|
| 3e3fbed470 | |||
| ada9f25c41 | |||
| 66cacaaf9d | |||
| 15cd07d12f | |||
| fae57c0375 | |||
| 3745eedd74 | |||
| 1cd4fc8b40 |
@@ -652,6 +652,37 @@ templates = Jinja2Templates(directory=os.path.join(_BASE_DIR, "templates"))
|
|||||||
|
|
||||||
# ── Static asset cache-busting ────────────────────────────────────
|
# ── Static asset cache-busting ────────────────────────────────────
|
||||||
|
|
||||||
|
def _compute_asset_version() -> str:
|
||||||
|
"""Return a 16-char asset version from Nix store hash or static/template metadata."""
|
||||||
|
nix_match = re.search(r"/nix/store/([a-z0-9]{32})-", os.path.realpath(_BASE_DIR))
|
||||||
|
if nix_match:
|
||||||
|
return nix_match.group(1)[:16]
|
||||||
|
|
||||||
|
hasher = hashlib.sha256()
|
||||||
|
for root in (
|
||||||
|
os.path.join(_BASE_DIR, "static"),
|
||||||
|
os.path.join(_BASE_DIR, "templates"),
|
||||||
|
):
|
||||||
|
if not os.path.isdir(root):
|
||||||
|
continue
|
||||||
|
for dirpath, dirnames, filenames in os.walk(root):
|
||||||
|
dirnames.sort()
|
||||||
|
for filename in sorted(filenames):
|
||||||
|
path = os.path.join(dirpath, filename)
|
||||||
|
try:
|
||||||
|
stat = os.stat(path)
|
||||||
|
except OSError:
|
||||||
|
continue
|
||||||
|
hasher.update(path.encode())
|
||||||
|
hasher.update(b"\0")
|
||||||
|
hasher.update(f"{stat.st_mtime_ns}:{stat.st_size}".encode())
|
||||||
|
hasher.update(b"\0")
|
||||||
|
return hasher.hexdigest()[:16]
|
||||||
|
|
||||||
|
|
||||||
|
ASSET_VERSION = _compute_asset_version()
|
||||||
|
|
||||||
|
|
||||||
def _file_hash(filename: str) -> str:
|
def _file_hash(filename: str) -> str:
|
||||||
"""Return first 8 chars of the MD5 hex digest for a static file."""
|
"""Return first 8 chars of the MD5 hex digest for a static file."""
|
||||||
path = os.path.join(_BASE_DIR, "static", filename)
|
path = os.path.join(_BASE_DIR, "static", filename)
|
||||||
@@ -1894,7 +1925,10 @@ def _verify_support_removed() -> bool:
|
|||||||
|
|
||||||
@app.get("/login", response_class=HTMLResponse)
|
@app.get("/login", response_class=HTMLResponse)
|
||||||
async def login_page(request: Request):
|
async def login_page(request: Request):
|
||||||
return templates.TemplateResponse("login.html", {"request": request})
|
return templates.TemplateResponse("login.html", {
|
||||||
|
"request": request,
|
||||||
|
"asset_version": ASSET_VERSION,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
@app.get("/auto-login")
|
@app.get("/auto-login")
|
||||||
@@ -1961,6 +1995,7 @@ async def api_logout(request: Request):
|
|||||||
async def index(request: Request):
|
async def index(request: Request):
|
||||||
return templates.TemplateResponse("index.html", {
|
return templates.TemplateResponse("index.html", {
|
||||||
"request": request,
|
"request": request,
|
||||||
|
"asset_version": ASSET_VERSION,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@@ -1969,6 +2004,7 @@ async def onboarding(request: Request):
|
|||||||
_ensure_onboarding_reopened_for_migration()
|
_ensure_onboarding_reopened_for_migration()
|
||||||
return templates.TemplateResponse("onboarding.html", {
|
return templates.TemplateResponse("onboarding.html", {
|
||||||
"request": request,
|
"request": request,
|
||||||
|
"asset_version": ASSET_VERSION,
|
||||||
"onboarding_js_hash": _ONBOARDING_JS_HASH,
|
"onboarding_js_hash": _ONBOARDING_JS_HASH,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -4,17 +4,17 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Sovran_SystemsOS Hub</title>
|
<title>Sovran_SystemsOS Hub</title>
|
||||||
<link rel="stylesheet" href="/static/css/base.css" />
|
<link rel="stylesheet" href="/static/css/base.css?v={{ asset_version }}" />
|
||||||
<link rel="stylesheet" href="/static/css/buttons.css" />
|
<link rel="stylesheet" href="/static/css/buttons.css?v={{ asset_version }}" />
|
||||||
<link rel="stylesheet" href="/static/css/header.css" />
|
<link rel="stylesheet" href="/static/css/header.css?v={{ asset_version }}" />
|
||||||
<link rel="stylesheet" href="/static/css/layout.css" />
|
<link rel="stylesheet" href="/static/css/layout.css?v={{ asset_version }}" />
|
||||||
<link rel="stylesheet" href="/static/css/tiles.css" />
|
<link rel="stylesheet" href="/static/css/tiles.css?v={{ asset_version }}" />
|
||||||
<link rel="stylesheet" href="/static/css/modals.css" />
|
<link rel="stylesheet" href="/static/css/modals.css?v={{ asset_version }}" />
|
||||||
<link rel="stylesheet" href="/static/css/features.css" />
|
<link rel="stylesheet" href="/static/css/features.css?v={{ asset_version }}" />
|
||||||
<link rel="stylesheet" href="/static/css/onboarding.css" />
|
<link rel="stylesheet" href="/static/css/onboarding.css?v={{ asset_version }}" />
|
||||||
<link rel="stylesheet" href="/static/css/support.css" />
|
<link rel="stylesheet" href="/static/css/support.css?v={{ asset_version }}" />
|
||||||
<link rel="stylesheet" href="/static/css/domain-setup.css" />
|
<link rel="stylesheet" href="/static/css/domain-setup.css?v={{ asset_version }}" />
|
||||||
<link rel="stylesheet" href="/static/css/security.css" />
|
<link rel="stylesheet" href="/static/css/security.css?v={{ asset_version }}" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@@ -263,16 +263,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/js/constants.js"></script>
|
<script src="/static/js/constants.js?v={{ asset_version }}"></script>
|
||||||
<script src="/static/js/state.js"></script>
|
<script src="/static/js/state.js?v={{ asset_version }}"></script>
|
||||||
<script src="/static/js/helpers.js"></script>
|
<script src="/static/js/helpers.js?v={{ asset_version }}"></script>
|
||||||
<script src="/static/js/tiles.js"></script>
|
<script src="/static/js/tiles.js?v={{ asset_version }}"></script>
|
||||||
<script src="/static/js/service-detail.js"></script>
|
<script src="/static/js/service-detail.js?v={{ asset_version }}"></script>
|
||||||
<script src="/static/js/support.js"></script>
|
<script src="/static/js/support.js?v={{ asset_version }}"></script>
|
||||||
<script src="/static/js/update.js"></script>
|
<script src="/static/js/update.js?v={{ asset_version }}"></script>
|
||||||
<script src="/static/js/rebuild.js"></script>
|
<script src="/static/js/rebuild.js?v={{ asset_version }}"></script>
|
||||||
<script src="/static/js/features.js"></script>
|
<script src="/static/js/features.js?v={{ asset_version }}"></script>
|
||||||
<script src="/static/js/security.js"></script>
|
<script src="/static/js/security.js?v={{ asset_version }}"></script>
|
||||||
<script src="/static/js/events.js"></script>
|
<script src="/static/js/events.js?v={{ asset_version }}"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Sovran Hub — Login</title>
|
<title>Sovran Hub — Login</title>
|
||||||
<link rel="stylesheet" href="/static/css/base.css" />
|
<link rel="stylesheet" href="/static/css/base.css?v={{ asset_version }}" />
|
||||||
<link rel="stylesheet" href="/static/css/buttons.css" />
|
<link rel="stylesheet" href="/static/css/buttons.css?v={{ asset_version }}" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="login-wrapper">
|
<div class="login-wrapper">
|
||||||
|
|||||||
@@ -26,6 +26,13 @@
|
|||||||
nix.settings = {
|
nix.settings = {
|
||||||
experimental-features = [ "nix-command" "flakes" ];
|
experimental-features = [ "nix-command" "flakes" ];
|
||||||
download-buffer-size = 524288000;
|
download-buffer-size = 524288000;
|
||||||
|
|
||||||
|
# Network resilience for cache.nixos.org (Fastly) flakiness.
|
||||||
|
connect-timeout = 10; # fail-fast on dead TCP connects (default: 0 = unlimited)
|
||||||
|
stalled-download-timeout = 90; # default 300s; retry sooner on stalled transfers
|
||||||
|
download-attempts = 7; # default 5
|
||||||
|
http-connections = 25; # cap concurrency (helps MTU/middlebox paths)
|
||||||
|
fallback = true; # build locally if a substitute can't be fetched
|
||||||
};
|
};
|
||||||
|
|
||||||
# ── Networking ──────────────────────────────────────────────
|
# ── Networking ──────────────────────────────────────────────
|
||||||
|
|||||||
@@ -138,7 +138,11 @@ let
|
|||||||
RC=0
|
RC=0
|
||||||
|
|
||||||
echo "── Step 1/3: nix flake update ────────────────────"
|
echo "── Step 1/3: nix flake update ────────────────────"
|
||||||
if ! nix flake update --flake /etc/nixos --print-build-logs 2>&1; then
|
if ! nix flake update --flake /etc/nixos --print-build-logs \
|
||||||
|
--option connect-timeout 10 \
|
||||||
|
--option stalled-download-timeout 90 \
|
||||||
|
--option download-attempts 7 \
|
||||||
|
--option fallback true 2>&1; then
|
||||||
echo "[ERROR] nix flake update failed"
|
echo "[ERROR] nix flake update failed"
|
||||||
RC=1
|
RC=1
|
||||||
fi
|
fi
|
||||||
@@ -146,7 +150,11 @@ let
|
|||||||
|
|
||||||
if [ "$RC" -eq 0 ]; then
|
if [ "$RC" -eq 0 ]; then
|
||||||
echo "── Step 2/3: nixos-rebuild ──────────────────────────"
|
echo "── Step 2/3: nixos-rebuild ──────────────────────────"
|
||||||
SWITCH_OUT=$(nixos-rebuild switch --flake /etc/nixos --print-build-logs 2>&1)
|
SWITCH_OUT=$(nixos-rebuild switch --flake /etc/nixos --print-build-logs \
|
||||||
|
--option connect-timeout 10 \
|
||||||
|
--option stalled-download-timeout 90 \
|
||||||
|
--option download-attempts 7 \
|
||||||
|
--option fallback true 2>&1)
|
||||||
SWITCH_RC=$?
|
SWITCH_RC=$?
|
||||||
echo "$SWITCH_OUT"
|
echo "$SWITCH_OUT"
|
||||||
if [ "$SWITCH_RC" -eq 0 ]; then
|
if [ "$SWITCH_RC" -eq 0 ]; then
|
||||||
@@ -155,7 +163,11 @@ let
|
|||||||
echo ""
|
echo ""
|
||||||
echo " ✓ Build succeeded — a reboot is required to apply this update"
|
echo " ✓ Build succeeded — a reboot is required to apply this update"
|
||||||
echo " (Critical system components changed; running nixos-rebuild boot instead)"
|
echo " (Critical system components changed; running nixos-rebuild boot instead)"
|
||||||
if nixos-rebuild boot --flake /etc/nixos --print-build-logs 2>&1; then
|
if nixos-rebuild boot --flake /etc/nixos --print-build-logs \
|
||||||
|
--option connect-timeout 10 \
|
||||||
|
--option stalled-download-timeout 90 \
|
||||||
|
--option download-attempts 7 \
|
||||||
|
--option fallback true 2>&1; then
|
||||||
echo "REBOOT_REQUIRED" > "$STATUS"
|
echo "REBOOT_REQUIRED" > "$STATUS"
|
||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
@@ -209,7 +221,11 @@ let
|
|||||||
echo "══════════════════════════════════════════════════"
|
echo "══════════════════════════════════════════════════"
|
||||||
echo ""
|
echo ""
|
||||||
echo "── Rebuilding system configuration ──────────────"
|
echo "── Rebuilding system configuration ──────────────"
|
||||||
SWITCH_OUT=$(nixos-rebuild switch --flake /etc/nixos --print-build-logs 2>&1)
|
SWITCH_OUT=$(nixos-rebuild switch --flake /etc/nixos --print-build-logs \
|
||||||
|
--option connect-timeout 10 \
|
||||||
|
--option stalled-download-timeout 90 \
|
||||||
|
--option download-attempts 7 \
|
||||||
|
--option fallback true 2>&1)
|
||||||
SWITCH_RC=$?
|
SWITCH_RC=$?
|
||||||
echo "$SWITCH_OUT"
|
echo "$SWITCH_OUT"
|
||||||
if [ "$SWITCH_RC" -eq 0 ]; then
|
if [ "$SWITCH_RC" -eq 0 ]; then
|
||||||
@@ -222,7 +238,11 @@ let
|
|||||||
echo ""
|
echo ""
|
||||||
echo " ✓ Build succeeded — a reboot is required to apply this rebuild"
|
echo " ✓ Build succeeded — a reboot is required to apply this rebuild"
|
||||||
echo " (Critical system components changed; running nixos-rebuild boot instead)"
|
echo " (Critical system components changed; running nixos-rebuild boot instead)"
|
||||||
if nixos-rebuild boot --flake /etc/nixos --print-build-logs 2>&1; then
|
if nixos-rebuild boot --flake /etc/nixos --print-build-logs \
|
||||||
|
--option connect-timeout 10 \
|
||||||
|
--option stalled-download-timeout 90 \
|
||||||
|
--option download-attempts 7 \
|
||||||
|
--option fallback true 2>&1; then
|
||||||
echo "REBOOT_REQUIRED" > "$STATUS"
|
echo "REBOOT_REQUIRED" > "$STATUS"
|
||||||
else
|
else
|
||||||
echo "[ERROR] nixos-rebuild boot also failed"
|
echo "[ERROR] nixos-rebuild boot also failed"
|
||||||
|
|||||||
Reference in New Issue
Block a user